¿Cómo puedo multiplicar dos números de 64 bits usando lenguaje ensamblador x86?

StackOverflow https://stackoverflow.com/questions/87771

  •  01-07-2019
  •  | 
  •  

Pregunta

¿Cómo haría...?

  • multiplicar dos números de 64 bits

  • multiplicar dos números hexadecimales de 16 dígitos

...usando lenguaje ensamblador.

Solo puedo usar los registros %eax, %ebx, %ecx, %edx y la pila.

EDITAR:Oh, estoy usando la sintaxis ATT en el x86
EDITAR2:No se permite descompilar en ensamblador...

¿Fue útil?

Solución

Utilice lo que probablemente debería ser el libro de texto de su curso, "El arte del lenguaje ensamblador" de Randall Hyde.

Ver 4.2.4 - Multiplicación de precisión extendida

Aunque una multiplicación de 8x8, 16x16 o 32x32 suele ser suficiente, hay ocasiones en las que es posible que desees multiplicar valores más grandes.Utilizará las instrucciones MUL e IMUL de operando único x86 para una multiplicación de precisión extendida.

Probablemente lo más importante para recordar. al realizar una multiplicación de precisión extendida es que también debes realizar una suma de precisión múltiple al mismo tiempo.Sumar todos los productos parciales requiere varias sumas que producirán el resultado.La siguiente lista demuestra la forma correcta de multiplicar dos valores de 64 bits en un procesador de 32 bits.

(Consulte el enlace para obtener una lista completa del ensamblaje e ilustraciones).

Otros consejos

Si esto fuera 64x86,

function(x, y, *lower, *higher)
movq %rx,%rax     #Store x into %rax
mulq %y           #multiplies %y to %rax
#mulq stores high and low values into rax and rdx.
movq %rax,(%r8)   #Move low into &lower
movq %rdx,(%r9)   #Move high answer into &higher

Como estás en x86, necesitas 4 instrucciones múltiples.Divida las cantidades de 64 bits en dos palabras de 32 bits y multiplique las palabras bajas a la palabra más baja y a la segunda más baja del resultado, luego ambos pares de palabras bajas y altas de diferentes números (van a la segunda y tercera palabra más bajas del resultado) y finalmente ambas palabras altas en las 2 palabras más altas del resultado.Agrégalos todos juntos sin olvidarte de ocuparte del transporte.No especificó el diseño de la memoria de las entradas y salidas, por lo que es imposible escribir código de muestra.

Este código supone que desea x86 (no código x64), que probablemente solo desee un producto de 64 bits y que no le importa el desbordamiento o los números con signo.(Una versión firmada es similar).

MUL64_MEMORY:
     mov edi, val1high
     mov esi, val1low
     mov ecx, val2high
     mov ebx, val2low
MUL64_EDIESI_ECXEBX:
     mov eax, edi
     mul ebx
     xch eax, ebx  ; partial product top 32 bits
     mul esi
     xch esi, eax ; partial product lower 32 bits
     add ebx, edx
     mul ecx
     add ebx, eax  ; final upper 32 bits
; answer here in EBX:ESI

Esto no respeta las restricciones de registro exactas de OP, pero el resultado encaja completamente en los registros ofrecidos por x86.(Este código no ha sido probado, pero creo que es correcto).

[Nota:Transferí (mi) esta respuesta de otra pregunta que se cerró, porque NINGUNA de las otras "respuestas" aquí respondió directamente a la pregunta].

Depende del idioma que estés usando.Por lo que recuerdo de aprender a ensamblar MIPS, hay un comando Mover desde alto y un comando Mover desde bajo, o mflo y mfhi.mfhi almacena los 64 bits superiores mientras que mflo almacena los 64 bits inferiores del número total.

Ah, ensamblaje, ha pasado un tiempo desde que lo usé.Entonces, supongo que el verdadero problema aquí es que el microcontrolador (para el que solía escribir código en ensamblaje) en el que estás trabajando no tiene registros de 64 bits.Si ese es el caso, tendrás que separar los números con los que estás trabajando y realizar múltiples multiplicaciones con las piezas.

Esto suena como si fuera una tarea por la forma en que lo has redactado, así que no lo explicaré mucho más :P

Simplemente haga una multiplicación larga normal, como si estuviera multiplicando un par de números de 2 dígitos, excepto que cada "dígito" es en realidad un entero de 32 bits.Si multiplicas dos números en las direcciones X e Y y almacenas el resultado en Z, entonces lo que quieres hacer (en pseudocódigo) es:

Z[0..3] = X[0..3] * Y[0..3]
Z[4..7] = X[0..3] * Y[4..7] + X[4..7] * Y[0..3]

Tenga en cuenta que estamos descartando los 64 bits superiores del resultado (ya que un número de 64 bits multiplicado por un número de 64 bits es un número de 128 bits).También tenga en cuenta que esto supone little-endian.Además, tenga cuidado con una multiplicación con signo o sin signo.

Encuentre un compilador de C que admita 64 bits (GCC hace IIRC), compile un programa que haga precisamente eso y luego realice el desmontaje.GCC puede escupirlo por sí solo y usted puede sacarlo del archivo objeto con las herramientas adecuadas.

