Для сложения числа с флагом переноса применяется инструкция ADC. Рассмотрим ситуацию, где она может использоваться. Например, нам надо эмулировать 128-разрядные числа. Однин 64-разрядный регистр может хранить младшие или старшие 64 бита подобного числа. Соответственно для хранения двух 128-разрядных чисел применяется 4 регистра.
Для сложения 128-битных чисел можно использовать комбинацию инструкций ADDS и ADC:
ADDS X1, X3, X5 // складываем младшие 64 бита ADC X0, X2, X4 // складываем старшие 64 бита с учетном флага переноса
Здесь регистры X2 и X3 хранят первое число , а X4 и X5 - второе. А результат сложения помещается в регистры X0 и X1. Инструкция ADDS складывает младшие 64 биты чисел и устанавливает флаг переноса C. Вторая инструкция - ADC складывает старшие биты чисел и флаг переноса.
Посмотрим на конкретном примере:
// пример сложения 128-х разрядных чисел с помощью ADD/ADC .global _start // первое 128-разрядное число - 0x0000000000000003FFFFFFFFFFFFFFFF _start: MOV X2, #0x0000000000000003 MOV X3, #0xFFFFFFFFFFFFFFFF // второе 128-разрядное число - 0x00000000000000050000000000000001 MOV X4, #0x0000000000000005 MOV X5, #0x0000000000000001 ADDS X1, X3, X5 // складываем младшие 64 бита ADC X0, X2, X4 // складываем старшие 64 бита MOV X8, #93 // номер функции Linux для выхода из программы - 93 SVC 0 // Вызываем функцию и выходим из программы
В данном случае мы получим следующую ситуацию:
0000000000000003 FFFFFFFFFFFFFFFF + 0000000000000005 0000000000000001 = 0000000000000009 0000000000000000
Сложение по шагам:
Сначала выполняется сложение младших битов:
0xFFFFFFFFFFFFFFFF + 0x0000000000000001
Формально результат будет равен
0x1 0000 0000 0000 0000
Но это значение не помещается в 64-разрядный регистр, поэтому устанавливается флаг переноса. А в регистре X1 в реальности будет число 0x0000 0000 0000 0000
, то есть 0.
Далее выполняется сложение старших битов, к которым добавляет 1 бит из флага переноса:
0x0000000000000003 + 0x0000000000000005 + 1
В итоге в регистре X0 будет
0x0000000000000009
А все 128-разрядное число будет равно
0x0000000000000009 0000000000000000
Подобным образом мы можем складывать числа и большей разрядности, например, 192-разрядные числа:
// пример сложения 192-х разрядных чисел с помощью ADD/ADC .global _start // первое 192-х разрядное число - 0x0000000000000005 FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF _start: MOV X3, #0x0000000000000005 MOV X4, #0xFFFFFFFFFFFFFFFF MOV X5, #0xFFFFFFFFFFFFFFFF // второе 192-х разрядное число - 0x0000000000000006 0000000000000002 0000000000000001 MOV X6, #0x0000000000000006 MOV X7, #0x0000000000000002 MOV X8, #0x0000000000000001 ADDS X2, X5, X8 // складываем младшие 64 бита ADC X1, X4, X7 // складываем средние 64 бита ADC X0, X3, X6 // складываем старшие 64 бита MOV X8, #93 // номер функции Linux для выхода из программы - 93 SVC 0 // Вызываем функцию и выходим из программы
Результат равен
0x0000000000000005 FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF + 0x0000000000000006 0000000000000002 0000000000000001 = 0x0000000000000012 0000000000000002 0000000000000000