Vous êtes sur la page 1sur 21

1

ICT104 – Computer Systems

Chap 4 – Le langage Assembleur x86

1. Introduction
Un CPU exécute des instructions qui sont la résultante des programmes écrits avec des langages de
programmation. Ces instructions contenues dans la mémoire composent le langage d’assemblage
(assembleur). Pour qu’un programme fonctionne, le CPU lit une part une les instructions. Donc
pour les atteindre, les instructions sont rangées dans la mémoire via des adresses. Au chargement
d’un programme, le CPU dispose de l’adresse d’origine et contient dans son Compteur Ordinal
l’adresse de la future instruction à exécuter.

Les instructions disposent de toutes les données pour que combinées entre elles et exécutées sur le
CPU l’algorithme original traduit en langage de programmation fonctionne. Les données des
instructions se retrouveront soit dans les registres du CPU soit dans la mémoire centrale.

Il existe 2 familles d’architecture de CPU, les CISC (Complex Instruction Set Computer) et
les RISC (Reduced Instruction Set Computer). Leurs différences résident dans la construction des
instructions. Un ensemble d’instructions est appelé jeux d’instructions ou ISA (Instruction Set
Architecture).
• Le CISC dispose de jeux d’instructions pouvant effectuer plusieurs opérations, comme les
opérations arithmétiques, les opérations logiques, le chargement et la récupération de
données en mémoire. Ils sont présents dans les x86, les pentiums, d’Intel, les 68xx de
Motorola.
• Le RISC dispose d’instructions relativement petites, unitaires, codées sur les mêmes tailles
de mot avec une exécution cadencée sur les mêmes cycles d’horloge. Les instructions RISC
sont possibles grâce à ses nombreux registres, caches (et aux mémoires) ne limitant pas la
taille des demandes. Les puces RISC sont conçues pour exécuter ces instructions très
rapidement.

Un registre est un circuit ou composant de mémorisation interne au CPU et permet de stocker


adresse et donnée utiles au bon fonctionnement d’un programme en cours d’exécution. Dans un
CPU, les registres sont toujours en nombre limités et pour un meilleur contrôle chaque registre
dispose d’un nom.

L'objectif de ce chapitre est de permettre d’appréhender la programmation en assembleur 8086. Il


vous propose d’apprendre l’organisation interne d’un processeur 80x86 16bits (base de toute une

Computer Systems – ICT1, tiré du cours de Pr. Cyril-Alexandre PACHON


2

lignée de micro-processeur INTEL), de comprendre l’utilité des différents registres de son CPU et
de découvrir les bases du langage assembleur 8086 au travers de l’émulateur EMU 8086.

2. Le microprocesseur 80x86
Le microprocesseur 80x86o (voir figure 1) se présente sous la forme d'un boîtier DIP (Dual Inline
Package). En électronique un DIP est un boitier contenant des circuits intégrés qui dispose de
sorties pour se connecter à un environnement externe (de nouveau un circuit intégré). Ce processeur
a un bus d'adresses et un bus de données multiplexés (certaines pattes transmettent à certains
moments un bit d'adresse et à d'autres moments un bit de donnée). Il est organisé autour d’un bus
interne de données de 16 bits, il comporte:
• 16 broches pour transporter les données (AD0 ... AD15).

• 20 broches pour véhiculer les adresses (A0 ... A19). Il peut adresser 220 (= 1048576)
positions mémoire différentes contenant chacune 1 octet (8 bits) : la mémoire a au plus une
capacité de 1 Mo.
• de registres, 16 bits pour stocker des données.

• de registres, 16 bits pour stocker des adresses (codées sur 20 bits).

• de registres temporaires directement pour UAL.

• un bloc pour former des adresses, 20 bits à partir de morceaux d’adresse codés sur 16 bits.

Figure 1 - Representation externe du microprocesseur x86


Un registre est un composant de mémoire interne au microprocesseur dont sa capacité est calibrée
sur une taille des instructions. Ainsi nous pouvons avoir des tailles de registres de 8 bits, 16 bits, 32

Computer Systems – ICT1, tiré du cours de Pr. Cyril-Alexandre PACHON


3

bits, 64 bits, … selon le modèle et la structure du micro-processeur. Le rôle du registre est de


stocker des adresses ou des résultats intermédiaires des calculs en cours. Les commandes de
l'assembleur manipulent les registres. Pour les contrôler et les utiliser, chaque registre dispose d’un
nom.

Comme tout CPU, le 8086 dispose d'un certain nombre de type de registres:
• les registres généraux destinés au traitement des valeurs. Ils permettent d’effectuer des
additions, des multiplications, des calculs logiques, nommés A (Accumulateur), B (Base), C
(Compteur), D (Donnée).
• les registres d'adressages (registres de segments, pointeurs et index) utilisés pour pointer,
lire ou écrire un endroit en mémoire, nommés D (Donnée), S (Stack - Pile), C (Code), E
(Extra). L’IP (Instruction Pointer) sera aussi nommé CO (Compteur Ordinal).

