2017-06-02 1 views
4

En regardant le volume intel des instructions, je trouve ceci:Différence entre MOV r/m8, r8 et MOV r8, r/m8

1) 88 /r MOV r/m8,r8
2) 8A /r MOV r8,r/m8

Quand j'écris un ligne comme celui-ci en MSNA et assembler avec l'option liste:

mov al, bl

Je reçois ce dans la liste:

88D8 mov al, bl

Alors, évidemment, NASM a choisi la première instruction des deux précédentes, mais la seconde instruction n'est-elle pas une option deux? Si oui, sur quelle base NASM a choisi le premier?

+5

Aucune raison particulière, il suffit de choisir quelque chose. – harold

Répondre

3

Ces deux codages existent car un octet modr/m ne peut coder qu'un seul opérande de mémoire. Donc, pour permettre à la fois mov r8,m8 et mov m8,r8, deux codages sont nécessaires. Bien sûr, de cette façon, nous pouvons coder les utilisations de mov avec les deux opérandes étant des registres utilisant l'un ou l'autre codage et nasm en choisit un au hasard. Il n'y a pas de raison particulière au choix et j'ai vu des assembleurs faire des choix différents.

J'ai également entendu parler d'un assembleur qui filigra les fichiers binaires assemblés en choisissant un codage d'instructions d'une manière spécifique. De cette façon, l'auteur de l'assembleur pourrait traquer et poursuivre les personnes qui ont utilisé son assembleur sans payer.

+0

J'ai entendu ça aussi, je ne me souviens pas vraiment de ce que j'ai entendu ... c'était HLA? – harold

+2

Je pense qu'il était as86 mais jusqu'à présent ne peux pas trouver le bon coup en ligne, j'ai un manuel imprimé à la maison si je peux le trouver ... –

+0

@old_timer il serait cool de le voir –

3

Comme Harold souligné: il n'y a pas de raison.
Peut-être que les auteurs ont trouvé l'utilisation d'un mouvement vers l'avant plus attrayant qu'un mouvement inverse ou peut-être qu'ils ont juste pris le premier opcode. J'ai jeté un coup d'œil sur le code source de la NASM et j'ai découvert que l'encodage est fait essentiellement avec une grande table de recherche, donc c'est vraiment une question de goût. L'utilisation de l'autre opcode (8AC3) aurait simplifié le code (je suppose) si l'analyse n'avait pas utilisé une table de recherche: les instructions comme addps sont asymétriques et en utilisant 8A /r pour mov al, bl le code pourrait être réutilisé pour calculer l'octet ModR/M pour addps et des instructions similaires aussi. addps xmm0, xmm3 utilise le même octet ModR/M (C3) que mov al, bl lorsque le 8A /r est utilisé.
Notez que les registres A (B) et xmm0 (xmm0) sont codés avec les mêmes numéros.

Il est toujours amusant, cependant, de comprendre pourquoi il existe deux codages.


Comme Mark Hopkins (re)discovered, the earlier x86 instructions encoding make a lot more sense in octal.
Un octet en octal a trois chiffres que j'appellerai G P F (Group, oPeration, Flags).

G est le groupe octal, les instructions dans le même groupe ont tendance à effectuer des tâches similaires (par exemple, arithmétique vs mouvements).
Cependant, ce n'est pas une division stricte.
P est l'opération; par exemple, dans le groupe arithmétique, une opération est la soustraction et une autre est l'addition.
F est un bitet utilisé pour contrôler le comportement de l'opération.Chaque groupe et chaque opération utilise le chiffre F à sa guise, il peut même ne pas être un bit défini (par exemple G = 2, P = 7 est mov r16, imm16 et F est utilisé pour sélectionner r16).

Pour les instructions mov qui se déplacent à partir de la mémoire/registre dans un registre ou dans l'autre sens autour de la G est égal à 2 et p est égal à 1. Le
F est un champ de 3 bits avec sémantique:

2 1 0 bit 
+---+---+---+ 
| s | d | b | 
+---+---+---+ 

s = 1 if moving to/from a segment register 
    0 if moving to/from a gp register 

d = 1 if moving mem -> reg 
    0 if moving mem <- reg 

b = 1 if moving a WORD 
    0 if moving a BYTE 

Nous pouvons commencer à former des opcodes, mais nous manquons toujours un moyen de sélectionner les opérandes.

