10
votes

Problème dans la compréhension des instructions de mul & imul de la langue d'assemblage

J'apprends 80386 de Assemblage PC par Paul Cauter

imul eax ; edx:eax = eax * eax
mov ebx, eax ; save answer in ebx
mov eax, square_msg ; square_msg db "Square of input is ", 0
call print_string ; prints the string eax
mov eax, ebx 
call print_int ;  prints the int stored in eax
call print_nl ; prints new line
  • Si l'opérande est octet dimensionné, il est multiplié par l'octet dans l'al. registre et le résultat est stocké dans Les 16 bits de hache fort>. li> ul> blockQuote>

    Bien. P>

    • Si la source est de 16 bits, elle est multipliée par le mot dans la hache et la Le résultat 32 bits est stocké dans DX: AX. LI> ul> blockQuote>

      Q1: strong> Pourquoi DX: hache? Pourquoi ne peut-il pas stocker dans eAx / edx? P>

      imul code> est vraiment déroutant p> xxx pré>

       text alt P>

      J'ai un problème dans la compréhension de la table. P>

      Q2: strong> dans la 2e entrée de la table. Encore une fois, pourquoi DX: hache. Pourquoi pas EAX ou EDX? P>

      Prenez maintenant en compte le suivant Code Snippet: P>

      imul dest, source1
      imul dest, source1, source2
      


0 commentaires

5 Réponses :


7
votes

Q1 / Q2: L'ensemble d'instructions X86 maintient son historique de 16 bits. Lors de la multiplication de 16 bits, la réponse est stockée dans DX: AX. C'est comme ça que c'est, car c'est comme ça que c'était dans des terres de 16 bits.

Q3: le code que vous avez montré a un bogue si vous essayez de calculer le carré d'un nombre supérieur à 2 ^ 16, car le code ignore les 32 bits hauts du résultat stocké dans edx .

Q4: Je pense que vous risquez d'être mal interprété la table. Les multiplications à 8 bits sont stockées dans un résultat de 16 bits; Les multiplications 16 bits sont stockées dans un résultat 32 bits; Les multiplications 32 bits sont stockées dans un résultat 64 bits. Quelle ligne parlez-vous spécifiquement?


5 commentaires

@ Q3: Je le savais. C'est le code de ce livre de Paul Carter. Pouvez-vous me dire comment le code devrait être? Je suis confus comment imprimer le résultat.


Le code comme indiqué n'est qu'un exemple; Le texte doit mentionner quelque part qu'il ne calculera pas correctement le carré si l'entrée est en dehors de la plage attendue. Puisque vous appelez une fonction imprimer_int pour imprimer un entier 32 bits, voir si vous pouvez trouver une fonction print_int64 pour imprimer un entier 64 bits.


@ Q4: Oui, c'est ainsi que c'est censé être, mais la table dit 16 bits multiplication est stockée dans le résultat 16 bits. 4ème entrée: DEST * = SOURCE1 => DEST = DEST * SOURCE1; Dest est 16bit & Source1 est 16 bits. Et c'est le même cas pour toutes les entrées de table. Même la dernière entrée Source1 & Source2 sont 32 bits et DESTS est également 32 bits.


Je vois ce que tu veux dire. Le registre "DEST" indique la taille d'un enregistrement Single dans le résultat; Donc, un résultat 32 bits va dans deux registres de 16 bits 16 bits. De même, un résultat de 64 bits va dans deux registres 32 bits 32 bits. C'est un peu déroutant, mais je pense que vous l'avez.


