0% ont trouvé ce document utile (0 vote)
218 vues28 pages

Introduction au langage machine

Ce chapitre introduit la programmation en langage machine et en assembleur pour le microprocesseur 80486, en se concentrant sur un sous-ensemble d'instructions et de registres. Il décrit les caractéristiques des processeurs 80x86, les types d'instructions, les modes d'adressage, et la manière d'écrire des programmes en langage symbolique. Le chapitre aborde également les instructions de branchement et fournit un tableau des instructions avec leur code opération et explication.

Transféré par

yahyaouiimen
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
218 vues28 pages

Introduction au langage machine

Ce chapitre introduit la programmation en langage machine et en assembleur pour le microprocesseur 80486, en se concentrant sur un sous-ensemble d'instructions et de registres. Il décrit les caractéristiques des processeurs 80x86, les types d'instructions, les modes d'adressage, et la manière d'écrire des programmes en langage symbolique. Le chapitre aborde également les instructions de branchement et fournit un tableau des instructions avec leur code opération et explication.

Transféré par

yahyaouiimen
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Chapitre 3 : Introduction au langage

machine

Introduction :
Dans cette partie on va étudier la programmation en langage machine et en
assembleur d’un microprocesseur. L’étude complète d’un processeur réel, comme le
80486 ou le Pentium fabriqués par Intel, dépasse largement le cadre de ce cours : le
nombre d’instructions et de registres est très élevé. Nous allons ici nous limiter à un
sous ensemble du microprocesseur 80486 (seuls les registres et les instructions les
plus simples seront étudiés). De cette façon, nous pourrons tester sur un PC les
programmes en langage machine que nous écrirons.

I- Caractéristiques du processeur 80x86 :


La gamme de microprocesseurs 80x86 équipe les micro-ordinateurs de type PC et
compatibles. Les premiers modèles de PC, commercialisés au début des années 1980,
utilisaient le 8086, un microprocesseur 16 bits (Avec un bus d’adresses de 20 bits
pour gérer jusqu’a 1Mo de mémoire).
Le modèles suivants ont utilisé successivement le 80286, 80386, 80486 et Pentium
(ou 80586). Chacun de ces processeurs est plus puissant que les précédents :
augmentation de la fréquence d’horloge, de la largeur de bus (32 bits d’adresse et de
données), introduction de nouvelles instructions (par exemple calcul sur les réels) et
ajout de registres.
Chacun d’entre eux est compatible avec les modèles précédents ; un programme écrit
dans le langage machine du 286 peut s’exécuter sans modification sur un 486.
L’inverse n’est pas vrai, puisque chaque génération a ajouté des instructions
nouvelles. On parle donc de compatibilité ascendante.

Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 69


II- Jeu d’instruction :
II.1- Types d’instructions :
II.1.1- Instructions d’affectation:
Déclenchent un transfert de données entre l’un des registres du processeur et la
mémoire principale.
 Transfert CPU  Mémoire Principale (MP) (= lecture en MP) ;
 Transfert CPU  Mémoire Principale (MP) (= écriture en MP) ;

II.1.2- Instructions arithmétiques et logiques:


Opérations entre une donnée et l’accumulateur AX. Le résultat est placé dans
l’accumulateur. La donnée peut être une constante ou une valeur contenue dans un
emplacement mémoire.
Exemple :
 Addition : AX  AX + donnée ;
 Soustraction : AX  AX - donnée ;
 Incrémentation de AX : AX  AX + 1 ;
 Décrémentation de AX : AX  AX – 1 ;
 Décalages à gauche et à droite ;

II.1.3- Instructions de comparaison:


Comparaison du registre AX à une donnée et positionnement des indicateurs.

II.1.4- Instructions de branchement:


La prochaine instruction à exécuter est repérée en mémoire par le registre IP. Les
instructions de branchement permettent de modifier la valeur de IP pour exécuter une
autre instruction (boucles, tests, etc.).
On distingue deux types de branchements :
 Branchements inconditionnels : IP  adresse d’une instruction ;
 Branchements conditionnels : Si une condition est satisfaite, alors branchement,
sinon passage simple à l’instruction suivante.

II.2- Codage des instructions et mode d’adressage :


Les instructions et leurs opérandes (paramètres) sont stockés en mémoire principale.
Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 70
La taille totale d’une instruction (nombre de bits nécessaires pour la représenter en
mémoire) dépend du type d’instruction et aussi du type d’opérande. Chaque
instruction est toujours codée sur un nombre entier d’octets, afin de faciliter son
décodage par le processeur.
Une instruction est composée de deux champs :
 Le code opération, qui indique au processeur quelle instruction réaliser ;
 Le champ opérande qui contient la donnée, ou la référence à une donnée en
mémoire (son adresse).

Champ Champ
Code Opération Code opérande

Selon la manière dont la donnée est spécifiée, c’est à dire selon le mode d’adressage
de la donnée, une instruction sera codée par 1, 2, 3 ou 4 octets.
Nous distinguerons ici quatre modes d’adressage : implicite, immédiat, direct et
relatif (nous étudierons plus tard un autre mode, l’adressage indirect).

II.2.1- Adressage implicite:


L’instruction contient seulement le code opération, sur 1 ou 2 octets.

Code opération
(1 ou 2 octets)