Figure 2- Régistre d'état


• les registres d'états (Flags - Indicateurs) pour indiquer l'état du CPU (voir figure 2). Ils
donnent une indication sur le résultat de la dernière instruction exécutée, par exemple ZF
(Zero Flag) qui indique zéro, CF (Carry Flag) qui indique une retenue, NF (Negative Flag)
ou SF (Sign Flag) qui indique si le résultat est négatif ou positif, OF (Overflow Flag) qui
indique un dépassement de capacité de registre, PF (Parity Flag) qui indique si les 8 bits de
poids fort sont constitué d’un nombre pair de 1 ….

Les flags du registre d’état de la figure ci-dessus contiendront alors les valeurs mis à 0 ou 1 par le
processeur (à la suite de l'exécution d'une instruction) ou par le programmeur pour modifier le mode
de fonctionnement du processeur.
La gestion des registres est faite avec des instructions processeurs. Ces instructions sont formées
avec le code binaire des programmes à exécuter. Il est possible de programmer le processeur avec
des langages de plus bas niveau et pour rendre plus facile la manipulation, la lecture et l’écriture de

Computer Systems – ICT1, tiré du cours de Pr. Cyril-Alexandre PACHON


4

telles instructions binaires, le programmeur utilise des symboles (mots clés, ou mnémoniques) à la
place de des codes binaires et fait appel à l’assembleur. Les symboles en assembleur sont souvent la
compression d'un mot ou d'une expression réalisant une action. Par exemple MOV (MOVe), MUL
(MULtiply), … . Les mnémoniques sont suivies de registres, de valeurs, … .
Si l'on manipule des nombres codés sur 16 bits (1 mot mémoire), 4 registres généraux sont utilisés :

• AX, l‘Accumulateur, utilisé pour stocker les résultats de certains calculs arithmétiques et
logiques.
• BX, la Base, utilisé comme registre de base pour des données dans le segment pointé par le
registre de segment ES. Un segment est un ensemble d’octets consécutifs dont un octet
désigne le couple (numéro du segment, déplacement dans le segment).
• CX, le Compteur, utilisé pour les boucles.
• DX, le registre de Données, contient l'adresse des ports d'entrée/sortie (pour les instructions
IN et OUT) et sert également d'extension à AX pour manipuler les données sur 32 bits.
Si l'on utilise des nombres codés sur 8 bits (1 octet), les 4 registres généraux en mode 8 bits sont
utilisés pour en produire 8 registres AH, AL, BH, BL, CH, CL, DH et DL avec comme convention,
H est mis pour "High" et L pour "Low" :
• AH contient l'octet de poids fort du registre AX.
• BH contient l'octet de poids fort du registre BX.
• ….
• AL contient l'octet de poids faible du registre AX.
• BL contient l'octet de poids faible du registre BX.
• ….

Les 2 registres d'index, notés SI (Source Index) et DI (Destination Index), sont utilisés pour indexer
les éléments d'un tableau.
Une instruction dans le CPU est composée de plusieurs segments. Un segment qui désigne le code
du programme, un segment qui désigne les données, un segment qui désigne l’organisation des
appels, ….. Une instruction contient donc tous ses segments, mais sous forme d’adresse pointant
vers la mémoire. Pour accéder à sa mémoire centrale, le 80x86 dispose de registres de segment
suivants:
• CS (Code Segment) pointe sur la base du segment qui contient le code (les instructions que
doit exécuter le processeur).
• DS (Data Segment) pointe sur la base du segment contenant les données (variables, tableaux
...).
• SS (Stack Segment) pointe sur la base du segment qui contient la pile gérée par les registres
SP et BP.
Computer Systems – ICT1, tiré du cours de Pr. Cyril-Alexandre PACHON
5

• ES (Extra Segment) pointe sur la base d'un segment supplémentaire qui est généralement
utilisé pour compléter le segment de données.
Ces segments sont situés selon la place disponible dans la mémoire. Parfois, ces segments peuvent
se chevaucher partiellement. Les adresses des segments sont données et gérées par le système
d’exploitation lors du mécanisme de chargement du programme à exécuter. Comme les adresses
sont ensuite contrôlées de façon automatique, normalement il n’y a pas de risque de confusions
d’accès (des systèmes de gestion d’erreurs peuvent se mettre en place comme les blue Sreen). Par
contre, les déplacements sont liés aux instructions contenues dans le programme en se base sur une
adresse d’origine.

La pile est une zone de mémoire qui permet de conserver de manière temporaire des données (par
exemple, l’état des registres lors d’un appel de procédure). Nous utilisons 2 registres pointeurs :
• SP (Stack Pointer) pointe sur le sommet de la pile et se met à jour automatiquement par les
instructions d'empilement et de dépilement.
• BP (Base Pointer) pointe la base de la région de la pile contenant les données accessibles
(variables locales, paramètres,...) à l'intérieur d'une procédure. Il doit être mis à jour par le
programmeur.

