Programmer en assembleur
Historique
29 juillet 2010 : Création de cette page sur l'ancien site de Païou.
Difficulté
Pour : un esprit logique et curieux.
Introduction
L'assembleur est le langage de programmation le plus proche du langage machine. C'est en fait un mnémonique des instructions machine.
Le programme (qui est constitué de lignes de texte) est ensuite assemblé pour donner le code exécutable. Ce n'est pas vraiment une compilation. Le terme de traduction mot à mot serait plus réel.
Il n'est pas utilisé pour faire de grands programmes, ce serait trop pénible.
Pour ma part, je m'y intéresse plutôt pour désassembler les petits programmes d'amorçage sur les disquettes et disques durs.
Vous ne trouverez pas, ici, un cours d'assembleur complet, mais quelques instructions dont j'ai eu besoin.
Une liste plus complète et plus détaillée , 
Décomposition d'une instruction
Il faut faire une distinction entre les instructions en code machine et les instructions en assembleur ou mnémonique. Prenons comme exemple le chargement du registre AH avec la valeur 0Dh.
Dans les deux cas, une instruction est composée d'un opérateur et d'un ou plusieurs opérandes :
en mnémonique :MOV AH,0Davec MOV = opérateur, AH et 0D = opérandes.
en code machine :B40DB4 = opérateur, 0D = opérande. Ici, l'opérateur intègre déjà la destination AH.
L'opérateur indique quelle opération il faut effectuer, ici, charger un registre.
Les opérandes indiquent, ici, une destination et une source. On parle d'adressage. Plusieurs formes d'adressage peuvent se présenter.
Modes d'adressage
Ce paragraphe donne quelques exemples d'instructions qui utilisent différents modes d'adressage.
Vous pourrez voir le "opcode", le "mnémonique" et la décomposition du opcode en opérateur et opérande(s).
La signification des opérateurs sera donnée plus tard.
- adressage implicite
L'opération ne nécessite pas d'indication supplémentaire.
fa CLIfa = opérateur, il n'y a pas d'opérande.
- adressage inhérent (ou registre)
Le code de l'opération indique déjà sur quel registre s'effectue l'opération.
0E PUSH CS0E = opérateur, il n'y a pas d'opérande.
- adressage immédiat
L'opérande représente la valeur. Ce n'est pas vraiment un adressage.
B40E MOV AH,0EB4 = opérateur (indique ici également la destination = inhérent), 0E = opérande : valeur à charger (immédiat).
- adressage relatif
L'opérande donne un décalage par rapport à l'adresse actuelle. C'est un entier relatif (signé). Cet adressage est utilisé pour les sauts.
740B JZ xxxxJZ = opérateur, 0B = opérande : valeur du saut (relatif).
-
adressage direct
L'opérande donne l'adresse (offset) de la cellule mémoire qui contient la valeur.
Par défaut, c'est le registre DS qui précise la valeur du segment de mémoire.
8A260001 MOV AH,[0100]8A26 = opérateur, 0001 = opérande : adresse de la cellule DS:0100.
88260001 MOV [0100],AH8826 = opérateur (indique ici également la source = inhérent). 0001 = opérande : adresse de destination DS:0100.
Mais vous pouvez spécifier un segment différent de DS :
268A260001 MOV AH,ES:[0100]268A26 = opérateur (indique ici également la destination et le registre segment = inhérent). 0001 = opérande
ATTENTION : si le registre à charger a une taille 16 bits, ce sont deux cellules mémoire consécutives qui sont concernées !
Ainsi, dans l'exemple ci-contre, ce sont les adresses DS:0100 et DS:0101 qui sont lues.
À NOTER : dans le système adopté par Intel, dans un mot, les octets de poids faible sont lus avant les octets de poids fort (système little-endian)
- adressage indirect
Le code opérateur contient le nom d'un registre qui pointe vers l'offset de la cellule mémoire dont il faut charger le contenu. Le registre doit être un des registres 16 bits suivants : BX, BP, SI, DI.
Avec les registres BX, SI et DI, le segment de l'adresse est donné par DS, comme sur la figure. L'adresse complète est DS:BX.
Mais, pour BP, il s'agit deSS et l'adresse complète devient SS:BP.
8A27 MOV AH,[BX]8A27 = opérateur (indique également la destination et le registre qui contient l'adresse).
Ici également, vous pouvez spécifier un registre segment différent du registre par défaut :
268A27 MOV AH,ES:[BX]268A27 = opérateur (indique également la destination et les registres qui contiennent l'adresse).
adressage indexé
Comme pour l'adressage indirect, un registre pointe vers une adresse. Mais cette adresse est une adresse de base, à laquelle s'ajoute encore un déplacement, spécifié par l'opérande.
8A6605 MOV AH,[BP+05]
Comme précédemment, il est possible de préciser un registre de segment différent du registre par défaut :
268A6705 MOV AH,ES:[BX+05]
Quelques instructions
Chargement de données
- MOV destination,sourceCopie le contenu de source dans destination.
destination = Registre général, source = Registre quelconque.
destination = Mémoire, source = Registre quelconque.
destination = Registre général, source = Mémoire.
destination = Registre général, source = Constante.
destination = Mémoire, source = Constante.
destination = Registre de segment, source = Registre général.
ATTENTION : Source et Destination doivent avoir la même taille. On ne peut charger dans un registre de segment que le contenu d’un registre général (SI, DI et BP sont considérés ici comme des registres généraux).
ATTENTION : si le registre à charger a une taille 16 bits, ce sont deux cellules mémoire consécutives qui sont concernées !
- XCHG destination,sourceéchange les valeurs de source et de destination
destination = Registre général, source = Registre général.
destination = Registre général, source = Mémoire.
destination = Mémoire, source = Registre général.
- LEA destination,sourceLoad effective address : l'instruction est utilisée pour préparer les valeurs d'un pointeur.
destination = Registre 16 bits, source = Mémoire
Exemple : LEA AX, 3 [BX] ou, ce qui revient au même :LEA AX,[BX+3]
- LDS destination,sourceLoad pointer using DS : l'instruction charge un double mot (4 octets).
destination1 = Registre DS, source = mot d'adresse la plus élevée (cellules +2 et +3)
destination2 = Registre général 16 bits, source = mot d'adresse faible (cellules 0 et +1)
Exemple : LDS SI, SS:[BX]
Gestion de caractères
- LODSB Load String Byte = charge un octet
destination = AL, source = octet adressé par DS:SI.
Le registre SI est automatiquement incrémenté (Si DF = 0) ou décrémenté (Si DF = 1)
- STOSB Store String Byte = décharge un octet
source = AL, destination = octet adressé par ES:DI.
Le registre DI est automatiquement incrémenté (Si DF = 0) ou décrémenté (Si DF = 1)
- MOVSB Move String Byte = copie un octet
source = octet adressé par DS:SI, destination = octet adressé par ES:DI.
Les registres SI et DI sont automatiquement incrémentés (Si DF = 0) ou décrémentés (Si DF = 1)
- LODSW Load String Word = charge un mot (2 octets)
destination = AX, source = mot (2 cellules) adressé par DS:SI.
Le registre SI est automatiquement incrémenté de 2 (Si DF = 0) ou décrémenté de 2(Si DF = 1)
- STOSW Store String Word = décharge un mot (2 octets)
source = AX, destination = mot (2 cellules) adressé par ES:DI.
Le registre DI est automatiquement incrémenté de 2 (Si DF = 0) ou décrémenté de 2(Si DF = 1)
- MOVSW Move String Word = copie un mot (2 octets)
source = mot (2 cellules) adressé par DS:SI, destination = mot (2 cellules) adressé par ES:DI.
Les registres SI et DI sont automatiquement incrémentés de 2 (Si DF = 0) ou décrémentés de 2(Si DF = 1)
- LODSD, STOSD, MOVSD Load (ou Store ou Move) String Double Word = charge (ou décharge ou copie) un double mot (4 octets)
Comme pour LODSW, STOSW et MOVSW, mais les opérations se font sur 4 octets à la fois et c'est EAX qui est utilisé.
Les registres SI et DI sont automatiquement incrémenté de 4 (Si DF = 0) ou décrémentés de 4(Si DF = 1)
- REP Repeat = répète l'instruction qui va suivre, jusqu'à ce que CX = 0
nombre de répétitions = registre CX
Exemple : REP MOVSB
Opérations logiques
- AND dest,src
réalise un ET logique entre src et dest et place le résultat dans dest
Ex. AND AX,00FFréalise un ET logique entre le registre AX et la valeur immédiate 00FF et place le résultat dans le registre AX
Rem AND sur la même source et destination permet de tester si cette valeur est nulle ou non.
- OR dest,src
réalise un OU logique entre src et dest et place le résultat dans dest
Ex. OR BX,00FFréalise un OU logique entre le registre BX et la valeur immédiate 00FF et place le résultat dans le registre BX.
- XOR dest,src
réalise un OU EXCLUSIF logique entre src et dest et place le résultat dans dest
Ex. XOR CX,CXréalise un OU EXCLUSIF logique entre le registre CX et lui-même et place le résultat dans le registre CX
Rem XOR sur la même source et destination permet de mettre cette valeur à zéro.
- NOT dest
inverse les bits de dest et laisse le résultat dans dest
Ex. NOT AXinverse les bits du registre AX.
Afficher
Dans tout programme, il faut pouvoir afficher : pour saisir des données, pour afficher des résultats ...
La gestion de l'affichage, en assembleur, 
Gérer la pile
Dans pratiquement tous les programmes on utilise la pile, par exemple avant d'appeler une routine.
La gestion de la pile, en assembleur, 