Ассемблер ADC (добавить с переносом) в C ++
Существует инструкция по сборке ADC
. Я нашел, что это означает «Добавить с переносом». Но я не знаю, что это значит. Или как написать эту инструкцию на C ++. И я знаю, что это не то же самое, что ADD
. Поэтому сделать простое суммирование неверно.
ИНФОРМАЦИЯ:
Составлено в Windows. Я использую 32-разрядную установку Windows. Мой процессор Core 2 Duo от Intel.
- Что делает NOPL в системе x86?
- Улучшенный REP MOVSB для memcpy
- Векторизация с неуравновешенными буферами: использование VMASKMOVPS: создание маски из подсчета несоосности? Или не использовать этот insn вообще
- Возможно ли, чтобы процессор x86 соответствовал процессору ARM с точки зрения производительности на ватт?
- 8086 на DOSBox: ошибка с инструкцией idiv?
- Заказ локального распределения переменных в стеке
- Какова цель XORing регистрации с собой?
- Может ли x86 переупорядочить узкий магазин с более широкой нагрузкой, которая полностью его содержит?
- Почему нарушение «выходной зависимости» LZCNT имеет значение?
- Можно ли сообщить предсказателю ветви, насколько вероятно, что он должен следовать за веткой?
- Какова цель инструкции «ПАУЗА» в x86?
- Сборка скомпилированного исполняемого файла на Bash на Ubuntu в Windows не производит вывод
- Почему медленная инструкция цикла? Не удалось ли 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