La pile n'est pas gérée avec des registres, elle utilise la mémoire (RAM). Dans cette pile, les
données sont naturellement stables et seuls les pointeurs SP et BP sont utilisés pour atteindre les
données. Cette pile est de type LIFO et elle est manipulée par :

• PUSH pour empiler une valeur de 16 bits (le pointeur SP est décrémenté automatiquement
de 2).
• POP pour dépiler une valeur de 16 bits (SP est incrémenté automatiquement de 2).
Le bus d’adresse de la mémoire réelle est de 20 bits. Or le 80x86 n’a pas de registre de 20 bits mais
que de 16 bits. Un registre de 16 bits référence 64Ko de mémoire, soit 216 = 65 536. Pour répondre
à cette problématique le 80x86 en mode réel combine un des neuf registres généraux avec l'un des
quatre registres de segments pour former une adresse de 20 bits.

Le registre IP (Instruction pointer) est appelé pointeur d'instruction ou compteur ordinal. La valeur
contenue dans ce registre aiguille instruction par instruction les déplacements le micro-processeur.
Il propose toujours la prochaine adresse de l’instruction à exécuter par le micro-processeur. Le
registre IP est constamment modifié après chaque fin instruction pour qu'il puisse pointer sur
l'instruction suivante. Ce registre permet de pointer une case mémoire dans le segment de code afin
que le 80x86 puisse charger la prochaine instruction à exécuter.

À chaque type d'accès en mémoire, il faut faire correspondre un registre de segment et parfois un
registre général, par exemple :

Computer Systems – ICT1, tiré du cours de Pr. Cyril-Alexandre PACHON


6

• CS et IP sont combinés pour accéder à la prochaine instruction à exécuter sous la forme


CS:IP.
• SS et SP sont combinés en SS:SP pour toutes les opérations concernant la pile.
II est possible d'utiliser un registre de segment autre que celui utilisé par défaut en le spécifiant
explicitement. Mais les valeurs de CS, DS et SS sont produites par le système d’exploitation dès le
lancement du programme. Ces valeurs de segments étant implicites, il sera possible de les utiliser
juste en spécifiant les offset de déplacements.

Pour adresser 1 méga-octet, il faut 4 bits, en plus des 16 bits d'un registre général. Cette
combinaison est obtenue en décalant le registre de segment de 4 bits et en ajoutant le résultat au
contenu du registre général pour obtenir l'adresse sur 20 bits.

Le résultat est appelé EA (Effective Address). L'EA est placée sur le bus d'adresses afin de pouvoir
accéder à l'emplacement mémoire correspondant. L’EA est constituée de deux parties, le registre de
segment définissant une zone mémoire de 64Ko, et le registre général spécifiant un déplacement à
partir de l'origine de ce segment de 64Ko (c'est-à-dire une adresse sur 16 bits à l'intérieur de ce
segment).

L’EA est exprimée en donnant le registre segment et le registre général séparés par deux points par
exemple: DS:SI. Le registre DS est le segment de 64Ko et SI contient le déplacement dans ce
segment. Si nous avons par exemple 1A8B:0010, l'adresse du segment est 1A8B et le déplacement
dans le segment est 0010 en hexadécimal. Pour obtenir l’EA il faut faire : 1A8B * 10 16 + 10 =
1A8C0.

3. Instructions de base en assembleur 8086


Un microprocesseur exécute un jeu d'instructions relatif au programme créé par le concepteur. Une
instruction réalise une action simple sur le microprocesseur, comme "récupérer une nombre en
mémoire", "additionner deux nombres et placer le résultat en mémoire". L'assembleur aura pour
rôle de convertir le fichier source contenant les instructions; en mnémoniques (indication sous
forme de lettre de l’opération à effectuer), en actions sur les registres et mémoire. Le fichier
exécutable produit contient les codes binaires de chacune des instructions, compréhensible
uniquement par le microprocesseur associé. L'assembleur est donc qu’une traduction d’un fichier
source éditer en langage de haut niveau vers un langage binaire (de bas niveau).

Le codage d’une instruction en langage de programmation est constitué d’un code-opérateur


(CodeOp) ou mnémonique (codé sur 1 ou 2 octets) suivi d’opérandes (valeur immédiate, registre,
adresse). Par exemple, pour additionner 6010 avec 1510, le code en assembleur sera le suivant:

Exemple: MOV AX, 60


ADD AX, 15

Computer Systems – ICT1, tiré du cours de Pr. Cyril-Alexandre PACHON


7

Ces instructions se traduisent en :

• placer la valeur immédiate 60 dans le registre AX (MOV Ax, 60).

• additionner le contenu de AX avec 15 et replacer le résultat dans AX (ADD AX, 15).

Opérations logiques
Le 8086 permet d’effectuer des opérations binaires classiques grâce au mnémoniques AND, OR,
NOT et XOR. Ces opérateurs peuvent être utilisés avec :

• AND/OR/XOR register ou variable, registre


• AND/OR/XOR register, register ou variable
• AND/OR/XOR register ou variable, constante
• AND/OR/XOR register ou variable, nombre
• NOT registre ou variable

