c++ assembly

Asamblea ADC(Agregar con acarreo) a C++



assembly (6)

Hay una instrucción de ensamblaje ADC . He encontrado que esto significa "Agregar con llevar". Pero no sé lo que eso significa. O cómo escribir esta instrucción en C ++. Y sé que no es lo mismo que ADD . Entonces hacer una suma simple no es correcto.

INFO:
Compilado en Windows. Estoy usando una instalación de Windows de 32 bits. Mi procesador es Core 2 Duo de Intel.


ADC es lo mismo que ADD, pero agrega un extra de 1 si se establece el indicador de acarreo del procesador.


El comportamiento de ADC se puede simular en C y C ++. El siguiente ejemplo agrega dos números (almacenados como matrices de unsigned ya que son demasiado grandes para caber en un solo unsigned).

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;

Espero que esto ayude.


Hay un error en esto. Pruebe esta entrada:

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

El resultado debería ser {0, 0, 1, ...} pero el resultado es {0, 0, 0, ...}

Cambiando esta línea:

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

a esto:

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

lo arregla


Desde aquí (roto) o aquí

Sin embargo, el procesador Intel tiene una instrucción especial llamada adc. Este comando se comporta de forma similar al comando add. Lo único adicional es que también agrega el indicador de llevar valor a lo largo. Por lo tanto, esto puede ser muy útil para agregar enteros grandes. Supongamos que desea agregar un entero de 32 bits con registros de 16 bits. ¿Cómo podemos hacer eso? Bueno, digamos que el primer entero se mantiene en el par de registros DX: AX, y el segundo en BX: CX. Así es como:

add ax, cx adc dx, bx

Ah, entonces primero, los 16 bits más bajos se agregan mediante add ax, cx. Luego, se agrega el valor más alto de 16 bits usando adc en lugar de agregar. Es porque: si hay desbordamientos, el bit de acarreo se agrega automáticamente en los 16 bits superiores. Por lo tanto, no hay control incómodo. Este método puede ampliarse a 64 bits y así sucesivamente ... Tenga en cuenta que: si la suma entera de 32 bits también se desborda en los 16 bits superiores, el resultado no será correcto y se establecerá la marca de transporte, por ejemplo, sumando 5 mil millones a 5 mil millones.

Todo a partir de ahora, recuerde que cae bastante en la zona de comportamiento definido de implementación.

Aquí hay una pequeña muestra que funciona para VS 2010 (32 bits, WinXp)

Advertencia: $ 7.4 / 1- "La declaración asm tiene soporte condicional, su significado está definido por la implementación. [Nota: por lo general se usa para pasar información a través de la implementación a un ensamblador. -finalización]"

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


El lenguaje C ++ no tiene ningún concepto de indicador de acarreo, por lo que crear un contenedor de funciones intrínsecas en torno a la instrucción ADC es torpe. Sin embargo, Intel lo hizo de todos modos: unsigned char _addcarry_u32 (unsigned char c_in, unsigned a, unsigned b, unsigned * out); . La última vez que lo verifiqué, gcc hizo un mal trabajo con esto (guardando el resultado de acarreo en un registro entero, en lugar de dejarlo en CF), pero espero que el propio compilador de Intel lo haga mejor.

Consulte también la wiki de la etiqueta x86 para ver la documentación de ensamblaje.

El compilador utilizará ADC para usted al agregar enteros más amplios que un solo registro, por ejemplo, agregando int64_t en código de 32 bits, o __int128_t en código de 64 bits.

#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

salida asm del explorador del compilador Godbolt . El -fverbose-asm clang no es muy verosímil, pero gcc 5.3 / 6.1 desperdicia dos instrucciones mov por lo que es menos legible.


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