L’instruction porte sur des registres ou spécifie une opération sans opérande
(exemple : “incrémenter AX”).

II.2.2- Adressage immédiat:


Le champ opérande contient la donnée (une valeur constante sur 1 ou 2 octets).
Code Opération Valeur
(1 ou 2 octets) (1 ou 2 octets)

Exemple :
“Ajouter la valeur 5 à AX”. Ici l’opérande 5 est codée sur 2 octets puisque
l’opération porte sur un registre 16 bits (AX).

Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 71


II.2.3- Adressage direct :
Le champ opérande contient l’adresse de la donnée en mémoire principale sur 2
octets.

Code Opération Adresse de la donnée


(1 ou 2 octets) (1 ou 2 octets)

Remarque : dans le 80x86, les adresses sont toujours manipulées sur 16 bits, quelle
que soit la taille réelle du bus. Nous verrons plus tard comment le processeur
fabrique les adresses réelles sur 32 bits.
Exemple :
“Placer dans AX la valeur contenue à l’adresse 130H”.

II.2.4- Adressage relatif :


Ce mode d’adressage est utilisé pour certaines instructions de branchement. Le
champ opérande contient un entier relatif codé sur 1 octet, nommé déplacement, qui
sera ajouté à la valeur courante de IP.

Code Opération Déplacement


(1 octets) (1 octets)

II.3- Temps d’exécution :


Chaque instruction nécessite un certain nombre de cycles d’horloges pour s’effectuer.
Le nombre de cycles dépend de la complexité de l’instruction et aussi du mode
d’adressage : il est plus long d’accéder à la mémoire principale qu’à un registre du
processeur.
La durée d’un cycle dépend bien sur de la fréquence d’horloge de l’ordinateur. Plus
l’horloge bat rapidement, plus un cycle est court et plus on exécute un grand nombre
d’instructions par seconde.

II.4- Ecriture des instructions en langage symbolique :


Voici un programme en langage machine 80486, implanté à l’adresse 0100H :
A1 01 10 03 06 01 12 A3 01 14

Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 72


Ce programme additionne le contenu de deux cases mémoire et range le résultat dans
une troisième. Nous avons simplement transcrit en hexadécimal le code du
programme. Il est clair que ce type d’écriture n’est pas très utilisable par un être
humain.
A chaque instruction que peut exécuter le processeur correspond une représentation
binaire sur un ou plusieurs octets, comme on l’a vu plus haut. C’est le travail du
processeur de décoder cette représentation pour effectuer les opérations
correspondantes.
Afin de pouvoir écrire (et relire) des programmes en langage machine, on utilise une
notation symbolique, appelée langage assembleur. Ainsi, la première instruction du
programme ci-dessus (code A1 01 10) sera notée :
MOV AX, [0110]
Elle indique que le mot mémoire d’adresse 0110H est chargé dans le registre AX du
processeur.
On utilise des programmes spéciaux, appelés assembleurs, pour traduire
automatiquement le langage symbolique en code machine.
Voici une transcription langage symbolique du programme complet. L’adresse de
début de chaque instruction est indiquée à gauche (en hexadécimal).
Adresse Contenu MP Langage symbolique Explication
0100 A1 01 10 MOV AX, [0110] Charger AX
avec le contenu
de 0110
0103 03 06 01 12 ADD AX, [0112] Ajouter le
contenu de
0112 a AX.
0107 A3 01 14 MOV [0114], AX Ranger AX en
0114

II.4.1- Sens des mouvements des données :


La plupart des instructions spécifient des mouvements de données entre la mémoire
principale et le microprocesseur. En langage symbolique, on indique toujours la
destination, puis la source. Ainsi l’instruction
Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 73
MOV AX, [0110]
Transfère le contenu de l’emplacement mémoire 0110H dans l’accumulateur, tandis
que MOV [0112], AX transfère le contenu de l’accumulateur dans l’emplacement
mémoire 0112.
L’instruction MOV (de l’anglais move, déplacer) s’écrit donc toujours :
MOV destination, source

II.4.2- Mode d’adressage :


 En adressage immédiat, on indique simplement la valeur de l’opérande en
hexadécimal.
Exemple :
MOV AX, 12
 En adressage direct, on indique l’adresse d’un emplacement en mémoire
principale en hexadécimal entre crochets :
Exemple :
MOV AX, [A340]
 En adressage relatif, on indique simplement l’adresse (hexa). L’assembleur
traduit automatiquement cette adresse en un déplacement (relatif sur un octet).
Exemple :
JNE 0108

II.4.3- Retour au dos :


A la fin d’un programme en assembleur, on souhaite en général que l’interpréteur de
commandes du DOS reprenne le contrôle du PC. Pour cela, on utilisera la séquence
de deux instructions :
MOV AH, 4C
INT 21

II.4.4- Programme Debug :


Debug est un programme qui s’exécute sur PC (sous DOS) et qui permet de
manipuler des programmes en langage symbolique. Il est normalement distribué avec
toutes les versions du système MS/DOS.
Les fonctionnalités principales de Debug sont les suivantes :

Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 74


 Affichage du contenu d’une zone mémoire en hexadécimal ou en ASCII ;
 Modification du contenu d’une case mémoire quelconque ;
 Affichage en langage symbolique d’un programme ;
 Entrée d’un programme en langage symbolique ; Debug traduit les instructions
