Programmation Assembleur/x86/Les branchements


Par défaut, les instructions sont exécutées les unes à la suite des autres, en séquence. Les instructions de branchement ou de saut permettent de passer à une autre instruction que la suivante, pour diverses raisons :

  • Effectuer une boucle, dans ce cas, le saut revient à une instruction antérieure,
  • Sauter les instructions conditionnelles quand la condition est fausse,
  • Appeler un sous-programme.
Programmation Assembleur/x86
Modifier ce modèle

Saut inconditionnel

modifier

Le saut réalisé par l'instruction JMP est effectué sans test de condition. Le seul paramètre de cette instruction est l'adresse où l'exécution doit se poursuivre. Cette adresse peut être une constante, ou contenue dans un emplacement mémoire (un pointeur).

JMP adresse

Exemples :

JMP 20E5       ; Offset (near pointer : adresse proche)
JMP FFFF:0000  ; Segment:offset (far pointer : adresse lointaine)

JMP [BX]       ; Offset contenu dans le mot de 16 bits pointé par DS:BX
es:
JMP [BX]         ; Offset contenu dans le mot de 16 bits pointé par ES:BX
JMP far ptr [BX] ; Segment:offset contenu dans les deux mots de 16 bits pointés par BX (BX pour l'offset, BX+2 pour le segment)

Saut conditionnel

modifier

Les instructions de saut conditionnel n'effectue le saut que si la condition correspondante est réalisée. Comme pour l'instruction JMP, le seul paramètre est une adresse. Toutefois, sur les anciens processeurs, l'adresse ne peut aller au delà de 128 octets en arrière ou 127 octets en avant, car l'adresse n'est codée que sur un octet signé. Dans ce cas, l'astuce est de sauter à une instruction JMP qui utilise une adresse codée sur plus d'octets.

Comparaison puis saut(s)

modifier

En assembleur, les conditions if sont décomposées en deux instructions :

  • Une opération de comparaison des opérandes réalisant une soustraction ne stockant pas le résultat mais effectue la mise à jour des indicateurs du registre FLAGS,
  • Un saut conditionnel basé sur l'état des indicateurs du registre FLAGS.

Cette décomposition permet d'optimiser certains cas :

  • Quand les mêmes opérandes sont comparés une seule comparaison est faite, suivie de plusieurs sauts conditionnels ;
cmp ax, bx             ; Comparaison : indicateurs mis à jour selon AX-BX
jl   quand_plus_petit  ; saut si AX < BX  (non signé)
jg   quand_plus_grand  ; saut si AX > BX  (non signé)
:quand_egaux
  • L'opération de comparaison peut être remplacée par une ou des instructions mettant à jour les indicateurs, ce qui peut éviter une comparaison après une opération arithmétique. Les appels aux fonctions systèmes retournent souvent un état erreur ou OK dans un indicateur du registre FLAGS pouvant être traité par un saut conditionnel dans l'application au retour de l'appel de la fonction.
call  sys_open      ; Appel à open(...)
jb    open_failure  ; saut si erreur

Sauts conditionnels

modifier

La table ci-dessous résume les instructions de saut conditionnel, une case contenant tous les alias de la même instruction :

Instruction et synonymes Signification (en anglais) Condition nécessaire
JCXZ Jump if CX is Zero Registre CX (16 bits) égal à 0
JECXZ Jump if ECX is Zero Registre CX (32 bits) égal à 0
JRCXZ Jump if RCX is Zero Registre CX (64 bits) égal à 0
JZ

JE

Jump if Zero flag is set

Jump if both operand were Equals

Indicateur Zéro à 1

ou quand les deux opérandes A et B sont égaux (A-B = 0)

JNZ

JNE

Jump if No Zero flag is set

Jump if both operand were Not Equals

Indicateur Zéro à 0

ou quand les deux opérandes A et B ne sont pas égaux (A-B != 0)

JS Jump if Sign flag is set Indicateur de signe à 1 (résultat négatif)
JNS Jump if No Sign flag is set Indicateur de signe à 0 (résultat positif)
JO Jump if Overflow flag is set Indicateur de débordement à 1
JNO Jump if No Overflow flag is set Indicateur de débordement à 0
JC

JB

JNAE

Jump if Carry flag is set

Jump if Borrow flag is set / Jump if Below

Jump if Not Above or Equal

Indicateur de retenue à 1 (A < B, c'est à dire A-B < 0, retenue)
JNC

JNB

JAE

Jump if No Carry flag is set

Jump if No Borrow flag is set / Jump if Not Below

Jump if Above or Equal

Indicateur de retenue à 0 (A >= B, c'est à dire A-B >= 0, pas de retenue)
JBE

JNA

Jump if Jump if Below or Equal

Jump if Not Above

Indicateur de retenue à 1 ou indicateur Zéro à 1 (A < B ou A = B, c'est à dire A-B <= 0)
JNBE

JA

Jump if Jump if Not Below or Equal

Jump if Above

Indicateur de retenue à 0 et indicateur Zéro à 0 (A >= B et A != B, c'est à dire A > B, A-B > 0)
JL

JNGE

Jump if Lower

Jump if Not Greater or Equal

Indicateur de débordement différent de l'indicateur de signe (A < B)
JNL

JGE

Jump if Not Lower

Jump if Greater or Equal

Indicateur de débordement et indicateur de signe égaux (A >= B)
JLE

JNG

Jump if Lower or Equal

Jump if Not Greater

Indicateur de débordement différent de l'indicateur de signe ou indicateur zéro à 1 (A <= B)
JNLE

JG

Jump if Not Lower or Equal

Jump if Greater

Indicateur de débordement et indicateur de signe égaux et indicateur zéro à 0 (A > B)
JP

JPE

Jump if Parity flag is set

Jump if Parity Even

Indicateur de parité à 1 (les 8 bits de poids faible du résultat de A-B comportent un nombre pair de bits à 1)
JNP

JPO

Jump if No Parity flag is set

Jump if Parity Odd

Indicateur de parité à 0 (les 8 bits de poids faible du résultat de A-B comportent un nombre impair de bits à 1)

Les deux dernières instructions de saut conditionnel JPE et JPO permettent de déterminer un bit de parité pour un octet. Elles sont notamment utilisées lors de la communication par port série.

Sauts conditionnels pour comparaison numérique

modifier

Les sauts conditionnels utilisables après la comparaison de deux nombres entiers varient selon que les nombres sont signés ou non-signés. L'interprétation diffère quand le bit de poids fort des deux nombres diffèrent.

Exemple de deux interprétations pour des mots de 16 bits
Valeur (hex) Interprétation en entiers non-signés Interprétation en entiers signés
AX = 8800 34816 -30720
BX = 45A0 17824 +17824
Comparaison AX > BX AX < BX

Le tableau suivant montre les sauts conditionnels à utiliser selon l'interprétation des nombres comparés en non-signés ou signés.

Comparaison Entiers non signés Entiers signés
  JB JL
  JA JG
  JBE JLE
  JAE JGE
  JE
  JNE

Appel à un sous-programme

modifier

L'instruction CALL permet d'appeler un sous-programme. Le seul paramètre de cette instruction est l'adresse du sous-programme à appeler. Cette adresse peut être une constante, ou contenue dans un emplacement mémoire (un pointeur).

CALL adresse

Exemples :

CALL 20E5       ; Offset (near pointer : adresse proche)
CALL 2AE1:0200  ; Segment:offset (far pointer : adresse lointaine)

CALL [BX]       ; Offset contenu dans le mot de 16 bits pointé par DS:BX
es:
CALL [BX]         ; Offset contenu dans le mot de 16 bits pointé par ES:BX
CALL far ptr [BX] ; Segment:offset contenu dans les deux mots de 16 bits pointés par BX (BX pour l'offset, BX+2 pour le segment)

L'instruction CALL est similaire à JMP excepté qu'avant d'effectuer le saut à l'adresse indiquée, l'adresse de retour est enregistrée dans la pile :

  • Pour un appel d'adresse proche, seul l'offset est sauvegardé.
  • Pour un appel d'adresse lointaine, le segment et l'offset sont sauvegardés.

L'adresse de retour sauvegardée dans la pile est celle du premier octet de l'instruction qui suit l'instruction CALL. Le sous-programme doit se terminer par une instruction de retour pour restituer l'adresse sauvegardée dans les registres CS:IP :

  • L'instruction RET (Return) restitue l'offset (sous-programme appelé avec adresse proche) ;
  • L'instruction RETF (Return Far) restitue l'offset et le segment (sous-programme appelé avec adresse lointaine).

Il existe aussi une instruction IRET (Interruption Return) qui en plus restitue les indicateurs mais elle est utilisée plutôt en fin de routine de traitement des interruptions.