Non, imul Dest, source1 [ source2] (code> produit le résultat de la même taille que les opérandes et jetez les bits hauts, pas dans deux registres comme vous avez dit. Seul [i] Mul Source émettra les bits de résultat complet d'un opérande de taille N-bits



1
votes

Q1 / Q2: Je pense que la raison est historique. Avant 32 bits était une option, il n'y avait pas d'EAX ou d'EDX. La fonctionnalité 32 bits a été ajoutée pour être compatible inverse.

Q3: Les bits d'ordre bas vont être dans EAX. Ce sont les seuls que vous aimez à moins que vous soyez trop débordement dans les hauts bits.

Q4: Certainement une table impaire. Je pense que vous l'obtenez bien.


0 commentaires

1
votes

A1: mul était présent à l'origine sur les processeurs 8086/8088888888/80186/80286, qui n'avaient pas le e ** (E pour prolongé, c'est-à-dire 32 -Bit) registres.

A2: Voir A1.

Comme mon travail en tant que programmeur de langues de montage a déménagé dans la famille Motorola 680x0 avant que ces Intels 32 bits ne deviennent pas monnaie courante, je vais arrêter là-bas: -)


1 commentaires

J'avais aimé le processeur 680x0, je les ai trouvé plus faciles à programmer que x86 :)



7
votes

Il y a beaucoup de variations différentes de l'instruction imul.

La variante que vous avez trébutée est une multiplication de 16 bits. Il multiplie le registre AX avec tout ce que vous transmettez comme argument à imul et stocke le résultat dans DX: hache. P>

Une variante 32 bits fonctionne comme la multiplication 16 bits mais écrit le registre dans EDX: EAX. Pour utiliser cette variante, tout ce que vous avez à faire est d'utiliser un opérande source 32 bits. P>

par exemple: p> xxx pré>


sur un 386 ou ultérieur , vous pouvez également écrire un imul code> dans les deux opérandes former. Cela rend beaucoup plus flexible et plus facile à travailler. Dans cette variante, vous pouvez choisir librement des 2 registres comme source et destination, et la CPU ne perdra pas de temps à écrire un résultat élevé. Et ne détruira pas Edx. P> xxx pré>

ou pour des entrées 16 bits signées pour correspondre à votre imul code>. (Utilisez MOVZX pour des entrées non signées) p> xxx pré>

Cette variante d'imul était introduit avec 386 et est disponible en taille d'opérande 16 et 32 ​​bits. (Et 64 bits d'opérande en mode 64 bits). P>

en code 32 bits Vous pouvez toujours supposer que 386 instructions telles que imul REG, REG / MEM CODE> sont disponibles. , mais vous pouvez l'utiliser dans un code de 16 bits si vous ne vous souciez pas des anciens processeurs. P>

186 introduit un formulaire immédiat à 3 opérandes. P>

imul  cx, bx, 123        ; requires 186

imul  ecx, ebx, 123      ; requires 386


2 commentaires

Pouvez-vous s'il vous plaît montrer comment imprimeriez-vous le résultat d'EDX: EAX ; une multiplication 32 bits: .


@Claws: En hexagonal, c'est facile, car chaque nibble est séparé. Vous pouvez faire les 8 chiffres pour EDX, puis les 8 chiffres hexagonales pour EAX. Pour imprimer en tant que décimal, vous devez effectuer une division 64 bits par 10, ce qui n'est facile que sur X86-64 avec des registres 64 bits. Le chiffre décimal le plus bas dépend de tous les bits supérieurs. Donc, vous devez faire une division de précision étendue par 10. Passer un INT64_T à PrintF ("% LLD", X) est évidemment le moyen le plus simple, mais Imprimer 64 bits Nombre stocké dans EDX: EAX à Standard Out est-il à la main pour non signé . Pour négatif, imprimez un - et nier.



6
votes

q1 / q2: pourquoi dx: hache? Pourquoi ne peut-il pas stocker dans EAX / EDX?

Comme les autres ont dit, c'est juste pour Compatibilité en retard . L'original (i) mul Les instructions sont de 16 bits X86 qui étaient venus long avant l'ensemble de l'instruction X86 32 bits apparu, ils ne pouvaient donc pas stocker le résultat à Le FAX / EDX depuis Il n'y avait pas d'e-registre .

Q3: Dans le code ci-dessus, nous n'avons pas tenu compte d'EDX que nous faisons simplement référence à EAX Comment cela fonctionne-t-il toujours?

Vous avez entré de petites valeurs qui ne causent pas le résultat au débordement, vous n'avez donc pas vu les différences. Si vous utilisez des valeurs suffisamment grandes (> = 16 bits), vous verrez que EDX! = 0 et le résultat imprimé sera incorrect.

Q4: Comment se fait-il que sa conservation du résultat de la multiplication de 16/32 bits résultat de la même taille elle-même?

Ce n'est pas que le résultat est toujours la même taille comme les opérandes. multiplier deux valeurs N-bits produisent toujours une valeur de 2n-bits . Mais dans imul R16, R / M16 [ ​​IMM8 / 16] et leurs contreparties 32/64 bits Les résultats élevés N-Bit sont supprimés. Ils sont utilisés lorsque vous n'avez besoin que des bits inférieurs 16/32/64 du résultat (c'est-à-dire non Élargissement de la multiplication ), ou lorsque vous pouvez vous assurer que le résultat ne déborde pas.

  • Formulaire à deux opérandes - avec ce formulaire, l'opérande de destination (le premier opérande) est multiplié par l'opérande source (deuxième opérande). L'opérande de destination est un registre à usage général et l'opérande source est une valeur immédiate, un registre général ou un emplacement de mémoire. Le produit intermédiaire (deux fois la taille de l'opérande d'entrée) est tronqué et stocké dans l'emplacement de la destination.
  • [... même pour la forme de trois opérandes]

    https://www.felixcloutier.com/x86/imul.html

    compilateurs modernes de nos jours utilisez presque exclusivement le multi-opérande imul pour les multiplications signées et non signées, car


0 commentaires