registers register language assembly x86

assembly - register - x86 montaje abs() implementación?



registers assembly x86 (8)

Necesito obtener la diferencia de 2 enteros con signo. ¿Existe una función ABS () en lenguaje ensamblador x86, así que puedo hacer esto? Cualquier ayuda sería muy apreciada.


ABS (EAX)

test eax, eax ; Triger EFLAGS [CF, OF, PF, SF, and ZF] jns AbsResult ; If (SF) is off, jmp AbsResult neg eax ; If (SF) is on. (negation nullify by this opcode) AbsResult:

Si los indicadores ya están establecidos por lo que haya generado el valor en eax, no necesita la test . Las predicciones erróneas de la rama harán esto lento si los valores de entrada se distribuyen aleatoriamente entre positivo y negativo.

Esto funciona de la misma manera para RAX, AX, AL.


Ahí está la instrucción SUB, si lo que quieres es hacer AB. HTH


Hilo viejo, pero si he navegado aquí tarde, es posible que también tengas ... abs es un ejemplo brillante, así que esto debería estar aquí.

; abs(eax), with no branches. ; intel syntax (dest, src) mov ebx, eax ;store eax in ebx neg eax cmovl eax, ebx ;if eax is now negative, restore its saved value


Si desea manejar todos los casos correctamente, no puede simplemente restar y luego tomar el valor absoluto. Tendrá problemas porque la diferencia de dos enteros con signo no es necesariamente representable como un entero con signo. Por ejemplo, supongamos que está utilizando enteros del complemento de 32 bits 2s y desea encontrar la diferencia entre INT_MAX ( 0x7fffffff ) y INT_MIN ( 0x80000000 ). La resta da:

0x7fffffff - 0x80000000 = 0xffffffff

que es -1 ; cuando toma el valor absoluto, el resultado que obtiene es 1 , mientras que la diferencia real entre los dos números es 0xffffffff interpretada como un entero sin signo ( UINT_MAX ).

La diferencia entre dos enteros con signo siempre se puede representar como un entero sin signo. Para obtener este valor (con hardware de complemento 2s), simplemente reste la entrada más pequeña de la más grande e interprete el resultado como un entero sin signo; No hay necesidad de un valor absoluto.

Aquí hay una (de muchas, y no necesariamente la mejor) manera de hacer esto en x86, asumiendo que los dos enteros están en eax y edx :

cmp eax, edx // compare the two numbers jge 1f xchg eax, edx // if eax < edx, swap them so the bigger number is in eax 1: sub eax, edx // subtract to get the difference


Si es un ensamblaje x86, lo siguiente de acuerdo con la wikipedia siempre útil debería funcionar. Reste un valor de otro y luego use estas instrucciones en el resultado:

cdq xor eax, edx sub eax, edx


Suponiendo que sus enteros están en registros MMX o XMM, use psubd para calcular la diferencia, luego pabsd para obtener el valor absoluto de la diferencia.

Si sus enteros están en el plano, los registros "normales", luego hacen una resta, luego el truco de cdq para obtener el valor absoluto. Esto requiere el uso de algunos registros específicos ( cdq sign-extiende eax en edx , no utilizando ningún otro registro), por lo que es posible que desee hacer cosas con otros códigos de operación. P.ej:

mov r2, r1 sar r2, 31

calcula en el registro r2 la extensión de signo de r1 (0 si r1 es positivo o cero, 0xFFFFFFFF si r1 es negativo). Esto funciona para todos los registros de 32 bits r1 y r2 y reemplaza la instrucción cdq .


Una forma corta pero directa, usando la instrucción de movimiento condicional (Pentium disponible y superior, creo):

; compute ABS(r1-r2) in eax, overwrites r2 mov eax, r1 sub eax, r2 sub r2, r1 cmovg eax, r2

La instrucción secundaria establece los indicadores de la misma manera que la instrucción cmp.


Así es como la función de biblioteca C abs() hace en ensamblado sin bifurcar:

abs(x) = (x XOR y) - y

donde y = x >>> 31 (suponiendo una entrada de 32 bits), y >>> es un operador aritmético de desplazamiento a la derecha.

Explicación de la fórmula anterior: Queremos generar el complemento de 2 de x negativo solamente.

y = 0xFFFF, if x is negative 0x0000, if x is positive

Entonces cuando x es positivo x XOR 0x0000 es igual a x . Y cuando x es negativo, x XOR 0xFFFF es igual al complemento de 1 de x . Ahora solo necesitamos agregar 1 para obtener el complemento de 2, que es lo que está haciendo la expresión -y . Porque 0xFFFF es -1 en decimal.

Veamos el ensamblado generado para el siguiente código por gcc (4.6.3 en mi máquina):

Código C:

main() { int x; int output = abs(x); }

gcc 4.6.3 generó un fragmento de ensamblaje (sintaxis de AT&T), con mis comentarios:

movl -8(%rbp), %eax # -8(%rbp) is memory for x on stack sarl $31, %eax # shift arithmetic right: x >>> 31, eax now represents y movl %eax, %edx # xorl -8(%rbp), %edx # %edx = x XOR y movl %edx, -4(%rbp) # -4(%rbp) is memory for output on stack subl %eax, -4(%rbp) # (x XOR y) - y

BONIFICACIÓN (de Hacker''s Delight ): si tienes una multiplicación rápida por +1 y -1, lo siguiente te dará abs(x) :

((x >>> 30) | 1) * x