Exemple:
AND AL, 11000000b ; résultat 01000001b
OR AL, 01110110b; résultat 01110111b
NOT AL; résultat 10001000b
XOR AL, 01010101b; résultat 11011101b
XOR BX, BX ; résultat 00000000b
Le 8086 permet d’effectuer des opérations de décalage sur des nombres signés ou non signés. Les
bits qui sont expulsés sont stockés dans le bit CF :

• SHL (SHift Left) et SAL (Shift Arithmetic Left).


• SHR (SHift Right) et SAR (Shift Arithmetic Right).
La différence entre les 2 types de décalage est que dans la seconde version le bit de signe n’est pas
concerné par ce décalage. Cette opération est plus rapide à exécuter que l’opération de
multiplication ou de division par 2. Le nombre de bits de décalage est indiqué dans la seconde
opérande.

MOV AL, 11110000b


SHL AL, 1; résultat 11100000b
SAL AL, 1; résultat 11000000b

Computer Systems – ICT1, tiré du cours de Pr. Cyril-Alexandre PACHON


8

SAR AL, 1; résultat 10100000b


SHR AL, 1; résultat 01010000b
Opérations arithmétiques
Comme pour tous calculateurs et microprocesseurs les unités de traitements arithmétiques font les
opérations +, -, * et /. Pour les utiliser et le décrire, nous passons toujours par des symboles. Par
contre pour effectuer des calculs dits non de bases, il faudra faire des programmes et enchainer les
calculs, décalages et rotations.

Les mnémoniques ADD (ADDition) et SUB (SUBtraction) permettent d’effectuer des additions et
des soustractions. Ils sont construit par :
• ADD registre, registre ou variable
• ADD registre ou variable, registre
• ADD registre ou variable, constante
• SUB registre, registre ou variable
• SUB registre ou variable, registre
• SUB registre ou variable, constante

Exemple:
ADD AX, BX; effectur l'opération Ax=AX+BX
SUB AX, BX; effectue l'opération AX = AX - BX
Si une addition ou une soustraction sont codées sur 16 bits, alors leurs résultats seront codés sur au
plus 16 + 1 bits soit 16 bits de résultat + 1 bit de retenue (le bit CF). Les opérations ADD et SUB
sont effectuées sans tenir compte de l’état initial du bit CF du registre d’état mais l’état de ce bit
peut être modifié à l’issue de l’opération.

Le 8086 propose 2 instructions INC et DEC pour incrémenter ou de décrémenter :


• INC registre ou variable
• DEC registre ou variable
Par exemple :
• INC AX incrémente la valeur stockée dans AX. INC AX donne le même résultat que ADD
AX, 1 mais le premier mnémonique correspond à un codeop de 1 octet alors que le codeop
correspondant au second mnémonique en occupe 3.
• DEC BX décrémente la valeur stockée dans BX. DEC BX donne le même résultat que SUB
BX, 1 mais le premier mnémonique correspond à un codeop de 1 octet alors que le codeop
correspondant au second mnémonique en occupe 3.

Computer Systems – ICT1, tiré du cours de Pr. Cyril-Alexandre PACHON


9

Pour demander au processeur d’effectuer des additions qui tiennent compte du bit CF, nous devons
utiliser un nouveau mnémonique : ADC (ADdition with Carry).

Il n’existe pas de mnémonique SUBC mais un mnémonique appelé SBB (SuBtract with Borrow)
qui effectue le même type d’opération mais en sens inverse.

Comme les opérations ADC et SBB modifient l’état du bit CF, il sert à effectuer des opérations sur
48 bits, 64 bits … en stockant les résultats intermédiaires en mémoire, l’opération se fera par bloc
de 16 bits.

Le mnémonique MUL (MULtiply) permet les multiplications, mais son mode de fonctionnement est
très différent par rapport à celui de l'addition et la soustraction.
• MUL registre ou variable.
La multiplication MUL avec deux données de 8 bits donne comme résultat une valeur codée sur 16
bits, donc de façon directe les valeurs données ne peuvent pas dépasser les bits de poids faibles. Si
les valeurs dépassent les 8 bits, il faut alors utiliser plusieurs registres. Pour multiplier un nombre N
par un nombre M, nous devons d'abord copier le nombre N dans le registre AL (ou AX) puis
invoquer l’instruction MUL M :
• si l’opérande est un octet, nous calculons AL * opérande et le résultat est stocké dans AX.
• si l’opérande est un mot, nous calculons AX * opérande et le résultat est stocké dans les
registres DX et AX (DX contenant la partie de poids fort).
Le 8086 propose un autre mnémonique pour effectuer les multiplications entre 2 nombres signés.
Le mnémonique IMUL est utilisé de la même manière que le mnémonique MUL à la différence que
les opérandes sont considérés comme des nombres signés (codés en complément à 2) et que le
résultat est un nombre signé.

Le mnémonique de la division euclidienne est DIV et son mode de fonctionnement est voisin de
celui de MUL.