en langage machine et calcule automatiquement les déplacements en adressage
relatif.
 Affichage et modification de la valeur des registres du processeur.

II.5- Branchements :
Normalement, le processeur exécute une instruction puis passe à celle qui suit en
mémoire, et ainsi de suite séquentiellement. Il arrive fréquemment que l’on veuille
faire répéter au processeur une certaine suite d’instructions, comme ici :
Repeter 3 fois:
Ajouter 5 au registre BX
En d’autres occasions, il est utile de déclencher une action qui dépend du résultat
d’un test :
Si x < 0:
y=-x
Sinon
y=x
Dans ces situations, on doit utiliser une instruction de branchement, ou saut, qui
indique au processeur l’adresse de la prochaine instruction à exécuter.
Rappelons que le registre IP du processeur conserve l’adresse de la prochaine
instruction à exécuter. Lors d’un déroulement normal, le processeur effectue les
actions suivantes pour chaque instruction :
1. lire et décoder l’instruction à l’adresse IP ;
2. IP  IP + taille de l’instruction ;
3. exécuter l’instruction.
Pour modifier le déroulement normal d’un programme, il suffit que l’exécution de
l’instruction modifie la valeur de IP. C’est ce que font les instructions de
branchement.

Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 75


On distingue deux catégories de branchements, selon que le saut est toujours effectué
(sauts inconditionnels) ou qu’il est effectué seulement si une condition est vérifiée
(sauts conditionnels).

II.6- Tableau des instructions :


Voici quelques instructions du 80x86. Le code de l’instruction est donné en
hexadécimal dans la deuxième colonne. La colonne suivante précise le nombre
d’octets nécessaires pour coder l’instruction complète (opérande inclus). On note
valeur une valeur sur 16 bits, et adr une adresse sur 16 bits également.
Symbole Code opération Octets Explication
MOV AX, valeur B8 3 AX  valeur
MOV AX, [adr] A1 3 AX  contenu de l’adresse adr.
MOV [adr], AX A3 3 range AX à l’adresse adr.
ADD AX, valeur 05 3 AX  AX + valeur
ADD AX, [adr] 03 06 4 AX  AX + contenu de adr.
SUB AX, valeur 2D 3 AX  AX - valeur
SUB AX, [adr] 2B 06 4 AX  AX - contenu de adr.
SHR AX, 1 D1 E8 2 décale AX à droite.
SHL AX, 1 D1 E0 2 décale AX à gauche.
INC AX 40 1 AX  AX + 1
DEC AX 48 1 AX  AX - 1
CMP AX, valeur 3D 3 compare AX et valeur.
CMP AX, [adr] 3B 06 4 compare AX et contenu de adr.
JMP adr EB 2 saut inconditionnel (adr. relatif).
JE adr 74 2 saut si =
JNE adr 75 2 saut si #
JG adr 7F 2 saut si >
JLE adr 7E 2 saut si < ou =
JA adr saut si CF = 0
JB adr saut si CF = 1
MOV AH, 4C B4 4C 2 Fin du programme (retour au
INT 21 DOS)
CD 21 2

Dans ces situations, on doit utiliser une instruction de branchement, ou saut, qui
indique au processeur l’adresse de la prochaine instruction à exécuter.
Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 76
Rappelons que le registre IP du processeur conserve l’adresse de la prochaine
instruction à exécuter. Lors d’un déroulement normal, le processeur effectue les
actions suivantes pour chaque instruction :
1. lire et décoder l’instruction à l’adresse IP ;
2. IP  IP + taille de l’instruction ;
3. exécuter l’instruction.
Pour modifier le déroulement normal d’un programme, il suffit que l’exécution de
l’instruction modifie la valeur de IP. C’est ce que font les instructions de
branchement.
On distingue deux catégories de branchements, selon que le saut est toujours effectué
(sauts inconditionnels) ou qu’il est effectué seulement si une condition est vérifiée
(sauts conditionnels).

II.7- Les sauts:


II.7.1- Saut inconditionnel:
La principale instruction de saut inconditionnel est JMP. En adressage relatif,
l’opérande de JMP est un déplacement, c’est à dire une valeur qui va être ajoutée à IP.
L’action effectuée par JMP est :
IP = IP + déplacement
Le déplacement est un entier relatif sur codée 8 bits. La valeur du déplacement à
utiliser pour atteindre une certaine instruction est :
Déplacement = adr. Instruction visée - adr. Instruction suivante
Exemple :
Le programme suivant écrit indéfiniment la valeur 0 à l’adresse 0140H. La première
instruction est implantée à l’adresse 100H.
Adresse Contenu MP Langage symbolique Explication
0100 B8 00 00 MOV AX, 0 met AX a zéro
0103 A3 01 40 MOV [140], AX écrit à
l’adresse 140
0105 EB FC JMP 0103 branche en 103
0107 Instruction jamais
exécutée

Le déplacement est ici égal à FCH, c’est à dire -4 (=103H-107H).

II.7.2- Indicateurs :
Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 77
Les instructions de branchements conditionnels utilisent les indicateurs, qui sont des
bits spéciaux positionnés par l’UAL après certaines opérations. Les indicateurs sont
regroupés dans le registre d’état du processeur. Ce registre n’est pas accessible
globalement par des instructions ; chaque indicateur est manipulé individuellement
par des instructions spécifiques.
Nous étudierons ici les indicateurs nommés ZF, CF, SF et OF.

