Подтвердить что ты не робот

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

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

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

4b9b3361

Ответ 1

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

Ответ 2

Поведение ADC можно моделировать как на 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;

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

Ответ 3

Из здесь (сломан) или здесь

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

add  ax, cx
adc  dx, bx

Ah, так что сначала нижний 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
   }
}

Ответ 4

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

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]);

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

Ответ 5

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

См. также tag wiki для документации по сборке.


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

#include <stdint.h>
#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 explorer компилятора. clang -fverbose-asm не очень вегетативна, но gcc 5.3/6.1 тратит две инструкции mov, поэтому она менее читаема.

Ответ 6

unsigned long result;
unsigned int first;
unsigned int second;

result = first + second;
result += (result & 0x10000) ? 1 : 0;
result &= 0xFFFF