Pour diviser un nombre N par un nombre M, nous devons d'abord copier le nombre N dans le
registre AX (ou AX et DX) puis invoquer l’instruction DIV M :
• si l’opérande est un octet, nous calculons AX / opérande : le quotient est stocké dans AL et le
reste dans AH.
• dans le cas d’un mot, nous calculons (AX DX) / opérande : le quotient est stocké dans AX et
le reste dans DX.
Le 8086 propose un autre mnémonique pour effectuer les divisions euclidiennes entre 2 nombres
signés. Le mnémonique IDIV est utilisé de la même manière que le mnémonique DIV. Les
différences sont que les opérandes sont considérées comme des nombres signés (codés en
complément à 2) et que le résultat est un nombre signé.

Computer Systems – ICT1, tiré du cours de Pr. Cyril-Alexandre PACHON


10

Les contrôles

Le 8086 offre 3 instructions pour modifier directement l’état du bit CF :


• STC (SeT Carry flag) permet de mettre CF à 1.
• CLC (CLear Carry flag) permet de mettre CF à 0.
• CMC (CoMplement Carry flag) permet d’inverser l’état du bit CF.
Le 8086 offre 4 instructions pour modifier directement l’état des bits DF (direction) et IF
(interruptions externes) :
• STD (SeT Direction flag) permet de mettre DF à 1.
• CLD (CLear Direction flag) permet de mettre DF à 0.
• STI (SeT Interrupt flag) permet de mettre IF à 1.
• CLI (CLear Interrupt flag) permet de mettre IF à 0.
Les mnémoniques STI et CLI sont utiles pour implanter au niveau assembleur des procédures qui
ne peuvent pas être interrompues par des événements externes.

Les mnémoniques STD et CLD sont utiles pour aiguiller le sens de manipulation des chaines
d’octets. Si le flag DF est à 0, le traitement est de gauche à droite, si DF est à 1 le traitement va de
droite à gauche.

4. Les variables
Comme pour tous les langages, avant d’utiliser une variable, il faut initialement la déclarer. Les
variables sont considérées comme des emplacements mémoire qui peuvent être manipulés par
l’intermédiaire de leur adresse ou de leur nom.

Le nom d’une variable peut être une combinaison de chiffres, de lettres et du caractère « _ », mais il
doit respecter les contraintes suivantes :

1. ne jamais commencer par un chiffre.


2. ne pas correspondre à un mot clé du langage Assembleur.
Lorsque l’association entre le nom et la variable est créée, ce nom peut être utilisé pour désigner
l’emplacement mémoire contenant l’information. L’assembleur traduit le nom en une adresse lors
du processus d’assemblage.

L'association entre le nom et l'adresse s'effectue en utilisant les instructions DB (Define Byte – 1
octet pour 8086) ou DW (Define Word – 2 octets pour 8086) selon que la variable contient des
octets ou des mots.

La valeur associée à la variable peut être un nombre (hexadécimal, décimal ou binaire) ou le


symbole ? (si la variable n’est pas initialisée), par exemple :
Computer Systems – ICT1, tiré du cours de Pr. Cyril-Alexandre PACHON
11

var1 DB 50h
var2 DB?
var3 DW 1110h

Il peut être intéressant de connaître l’adresse d’une variable pour utiliser cette dernière comme
argument des interruptions logicielles. Celle-ci se décompose en 2 parties :

1. l’adresse du segment, obtenue par l’opérateur SEG.


2. le déplacement à l’intérieur du segment, accessible grâce à l’opérateur OFFSET ou
l’instruction LEA (Load Effective Address). L’instruction LEA CX, var1 sera traduite en
MOV CX, 00100h (avec par exemple 00100h étant la valeur du déplacement pour la
variable var1).

Lors du processus d’assemblage, des transformations sont opérées au niveau du code :

1. SEG nom remplacé par le nom du registre associé au segment contenant la donnée.
2. OFFSET nom remplacé par la valeur du déplacement.
var1 DB 50h

MOV AX, SEG var1

MOV BX, OFFSET var1

Un tableau peut être vu comme une liste de variables qui sont associées à un même nom. La
création d’un tel tableau s’effectue alors de la manière suivante :
tab DB 01h, 02h, 03h

Il est alors possible d’accéder à une case précise du tableau en utilisant un opérateur d’indexation :
nous écrivons le nom du tableau suivi du numéro de la case placé entre crochet (les cases étant
numérotées de 0 à N-1 pour un tableau comportant N éléments) :

MOV AX, tab[1]

Une chaîne de caractères peut être vue comme un tableau contenant une suite de code ASCII
séparés par une virgule (correspondant chacun à une lettre). Dans tous les cas, et pour faciliter les
parcours et les recherches, les chaines de caractères seront terminées par le symbole $ (pour
marquer la fin de la chaîne de caractères).

var4 DB 72, 69, 76, 76, 111, 36

Computer Systems – ICT1, tiré du cours de Pr. Cyril-Alexandre PACHON


12

Cette écriture est fastidieuse car il faut connaître le code ASCII de chaque caractère. Nous pouvons
opter pour une séquence dissociée de caractères notés chacun entre symboles ' ' et séparés par une
virgule.