ZF Zero Flag
Cet indicateur est mis à 1 lorsque le résultat de la dernière opération est zéro.
Lorsque l’on vient d’effectuer une soustraction (ou une comparaison), ZF=1 indique
que les deux opérandes étaient égaux. Sinon, ZF est positionné à 0.

CF Carry Flag
C’est l’indicateur de report (retenue), qui intervient dans les opérations d’addition et
de soustractions sur des entiers naturels. Il est positionné en particulier par les
instructions ADD, SUB et CMP.
CF = 1 s’il y a une retenue après l’addition ou la soustraction du bit de poids fort des
opérandes. Exemples (sur 4 bits pour simplifier) :
0 1 0 0 1 1 0 0 1 1 1 1
+ 0 1 1 0 + 0 1 1 0 + 0 0 0 1
--------- --------- ---------
CF=0 1 0 1 0 CF=1 0 0 1 0 CF=1 0 0 0 0

SF Sign Flag
SF est positionné à 1 si le bit de poids fort du résultat d’une addition ou soustraction
est 1 ; sinon SF=0. SF est utile lorsque l’on manipule des entiers relatifs, car le bit de
poids fort donne alors le signe du résultat. Exemples (sur 4 bits) :
0 1 0 0 1 1 0 0 1 1 1 1
+ 0 1 1 0 + 0 1 1 0 + 0 0 0 1
--------- --------- ---------
SF=1 1 0 1 0 SF=0 0 0 1 0 SF=0 0 0 0 0

OF Overflow Flag

Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 78


Indicateur de débordement OF=1 si le résultat d’une addition ou soustraction donne
un nombre qui n’est pas codable en relatif dans l’accumulateur (par exemple si
l’addition de 2 nombres positifs donne un codage négatif).
0 1 0 0 1 1 0 0 1 1 1 1
+ 0 1 1 0 + 0 1 1 0 + 0 0 0 1
--------- --------- ---------
OF=1 1 0 1 0 OF=0 0 0 1 0 OF=1 0 0 0 0

Lorsque l’UAL effectue une addition, une soustraction ou une comparaison, les
quatre indicateurs sont positionnés. Certaines autres instructions peuvent modifier les
indicateurs.

II.7.3.1- Instruction CMP :


Il est souvent utile de tester la valeur du registre AX sans modifier celui-ci.
L’instruction CMP effectue exactement les mêmes opérations que SUB, mais ne
stocke pas le résultat de la soustraction. Son seul effet est donc de positionner les
indicateurs.
Exemple :
Après l’instruction :
CMP AX, 5
On aura ZF = 1 si AX contient la valeur 5, et ZF = 0 si AX est différent de 5.

II.7.3.2- Instructions STC et CLC:


Ces deux instructions permettent de modifier la valeur de l’indicateur CF.
Symbole Explication
STC CF  1 (Set Carry)
CLC CF  0 (Clear Carry)

II.7.4- Sauts conditionnels:


Les instructions de branchements conditionnels effectuent un saut (comme JMP) si
une certaine condition est vérifiée. Si ce n’est pas le cas, le processeur passe à
l’instruction suivante (l’instruction ne fait rien).
Les conditions s’expriment en fonction des valeurs des indicateurs. Les instructions
de branchement conditionnel s’utilisent en général immédiatement après une
instruction de comparaison CMP.
Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 79
Voici la liste des instructions de branchement les plus utiles :
JE Jump if Equal
saut si ZF = 1 ;
JNE Jump if Not Equal
saut si ZF = 0 ;
JG Jump if Greater
saut si ZF = 0 et SF = OF ;
JLE Jump if Lower or Equal
saut si ZF=1 ou SF6=OF ;
JA Jump if Above
saut si CF=0 et ZF=0 ;
JBE Jump if Below or Equal
saut si CF=1 ou ZF=1.
JB Jump if Below
saut si CF=1.
Remarque : les instructions JE et JNE sont parfois écrites JZ et JNZ (même code
opération).

II.8- Instructions arithmétiques et logiques:


Les instructions arithmétiques et logiques sont effectuées par l’UAL. Nous avons
déjà vu les instructions d’addition et de soustraction (ADD, SUB). Nous abordons ici
les instructions qui travaillent sur la représentation binaire des données : décalages de
bits, opérations logiques bit à bit.
Notons que toutes ces opérations modifient l’état des indicateurs.

II.8.1- Instructions de décalage et de rotation:


Ces opérations décalent vers la gauche ou vers la droite les bits de l’accumulateur.
Elles sont utilisées pour décoder bit à bit des données, ou simplement pour diviser ou
multiplier rapidement par une puissance de 2. En effet, décaler AX de n bits vers la
n
gauche revient à le multiplier par 2 (sous réserve qu’il représente un nombre naturel
et qu’il n’y ait pas de dépassement de capacité). De même, un décalage vers la droite
n
revient à diviser par 2 .

Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 80


