Ассемблер ADC (добавить с переносом) в C ++

Существует инструкция по сборке ADC . Я нашел, что это означает «Добавить с переносом». Но я не знаю, что это значит. Или как написать эту инструкцию на C ++. И я знаю, что это не то же самое, что ADD . Поэтому сделать простое суммирование неверно.

ИНФОРМАЦИЯ:
Составлено в Windows. Я использую 32-разрядную установку Windows. Мой процессор Core 2 Duo от Intel.

ADC совпадает с ADD, но добавляет дополнительный 1, если установлен флаг переноса процессора.

Отсюда (сломанный) или здесь

Однако процессор Intel имеет специальную инструкцию под названием adc. Эта команда ведет себя аналогично команде add. Единственное, что может быть связано с добавлением флага переноса ценности. Таким образом, это может быть очень удобно для добавления больших целых чисел. Предположим, вы хотели бы добавить 32-битные целые числа с 16-разрядными регистрами. Как мы можем сделать это? Ну, допустим, первое целое удерживается в паре регистров DX: AX, а вторая – на BX: CX. Вот как это сделать:

 add ax, cx adc dx, bx 

Ах, так во-первых, нижний 16-бит добавляется добавлением ax, cx. Тогда более высокий 16-бит добавляется с помощью adc вместо add. Это происходит потому, что: если есть переполнения, бит переноса автоматически добавляется в более высокий 16-бит. Таким образом, нет громоздкой проверки. Этот метод может быть расширен до 64 бит и т. Д. Обратите внимание: если 32-разрядное целочисленное добавление переполняется слишком большим 16-битным, результат будет неправильным и флаг переноса будет установлен, например, добавление 5 миллиардов до 5 миллиардов.

Все отсюда, помните, что он в значительной степени попадает в зону реализации определенного поведения.

Вот небольшой пример, который работает для VS 2010 (32-разрядный, WinXp)

Предостережение: $ 7.4 / 1- «Объявление asm условно поддерживается, его значение определяется реализацией. [Примечание: обычно используется для передачи информации через реализацию ассемблеру. -End note]

 int main(){ bool carry = false; int x = 0xffffffff + 0xffffffff; __asm { jc setcarry setcarry: mov carry, 1 } } 

Поведение ADC можно моделировать как на C, так и на C ++. В следующем примере добавляются два числа (хранятся в виде массивов без знака, поскольку они слишком велики, чтобы вписаться в одно беззнаковое).

 unsigned first[10]; unsigned second[10]; unsigned result[11]; .... /* first and second get defined */ unsigned carry = 0; for (i = 0; i < 10; i++) { result[i] = first[i] + second[i] + carry; carry = (first[i] > result[i]); } result[10] = carry; 

Надеюсь это поможет.

В этом есть ошибка. Попробуйте этот вход:

 unsigned first[10] = {0x00000001}; unsigned second[10] = {0xffffffff, 0xffffffff}; 

Результат должен быть {0, 0, 1, …}, но результат равен {0, 0, 0, …}

Изменение этой строки:

 carry = (first[i] > result[i]); 

к этому:

 if (carry) carry = (first[i] >= result[i]); else carry = (first[i] > result[i]); 

исправляет его.

Язык C ++ не имеет понятия флага переноса, поэтому создание встроенной оболочки функций вокруг инструкции ADC неудобно. Однако Intel все равно: unsigned char _addcarry_u32 (unsigned char c_in, unsigned a, unsigned b, unsigned * out); , Последнее, что я проверил, gcc плохо справился с этим (сохранение результата переноса в целочисленный регистр вместо того, чтобы оставить его в CF), но, надеюсь, собственный компилятор Intel делает лучше.

См. Также вики-файлы x86 для документации по сборке.


Компилятор будет использовать ADC для вас при добавлении целых чисел, превышающих один регистр, например, добавление int64_t в 32- int64_t код или __int128_t в 64- __int128_t коде.

 #include  #ifdef __x86_64__ __int128_t add128(__int128_t a, __int128_t b) { return a+b; } #endif # clang 3.8 -O3 for x86-64, SystemV ABI. # __int128_t args passed in 2 regs each, and returned in rdx:rax add rdi, rdx adc rsi, rcx mov rax, rdi mov rdx, rsi ret 

asm вывод из проводника компилятора Godbolt . clang’s -fverbose-asm не очень вегетативный, но gcc 5.3 / 6.1 тратит две команды mov поэтому он менее читабельен.

 unsigned long result; unsigned int first; unsigned int second; result = first + second; result += (result & 0x10000) ? 1 : 0; result &= 0xFFFF 
  • Возможно ли запустить двоичный файл x86 на процессоре ARM?
  • Как выглядит многоярусный язык ассемблера?
  • Разница в производительности между MSVC и GCC для высоко оптимизированного матричного кода
  • Относительная производительность команды x86 inc vs. add
  • В чем смысл MOV (% r11,% r12,1),% edx?
  • Что означает «int 0x80» в коде сборки?
  • Самый быстрый способ вычисления абсолютного значения с помощью SSE
  • Оптимизация для быстрого умножения, но медленное добавление: FMA и doubleedouble
  • `testl` eax против eax?
  • Использование LEA для значений, которые не являются адресами / указателями?
  • Печать шестнадцатеричных цифр со сборкой
  • Давайте будем гением компьютера.