var5 DB 'H', 'E', 'L', 'L', 'O', '$'

5. Instructions de saut
Les étiquettes (nommées aussi labels) permettent de spécifier au programme à quel endroit
continuer une exécution. Il s’agit d’une ancre de programmation, comme un point d’entrée pour
commencer des instructions. Pour accéder à une étiquette, il est possible d’utiliser un mnémonique
de contrôle du flot d'instructions : JMP (pour jump). Nous parlons ainsi de programmation par Go
To non conditionnelle. Le branchement inconditionnel consiste simplement à sauter d'une position
dans le code à une autre position pour continuer l'exécution. Ce type de branchement est opposé aux
branchements conditionnels qui réalisent des sauts en fonction des tests effectués sur les bits du
registre d'état. Le saut inconditionnel est JMP suivi d'un nom de l'étiquette (ou d'un nombre
représentant l'adresse de destination codée sur 4 octets mais ce n'est pas conseillé). Pour que les
instructions s’exécutent, l’étiquette atteinte est convertie en une adresse au moment de l'assemblage
des instructions de saut.

Pour ajouter une étiquette dans le programme assembleur, il suffit de déclarer un nom (qui ne
commence pas par un chiffre) suivi des 2 points ( : ).

Par exemple :
etiquette1: MOV AX, BX
JMP etiquette1
A l'instar de l'instruction JMP, les instructions de branchement conditionnel s'utilisent avec un label.
Elles doivent être placées juste après l'instruction (CMP, DEC, INC ...) qui modifient l'état du
registre d'état sinon les autres mnémoniques placées entre cette instruction et le branchement
conditionnel pourraient altérer les bits du registre d'état. L'instruction CMP (pour compare) affecte
les drapeaux du registre d'état (instructions de branchement conditionnel afin d'implanter des
boucles, des tests ...). La comparaison de 2 nombres s'effectue en faisant une pseudo-soustraction
qui affecte les drapeaux dans le registre d’état :

• OF : Overflow Flag
• SF : Sign Flag
• ZF : Zero Flag
• AF : Auxiliary Carry Flag
• PF : Parity Flag

Computer Systems – ICT1, tiré du cours de Pr. Cyril-Alexandre PACHON


13

• CF : Carry Flag
Compte tenu de la ressemblance avec l'instruction SUB, les types des opérandes traités sont :
• CMP registre, mémoire
• CMP mémoire, registre
• CMP registre, registre
• CMP mémoire, immédiat
• CMP registre, immédiat
Instruction
Instruction Description Condition Condition
opposée
CF = 0 et ZF
JA Jump if Above JNA CF = 1 ou ZF = 1
=0
Jump if Above
JAE CF = 0 JNAE CF = 1
or equal
JB Jump if Below CF = 1 JNB CF = 0
Jump if Below CF = 1 ou
JBE JNBE CF = 0 et ZF = 0
or Equal ZF = 1
JC Jump if Carry CF = 1 JNC CF = 0
JE Jump if Equal ZF = 1 JNE ZF = 0
ZF = 0 et SF
JG Jump if Greater JNG ZF = 1 ou SF != OF
= OF
Jump if Greater
JGE SF = OF JNGE SF != OF
or Equal
JL Jump if Less SF != OF JNL SF = OF
Jump if Less or SF != OF ou
JLE JNLE SF = OF et ZF = 0
Equal ZF = 1
Jump if
JO OF = 1 JNO OF = 0
Overflow
JP Jump if Parity PF = 1 JNP PF = 0
Jump if Parity
JPE PF = 1 JNPE PF = 0
Even
Jump if Parity
JPO PF = 0 JNPO PF = 1
Odd
JS Jump if Sign SF = 0 JNS SF = 1
JZ Jump if Zero ZF = 1 JNZ ZF = 0
Table Instructions de branchement en assembleur
La première manière d'implanter des boucles en utilisant les mnémoniques de sauts conditionnels
(JNZ, JMP ...) : cela se rapproche du while et du do ... while.

La seconde méthode (qui se rapproche plutôt du for) consiste à utiliser le registre CX comme un
compteur par l’intermédiaire d’une des mnémoniques suivantes :

Computer Systems – ICT1, tiré du cours de Pr. Cyril-Alexandre PACHON


14

Instruc
Description
tion
Décrémente CX et va à l’étiquette
LOOP
si CX != 0
LOOP Décrémente CX et va à l’étiquette
E si CX != 0 et ZF = 1
LOOP Décrémente CX et va à l’étiquette
NE si CX != 0 et ZF = 0
LOOP Décrémente CX et va à l’étiquette
NZ si CX != 0 et ZF = 0
LOOP Décrémente CX et va à l’étiquette
Z si CX != 0 et ZF = 1
JCXZ va à l’étiquette si CX = 0
Table Instructions de boucle en assembleur

La construction d'une boucle s’effectue en respectant quelques contraintes :