Voici les variantes les plus utiles de ces instructions. Elles peuvent opérer sur les
registres AX ou BX (16 bits) ou sur les registres de 8 bits AH, AL, BH et BL.
SHL registre, 1 (Shift Left)
Décale les bits du registre d’une position vers la gauche. Le bit de gauche est
transféré dans l’indicateur CF. Les bits introduits à droite sont à zéro.

CF 0

SHR registre, 1 (Shift Right)


Comme SHL mais vers la droite. Le bit de droite est transféré dans CF.
0
CF

SHL et SHR peuvent être utilisé pour multiplier/diviser des entiers naturels (et non
des relatifs car le bit de signe est perdu).
ROL registre, 1 (Rotate Left)
Rotation vers la gauche : le bit de poids fort passe à droite, et est aussi copié dans CF.
Les autres bits sont décalés d’une position.

CF

ROR registre, 1 (Rotate Right)


Comme ROL, mais à droite.

CF

RCL registre, 1 (Rotate Carry Left)


Rotation vers la gauche en passant par l’indicateur CF. CF prend la place du bit de
poids faible ; le bit de poids fort part dans CF.

CF

RCR registre, 1 (Rotate Carry Right)


Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 81
Comme RCL, mais vers la droite.

CF

RCL et RCR sont utiles pour lire bit à bit le contenu d’un registre (On pourra utiliser
l’instruction JB pour brancher si CF=1 après RCL ou RCR).

.II.8.2- Instructions Logiques:


Les instructions logiques effectuent des opérations logiques bit à bit. On dispose de
trois opérateurs logiques : ET, OU et OU exclusif. Il n’y a jamais propagation de
retenue lors de ces opérations (chaque bit du résultat est calculé indépendamment des
autres).
0 0 1 1 0 0 1 1 0 0 1 1
OU 0 1 0 1 ET 0 1 0 1 OU EX 0 1 0 1
----------- ----------- -----------
0 1 1 1 0 0 0 1 0 1 1 0

Les trois instructions OR, AND et XOR sont de la forme :


OR destination, source.
Destination désigne le registre ou l’emplacement mémoire (adresse) où doit être
placé le résultat. source désigne une constante (adressage immédiat), un registre
(adressage implicite), ou une adresse (adressage direct).

Exemple :
OR AX, FF00 ; AX  AX ou FFOO
OR AX, BX ; AX  AX ou BX
OR AX, [1492] ; AX  AX ou [1492]
OR destination, source (OU)
OU logique. Chaque bit du résultat est égal à 1 si au moins l’un des deux bits
opérande est 1.
OR est souvent utilisé pour forcer certains bits à 1. Par exemple après OR AX, FF00 ;
l’octet de poids fort de AX vaut FF, tandis que l’octet de poids faible est inchangé.
AND destination, source (ET)
ET logique. Chaque bit du résultat est égal à 1 si les deux bits opérandes sont à 1.
Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 82
AND est souvent utilisé pour forcer certains bits à 0. Après AND AX, FF00; l’octet
de poids faible de AX vaut 00, tandis que l’octet de poids fort est inchangé.
XOR destination, source (OU EXCLUSIF)
OU exclusif. Chaque bit du résultat est égal à 1 si l’un ou l’autre des bits opérandes
(mais pas les deux) vaut 1.
XOR est souvent utilisé pour inverser certains bits. Après XOR AX, FFFF, tous les
bits de AX sont inversés.

Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 83


CHAPITRE 4
L’assembleur 80X86

Objectif général :
Apprendre la programmation en assembleur
d’un processeur 80X86.

Objectifs spécifiques :
 Définir la structure d’un programme en
assembleur.
 Pouvoir programmer avec l’assembleur.

Durée : 6h

Chapitre 4 : L’assembleur 80x86

Introduction :
Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 84
Chaque microprocesseur reconnaît un ensemble d’instructions appelé jeu
d’instructions (Instruction Set) fixé par le constructeur. Pour les microprocesseurs
classiques, le nombre d’instructions reconnues varie entre 75 et 150
(microprocesseurs CISC : Complex Instruction Set Computer). Il existe aussi des
microprocesseurs dont le nombre d’instructions est très réduit (microprocesseurs
RISC : Reduced Instruction Set Computer) : entre 10 et 30 instructions, permettant
d’améliorer le temps d’exécution des programmes.

I- Pourquoi l’assembleur :
Lorsque l’on doit lire ou écrire un programme en langage machine, il est difficile
d’utiliser la notation hexadécimale. On écrit les programmes à l’aide de symboles
(appelé parfois Mnémoniques) comme MOV, ADD, etc. Les concepteurs de
processeur, comme Intel, fournissent toujours une documentation avec les codes des
instructions de leur processeur, et les symboles correspondant.
Nous avons déjà utilisé un programme, debug, très utile pour traduire
automatiquement les symboles des instructions en code machine. Cependant, debug
n’est utilisable que pour mettre au point de petits programmes. En effet, le
programmeur doit spécifier lui même les adresses des données et des instructions.
Exemple :
Le programme suivant, qui multiplie une donnée en mémoire par 8 :
0100 MOV BX, [0112] ; charge la donnee
0103 MOV AX, 3
0106 SHL BX ; decale a gauche
0108 DEC AX
0109 JNE 0106 ; recommence 3 fois
010B MOV [0111], BX ; range le resultat
010E MOV AH, 4C
0110 INT 21H
0112 ; on range ici la donnee
Nous avons spécifié que la donnée était rangée à l’adresse 0111H, et que l’instruction
de branchement JE allait en 0106H. Si l’on désire modifier légèrement ce programme,
par exemple ajouter une instruction avant MOV BX, [0111], il va falloir modifier ces
Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 85
deux adresses. On conçoit aisément que ce travail devienne très difficile si le
programme manipule beaucoup de variables.
L’utilisation d’un assembleur résout ces problèmes. L’assembleur permet en
particulier de nommer les variables (un peu comme en langage C) et de repérer par
des étiquettes certaines instructions sur lesquelles on va effectuer des branchements.