OTOH, es una operación 32bX32b = 64b en x86

a:b * c:d = e:f
// goes to
e:f = b*d;
x:y = a*d;  e += x;
x:y = b*c;  e += x;

todo lo demás se desborda

(no probado)

Editar Sólo sin firmar

Apuesto a que eres estudiante, así que mira si puedes hacer que esto funcione:Hágalo palabra por palabra y utilice cambios de bits.Piense en la solución más eficiente.Cuidado con el bit de señal.

Si quieres el modo 128, prueba esto...

__uint128_t AES::XMULTX(__uint128_t TA,__uint128_t TB)
{
    union
    {
        __uint128_t WHOLE;
        struct
        {
            unsigned long long int LWORDS[2];
        } SPLIT;
    } KEY;
    register unsigned long long int __XRBX,__XRCX,__XRSI,__XRDI;
    __uint128_t RESULT;

    KEY.WHOLE=TA;
    __XRSI=KEY.SPLIT.LWORDS[0];
    __XRDI=KEY.SPLIT.LWORDS[1];
    KEY.WHOLE=TB;
    __XRBX=KEY.SPLIT.LWORDS[0];
    __XRCX=KEY.SPLIT.LWORDS[1];
    __asm__ __volatile__(
                 "movq          %0,             %%rsi           \n\t"       
                 "movq          %1,             %%rdi           \n\t"
                 "movq          %2,             %%rbx           \n\t"
                 "movq          %3,             %%rcx           \n\t"
                 "movq          %%rdi,          %%rax           \n\t"
                 "mulq          %%rbx                           \n\t"
                 "xchgq         %%rbx,          %%rax           \n\t"
                 "mulq          %%rsi                           \n\t"
                 "xchgq         %%rax,          %%rsi           \n\t"
                 "addq          %%rdx,          %%rbx           \n\t"
                 "mulq          %%rcx                           \n\t"
                 "addq          %%rax,          %%rbx           \n\t"
                 "movq          %%rsi,          %0              \n\t"
                 "movq          %%rbx,          %1              \n\t"
                 : "=m" (__XRSI), "=m" (__XRBX)
                 : "m" (__XRSI),  "m" (__XRDI), "m" (__XRBX), "m" (__XRCX)
                 : "rax","rbx","rcx","rdx","rsi","rdi"
                 );
    KEY.SPLIT.LWORDS[0]=__XRSI;
    KEY.SPLIT.LWORDS[1]=__XRBX;
    RESULT=KEY.WHOLE;
    return RESULT;
}

Si desea una multiplicación de 128 bits, esto debería funcionar, está en formato AT&T.

__uint128_t FASTMUL128(const __uint128_t TA,const __uint128_t TB)
{
    union
    {
        __uint128_t WHOLE;
        struct
        {
            unsigned long long int LWORDS[2];
        } SPLIT;
    } KEY;
    register unsigned long long int __RAX,__RDX,__RSI,__RDI;
    __uint128_t RESULT;

KEY.WHOLE=TA;
__RAX=KEY.SPLIT.LWORDS[0];
__RDX=KEY.SPLIT.LWORDS[1];
KEY.WHOLE=TB;
__RSI=KEY.SPLIT.LWORDS[0];
__RDI=KEY.SPLIT.LWORDS[1];
__asm__ __volatile__(
    "movq           %0,                             %%rax                   \n\t"
    "movq           %1,                             %%rdx                   \n\t"
    "movq           %2,                             %%rsi                   \n\t"
    "movq           %3,                             %%rdi                   \n\t"
    "movq           %%rsi,                          %%rbx                   \n\t"
    "movq           %%rdi,                          %%rcx                   \n\t"
    "movq           %%rax,                          %%rsi                   \n\t"
    "movq           %%rdx,                          %%rdi                   \n\t"
    "xorq           %%rax,                          %%rax                   \n\t"
    "xorq           %%rdx,                          %%rdx                   \n\t"
    "movq           %%rdi,                          %%rax                   \n\t"
    "mulq           %%rbx                                                   \n\t"
    "xchgq          %%rbx,                          %%rax                   \n\t"
    "mulq           %%rsi                                                   \n\t"
    "xchgq          %%rax,                          %%rsi                   \n\t"
    "addq           %%rdx,                          %%rbx                   \n\t"
    "mulq           %%rcx                                                   \n\t"
    "addq           %%rax,                          %%rbx                   \n\t"
    "movq           %%rsi,                          %%rax                   \n\t"
    "movq           %%rbx,                          %%rdx                   \n\t"
    "movq           %%rax,                          %0                      \n\t"
    "movq           %%rdx,                          %1                      \n\t"
    "movq           %%rsi,                          %2                      \n\t"
    "movq           %%rdi,                          %3                      \n\t"
    : "=m"(__RAX),"=m"(__RDX),"=m"(__RSI),"=m"(__RDI)
    :  "m"(__RAX), "m"(__RDX), "m"(__RSI), "m"(__RDI)
    : "rax","rbx","ecx","rdx","rsi","rdi"
);
KEY.SPLIT.LWORDS[0]=__RAX;
KEY.SPLIT.LWORDS[1]=__RDX;
RESULT=KEY.WHOLE;
return RESULT;
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top