• charger la valeur de la boucle dans le registre CX.
• faire précéder la première instruction de la boucle par une étiquette.
• terminer la boucle par une instruction de type LOOP qui pointe sur l'étiquette.
L’exemple de code suivant propose une itération avec un loop dont les tours de décrémentation sont
gérés par le registre CX :
MOV CX, 5
boucle: INC AX

LOOP boucle ; Si CX != 0, reboucler

6. Les procédures
A l’instar de sauts, la mise en place d’une procédure s’effectue en respectant quelques contraintes :

• nous devons d’abord écrire son nom (une étiquette mais sans les 2 points) suivi de la
directive PROC pour signaler à l’assembleur qu'il s'agit d'une procédure.
• nous plaçons ensuite les instructions assembleur correspondant à l'implantation de la
procédure (la dernière étant l’instruction RET).
• nous terminons le code de la procédure par une ligne comportant le nom de la procédure
suivi de la directive ENDP.
L'écriture d'une procédure peut se résumer par le squelette de code ci-dessous :
Name PROC

Computer Systems – ICT1, tiré du cours de Pr. Cyril-Alexandre PACHON


15

….
RET
Name ENDP

Lorsqu’une procédure est implantée, elle peut être appelée par l'instruction CALL suivie du nom de
la procédure. Le mnémonique sauvegarde l’IP dans la pile, indiquant ainsi l'adresse de retour. La
procédure est déroulée jusqu’à l’instruction RET qui restaure l’IP depuis la pile et qui entraîne
l’exécution de l’instruction qui suit CALL.

Figure – Appel de fonction en assembleur

Lorsque nous quittons une procédure appelante pour entrer dans une procédure appelée, nous
devons sauvegarder le contexte d’exécution de la procédure appelante afin de pouvoir reprendre son
exécution lors du retour de la procédure appelée. Ce contexte d’exécution de la procédure appelante
est défini par le contenu de l’ensemble des registres qu’elle utilise :

• les registres AX, BX …


• le registre d’état.
Si nous ne conservons pas l’état de ces registres et si ces derniers sont modifiés par la procédure
appelée, le fonctionnement de la procédure appelante est corrompu. Le moyen le plus couramment
utilisé pour conserver ces données est la pile (le segment SS) que nous manipulons grâce aux
instructions suivantes :
• PUSH, PUSHA et PUSHF.
• POP, POPA et POPF.

Computer Systems – ICT1, tiré du cours de Pr. Cyril-Alexandre PACHON


16

L’instruction PUSH permet de conserver une donnée à l’adresse SS:[SP] et de décrémenter SP de 2


(octets). Nous pouvons réaliser 3 types de PUSH :
• avec un nombre : PUSH 10h
• avec le contenu d’un registre : PUSH AX
• avec le contenu d’une case mémoire : PUSH [BX].
L’instruction PUSHA permet d’empiler le contenu des registres AX, CX, DX, BX, SP, BP, SI et DI.
Nous avons donc une équivalence entre l’instruction PUSHA et l’ensemble des instructions pris
dans l’ordre PUSH AX, PUSH CX, PUSH DX, ….

L’instruction PUSHF permet de conserver le contenu du registre d’état à l’adresse SS:[SP]. Comme
ce registre a une taille de 16 bits SP est décrémentée de 2 (octets) à l’issue de cette opération.

L’instruction POP permet de récupérer une donnée à l’adresse SS:[SP] et d’incrémenter SP de 2


(octets). Nous pouvons réaliser 2 types de POP :
• pour stocker l’information récupérée dans un registre : POP AX
• pour stocker l’information récupérée dans une case mémoire : POP[BX]
L’instruction POPA effectue le traitement inverse de PUSHA. POPA récupère les informations
stockée dans la pile pour les placer dans les registres DI, SI, BP, SP, BX, DX, CX, et AX. Comme
précédemment, nous avons une équivalence entre l’instruction POPA et l’ensemble des instructions
pris dans l’ordre (ordre inversé de PUSHA). Il est à noter que POP SP est ignoré pour ne pas
perturber le fonctionnement de la pile.

L’instruction POPF effectue le traitement inverse de PUSHF. POPF permet de copier le contenu
stocké à l’adresse SS:[SP] vers le registre d’état. Comme ce registre a une taille de 16 bits SP est
incrémentée de 2 (octets) à l’issue de cette opération.

7. Les interruptions
Lors d’une utilisation normale d’un ordinateur, il y a en permanence l’exécution d’instructions.
Toutes ces instructions ne dépendent pas d’un même programme. En effet, il y a plusieurs
programmes qui se partagent le CPU, les entrées/sorties et donc pour exécuter les instructions, il y a
un procédé cyclique d’interruption / sauvegarde / exécution / restauration. Il existe un ensemble
d’interruptions :

Computer Systems – ICT1, tiré du cours de Pr. Cyril-Alexandre PACHON


17

Le principe de fonctionnement des interruptions est :

• de stopper le programme principal.