II- Le langage assembleur:


II.1- Structure du programme source :
La structure générale d’un programme assembleur est représentée figure IV.1.
Comme tout programme, un programme écrit en assembleur comprend des
définitions de données et des instructions, qui s’écrivent chacune sur une ligne de
texte.
Les données sont déclarées par des directives, mots clef spéciaux que comprend
l’assembleur. Les directives qui déclarent des données sont regroupées dans le
segment de données, qui est délimité par les directives SEGMENT et ENDS.
Les instructions sont placées dans un autre segment, le segment de code.
La directive ASSUME est toujours présente et sera expliquée plus loin.
La première instruction du programme (dans le segment d’instruction) doit toujours
être repérée par une étiquette. Le fichier doit se terminer par la directive END avec le
nom de l’étiquette de la première instruction (ceci permet d’indiquer à l’éditeur de
liens quelle est la première instruction à exécuter lorsque l’on lance le programme).
Les points-virgules indiquent des commentaires.

II.2- Déclaration des variables :


On déclare les variables à l’aide de directives. L’assembleur attribue a chaque
variable une adresse. Dans le programme, on repère les variables grâce à leur nom.
Les noms des variables (comme les étiquettes) sont composés d’une suite de 31
caractères au maximum, commençant obligatoirement par une lettre. Le nom peut
comporter des majuscules, des minuscules, des chiffres, plus les caractères @, ? et _.
Remarque :
Lors de la déclaration d’une variable, on peut lui affecter une valeur initiale.

Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 86


Figure IV-1 : Structure d’un programme en assembleur (fichier .ASM).

II.2.1- Variables de 8 ou 16 bits :


Les directives DB (Define Byte) et DW (Define Word) permettent de déclarer des
variables de respectivement 1 ou 2 octets.
Exemple :
data SEGMENT
entree DW 15 ; 2 octets initialises a 15
sortie DW ? ; 2 octets non initialises
cle DB ? ; 1 octet non initialise
nega DB -1 ; 1 octet initialise a -1
data ENDS
Les valeurs initiales peuvent être données en hexadécimal (constante terminée par H)
ou en binaire (terminée par b) :
data SEGMENT
truc DW 0F0AH ; en hexa
masque DB 01110000b ; en binaire
data ENDS
Les variables s’utilisent dans le programme en les désignant par leur nom. Après la
déclaration précédente.
Exemple :
Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 87
MOV AX, truc
AND AL, masque
MOV truc, AX
 L’assembleur se charge de remplacer les noms de variable par les adresses
correspondantes.

II.2.2- Tableaux :
Il est aussi possible de déclarer des tableaux, c’est à dire des suite d’octets ou de mots
consécutifs.
Pour cela, utiliser plusieurs valeurs initiales :
data SEGMENT
machin db 10, 0FH ; 2 fois 1 octet
chose db -2, ’ALORS’
data ENDS
Remarque :
La déclaration de la variable chose : un octet à -2 (=FEH), suivi d’une suite de
caractères. L’assembleur n’impose aucune convention pour la représentation des
chaînes de caractères : c’est à l’utilisateur d’ajouter si nécessaire un octet nul pour
marquer la fin de la chaîne.
Après chargement de ce programme, la mémoire aura le contenu suivant :

Si l’on veut écrire un caractère X à la place du O de ALORS, on pourra écrire :


MOV AL, ’X’
MOV chose+1, AL
Notons que chose +1 est une constante (valeur connue au moment de l’assemblage) :
l’instruction générée par l’assembleur pour

Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 88


MOV chose+1, AL
Est MOV [adr], AL.

II.2.3- Directive dup :


Lorsque l’on veut déclarer un tableau de n cases, toutes initialisées à la même valeur,
on utilise la directive dup :
tab DB 100 dup (15) ; 100 octets valant 15
zzz DW 10 dup (?) ; 10 mots de 16 bits non initialises

II.3- Segmentation de la mémoire :


Nous abordons ici le problème de la segmentation de la mémoire. Nous venons de
voir qu’en assembleur, les données étaient normalement regroupées dans une zone
mémoire nommé segment de données, tandis que les instructions étaient placées
dans un segment d’instructions. Ce partage se fonde sur la notion plus générale de
segment de mémoire, qui est à la base du mécanisme de gestion des adresses par les
processeurs 80x86.
Nous avons vu plus haut que les instructions utilisaient normalement des adresses
codées sur 16 bits. Nous savons aussi que le registre IP, qui stocke l’adresse d’une
16
instruction, fait lui aussi 16 bits. Or, avec 16 bits il n’est possible d’adresser que 2
= 64 Kilo octets.
Le bus d’adresse du 80486 possède 32 bits. Cette adresse de 32 bits est formée par la
juxtaposition d’un registre segment (16 bits de poids fort) et d’un déplacement (offset,
16 bits de poids faible). Les adresses que nous avons manipulées jusqu’ici sont des
déplacements.
Le schéma suivant illustre la formation d’une adresse 32 bits à partir du segment et
du déplacement sur 16 bits :

