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
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é> 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
5 Réponses :
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. P>
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 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? P> edx code>. p>
@ 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 code> imprimer_int code> pour imprimer un entier 32 bits, voir si vous pouvez trouver une fonction print_int64 code> 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 CODE> => 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 i> dans le résultat; Donc, un résultat 32 bits va dans deux registres de 16 bits i> 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 b> dans deux registres comme vous avez dit. Seul
[i] Mul Source code> émettra les bits de résultat complet d'un opérande de taille N-bits
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. P>
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. P>
Q4: Certainement une table impaire. Je pense que vous l'obtenez bien. P>
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: -) p> mul code> é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. P>
J'avais aimé le processeur 680x0, je les ai trouvé plus faciles à programmer que x86 :)
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> sur un 386 ou ultérieur , vous pouvez également écrire un ou pour des entrées 16 bits signées pour correspondre à votre 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 186 introduit un formulaire immédiat à 3 opérandes. P>
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> imul code>. (Utilisez MOVZX pour des entrées non signées) p>
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>
imul cx, bx, 123 ; requires 186
imul ecx, ebx, 123 ; requires 386
Pouvez-vous s'il vous plaît montrer comment imprimeriez-vous le résultat d'EDX: EAX ; une multiplication 32 bits: code>.
@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 i> les bits supérieurs. Donc, vous devez faire une division de précision étendue par 10. Passer un INT64_T à PrintF ("% LLD", X) code> 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é i>. Pour négatif, imprimez un
- code> et nier.
q1 / q2: strong> pourquoi dx: hache? Pourquoi ne peut-il pas stocker dans EAX / EDX? P> blockQuote>
Comme les autres ont dit, c'est juste pour Compatibilité en retard forte>. L'original
(i) mul code> Les instructions sont de 16 bits X86 qui étaient venus
long forts> 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 fort>. p> Q3: strong> 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? P> blockQuote>
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. P>
Q4: strong> 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? p> blockQuote>
Ce n'est pas que le résultat est toujours la même taille em> comme les opérandes. multiplier deux valeurs N-bits produisent toujours une valeur de 2n-bits . Mais dans
imul R16, R / M16 [ IMM8 / 16] Code> 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 em> (c'est-à-dire non Élargissement de la multiplication ), ou lorsque vous pouvez vous assurer que le résultat ne déborde pas. P>
- 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. strong> li>
- [... même pour la forme de trois opérandes] li> ul>
https://www.felixcloutier.com/x86/imul.html p> blockQuote>
compilateurs modernes de nos jours utilisez presque exclusivement le multi-opérande
imul code> pour les multiplications signées et non signées, car p>
- Les bits inférieurs sont
toujours les mêmes forts> pour les deux cas et dans la multiplication de deux Variables génère un résultat de même taille (int code> x
int code> →
int code>,
long code> x
long code > →
long code> ...) qui ajustement
imul code> s bien. La seule façon de forcer les compilateurs à émettre
monobloc-opérand mul code> ou
imul code> strong> est Utiliser un type deux fois la taille du registre li>
- Il est très rare de voir une multiplication où le résultat est plus large que la taille du registre comme
int64_t a; __int128_t p = (__int128_t) a * b; code> SO OPÉRAINE
(i) mul code> est rarement nécessaire li>
- Calculer uniquement les bits inférieurs seront plus rapides que d'obtenir tout le résultat. Li>
- beaucoup plus de flexibilité dans l'utilisation en raison de diverses formes de
imul code> instruction
- dans le formulaire à 2 opérandes, vous n'avez pas besoin de sauvegarder / restaurer EDX et EAX LI>
- Le formulaire à 3 opérandes vous permet de faire en outre une multiplication non destructive li> ul> li>
- Les CPU modernes optimisent souvent les versions multi-opérandes de
imul code>
(car Les compilateurs modernes utilisent presque exclusivement le multi-opérandeimul code> pour les multiplications signées et non signées em>), donc Ils seront plus rapides que monobloc-opérande
(i) mul code>
li> ul>