Сложение со знаком переноса. ADC

Последнее обновление: 16.07.2022

Для сложения числа с флагом переноса применяется инструкция 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

Сложение по шагам:

  1. Сначала выполняется сложение младших битов:

    0xFFFFFFFFFFFFFFFF + 0x0000000000000001
    

    Формально результат будет равен

    0x1 0000 0000 0000 0000
    

    Но это значение не помещается в 64-разрядный регистр, поэтому устанавливается флаг переноса. А в регистре X1 в реальности будет число 0x0000 0000 0000 0000, то есть 0.

  2. Далее выполняется сложение старших битов, к которым добавляет 1 бит из флага переноса:

    0x0000000000000003 + 0x0000000000000005 + 1
    

    В итоге в регистре X0 будет

    0x0000000000000009
    

    А все 128-разрядное число будет равно

    0x0000000000000009 0000000000000000
    

Сложение 192-разрядных чисел

Подобным образом мы можем складывать числа и большей разрядности, например, 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
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850