Figure IV-2 : Adresse de 32 bits.

Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 89


On appellera segment de mémoire une zone mémoire adressable avec une valeur
fixée du segment (les 16 bits de poids fort). Un segment a donc une taille maximale
de 64 Ko.

II.3.1- Segmentation de code et de données :


La valeur du segment est stockée dans des registres spéciaux de 16 bits. Le registre
DS (Data Segment) est utilisé pour le segment de données, et le registre CS (Code
Segment) pour le segment d’instructions.
Registre CS
Lorsque le processeur lit le code d’une instruction, l’adresse 32 bits est formée à
l’aide du registre segment CS et du registre déplacement IP. La paire de ces deux
registres est notée CS:IP.
Registre DS
Le registre DS est utilisé pour accéder aux données manipulées par le programme.
Ainsi, l’instruction
MOV AX, [0145]
Donnera lieu à la lecture du mot mémoire d’adresse DS:0145H.
Initialisation des registres segment
Dans ce cours, nous n’écrirons pas de programmes utilisant plus de 64 Ko de code et
64 Ko de données, ce qui nous permettra de n’utiliser qu’un seul segment de chaque
type. Par conséquent, la valeur des registres CS et de DS sera fixée une fois pour
toute au début du programme.
Le programmeur en assembleur doit se charger de l’initialisation de DS, c'est-à-dire
de lui affecter l’adresse du segment de données à utiliser.
Par contre, le registre CS sera automatiquement initialisé sur le segment contenant la
première instruction au moment du chargement en mémoire du programme (par le
chargeur du système d’exploitation).

II.3.2- Déclaration d’un segment en assembleur :


Comme nous l’avons vu (voir figure IV.1), les directives SEGMENT et ENDS
permettent de définir les segments de code et de données.

Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 90


La directive ASSUME permet d’indiquer à l’assembleur quel est le segment de
données et celui de code, afin qu’il génère des adresses correctes.
Enfin, le programme doit commencer, avant toute référence au segment de données,
par initialiser le registre segment DS, de la façon suivante :
MOV AX, nom_segment_de_donnees
MOV CS, AX
 Il serait plus simple de faire MOV CS, nom_segment_de_donnees mais il se
trouve que cette instruction n’existe pas.

II.4- Adressage indirect :


Nous introduisons ici un nouveau mode d’adressage, l’adressage indirect, qui est très
utile par exemple pour traiter des tableaux (Il existe encore d’autres modes
d’adressage, comme l’adressage indexé, que nous n’aborderons pas dans ce cours).
L’adressage indirect utilise le registre BX pour stocker l’adresse d’une donnée.
En adressage direct, on note l’adresse de la donnée entre crochets :
MOV AX, [130] ; adressage direct
De façon similaire, on notera en adressage indirect :
MOV AX, [BX] ; adressage direct
Ici, BX contient l’adressage de la donnée. L’avantage de cette technique est que l’on
peut modifier l’adresse en BX, par exemple pour accéder à la case suivante d’un
tableau.
Avant d’utiliser un adressage indirect, il faut charger BX avec l’adresse d’une donnée.
Pour cela, on utilise une nouvelle directive de l’assembleur, offset.
Exemple :
Programme calculant la somme de deux entiers de 16 bits
data SEGMENT
A DW 10 ; A = 10
B DW 1789 ; B = 1789
Result DW ? ; resultat
data ENDS
code SEGMENT
ASSUME DS:data, CS:code
Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 91
debut: MOV AX, data ; etiquette car 1ere instruction
MOV DS, AX ; initialise DS
; Le programme:
MOV AX, A
ADD AX, B
MOV result, AX ; range resultat
; Retour au DOS:
MOV AH, 4CH
INT 21H
code ENDS
END debut ; etiquette de la 1ere inst.
 On a calculer ici la somme de deux variables A et B et on a ranger le resultat dans
une variable nommé result.
Exemple (parcour d’un tableau) :
Voici un exemple plus complet utilisant l’adressage indirect. Ce programme passe
une chaîne de caractères en majuscules. La fin de la chaîne est repérée par un
caractère $. On utilise un ET logique pour masquer le bit 5 du caractère et le passer
en miniscule (voir le code ASCII).
data SEGMENT
tab DB ‘bienvenue à ISET Béja’, ’$’
data ENDS
code SEGMENT
ASSUME DS:data, CS:code
debut: MOV AX, data
MOV DS, AX
MOV BX, offset tab ; adresse debut tableau
repet: MOV AL, [BX] ; lis 1 caractere
AND AL, 1110110b ; (code ASCII)
MOV [BX], AL ; range le caractere
INC BX ; passe au suivant
CMP AL, ’$’ ; arrive au $ final ?
Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 92
JNE repet ; sinon recommencer
MOV AH, 4CH
INT 21H ; Retour au DOS
code ENDS
END debut
Spécification de la taille des données :
Dans certains cas, l’adressage indirect est ambigu. Par exemple, si l’on écrit
MOV [BX], 0 ; range 0 a l’adresse specifiee par BX
L’assembleur ne sait pas si l’instruction concerne 1, 2 ou 4 octets consécutifs.
Afin de lever l’ambiguïté, on doit utiliser une directive spécifiant la taille de la
donnée à transférer :
MOV byte ptr [BX], val ; concerne 1 octet
MOV word ptr [BX], val ; concerne 1 mot de 2 octets