• de lire la table des vecteurs d’interruption pour connaître l’adresse de la procédure chargée
de traiter l’interruption.
• de sauvegarder le contexte d’exécution.
• d’exécuter la procédure.
• de recharger le contexte d’exécution afin de reprendre l’exécution du programme principal.

Interruption matérielle

Figure – Intérruption matérielle


Les interruptions matérielles permettent au processeur de réagir aux actions des périphériques
externes comme le clavier, la souris et autres.
Le processeur exécute un programme stocké dans le segment de code de la mémoire. Il doit être
capable de réagir à des événements externes en interrompant le programme en cours pour exécuter
une procédure de traitement de l’événement. Ce mécanisme nécessite l’adjonction d’un composant
appelée contrôleur d’interruption sur lequel sont branchés les périphériques susceptibles de
demander au processeur d’effectuer des traitements en réaction à leur fonctionnement (le clavier, la
souris, le disque dur …).

Computer Systems – ICT1, tiré du cours de Pr. Cyril-Alexandre PACHON


18

Si nous rentrons dans les détails, le contrôleur d’interruption est composé de 2 circuits 8259A mis
en cascade. Dont chaque fil, appelé IRQ (Interrupt ReQuest) est raccordé à un périphérique (ou un
ensemble de périphériques).

Figure – Circuit des IRQ


La sauvegarde des contextes
Avant le traitement de l’IRQ, le processeur exécutait un programme, il existe un contexte
d’exécution correspondant à l’état des différents registres au moment de cette interruption (en
particulier le compteur ordinal qui pointe la prochaine instruction à exécuter). Le processeur lit
d’abord une zone mémoire appelée la table des vecteurs d’interruption, afin de connaître l’adresse
de la procédure de traitement en fonction du numéro de l’interruption. Le processeur exécute alors
la première instruction de la procédure. Les premières instructions consistent généralement à
sauvegarder le contexte dans la pile avant d’exécuter la procédure de traitement de l’interruption
proprement dite. Lorsque le traitement proprement dit est terminé, nous rechargeons le contexte
d’exécution depuis la pile (le compteur ordinal pointe sur l’instruction du programme principal dont
l’exécution pourra reprendre) et nous exécutons l’instruction IRET (Interrupt RETurn). Ce procédé
de sauvegarde est illustré par les schémas de la figure ci-dessous.

Computer Systems – ICT1, tiré du cours de Pr. Cyril-Alexandre PACHON


19

Figure – Sauvegarde d'un contexte


8. L'outil Emu 8086
l'interface d'entrée de l'Emu 8086 est la suivante:

Le bouton "Code Examples" donne accès à une liste d'exemple. Le programme Hello World étant
trop complexe à ce stade de l’apprentissage (il faut d'abord décrire le fonctionnement de la carte
vidéo pour comprendre cet exemple), nous allons nous orienter vers un programme de calcul avec
des "ADD/ SUB". Pour utiliser les opérations, nous devons au préalable charger dans les registres
des valeurs, et nous le ferons avec l’instruction MOV et les registres de bases AX et BX.

Computer Systems – ICT1, tiré du cours de Pr. Cyril-Alexandre PACHON


20

Pour une création d’édition d’un programme, nous le faisons en 3 étapes,


• New,

• Empty workspace et

• valider.

Chaque instruction assembleur (mnémonique) possède une correspondance en langage machine,


appelée code opérateur (CodeOp), qui peut être codé sur un ou plusieurs octets. Cette
correspondance est spécifiée par le constructeur - Intel - dans son databook ce qui permet au
programmeur de développer leur assembleur et/ou leur émulateur.

Les instructions opération registre, valeur sont dites à adressage immédiat car nous indiquons "en
dur" la valeur qui doit être chargée dans le registre :

MOV AX, 15; CodeOp = B815

Les instructions opération registre, registre sont dites de registres car les données, sur lesquelles les
opérations sont effectuées, sont contenues dans les registres :
MOV BX, AX; CodeOp = 8BD8

Pour générer et commencer à exécuter le programme de la figure ci-dessus, il faut cliquer sur
Computer Systems – ICT1, tiré du cours de Pr. Cyril-Alexandre PACHON
21

EMULATE. L'assembleur attribue aux instructions traduite en langage machine des adresses
relatives. Le processus d’émulation respecte les conditions architecturales car les adresses sont
attribuées de façon séquentielle à partir du début du programme. C’est la raison pour laquelle une
origine doit être définie; fait par l’instruction ORG 100h.

Un programme, pour être exécuté est chargé en mémoire par le système d’exploitation. Le DOS
distingue 2 modèles de programmes :

1. les fichiers exécutables COM (utilisant un seul segment dans la mémoire de taille 64 Ko).
2. les fichiers exécutables EXE (limités que par la mémoire disponible dans l’ordinateur).
Le DOS charge le fichier COM et lui alloue toute la mémoire disponible. Mais si la mémoire est
insuffisante, il annule le chargement en l’indiquant à l’utilisateur.

Computer Systems – ICT1, tiré du cours de Pr. Cyril-Alexandre PACHON

Vous aimerez peut-être aussi