Comment puis-je multiplier deux nombres 64 bits à l'aide du langage d'assemblage x86?

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

  •  01-07-2019
  •  | 
  •  

Question

Comment pourrais-je me débrouiller ...

  • multipliant deux nombres de 64 bits

  • multipliant deux nombres hexadécimaux à 16 chiffres

... en utilisant le langage d'assemblage.

Je ne peux utiliser que les registres% eax,% ebx,% ecx,% edx et la pile.

EDIT: Oh, j'utilise la syntaxe ATT sur le x86
EDIT2: Interdit de décompiler en assembleur ...

Était-ce utile?

La solution

Utilisez ce qui devrait probablement être votre manuel de cours, celui de Randall Hyde "The Art of Assembly Language".

Voir 4.2.4 - Extension Multiplication de précision

  

Bien qu'une multiplication 8x8, 16x16 ou 32x32 soit généralement suffisante, il peut arriver que vous souhaitiez multiplier des valeurs plus grandes ensemble. Vous utiliserez les instructions MUL et IMUL d'opérande unique x86 pour la multiplication de précision étendue.

     

La chose la plus importante à retenir lors de l'exécution d'une multiplication à précision étendue est que vous devez également effectuer une addition à plusieurs précisions en même temps . L'ajout de tous les produits partiels nécessite plusieurs ajouts qui produiront le résultat. La liste suivante montre la méthode appropriée pour multiplier deux valeurs 64 bits sur un processeur 32 bits.

(Voir le lien pour obtenir la liste complète des assemblages et des illustrations.)

Autres conseils

Si c'était 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

Puisque vous êtes sur x86, vous avez besoin de 4 instructions pour créer. Divisez les quantités 64 bits en deux mots de 32 bits et multipliez les mots bas par le mot le plus bas et le deuxième mot le plus bas du résultat, puis les deux paires de mots bas et mots hauts de nombres différents (elles vont au deuxième et au troisième mots du résultat) et enfin les deux mots les plus élevés dans les 2 mots les plus élevés du résultat. Ajoutez-les tous ensemble sans oublier de traiter avec carry. Vous n'avez pas spécifié la configuration de la mémoire des entrées et des sorties, il est donc impossible d'écrire un exemple de code.

Ce code suppose que vous voulez un code x86 (et non un code x64), que vous ne voulez probablement qu'un produit 64 bits et que vous ne vous souciez pas des débordements ni des nombres signés. (Une version signée est similaire).

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

Ceci ne respecte pas les contraintes de registre exactes de OP, mais le résultat s’inscrit entièrement dans les registres proposés par le x86. (Ce code n’a pas été testé, mais je pense que c’est bien).

[Remarque: j'ai transféré (mon) cette réponse à partir d'une autre question qui a été close, car AUCUN de l'autre "répond" " ici directement répondu à la question].

Cela dépend de la langue que vous utilisez. D'après mes souvenirs de l'apprentissage de l'assemblage MIPS, il existe une commande Move From High et une commande Move From Lo, ou mflo and mfhi. mfhi stocke les 64 bits supérieurs, tandis que mflo enregistre les 64 bits inférieurs du nombre total.

ah assemblage, ça fait longtemps que je ne l’utilise pas. Je suppose donc que le vrai problème est que le microcontrôleur (ce que j’écrivais de toute façon pour écrire du code dans l’assemblage) sur lequel vous travaillez n’a pas de registres 64 bits? Si tel est le cas, vous séparez les chiffres avec lesquels vous travaillez et multipliez les multiplications.

cela ressemble à un devoir de la façon dont vous l'avez libellé, donc je ne vais pas l'épeler plus loin: P

Effectuez simplement une multiplication longue normale, comme si vous multipliiez une paire de nombres à 2 chiffres, à l'exception de chaque "chiffre". est vraiment un entier 32 bits. Si vous multipliez deux nombres aux adresses X et Y et que vous stockez le résultat dans Z, vous souhaitez (en pseudocode):

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

Notez que nous éliminons les 64 bits supérieurs du résultat (puisqu'un nombre 64 bits multiplié par un nombre 64 bits est un nombre 128 bits). Notez également que cela suppose un peu endian. Faites également attention aux multiplications signée et non signée.

Recherchez un compilateur C prenant en charge la version 64 bits (GCC ne IIRC) compilez un programme qui le fait, puis effectuez le désassemblage. GCC peut le cracher tout seul et vous pouvez le sortir du fichier objet avec les bons outils.

OTOH leur est un 32bX32b = 64b op sur 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;

tout le reste déborde

(non testé)

Modifier uniquement non signé

Je parie que vous êtes un étudiant, alors voyez si vous pouvez le faire: faites-le mot par mot et utilisez des décalages. Imaginez la solution la plus efficace. Attention au bit de signe.

Si vous voulez le mode 128, essayez ceci ...

__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 vous souhaitez une multiplication à 128 bits, cela devrait fonctionner en format 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;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top