II.5- La pile :
II.5.1- Notion de pile :
Les piles offrent un nouveau moyen d’accéder à des données en mémoire principale,
qui est très utilisé pour stocker temporairement des valeurs.
Une pile est une zone de mémoire et un pointeur qui conserve l’adresse du sommet
de la pile.

II.5.2- Instructions PUSH et POP:


Deux nouvelles instructions, PUSH et POP, permettent de manipuler la pile.
PUSH registre empile le contenu du registre sur la pile.
POP registre retire la valeur en haut de la pile et la place dans le regsitre spécifié.
Exemple : transfert de AX vers BX en passant par la pile.
PUSH AX ; Pile  AX
POP BX ; BX  Pile
La pile est souvent utilisée pour sauvegarder temporairement le contenu des registres :
; AX et BX contiennent des données a conserver
PUSH AX
PUSH BX

Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 93


MOV BX, truc ; on utilise AX
ADD AX, BX ; et BX
MOV truc, BX
POP BX ; recupere l’ancien BX
POP AX ; et l’ancien AX
On voit que la pile peut conserver plusieurs valeurs. La valeur dépilée par POP est la
dernière valeur empilée ; c’est pourquoi on parle ici de pile LIFO (Last In First Out,
Premier Entré Dernier Sorti).

II.5.3- Registre SS et SP:


La pile est stockée dans un segment séparé de la mémoire principale. Le processeur
possède deux registres dédiés à la gestion de la pile, SS et SP.
Le registre SS (Stack Segment) est un registre segment qui contient l’adresse du
segment de pile courant (16 bits de poids fort de l’adresse). Il est normalement
initialisé au début du programme et reste fixé par la suite.
Le registre SP (Stack Pointer) contient le déplacement du sommet de la pile (16 bits
de poids faible de son adresse).

Figure IV-3 : La pile.

Cette figure donne une représentation schématique de la pile. L’instruction PUSH


effectue les opérations suivantes :
 SP  SP - 2
 [SP] valeur du registre 16 bits.
Notons qu’au début (pile vide), SP pointe “sous” la pile.
L’instruction POP effectue le travail inverse :
 registre destination [SP]

Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 94


 SP  SP + 2
Si la pile est vide, POP va lire une valeur en dehors de l’espace pile, donc
imprévisible.

II.5.4- Déclaration d’une pile:


Pour utiliser une pile en assembleur, il faut déclarer un segment de pile, et y réserver
un espace suffisant. Ensuite, il est nécessaire d’initialiser les registres SS et SP pour
pointer sous le sommet de la pile.
Voici la déclaration d’une pile de 200 octets :
seg_pile SEGMENT stack ; mot clef stack car pile
DW 100 dup (?) ; reserve espace
base_pile EQU this word ; étiquette base de la pile
seg_pile ENDS
Noter le mot clef “stack” après la directive SEGMENT, qui indique à l’assembleur
qu’il s’agit d’un segment de pile. Afin d’initialiser SP, il faut repérer l’adresse du bas
de la pile ; c’est le rôle de la ligne.
base_pile EQU this word

PUSH POP

Figure IV-4 : Une pile vide.

 C’est la base de pile, utiliser pour repérer la valeur initiale de la pile.


Après les déclarations ci-dessus, on utilisera la séquence d’initialisation :
ASSUME SS:seg_pile
MOV AX, seg_pile
MOV SS, AX ; init Stack Segment
MOV SP, base_pile ; pile vide
Noter que le registre SS s’initialise de façon similaire au registre DS ; par contre, on
peut accéder directement au registre SP.

III- Notion de compilation :


Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 95
III.1- Interpréteur et compilateur:
On distingue grossièrement deux familles de langages informatique, les langages
interprétés et les langages compilés.
Un programme en langage interprété va être traduit au fur et à mesure de son
exécution par un interpréteur. Un interpréteur est un programme chargé de décoder
chaque instruction du langage et de d’exécuter les actions correspondantes.
Dans le cas de programmes compilés, la traduction en langage machine a lieu une
fois pour toute. Le compilateur (traducteur) traduit chaque instruction du langage en
une suite plus ou moins complexe d’instructions en langage machine. Les
programmes compilés s’exécutent ainsi plus rapidement que les programmes
interprétés, car la traduction est déjà faite. On perd cependant en souplesse de
programmation, car les types de données doivent être connus au moment de la
compilation.
Un compilateur traduit un programme source écrit dans un langage de haut niveau
(par exemple C) en un autre programme dans un langage de bas niveau (par exemple
l’assembleur). Cette opération de traduction est assez complexe ; les compilateurs
sont des programmes sophistiqués, qui ont beaucoup progressé ces dernières années.

Cours Techniques de compilation ([email protected]) Hajji Saber Khalil 96

Vous aimerez peut-être aussi