# Programmation Assembleur/x86/Copie entre registres et mémoire

* Programmation Assembleur/x86
* Introduction
* Registres
* Les flags
* Copie entre registres et mémoire
* Les branchements
* La pile
* Les interruptions
* Opérations arithmétiques et logiques
* Interactions avec les périphériques
* Optimisation des performances
* Index
* Conclusion
* Modifier ce modèle

## Syntaxes des assembleurs

Il existe deux variantes principales et opposées de syntaxe pour les instructions en assembleur :
* la syntaxe Intel où l'opérande destination est placé avant l'opérande source.
    Exemple : mov ax, bx ; AX <- BX transfert des 16 bits de BX vers AX.
* la syntaxe AT&T où l'opérande source est placé avant l'opérande destination.
    Exemple : mov %bx, %ax ; BX -> AX transfert des 16 bits de BX vers AX.
L'interprétation peut donc être ambiguë si on ne sait pas quelle syntaxe est employée. Les assembleurs sous DOS et Windows utilisent la syntaxe Intel, qui semble plus naturelle pour les opérations arithmétiques et conserve l'ordre des expressions arithmétiques :
Les assembleurs sous Linux utilisent la syntaxe AT&T, qui conserve l'ordre des mots pour expliquer les expressions arithmétiques :
La distinction entre les deux syntaxes est donc importante à faire :
* Dans la syntaxe Intel où l'opérande destination est placé avant l'opérande source, le nom des registres et les valeurs littérales n'ont pas de préfixe.
* Dans la syntaxe AT&T où l'opérande source est placé avant l'opérande destination, le nom des registres est préfixé par le caractère pourcent et les valeurs littérales sont préfixé par le caractère dollar.
| syntaxe Intel (DOS, Windows, ...) | syntaxe AT&T (Linux, ...) |
| add eax, 7 mov dword ptr [rbp-0x4],edi imul ecx, [rdi + rax*4 + 20], 12345 | add $7, %eax mov %edi,-0x4(%rbp) imul $12345, 20(%rdi, %rax, 4), %ecx |

## Transfert unitaire

L'instruction la plus courante est MOV (abréviation de l'anglais move : déplacer) qui permet de transférer le contenu d'un emplacement mémoire vers un registre et inversement, ou entre deux registres de même taille.
Le nombre de bits transféré est défini par la taille du registre source ou cible du transfert. Exemple (syntaxe destination, source) :
Les assembleurs peuvent employer des suffixes pour le nom de l'instruction indiquant ce nombre d'octets :
* movb (Move Byte) : 8 bits,
* movw (Move Word) : 16 bits,
* movl (Move Long) : 32 bits.
Exemple (syntaxe source, destination) :

## Transfert de séquence

Une famille d'instructions gère les séquences d'octets en mémoire :
* les instructions MOVS transfèrent les octets d'une zone mémoire à une autre (Move Sequence),
* les instructions CMPS comparent le contenu de deux zones mémoire (Compare Sequence),
* les instructions LODS transfère les octets d'une zone mémoire vers le registre EAX (Load Sequence),
* les instructions STOS transfère le contenu du registre EAX vers une zone mémoire (Store Sequence),
* les instructions SCAS comparent le contenu du registre EAX à celui d'une zone mémoire (Scan Sequence).

### Quantité traitée

Le nom de ces opérations se termine par un suffixe indiquant la quantité traitée à chaque appel :
* B (Byte) pour un octet de 8 bits : MOVSB, CMPSB, LODSB, STOSB, SCASB ;
* W (Word) pour un mot de 16 bits : MOVSW, CMPSW, LODSW, STOSW, SCASW ;
* D (Double word) pour un double mot (32 bits) : MOVSD, CMPSD, LODSD, STOSD, SCASD.

### Registres utilisés et opérations