G=2, P=1, F={s=0, d=0, b=0} 210 (88) mov r/m8, r8 
G=2, P=1, F={s=0, d=0, b=1} 211 (89) mov r/m16, r16 
G=2, P=1, F={s=0, d=1, b=0} 212 (8A) mov r8, r/m8 
G=2, P=1, F={s=0, d=1, b=1} 213 (8B) mov r16, r/m16 
G=2, P=1, F={s=1, d=0, b=0} 214 (8C) mov r/m16, Sreg 
G=2, P=1, F={s=1, d=0, b=1} 215 (8D) Not a move, segment registers are 16-bit 
G=2, P=1, F={s=1, d=1, b=0} 216 (8E) mov Sreg, r/m16 
G=2, P=1, F={s=1, d=1, b=1} 217 (8F) Not a move, segment registers are 16-bit 

Après l'opcode doit venir l'octet ModR/M, il permet de sélectionner le mode d'adressage et le registre.

Le ModR/M octet peut être considéré, en octal, comme trois champs: X R M.

X et M sont combinés ensemble pour former le mode d'adressage.
R sélectionne le registre (par exemple 0 = A, 3 = B). L'un des modes d'adressage (X = 3, M = any) permet d'adresser les registres (via M) et non la mémoire.
Par exemple, X = 3, R = 0, M = 3 (C3) définit le registre B comme l'opérande «mémoire» et le registre A comme opérande de registre.
Lorsque X = 3, R = 3, M = 0 (D8) définit le registre A comme l'opérande "mémoire" et le registre B comme opérande de registre.

Ici, nous pouvons voir où se situe l'ambiguïté: l'octet ModR/M nous permet de coder un registre source et un registre de destination. Pendant ce temps, l'opcode permet de coder un déplacement de la source à la destination ou de la destination à la source - cela nous donne la liberté de choisir quel registre est quoi.

Par exemple, supposons que nous voulons déplacer B dans A.

Si nous nous installons sur A comme opérande de registre (source) et B comme opérande de mémoire (destination), puis l'octet ModR/M est X = 3, R = 0, M = 3 (C3). Pour passer de B à A, comme dans votre exemple, en utilisant les 8 bits inférieurs seulement, nous codons le mouvement comme suit: G = 2, P = 1, F = {s = 0, d = 1, b = 0} (8A) parce que nous déplaçons mem-> reg (B-> A). Ainsi, l'instruction finale est 8AC3. Si nous choisissons A comme opérande de mémoire (destination) et B comme opérande de registre (source), l'octet ModR/M est X = 3, R = 3, M = 0 (D8).
Le mouvement est G = 2, P = 1, F = {s = 0, d = 0, b = 0} (88) parce que nous déplaçons reg-> mem (B-> A).
L'instruction finale est 88D8.

Si nous voulons déplacer tout le registre 16 bits (nous négligeons préfixes taille d'opérandes ici) que nous venons de définir le bit b de F:

G = 2, P = 1, F = {s = 0 , d = 1, b = 1} pour le premier cas, conduisant à 8BC3.
G = 2, P = 1, F = {s = 0, d = 0, b = 1} pour le second cas, conduisant à 89D8.

Vous pouvez vérifier cela avec ndisasm

00000000 8AC3    mov al,bl 
00000002 88D8    mov al,bl 
00000004 8BC3    mov ax,bx 
00000006 89D8    mov ax,bx 
+0

merci pour l'effort et les explications, le lien pourrait être utile pour moi à l'avenir aussi. –

+0

@BiteBytes Mon plaisir. Je réponds aussi comme un moyen d'éclaircir mon esprit et pour les autres lecteurs, ce n'est pas vraiment un effort :) –

6

effet secondaire intéressant d'avoir différents encodages: A partir du manuel A86.

  1. A86 tire parti des situations dans lesquelles plus d'un ensemble de codes opérations peuvent être générées pour la même instruction. (Par exemple, MOV AX, BX peut être généré en utilisant un opcode 89 ou 8B, par en inversant les champs dans l'octet d'adresse effectif suivant Les deux formulaires sont absolument identiques en termes de fonctionnalité et d'exécution vitesse.) A86 adopte un mélange inhabituel des choix dans de telles situations. Cela crée une "empreinte" de génération de code qui n'occupe aucun espace dans votre fichier programme, mais qui me permettra de le dire, et à de démontrer devant un tribunal, si un fichier objet non trivial a été produit par A86 . La spécification pour cette "empreinte" est suffisamment obscure et compliquée qu'il serait impossible de dupliquer par accident. Je revendique des droits exclusifs sur la "empreinte" particulière que j'ai choisie et j'interdis à quiconque de la dupliquer. Cela a au moins deux implications spécifiques:

    a. Tout assembleur qui duplique "l'empreinte" est le mien. Si n'est pas identifié comme le mien et émis selon ces termes, alors ceux qui vendent ou distribuent l'assembleur seront sujet à des poursuites.

    b. Tout programme marqué avec "l'empreinte" a été produit par mon assembleur. Il est soumis à la condition 5 ci-dessus.

+0

hhhhh, cela a fait ma journée, merci pour tout. –