Utiliser des codages abrégés dans des cas spéciaux pour AL / AX / EAX et d'autres formes abrégées et instructions à un octet
Les exemples supposent un mode 32/64 bits, où la taille d'opérande par défaut est 32 bits. Un préfixe de taille opérande modifie l'instruction en AX au lieu de EAX (ou l'inverse en mode 16 bits).
inc/dec
un registre (autre que 8 bits): inc eax
/ dec ebp
. (Pas x86-64: les 0x4x
octets d'opcode ont été réutilisés en tant que préfixes REX, donc inc r/m32
c'est le seul encodage.)
8-bit inc bl
est de 2 octets, en utilisant le inc r/m8
code d' opération + ModR / M opérande codant . Alors utilisezinc ebx
pour incrémenter bl
, si c'est sûr. (par exemple, si vous n'avez pas besoin du résultat ZF dans les cas où les octets supérieurs peuvent être différents de zéro).
scasd
: e/rdi+=4
, nécessite que le registre pointe vers une mémoire lisible. Parfois utile même si vous ne vous souciez pas du résultat FLAGS (comme cmp eax,[rdi]
/ rdi+=4
). Et en mode 64 bits, scasb
peut fonctionner comme un octetinc rdi
, si lodsb ou stosb ne sont pas utiles.
xchg eax, r32
: C'est là 0x90 NOP est provenu xchg eax,eax
. Exemple: réorganiser 3 registres avec deux xchg
instructions dans une boucle cdq
/ pour GCD en 8 octets où la plupart des instructions sont à un octet, y compris un abus de / au lieu de /idiv
inc ecx
loop
test ecx,ecx
jnz
cdq
: signe-étendre EAX dans EDX: EAX, c'est-à-dire copier le bit élevé d'EAX sur tous les bits d'EDX. Pour créer un zéro avec non négatif connu, ou pour obtenir un 0 / -1 à ajouter / sous ou masque avec. Leçon d'histoire x86: cltq
vs.movslq
, et aussi les mnémoniques AT&T vs Intel pour cela et les éléments connexes cdqe
.
lodsb / d : comme mov eax, [rsi]
/rsi += 4
sans drapeaux clobbering. (En supposant que DF est clair, quelles conventions d'appel standard exigent lors de l'entrée de fonction.) Aussi stosb / d, parfois scas, et plus rarement movs / cmps.
push
/ pop reg
. par exemple en mode 64 bits, push rsp
/ pop rdi
est de 2 octets, mais a mov rdi, rsp
besoin d'un préfixe REX et est de 3 octets.
xlatb
existe, mais est rarement utile. Une grande table de recherche est à éviter. Je n'ai également jamais trouvé d'utilisation pour les instructions AAA / DAA ou d'autres instructions BCD ou à 2 chiffres ASCII.
1 octet lahf
/ sahf
sont rarement utiles. Tu pourrais lahf
/ and ah, 1
comme alternative à setc ah
, mais ce n'est généralement pas utile.
Et pour CF en particulier, il sbb eax,eax
doit y avoir un octetsalc
0 / -1, ou même non documenté mais universellement pris en charge (définir AL à partir de Carry), ce qui fait effectivementsbb al,al
sans affecter les indicateurs. (Supprimé dans x86-64). J'ai utilisé SALC dans le défi d'appréciation des utilisateurs n ° 1: Dennis ♦ .
1 octet cmc
/ clc
/ stc
(flip ("complément"), clear ou set CF) sont rarement utiles, bien que j'aie trouvé une utilisation pour l'cmc
addition en précision étendue avec des blocs de base 10 ^ 9. Pour régler / effacer inconditionnellement les FC, faites généralement en sorte que cela se fasse dans le cadre d'une autre instruction, par exemplexor eax,eax
efface CF ainsi que EAX. Il n'y a pas d'instructions équivalentes pour les autres drapeaux de condition, juste DF (direction de chaîne) et IF (interruptions). Le drapeau de transport est spécial pour de nombreuses instructions; les décalages le définissent, adc al, 0
peuvent l'ajouter à AL en 2 octets, et j'ai mentionné plus tôt le SALC non documenté.
std
/ cld
semblent rarement en valoir la peine . Surtout dans le code 32 bits, il est préférable de simplement utiliser dec
sur un pointeur et un mov
opérande source de mémoire sur une instruction ALU au lieu de définir DF so lodsb
/ stosb
go downward au lieu de up. Habituellement, si vous avez besoin de descendre, vous avez toujours un autre pointeur qui monte, vous en aurez donc besoin de plus d'un std
et cld
dans toute la fonction pour utiliser lods
/ stos
pour les deux. À la place, utilisez simplement les instructions de chaîne pour la direction ascendante. (Les conventions d'appel standard garantissent DF = 0 à l'entrée de la fonction, vous pouvez donc supposer cela gratuitement sans utiliser cld
.)
Historique 8086: pourquoi ces encodages existent
En 8086 d' origine, AX était très spécial: instructions aiment lodsb
/ stosb
, cbw
, mul
/ div
et d' autres utilisent implicitement. C'est toujours le cas bien sûr; le x86 actuel n'a abandonné aucun des opcodes de 8086 (du moins aucun des officiellement documentés). Mais les CPU ultérieurs ont ajouté de nouvelles instructions qui ont donné des moyens meilleurs / plus efficaces de faire les choses sans les copier ou les échanger d'abord vers AX. (Ou vers EAX en mode 32 bits.)
Par exemple, 8086 manquait d'ajouts ultérieurs comme movsx
/ movzx
pour charger ou déplacer + extension de signe, ou 2 et 3 opérandes imul cx, bx, 1234
qui ne produisent pas un résultat élevé et n'ont pas d'opérandes implicites.
En outre, le principal goulot d'étranglement du 8086 était la récupération d'instructions, il était donc important d'optimiser la taille du code pour les performances à l'époque . Le concepteur ISA de 8086 (Stephen Morse) a dépensé beaucoup d'espace de codage d'opcode sur des cas spéciaux pour AX / AL, y compris des opcodes de destination spéciaux (E) AX / AL pour toutes les instructions de base ALU immédiates-src , juste opcode + immediate sans octet ModR / M. 2 octets add/sub/and/or/xor/cmp/test/... AL,imm8
ou AX,imm16
ou (en mode 32 bits)EAX,imm32
.
Mais il n'y a pas de cas particulier pour EAX,imm8
, donc l'encodage ModR / M normal de add eax,4
est plus court.
L'hypothèse est que si vous allez travailler sur certaines données, vous en aurez besoin dans AX / AL, donc échanger un registre avec AX est quelque chose que vous voudrez peut-être faire, peut-être même plus souvent que de copier un registre vers AX avecmov
.
Tout ce qui concerne le codage d'instructions 8086 prend en charge ce paradigme, des instructions comme lodsb/w
à tous les codages de cas spéciaux pour les intermédiaires avec EAX à son utilisation implicite même pour la multiplication / division.
Ne vous laissez pas emporter; ce n'est pas automatiquement une victoire de tout échanger vers EAX, surtout si vous devez utiliser des intermédiaires avec des registres 32 bits au lieu de 8 bits. Ou si vous devez entrelacer des opérations sur plusieurs variables dans des registres à la fois. Ou si vous utilisez des instructions avec 2 registres, pas du tout immédiats.
Mais gardez toujours à l'esprit: est-ce que je fais quelque chose qui serait plus court dans EAX / AL? Puis-je réorganiser ce que j'ai en AL, ou suis-je actuellement en train de mieux tirer parti de AL avec ce que je l'utilise déjà.
Mélangez librement les opérations 8 bits et 32 bits pour en profiter chaque fois que cela est sûr (vous n'avez pas besoin d'effectuer dans le registre complet ou autre).
push 200; pop edx
- 3 octets pour l'initialisation.