Ces opérations utilisent les registres dédiés pour adresser la mémoire :
* DS:SI / DS:ESI pointant la zone mémoire source (instructions MOVS*, CMPS*, LODS*),
* ES:DI / ES:EDI pointant la zone mémoire destination (instructions MOVS*, CMPS*, STOS*, SCAS*).
Ces registres sont incrémentés ou décrémentés après chaque traitement par ces opérations, selon la valeur de l'indicateur de direction (DF) :
* s'il est positionné à 0, les registres (E)SI et (E)DI sont incrémentés de 1, 2 ou 4 selon la quantité traitée à chaque appel,
* S'il est positionné à 1, les registres (E)SI et (E)DI sont décrémentés de 1, 2 ou 4 selon la quantité traitée à chaque appel.
Mnémotechnique : DF = Decrement Flag
Cet indicateur est généralement positionné juste avant les instructions de traitement en utilisant l'instruction CLD (CLear Direction) ou STD (SeT Direction).
Le tableau ci-dessous résume les opérations effectuées par les instructions et les registres utilisés :
| Instructions | Source | Destination | Opération |
| MOVS* | [DS:(E)SI] | [ES:(E)DI] | Transfert de source vers destination (Move). |
| LODS* | [DS:(E)SI] | (E)AX | Transfert de source vers le registre EAX (Load). |
| STOS* | (E)AX | [ES:(E)DI] | Transfert du registre EAX vers destination (Store). |
| CMPS* | [DS:(E)SI] | [ES:(E)DI] | Met à jour les indicateurs après comparaison entre source et destination. |
| SCAS* | (E)AX | [ES:(E)DI] | Met à jour les indicateurs après comparaison entre le registre EAX et destination. |

### Répétition et boucle

Ces instructions effectuent le traitement d'une quantité unitaire de données, mais deviennent efficaces et puissantes en combinaison d'instructions répétant l'opération en boucle. Pour répéter ces opérations, les instructions de boucle et répétition utilisent le registre CX (Counter) en le décrémentant et répétant l'opération tant que le compteur n'atteint pas zéro. Le registre CX est donc initialisé avant la boucle ou répétition avec le nombre d'octets, de mots ou de double-mots à traiter.
* L'instruction REPZ répète l'instruction de séquence qui suit tant que le registre CX n'est pas à zéro et que le flag Zero (ZF) vaut 0 (c'est à dire jusqu'à ZF=1 : REPeat until Zero).
* L'instruction REPNZ répète l'instruction de séquence qui suit tant que le registre CX n'est pas à zéro et que le flag Zero (ZF) vaut 1 (c'est à dire jusqu'à ZF=0 : REPeat until Not Zero).
Le flag Zero (ZF) est pris en compte seulement si l'opération qui suit est CMPS* ou SCAS*.
Il est ignoré pour les opérations de transfert MOVS*, LODS* ou STOS*. Cela signifie que pour ces 3 opérations REPZ et REPNZ sont identiques, et les assembleurs ajoutent donc un alias REP qui en général est compilé en une instruction REPZ.
Les préfixes REPZ et REPNZ ne répète que l'instruction de séquence qui suit. Pour répéter une série d'instructions, notamment pour LODS* où l'assignation répétée dans le registre AX n'a pas d'intérêt réel, il faut utiliser les instructions LOOP*.
Les instructions LOOP* ont 3 variantes dont le comportement ne varient que par rapport au flag zéro (ZF) :
* L'instruction LOOPZ décrémente le registre (E)CX, puis s'il n'est pas à zéro et que le flag zéro (ZF) vaut 1 (LOOP while CX > 0 and Zero) saute à l'adresse indiquée.
* L'instruction LOOPNZ décrémente le registre (E)CX, puis s'il n'est pas à zéro et que le flag zéro (ZF) vaut 0 (LOOP while CX > 0 and Not Zero) saute à l'adresse indiquée.
* L'instruction LOOP décrémente le registre (E)CX, puis s'il n'est pas à zéro (LOOP while CX > 0) saute à l'adresse indiquée.
Enfin, il existe une instruction JCXZ qui ne modifie pas le registre (E)CX mais saute à l'adresse indiquée si le registre (E)CX vaut 0. Dans le cas d'un compteur d'itération, elle permet donc de sortir d'une boucle infinie.

### Préfixes de changement de segment

Les préfixes de changement de segment n'influence que le registre de segment DS utilisé pour la source. Cela signifie qu'ils n'influencent que les instructions MOVS*, CMPS*, LODS*.

## Autres transferts

Le transfert entre certains registres pour lesquels il n'existe pas d'instruction peut s'effectuer en utilisant la pile, comme par exemple pour le transfert entre le registre AX et celui des indicateurs (FLAGS) :
L'instruction LAHF (Load AH from Flags) permet de transférer la valeur des 8 bits de poids faible du registre FLAGS vers le registre AH (bits 15 à 8 du registre AX / EAX). L'instruction SAHF (Save AH to Flags) effectue le transfert inverse.
L'instruction XLAT transfère l'octet pointé par DS:(BX+AL) dans le registre AL.