2010-11-11 6 views
12

Hay una instrucción de montaje ADC. He encontrado que esto significa "Agregar con llevar". Pero no sé qué significa que 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.Asamblea ADC (Agregar con acarreo) a C++

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

+1

cual de los procesadores? – Chubsdad

+0

@Chubsdad: hice todo lo posible para agregar algo de información. Espero que sea suficiente –

+2

¿Qué compilador? Para acceder al indicador de acarreo, debe incrustar el código de ensamblador en su código de C++. Cómo lo hace depende del compilador que está utilizando. – TonyK

Respuesta

18

ADC es lo mismo que AGREGAR, pero agrega 1 adicional si se establece el indicador de transporte del procesador.

+0

¡Está bien! Gracias. Pero ahora, tengo que saber SI la bandera está configurada. ¿Es esto posible en C++? –

+1

No con C++ estándar, debe usar un bloque de código "asm". No recuerdo la sintaxis exacta, pero perderá la portabilidad del código. – Simone

+2

La idea de ADC no es conocer el indicador de acarreo, sino hacer un ADD antes del ADC, por lo que el acarreo se establecerá cuando el ADD se desborde – stefaanv

6

De here (roto) o here

Sin embargo, procesador Intel tiene una instrucción especial llamado 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 números enteros grandes. Supongamos que desea para agregar un número entero de 32 bits con registros de 16 bits . ¿Cómo podemos hacer eso? Bueno, digamos que el primer entero es retenido en el par de registros DX: AX, y el segundo está en BX: CX. Esta es la forma en :

add ax, cx 
adc dx, bx 

Ah, por lo que en primer lugar, el menor de 16 bits se añade por hacha complemento, cx. Luego, el valor más alto de 16 bits es agregado usando adc en lugar de agregar. Es porque: si hay desbordamientos, el bit de acarreo se agrega automáticamente en el más alto de 16 bits. Por lo tanto, no engorroso comprobación. Este método se puede ampliar a 64 bits y así sucesivamente ... Tenga en cuenta que: Si la suma de 32 bits también desborda en el valor más alto de 16 bits, el resultado no será correcto y se configurará el indicador de transporte , p.ej Agregar 5 mil millones a 5 mil millones.

Todo a partir de ahora, recuerde que se trata más o menos de la zona de implementación definida.

He aquí una pequeña muestra de que funcione para VS 2010 (32 bits, WinXP)

Advertencia:. $ 7.4/1- "es apoyado condicionalmente-La declaración de la AMS; su significado es definido por la implementación [Nota: Por lo general que se utiliza para pasar información a través de la implementación de un ensamblador. -fin nota]"

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

No puedo bloquear citar la parte "Advertencia ..." en mi respuesta. Algunas veces este formateo simplemente no se comporta bien. – Chubsdad

+0

0xffffffff es -1 o UINT_MAX, que se está almacenando en un int. Quizás 'x' debería ser unsigned int, o los sumandos deberían ser INT_MAX (0x7fffffff). Si tomamos los sumandos para que sean del mismo tipo que el resultado (es decir, un entero con signo), entonces el indicador OVERFLOW no se establece; el resultado es -2 (0xfffffffe). – jww

+0

Ese código es ridículo; ** no puede depender de que 'CF' esté configurado o no a partir de una instrucción C fuera del bloque' asm' **. Puede suceder que funcione en modo de depuración, pero eso no será útil con la optimización habilitada. Además, use 'setc carry' para establecer carry en 0 o 1, de acuerdo con' CF'. –

7

el comportamiento 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.

+5

Este código no establecerá el acarreo si 'segundo == ~ 0U && carry == 1'. por ejemplo, con 32 bits sin signo que serían 'segundo [i] == 0xFFFFFFFF && carry == 1'. En este caso 'primero [i] == resultado [i]' aunque haya ocurrido un desbordamiento (acarreo). –

+1

El código de trabajo es 'sin signo tmp = second [i] + carry; resultado [i] = primero [i] + tmp; carry = (primer [i]> resultado [i]) | (segundo [i]> tmp); ' –

+0

Y el hecho de que esto es extremadamente lento es el motivo por el que está escrito en ensamble en este momento. – Joshua

4

Hay un error en esto.Prueba esta entrada:

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

El resultado debe ser {0, 0, 1, ...}, pero el resultado es {0, 0, 0, ...}

Cambiar esta línea:

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

a esto:

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

arregla.

+2

'carry = (carry && first [i]> = result [i]) || (! Carry && first [i]> result [i])' evita la bifurcación y hace lo mismo, si alguien está interesado. – Hassedev

+2

En realidad '||' y '&&' causa ramificación, ya que solo evalúan el lado derecho según sea necesario. Hay más ramificaciones en el trazador de líneas que con la declaración if() fácil de leer. –

3

El lenguaje C++ no tiene ningún concepto de una marca de transporte, por lo que hacer una envoltura de función intrínseca alrededor de la ADC instruction 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 para la documentación de ensamblaje.


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

#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 salida del Godbolt compiler explorer. No es muy verosímil -fverbose-asm de clang, pero gcc 5.3/6.1 desperdicia dos instrucciones mov por lo que es menos legible.

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

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