°cours 1 Architecture Des Ordinateurs SMI4 FSO OUAJDA

Télécharger au format pdf ou txt
Télécharger au format pdf ou txt
Vous êtes sur la page 1sur 87

Université Mohammed Premier

Faculté des Sciences


Département d’Informatique
Oujda

Architecture des ordianteurs et langage


assembleur

Cours pour :

Filière : SMI
Semestre : 4

Prof. Mohammed GABLI


https ://sites.google.com/ump.ac.ma/gabli/

Année universitaire 2019/2020


Préambule

Pour bien comprendre la façon dont les programmes s’exécutent sur un ordinateur, il est né-
cessaire de comprendre quelques principes de base sur l’architecture des ordinateurs et de leur
organisation.
En informatique, le terme architecture désigne l’organisation des éléments d’un système et les
relations entre ces éléments.
— L’architecture logicielle concerne l’organisation de différents programmes entre eux.
— L’architecture matérielle concerne l’organisation des différents dispositifs physiques
que l’on trouve dans un ordinateur.
Ce cours a pour objectif de clarifier l’architecture des ordinateurs. Il vise à répondre à la question
suivante : comment fonctionne cette machine sur laquelle vous passez des heures et des heures ?.
Le cours se décompose en deux grandes parties : l’architecture des ordinateurs et le lan-
gage Assembleur. La programmation en langage assembleur développe une compréhension
très élémentaire de l’ordinateur. En effet, dans les langues de haut niveau tel que C ou Java,
il y a une certaine “distance” entre l’ordinateur et le programmeur. Cela est dû au fait que les
langages de haut niveau sont conçus pour être plus proches et plus conviviaux pour le program-
meur, créant ainsi une distance avec la machine. Cette distance est couverte par des traducteurs
appelés compilateurs et interprèteurs. Le but de la programmation en langage assembleur
est de contourner ces intermédiaires et de dialoguer directement avec l’ordinateur.
En général, la compréhension de l’architecture des ordinateurs et le langage Assembleur semble
utile dans les points suivants :
— optimiser la programmation ;
— développer des systèmes matériels tels que la mémoire, le micro-processeur, etc ;
— développer des systèmes de traitement de haute performance (audio, vidéo, médical,
spatial...) ;
— avoir une expertise en choix de matériel informatique ;
— développer des systèmes d’exploitation et des compilateurs...
Ce cours est destiné aux étudiants de la Licence en Informatique de l’Université Mohamed
Premier, Oujda. Il concerne le module de l’architecture des ordinateurs de la Filière
Sciences Mathèmatiques et Informatique (SMI, Semestre 4).

1
Table des matières

I Architecture des ordinateurs 9

1 Systèmes de numération 10
1.1 Systèmes de numération . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.1.1 Système décimal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.1.2 Système binaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.1.3 Système octal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.1.4 Système héxadécimal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.2 Conversion de systèmes de numération . . . . . . . . . . . . . . . . . . . . . . . 11
1.2.1 Conversion en décimal . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.2.2 Conversion en binaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.3 Codage des entiers négatifs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.3.1 Première mèthode : complèment à 1 . . . . . . . . . . . . . . . . . . . . . 14
1.3.2 Deuxième mèthode : complèment à 2 . . . . . . . . . . . . . . . . . . . . 14
1.4 Codage des nombres réels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.5 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

2 Mémoire 16
2.1 L’architecture Von Neumann . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.2 Caractéristiques d’une mémoire . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.3 Représentation de l’information et stockage en mèmoire . . . . . . . . . . . . . . 17
2.3.1 Bascule . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.3.2 Cas des entiers positifs . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.3.3 Cas des entiers négatifs . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.3.4 Cas des nombres réels . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.3.5 Cas des caractères . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.3.6 Types de données fondamentaux . . . . . . . . . . . . . . . . . . . . . . . 22
2.3.7 Ordre d’empilement lors de stockage sur plusieurs octets . . . . . . . . . 22

2
3 Processeur 24
3.1 Micro-Processeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.2 Familles de micro-processeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.2.1 La famille x86 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.2.2 La famille des PowerPC (PPC) . . . . . . . . . . . . . . . . . . . . . . . 25
3.2.3 La famille ARM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.3 historique de micro-processeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.4 Les registres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.4.1 Registres en 8086 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.4.2 Registres à partir de 80386 . . . . . . . . . . . . . . . . . . . . . . . . . . 29
3.5 Fonctionnement d’un microprocesseur . . . . . . . . . . . . . . . . . . . . . . . . 32
3.5.1 Cycle d’exécution d’une instruction . . . . . . . . . . . . . . . . . . . . . 32
3.5.2 Fonctionnement d’un microprocesseur . . . . . . . . . . . . . . . . . . . . 33
3.6 Architecture des microprocesseurs . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.6.1 Architectures CISC et RISC . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.6.2 Architecture Pipeline . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
3.7 Le processeur 8086 et la segmentation de la mémoire . . . . . . . . . . . . . . . 38
3.8 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

4 Les bus 41
4.1 Définitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
4.2 Largeur d’un bus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
4.2.1 Transportation avec un bus de données plus large . . . . . . . . . . . . . 42
4.2.2 Transportation avec un bus de données moins large . . . . . . . . . . . . 43
4.2.3 Transportation d’adresses . . . . . . . . . . . . . . . . . . . . . . . . . . 43
4.2.4 Propriétés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
4.3 Fréquence et débit d’un bus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
4.4 Fonctionnement : récapitulatif . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44

5 Entrées sorties 46
5.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
5.2 Contrôleur d’entrées sorties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
5.3 Types de périphériques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
5.4 Gestion d’entrées sorties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
5.4.1 Les entrées sorties programmées . . . . . . . . . . . . . . . . . . . . . . . 48
5.4.2 Les entrées sorties pilotées par les interruptions . . . . . . . . . . . . . . 49

3
5.4.3 Les entrées sorties avec DMA . . . . . . . . . . . . . . . . . . . . . . . . 49

II Langage Assembleur 50

6 Introduction au Langage Assembleur 51


6.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
6.1.1 Principe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
6.1.2 Premier exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
6.2 Directives et Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
6.2.1 Les directives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
6.2.2 Les macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
6.3 Description du premier exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
6.3.1 Description du programme . . . . . . . . . . . . . . . . . . . . . . . . . 53
6.3.2 Compilation et exécution . . . . . . . . . . . . . . . . . . . . . . . . . . . 54

7 Instructions de base 55
7.1 L’instruction mov et modes d’adressage . . . . . . . . . . . . . . . . . . . . . . 55
7.1.1 Adressage immédiat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
7.1.2 Adressage registre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
7.1.3 Adressage direct . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
7.1.4 Adressage indirect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
7.1.5 Adressage indirect avec OFFSET . . . . . . . . . . . . . . . . . . . . . . 57
7.1.6 L’instruction movzx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
7.2 La bibliothèque Irvine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
7.3 Directives de base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
7.3.1 Syntaxe de déclaration d’une variable . . . . . . . . . . . . . . . . . . . . 58
7.3.2 Exemple complet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
7.3.3 Les constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
7.4 Les opérations mathématiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
7.4.1 Addition et soustraction : ADD et SUB . . . . . . . . . . . . . . . . . . . 61
7.4.2 Multiplication : MUL et IMUL . . . . . . . . . . . . . . . . . . . . . . . 62
7.4.3 Division : DIV et IDIV . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
7.4.4 Incrémentation et décrémentation . . . . . . . . . . . . . . . . . . . . . . 63
7.5 Tableau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
7.5.1 déclaration d’un tableau . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

4
7.5.2 L’opérateur DUP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

8 Contrôle de flux de programme 67


8.1 Sauts inconditionnels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
8.2 Saut conditionnel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
8.2.1 Indicateurs (Flags) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
8.2.2 Sauts conditionnels courts . . . . . . . . . . . . . . . . . . . . . . . . . . 69
8.3 Les boucles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
8.3.1 Boucle Tant-Que . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
8.3.2 Boucle Répéter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
8.3.3 Boucle Pour (LOOP) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72

9 Les sous-programmes 74
9.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
9.2 Principe général de l’appel d’un sous-programme . . . . . . . . . . . . . . . . . . 74
9.2.1 Appel d’un sous-programme . . . . . . . . . . . . . . . . . . . . . . . . . 74
9.2.2 Retour à l’appelant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
9.2.3 Les piles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
9.3 Appel du sous-programme sans passage de paramétres . . . . . . . . . . . . . . 76
9.4 Appel du sous-programme avec passage de paramétres . . . . . . . . . . . . . . 77
9.4.1 Passage de paramètres par registre . . . . . . . . . . . . . . . . . . . . . 77
9.4.2 Passage de paramètres par pile . . . . . . . . . . . . . . . . . . . . . . . 78
9.5 Sous-programme et variables locales . . . . . . . . . . . . . . . . . . . . . . . . . 79
9.6 Sous-programmes retournant une valeur : les fonctions . . . . . . . . . . . . . . 81

10 Les interruptions 82
10.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
10.2 Types d’interruptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
10.3 Exemples d’interruptions du DOS . . . . . . . . . . . . . . . . . . . . . . . . . . 83
10.3.1 Afficher un caractère à l’écran . . . . . . . . . . . . . . . . . . . . . . . . 83
10.3.2 Afficher un message à l’écran . . . . . . . . . . . . . . . . . . . . . . . . 83
10.3.3 Saisir une frappe au clavier . . . . . . . . . . . . . . . . . . . . . . . . . 84
10.3.4 Lire l’heure courante . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
10.3.5 Lire la date courante . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85

5
Introduction

À ce niveau, vous avez commencé à programmer en utilisant le langage C, et vous savez que
le langage C n’est pas le seul langage de programmation. En fait, il y en a beaucoup tels que
C++, Java, Python, C# ... Mais, la première question à poser : c’est quoi un programme ?.
Nous avons donc besoin des définitions suivantes :
Définition 1
Un programme informatique est un ensemble d’opérations (une suite d’instructions) destinées
à être exécutées par un ordinateur.
Définition 2
Une instruction dicte à l’ordinateur l’action (l’ordre) nécessaire qu’il doit effectuer (avant de
passer à l’instruction suivante).

Figure 1 – Illustration des programmes et instructions

Nous devons écrire des instructions à l’ordinateur, mais dans quelle langue ? en un langage de
programmation tel que C ? mais l’odinateur ne comprend pas ces langues.
Nous devons donc utiliser la langue de l’ordinateur, qui est le langage binaire constitué de
0 et 1. La figure 2 présente des instructions en binaire.
Or, la programmation avec ce langage n’est pas facile. Heureusement que les programmeurs
ont inventé un système qui nous facilitera les choses : Le langage assembleur. La figure 3
présente quelques instructions de ce langage.
On a besoin donc, d’étudier le langage assembleur dans l’objectif de comprendre le fonctionne-
ment de l’ordinateur.
D’autre part, considérons le code C présenté dans le listing 1.

6
Figure 2 – Instructions en langage binaire

Figure 3 – Instructions en langage assembleur

#i n c l u d e <s t d i o . h>
main ( ) {
int x = 1 , y = 5;
do ub le z = 3 . 4 ;
char c ;
p r i n t f ( "%d" , x + y ) ;
getch ( ) ;
}
Listing 1 – Exemple d’un code en langage C

Ces informations doivent être stockèes dans la mèmoire. Cette dernière doit donc contenir
— des entiers positifs : 1
— des entiers négatifs : −3
— des caractères : ’A’
— des rèels : 3.4
— ...
Mais la mèmoire ne contient, en réalité, que de 0 et 1 ? !
Un autre problème donc à traiter, est que tout doit être codé en binaire !

7
Pour ceci nous avons besoin d’étudier le système de numèration et le fonctionnement de
la mèmoire.
Dans le programme précèdent, on a l’instruction :
p r i n t f ( "%d" , x + y ) ;

Mais, qui va se charger de faire le calcul x + y par exemple ?


Le troisième défi, donc, est d’étudier le fonctionnement du processeur, nommé plus précisem-
ment micro-processeur.
Dans le listing 1 et pour que le micro-processeur puisse calculer x + y, il faut avoir la valeur de
x et de y !
Il faut donc un moyen de communication entre le micro-processeur et la mèmoire. Ce sont les
bus.
La figure 4 présente un schéma simplifié des principaux composants d’un ordinateur.

Figure 4 – Principaux composants d’un ordinateur

Dans la première partie de ce cours on traitera ces composants. Dans la deuxième partie, on
étudiera le langage assembleur.

8
Première partie

Architecture des ordinateurs

9
Chapitre 1

Systèmes de numération

1.1 Systèmes de numération


Il existe plusieurs systèmes de numération. Dans ce cours on étudiera les quatre systèmes les
plus utilisés suivants.
— Système décimal
— Système binaire
— Système octal
— Système héxadécimal

1.1.1 Système décimal


La base du système décimal est 10. Ce système contient 10 symboles : 0,1,2,3,... et 9.
Tout nombre entier positif n peut être représenté à la base 10 comme suit :
k
X
n= ai × 10i
i=0

où ai ∈ {0, 1, ..., 9} et ak 6= 0
Exemple :
(124)10 = 1 × 102 + 2 × 101 + 4 × 100
.

1.1.2 Système binaire


La base du système binaire est 2. Deux symboles seulement existe dans ce système : 0 et 1.
Tout nombre entier positif n peut être représenté à la base 2 comme suit :
k
X
n= ai × 2i
i=0

où ai ∈ {0, 1} et ak 6= 0.

10
Exemple :
(101)2 = 1 × 22 + 0 × 21 + 1 × 20

1.1.3 Système octal


Dans ce système, la base est 8 et il contient 8 symboles : 0,1,2,3,... et 7.
Tout nombre entier positif n peut être représenté à la base 8 comme suit :
k
X
n= ai × 8i
i=0

où ai ∈ {0, 1, ..., 7} et ak 6= 0.
Exemple :
(124)8 = 1 × 82 + 2 × 81 + 4 × 80

1.1.4 Système héxadécimal


La base dans ce système est 16, et il contient 16 symboles : 0,1,2,3,...,9, A, B, C, D, E et F.
Tout nombre entier positif n peut être représenté à la base 16 comme suit :
k
X
n= ai × 16i
i=0

où ai ∈ {0, 1, ..., 9, , A, B, C, D, E, F } et ak 6= 0.
Exemple :
(124)16 = 1 × 162 + 2 × 161 + 4 × 160

1.2 Conversion de systèmes de numération

1.2.1 Conversion en décimal


Conversion : Binaire -> Décimal

(101)2 = 1 ∗ 22 + 0 ∗ 21 + 1 ∗ 20 = (5)10

Conversion : Décimal -> Binaire

(23)10 = (10111)2
La figure 1.1 présente une méthode de conversion du système décimal au système binaire.

11
Figure 1.1 – Conversion de Décimal au Binaire

Conversion : Octal -> Décimal

(124)8 = 1 × 82 + 2 × 81 + 4 × 80 = (84)10

Conversion : Décimal -> Octal

(23)10 = (27)8
La figure 1.2 présente une méthode de conversion du système décimal au système octal.

Figure 1.2 – conversion du décimal au système octal

Conversion : Héxadécimal -> Décimal

(124)16 = 1 × 162 + 2 × 161 + 4 × 160 = (292)10

Conversion : Décimal -> Héxadécimal

(292)10 = (124)16
La figure 1.3 présente une méthode de conversion du système décimal au système héxadécimal.

12
Figure 1.3 – conversion du décimal au héxadécimal

1.2.2 Conversion en binaire


Conversion : Octal -> Binaire

La conversion consiste à remplacer chaque chiffre octal par son équivalent binaire sur 3 bits.
Exemple :
(15)8 = 001 101 car (1)8 = (001)2 et (5)8 = (101)2

Conversion : Héxadécimal -> Binaire

La conversion consiste à remplacer chaque chiffre hexadécimal par son équivalent binaire sur 4
bits.
Exemple :
(3A)16 = 0011 1010 car (3)16 = (0011)2 et (A)16 = (1010)2

Conversion : Binaire -> Octal

On effectue le remplacement, de droite à gauche, de 3 bits par le chiffre octal correspondant.


Si le nombre de bits n’est pas un multiple de 3, on complète à gauche avec des zéros.
Exemple :
(101101)2 = (101)2 (101)2 = (55)8
(10110)2 = (010)2 (110)2 = (26)8

Conversion : Binaire -> Héxadécimal

On effectue le remplacement, de droite à gauche, de 4 bits par le chiffre hexadécimal corres-


pondant. Si le nombre de bits n’est pas un multiple de 4, on complète à gauche avec des zéros.
Exemple :
(10101110)2 = (1010)2 (1110)2 = (AE)16
(101101)2 = (0010)2 (1101)2 = (2D)16

13
1.3 Codage des entiers négatifs
Dans le calcul binaire, les nombres entiers positifs sont représentés uniquement en 0 et 1. Le
signe négatif précédant les valeurs entières pose un problème de représentation sur machine.
Plusieurs méthodes sont utilisées pour représenter les nombres négatifs dans un ordinateur,
parmi lesquelles nous citons : le complément à 1 et le complément à 2.

1.3.1 Première mèthode : complèment à 1


Pour les entiers négatifs, il faut, en plus, coder le signe (0 : positif et 1 : négatif).
Le complèment à 1 est obtenu en complèmentant sa valeur absolue, bit à bit, avec 1.
Exemple : représentation de −1510
| − 1510 | = 1510 = 000011112 .
Complèment à 1 de | − 1510 | est donc : 111100002 .
Limites
— Deux représentations de 0 : 00000000 et 11111111
— Plage de nombres représentés sur 8 bits est : −127 à 127 (représentant les deux bornes
10000000 et 01111111).

1.3.2 Deuxième mèthode : complèment à 2


Règle :

— Calculer la valeur absolue de l’entier consédéré ;


— Calculer son complèment à 1 ;
— Ajouter 1.
Exemple : représentation de −1510
| − 1510 | = 1510 = 000011112 .
Complèment à 1 de | − 1510 | est : 111100002 .
111100002 + 1 = 111100012
Avantages

— Une seule représentation de 0 qui est 00000000


— Plage de nombres représentés sur 8 bits est : −128 à 127.

1.4 Codage des nombres réels


L’idée de base pour la représentation des nombres décimaux est de définir l’emplacement d’une
virgule séparant la partie entière de la partie décimale et de considérer que les chiffres à droite
de la virgule correspondent aux puissances négatives de la base (comme pour les décimaux).
Exemple :
(123, 45)10 = 1 × 102 + 2 × 101 + 3 × 100 + 4 × 10−1 + 5 × 10−2

(101, 101)2 = 1 × 22 + 0 × 21 + 1 × 20 + 1 × 2−1 + 0 × 2−2 + 1 × 2−3 = 4 + 1 + 0.5 + 0.125 = 5.62510

14
(AB, 4E)16 = 10×161 +11×160 +4×16−1 +14×16−2 = 160+11+4×0, 0625+14×0, 003906 = 171, 304610
.

1.5 Exercices

Exercice 1
1. Convertir (12)10 en binaire.
2. Convertir (CA)16 en décimal.
3. Convertir (0, 11)2 en décimal.
4. Calculer (1010 + 1101)2 en binaire, puis en décimal.

Exercice 2
1. Combien de chiffres différents peut-on utiliser en Octal ?
2. Combien de chiffres différents peut-on utiliser en Hexadecimal ?
3. Quel est le nombre qui s’écrit 1 dans toutes les bases ? 0, 1, 10, autre ...
4. 10 en base quelconque est ? 2, la base, la base au carré, autre ...
5. 1h47mn20s = 6440s en quelle base ? 24, 48, 60 , autre ...
6. L’adresse IP de votre ordinateur communicant sur le Protocole Internet est 192.168.1.100.
Convertir cette adresse sous forme binaire et hexadécimale.

15
Chapitre 2

Mémoire

Introduction
Il existe dans le monde de la machine deux types d’architectures :
— L’architecture Von Neumann
— et l’architecture Harvard.
Chacune est plus adaptée à un type d’appareil spécifique.
Pour un ordinateur, c’est l’architecture Von Neumann qui est utilisée.

2.1 L’architecture Von Neumann

Figure 2.1 – Architecture Von Neumann

16
2.2 Caractéristiques d’une mémoire
La mémoire est la composante qui permet à un ordinateur de stocker de l’information. Après le
microprocesseur, la mémoire est sans doute le composant le plus important dans un ordinateur :
c’est l’espace de travail du microprocesseur.
La quantité et le type de mémoire vont influencer grandement la performance globale de l’or-
dinateur. En général, une mémoire est caractérisée par les propriétés suivantes.
1. Volatilité :
— mémoire volatile (vive) : son contenu disparaît ou est effacé lorsqu’on éteint l’or-
dinateur (RAM) ;
— mémoire non volatile : garde l’information dans tous les cas (ROM).
2. Vitesse et temps d’accès.
3. Modes de stockage :
— Stockage primaire : la mémoire est directement accessible par le microprocesseur,
par exemple la mémoire vive (RAM) ;
— Stockage secondaire : est une mémoire qui n’est pas directement accessible par le
microprocesseur, par exemple le disque dur.
Dans ce qui suit, on étudiera la RAM.

2.3 Représentation de l’information et stockage en mèmoire


Soit le programme en C présenté dans le listing 2.1.

#i n c l u d e <s t d i o . h>
main ( ) {
i n t x = 11 , y = 315;
do ub le z = 3 . 4 ;
char c ;
}
Listing 2.1 – Code en langage C

La question qu’on va essayer de répondre dans cette section est la suivante : où ces informations
(x, y, z et c) seront stockèes ? et comment ?

Figure 2.2 – Représentation de bascules

17
2.3.1 Bascule
Une bascule est un circuit logique séquentiel. Vous avez déja vu quelques circuits logiques
combinatoires comme les portes ET, OU, NAND, NOR, etc.
Ce qui différencie les bascules des portes logiques combinatoires est que la sortie maintient son
état même après disparition de son signal de commande. C’est pour cela que l’on appelle aussi
les bascules des mémoires élémentaires.
Une bascule chargée correspond au bit 1 et non chargée au bit 0. La figure 2.2 présente des
bascules chargées et des bascules non chargées.

2.3.2 Cas des entiers positifs


Revenons à notre code. Sur 8 bits, on peut représenté x comme suit :
x = (11)10 = (1011)2 = (00001011)2 .
La figure 2.3 illustre la représentation de x dans la mèmoire.

Figure 2.3 – Représentation de x = 1011 dans la mèmoire

x est stockée à l’adresse 1.


Si on veut chercher x, y, z ou c, comment faire ?
Comment l’ordinateur fait-il pour s’y retrouver et savoir quelles bascules contiennent quelles
informations ?
C’est grace à l’adresse que l’ordinateur arrive à se repérer dans la mémoire. Lorsqu’il stocke
une donnée, il la met à une adresse précise.
On ne peut stocker qu’une seule donnée par adresse. Donc, dans une mémoire de taille n, on a
n emplacements mémoires, numérotés de 0 à (n − 1).
La figure 2.4 représente l’adressage d’une mémoire de taille 8.
On a présenté dans ce qui précède comment stocker x = 1110 dans la mèmoire. Et maintenant
comment stocker y = 31510 ? On doit remarquer que y dépasse 255 !.
Rèponse : On va stocker y sur plusieurs emplacements mémoire consécutives. En effet
y = (315)10 = (100111011)2 = 00000001 00111011. Il faut donc deux emplacements pour y. La
figure 2.5 illustre ce stockage.

18
Figure 2.4 – Représentation des adresses dans une mémoire

Figure 2.5 – Représentation de y = (315)10 sur deux adresses consécutives

2.3.3 Cas des entiers négatifs


La majeure partie des constructeurs adopte la règle du complèment à 2.
Pour représenter l’opposé d’un nombre positif par son complément à deux, on inverse les bits
0 et 1 et on ajoute 1 au mot binaire obtenu. Voir la section 1.3 du premier chapitre.
Exemple : −8110
1. 8110 = 01010001
2. Inversion de bits : 10101110
3. 10101110 + 1 = 10101111.
Donc −8110 = 101011112 .
Les limites du codage des entiers relatifs sont dûs, principalement, au dépassement de ca-
pacité (overflow) lors d’un calcul. Une addition de deux nombres positifs ou négatifs peut

19
entrainer un dépassement de capacité, celui-ci peut être détecté en regardant le signe du résultat
par rapport au signe des deux opérandes (deux nombres positifs donnent un résultat négatif et
réciproquement).
Exemple de dépassement de capacité
sur 4 bits, 5 + 3 = 8 devient en binaire : 0101 + 0011 = 1000 ce qui est faux, en effet 5 + 3 6= −8.

2.3.4 Cas des nombres réels


On traitera dans ce qui suit seulement le cas de la conversion en binaire. Les autres cas tel que
la conversion en hexadécimal se fait de la même manière.

Conversion en binaire

1. Pour la partie entière, on fait comme pour les entiers positifs


2. Pour la partie décimale :
— On multiplie la partie décimale par 2 ;
— On note la partie entière obtenue ;
— On recommence avec la partie décimale restante ;
— On s’arrête quand la partie décimale est nulle ou quand la précision souhaitée est
atteinte ;
— La partie décimale est la concaténation des parties entières obtenues dans l’ordre de
leur calcul.

Exemple : conversion de 12, 6875 en binaire


Conversion de 12 : 1210 = 11002 .
Conversion de 0, 6875 :
0, 6875 × 2 = 1, 375 = 1 + 0, 375
0, 375 × 2 = 0, 75 = 0 + 0, 75
0, 75 × 2 = 1, 5 = 1 + 0, 5
0, 5 × 2 = 1 = 1 + 0.
Par suite (12, 6875)10 = (1100, 1011)2 .

Codage des réels : virgule flottante

Depuis les années soixante, on utilise une virgule flottante au lieu d’une virgule fixe.
la norme IEEE 754, adoptée par la plupart des fabricants d’ordinateurs distingue deux niveaux
de précision : simple (sur 4 octets) et double (sur 8 octets).
Prenons l’exemple des flottants en simple précision ; les 32 bits (bi ) sont répartis comme ceci :
— 1 bit de signe, c’est le b31 ;
— 8 bits d’exposant, b30 ...b23 ;
— 23 bits de mantisse, b22 ...b0 .
La figure 2.6 illustre la présentation des flottants en simple précision.
Remarques

20
Figure 2.6 – Présentation des flottants en simple précision

— Avec la norme IEEE 754, on doit normaliser nos nombres réels.


Exemple :
1000001101.1 = 1.0000011011 × 29
0.101 = 1.01 × 2−1 .
— Pour prendre en compte les exposants négatifs, on rajoute 127 à l’exposant, voir les
exemples qui viennent juste aprés.

Exemples de Codage des réels

Exemple 1

Soit le nombre réel positif 525.5 à coder en simple précision


— En base 2 : 525.5 = 1000001101.1
— En normalisant, on trouve : 1000001101.1 = 1.0000011011 × 29
— Le signe : b31 = 0 car le nombre est positif
— L’exposant : 9 + 127 = 13610 = 100010002
— La mantisse : est composée de la partie décimale de notre nombre normalisé, complétée
par des 0 sur 23 bits, soit 00000110110000000000000
La représentation de 525.5 en binaire avec la norme IEEE 754 est donc :
0 10001000 00000110110000000000000.
En hexadécimal, on obtient : 0x44036000
Exemple 2
Soit le nombre réel négatif −0.625 à coder en simple précision. Donc on va procéder de la même
manière que le premier exemple.
— En base 2 : −0.625 = 0.101
— En normalisant, on trouve : 0.101 = 1.01 × 2−1
— Le signe : b31 = 1 car le nombre est négatif
— L’exposant : −1 + 127 = 12610 = 011111102
— La mantisse : 01000000000000000000000
La représentation de −0.625 en binaire avec la norme IEEE 754 est donc :
1 01111110 01000000000000000000000.
En hexadécimal, on obtient : 0xBF 200000

21
2.3.5 Cas des caractères
Pour représenter des caractères dans un fichier texte, on associe un nombre (code) à une lettre,
à un chiffre ou à un symbole. Par exemple, 65 pour A, 66 pour B ...
— Représentation en ASCII (American Standard Code for Information Interchange) :
— code à 7 bits qui définit les codes de 0 à 127 pour les caractères anglais, les nombres
de 0 à 9 et certains caractères spéciaux ;
— ne comporte pas les lettres accentuées qui sont nécessaires pour la plupart des lan-
gages européens.
— Unicode : encodage sur 16 bits (65536 possibilités) pour représenter tous les caractères
de toutes les langues.
La figure 2.7 présente une table restreinte des caractères en ASCII.

Figure 2.7 – Représentation des caractères en ASCII

2.3.6 Types de données fondamentaux


Les ordinateurs modernes utilisent généralement des données de 8, 16, 32 ou 64 bits, bien que
d’autres tailles soient possibles. La nomenclature la plus utilisée par les éditeurs de langages de
développement logicielle est normalisée comme suit :
— bit
— octet : 8 bits
— mot (word) : 16 bits
— double mot (d-word) : 32 bits
— quad-mot (q-word) : 64 bits

2.3.7 Ordre d’empilement lors de stockage sur plusieurs octets


On a vu dans la section 2.3.2, qu’on doit stocker une donnée sur plusieurs emplacements (octets
par exemple) consécutives, si la taille de la donnée concernée dépasse la taille de l’emplacement

22
mémoire. Il existe en général deux modes d’empilement.
— Big-Endian : le système enregistre le bit le plus significatif (MSB, Most Signifiant Bit)
en premier. Voir figure 2.8 à droite. Les processeurs Sun et Motorola utilisent ce mode
de stockage.
— Little-Endian : le systeme enregistre le bit le moins significatif (Lsb, Least Signifiant
Bit) en premier. Voir figure 2.8 à gauche. Les processeurs Intel x86 family utilisent ce
mode de stockage.

Figure 2.8 – empilement en Big-Endian et Little-Endian

23
Chapitre 3

Processeur

Figure 3.1 – Le processeur dans l’architecture Von Neumann

3.1 Micro-Processeur
Le micro-processeur ou CPU (Central Processing Unit) est le coeur (cerveau) de l’ordinateur.
Il est chargé de faire les taches suivantes :
— Lecture d’instruction : Le processeur lit une instruction dans la mémoire ;
— Interprétation d’instruction : l’instruction est décodée pour déterminer l’action de-
mandée ;
— Lecture des données : lire des données dans la mémoire ou depuis un module d’E/S ;
— Traitements des données : exécutions d’une opération arithmétique ou logique sur
les données ;
— Écriture des données : écrire des données dans la mémoire ou dans un module d’entrée
sortie.
La figure 3.1 représente le CPU dans l’architecture Von Neumann.
Un micro-processeur est construit autour de quatre éléments principaux :

24
— Unité de contrôle ou séquenceur : se charge de gérer le processeur. Il peut décoder
les instructions, choisir les registres à utiliser, gérer les interruptions ou initialiser les
registres au démarrage ;
— Unité arithmétique et logique (UAL) : effectue les calculs et les opérations logiques
des différents instructions à exécuter ;
— Registre : une mémoire interne pour stocker temporairement les données et les instruc-
tion ;
— Un bus interne pour interconnecter ces différents composants.
La figure 3.2 donne un aperçu de fonctionnement de l’unité arithmétique et logique (UAL).

Figure 3.2 – Fonctionnement de l’unité arithmétique et logique (UAL)

3.2 Familles de micro-processeurs


On appelle un jeu d’instructions l’ensemble des opérations qu’un processeur d’ordinateur
peut exécuter. Une famille de microprocesseurs est, par suite, l’ensemble des microproces-
seurs qui peuvent exécuter le même jeu d’exécution.
Il existe de nombreuses familles de microprocesseur, les principales sont : x86, PowerPC,
ARM ....

3.2.1 La famille x86


La famille la plus connue est la famille x86, qui est développé principalement par Intel célèbre
fabricant de microprocesseur. Il existe aussi AMD, une famille concurrente de Intel.
On retrouve principalement les x86 sur les ordinateurs destinés au grand public. La figure 3.3
présente quelques microprocesseurs de la famille x86.

3.2.2 La famille des PowerPC (PPC)


Ces microprocesseurs sont utilisés essentiellement par Motorola et IBM. Les dérivés du Po-
werPC équipent des consoles de jeux vidéo comme la Wii, la Playstation 3 ou encore la Xbox
360. La figure 3.4 présente un microprocesseur de la famille PowerPC.

25
Figure 3.3 – Familles de micro-processeurs x86

Figure 3.4 – Microprocesseur PowerPC

3.2.3 La famille ARM


Fabriqués par la société Acorn RISC Machines. Les ARM sont très répandues notamment
dans la téléphonie mobile.
Un exemple est présenté dans la figure 3.5

Figure 3.5 – Un mobile utilisant un microprocesseur ARM

26
3.3 historique de micro-processeurs
Environ 1945 est apparue le premier ordinateur entièrement électronique. Avec 19000 tubes, il
pèse 30 tonnes, couvre 72 m2 et permet 330 multiplications par seconde. Voir figure 3.6.

Figure 3.6 – Un des premiers ordinateurs

En 1978, Intel créa un nouveau microprocesseur nommé 8086 qui contient des registres de 16
bits et des bus de données sur 16 bits.
Problème
À partir de cette date, énormément de programmes furent créés pour le 8086. Tous avaient été
écrits selon l’architecture de ce dernier. À cause de cela, Intel ne pouvait plus changer d’ar-
chitecture dans ses nouveaux processeurs, car s’il le faisait, tous les programmes ne serviraient
plus à rien et il faudrait tout réécrire.
Solution
Intel met au point des techniques complexes afin de pouvoir améliorer les processeurs tout en
conservant l’architecture du 8086. Le 8086 devint depuis lors le père de tous les processeurs
Intel, puisqu’ils suivent tous son architecture.
Compatibilité ascendante
Un nouveau processeur doit posséder toutes les fonctionnalités qui ont existé dans tous les
processeurs précédents depuis le 8086.
Exemple :
Lorsque Intel créa 80186, le successeur du 8086, il y inclut toutes les fonctionnalités du 8086,
en plus des nouvelles fonctionnalités propres au 80186.
lorsqu’on crée un nouveau processeur, on ne peut qu’ajouter de nouvelles fonctionnalités en

27
plus de celles du précédent, jamais en supprimer.
De ce point de vue, des nouveaux micro-processeurs sont construits.
— Intel 80386 offrait des registres sur 32 bits et un bus d’adresse sur 32 bits ;
— La famille Pentium a apporté de nombreuses améliorations de performances, notam-
ment une conception super-scalaire fondée sur deux pipelines parallèles rendant possibles
le décodage et l’exécution de deux instructions en même temps.
— En 1995, est apparue la sous-famille des processeurs dite P6 (Pentium Pro, le Pentium
II et le Pentium III). Elle permettait une extension de l’architecture de base 32 bits.
Processeur Année de sortie
8086 1978
80186 1982
80286 1982
80386 1985
80486 1989
Pentium 1993
Pentium 2 1997
Pentium 3 1999
Pentium 4 2000
Core 2 Duo 2006
Core 2 Quad 2007
Intel Core i5/i7 2009
Intel Core i3/i5/i7 (Coffe Lake) 2017

Table 3.1 – Historique de micro-processeurs

Le tableau 3.1 présente un petit historique de micro-processeurs.

3.4 Les registres


Les registres sont des emplacements où l’on peut stocker des données. Mais contrairement à la
mémoire qui est à l’extérieur du processeur, les registres sont placés à l’intérieur du processeur
lui-même.
Les registres d’un processeur sont ses mémoires les plus rapides. L’utilité des registres réside
dans les points suivants :
— stocker des opèrandes (paramètres) lors d’opérations logiques et arithmètiques ;
— stocker des opèrandes pour les calculs d’adresses ;
— stocker des pointeurs (adresses mèmoires).

3.4.1 Registres en 8086


Les registres en 8086 sont divisés en quatre groupes :
— Registres généraux (pour stocker les adresses et les opèrandes) : ax, bx, cx, dx, di, si,
sp, bp ;
— Registres de segments : cs, ds, es, ss ;

28
— Le registre flags (registre d’état et de controle).
— Le registre ip (instruction pointer : il pointe sur la prochaine instruction à exécuter).
Chacun des registres est constitué de 16 bascules, et peut donc contenir 16 bits (2 octets).

3.4.2 Registres à partir de 80386


À partir de 80386 les registres contiennent 32 bits. On utilise eax au lieu de ax pour signifie
une exention de ax. La figure 3.7 présente les 16 registres de 80386, et la figure 3.8 présente la

Figure 3.7 – Registres à partir de 80386

manipulation des registres 8, 16 et 32 bits.

Registres généraux

On peut diviser ces registres en deux groupes.


Les quatre premiers sont des registres de travaux. Ce sont des registres 32 bits et ils sont
utilisés principalement pour stocker des résultats.
— EAX : est un registre accumulateur (accumulator register). Il est utilisé pour les
opérations arithmétiques et le stockage de la valeur de retour des appels systèmes.
— EDX : est un registre de données (data register). Il est utilisé pour les opérations
arithmétiques et les opérations d’entrée/sortie.
— ECX : est un registre compteur (counter register).
— EBX : est un registre de base (base register). Il est utilisé comme pointeur de donnée
(située dans DS en mode segmenté).
Les registres d’offset sont utilisés lors de l’adressage indirect de la mémoire (pointeurs).
— EBP : est un pointeur de base (Extended Base Pointer) ;
— ESP : est un pointeur de pile (Extended Stack Pointer) ;
— ESI : est un pointeur source pour la manipulation des caractères (Extended Source
Index) ;

29
Figure 3.8 – Registres 8, 16 et 32 bits.

— EDI : est un pointeur destination pour la manipulation des caractères (Extended Des-
tination Index) .
Le nom des deux derniers registres vient du fait qu’ils sont utilisés pour la copie d’une zone
mémoire vers une autre.
La figure 3.9 illustre le fonctionnement des registres BP et SP.

Figure 3.9 – Registres de la gestion de la pile

Registres de segment

La mémoire dans l’architecture x86 est divisée en segments. Les registres de segment per-
mettent d’accéder, soit au segment du programme qui est la zone mémoire des instructions du
programme, soit au segment de données (zone mémoire contenant les données du programme),
ou encore au segment de pile.
— CS : est un registre qui pointe vers les instructions du programme (code segment).
— DS : est un registre qui pointe vers les données du programme (data segment).
— SS : est un registre qui pointe vers la pile (stack segment).

30
— ES : est un registre qui pointe vers les données du programme multi-segments (extra
segment).
— FS et GS : sont des registres qui pointent vers les données du programme multi-segments
en mode protégé.
La gestion de mémoire en multi-segments permet d’utiliser tous les registres d’offset. La plupart
des systèmes d’exploitation actuels utilisent un mode protégé où tous les registres de segment
pointent vers le même segment.

Registre d’état : EFLAGS

Le registre EFLAGS (Extended Flags) contient un mot de 32 bits. Chaque bit est un indicateur
d’état qui peut être modifié à chaque instruction exécutée comme pour le retenue (addition ou
soustraction), le dépassement, la comparaison, l’autorisation des interruptions ...
Ci-aprés les indicateurs d’état les plus utilisés :
— CF (Carry Flag) : Cet indicateur est positionné à 1 si la dernière opération a générée
une retenue (mode non signé), sinon à 0. Il est utilisé par les sauts conditionnels tel que
JC et JB, et les instructions arithmétiques ADC et SBB.
— PF (Parity Flag) : Cet indicateur est positionné à 1 si dans les 8 bits de poids faible
du résultat de la dernière opération, le nombre de bits à 1 est pair, à 0 si ce nombre est
impair.
— ZF (Zero Flag) :Cet indicateur est positionné à 1 si le résultat d’une opération arith-
métique vaut zéro, 0 sinon.
— SF (Sign Flag) : Cet indicateur est positionné à 1 si la dernière opération a générée
un résultat négatif, à 0 s’il est positif ou nul.
— OF (Overflow Flag) : Cet indicateur est positionné à 1 si le dernier résultat a débordé
de la taille du registre, sinon à 0. Il indique une condition de débordement pour les
opérations arithmétiques signées sur les entiers.
— DF (Direction Flag) : Cet indicateur est positionné à 1 si le transfert de données se
fait en décrémentant les offsets et à 0 en incrémentation des offsets.
Exemple :
Somme de deux nombres a et b :
a = 4294967295 = 0xffffffff et b = 1 = 0x00000001
Résultat 0x00000000

Flags : O=0 D=0 I=1 S=0 Z=1 A=1 P=1 C=1

Le pointeur d’instruction

Le registre EIP est utilisé avec le segment du registre CS par le processeur pour connaitre
la prochaine instruction à exécuter.
Ce registre est donc modifié implicitement par le processeur (instruction suivante, saut à
l’adresse indiquée, appel d’une fonction, interruption ...).

31
Figure 3.10 – Cycle d’exécution d’une instruction : phase 1

3.5 Fonctionnement d’un microprocesseur

3.5.1 Cycle d’exécution d’une instruction


L’exécution d’une instruction passe par trois étapes : chercher l’instruction, décoder l’instruction
et exécuter l’instruction.

Phase 1 : chercher l’instruction à exécuter

Dans cette phase, premièrement, le compteur ordinal (ou pointeur d’instruction IP) contient
l’adresse de l’instruction suivante du programme. Cette valeur est placée sur le bus d’adresses
par l’unité de commande qui émet un ordre de lecture.
Puis, au bout d’un certain temps qui est le temps d’accès à la mémoire, le contenu de la case
mémoire sélectionnée sera disponible sur le bus des données. Enfin, l’instruction sera stockée
dans le registre instruction du processeur.
L’image 3.10 illustre ces étapes.

Phase 2 : décoder l’instruction

Aprés la première phase, le registre d’instruction contient le premier mot de l’instruction qui
peut être codée sur plusieurs mots. Ce premier mot contient le code opératoire qui définit la
nature de l’opération à effectuer (addition, ...) et le nombre de mots de l’instruction. Cette
phase se divise généralement en trois étapes :
1. L’unité de commande transforme l’instruction en une suite de commandes élémentaires
nécessaires au traitement de l’instruction ;
2. Si l’instruction nécessite une donnée en provenance de la mémoire, l’unité de commande
récupère sa valeur sur le bus de données ;
3. L’opérande est stockée enfin dans un registre.

32
Figure 3.11 – Cycle d’exécution d’une instruction : phase 2

L’image 3.11 présente ces étapes.

Phase 3 : exécuter l’instruction

Dans cette phase l’instruction sera exécuté. Ensuite, les drapeaux sont positionnés (voir registre
d’état). Aprés, l’unité de commande positionne le compteur ordinal (PC) pour l’instruction
suivante. Voir l’image 3.12.

3.5.2 Fonctionnement d’un microprocesseur


Le microprocesseur est cadencé à une horloge interne qui envoie des impulsions, appelées top.
Ce nombre de top par seconde correspond à une fréquence (cycle ou horloge interne) exprimé
en Hertz (Hz). Il arrive assez souvent que sa fréquence soit un multiple de celle de la carte
mère. Le microprocesseur exécute une action (instructions ou partie d’instruction) à chaque
top.

Puissance d’un microprocesseur

Pour calculer la puissance on utilise Le CPI (Cycle Par Instruction) qui représente le nombre
moyen de cycles d’horloge nécessaire pour l’exécution d’une instruction. On utilise comme unité
le MIPS (Millions d’Instructions Par Seconde). Pour la calculer il faut diviser sa fréquence par
le CPI.
Le nombre de cycles dépend généralement de :
— la complexité de l’instruction ;
— le mode d’adressage, il est plus long d’accéder à la mémoire principale qu’à un registre
du processeur ;
— la fréquence d’horloge du séquenceur.

33
Figure 3.12 – Cycle d’exécution d’une instruction : phase 3

La majorité des processeurs modernes dépassent 1 GHz. Un processeur à 2 GHz peut effectuer
jusqu’à 2 milliards d’opérations par seconde. Actuellement, les processeurs les plus rapides
atteignent jusqu’à 5 GHz. Le niveau de performance d’un microprocesseur dépend généralement
de plusieurs facteurs tels que :
— la fréquence ;
— la mémoire cache intégrée ;
— le nombre de coeurs. La plupart des processeurs actuels possèdent plusieurs coeurs. Ils
permettent d’effectuer plusieurs opérations en même temps, une tâche par coeur et de
rendre l’ordinateur plus performant.
— ...
Le tableau 3.2 présente les caractéristiques de quelques microprocesseurs.

Processeur Année de sortie Fréquence de l’horloge Largeur des données


80286 1982 6 à 16 MHZ 16 bits/16 bits bus
80386 1985 16 à 40 MHZ 32 bits/32 bits bus
Pentium 4 2000 1.3 à 3.8 GHZ 32 bits/64 bits bus
Core 2 Duo 2006 2.4 GHZ 64 bits/64 bits bus
Core 2 Quad 2007 3 GHZ 64 bits/64 bits bus
Intel Core i5/i7 2009 3.06 GHZ 64 bits/64 bits bus
Intel Core i3/i5/i7 2017 3.7 à 4.7 GHZ 64 bits/64 bits bus
(Coffe Lake)

Table 3.2 – Caractéristiques de micro-processeurs

Mémoire cache

Le cache contient une copie des données originelles lorsqu’elles sont coûteuses (en termes de
temps d’accès).

34
Fonctionnement

1. le microprocesseur demande une information ;


2. le cache vérifie s’il possède cette information.
— S’il la possède, il la retransmet au microprocesseur. On parle alors de succès de
cache. La figure 3.13 (en haut) illustre ce cas ;
— S’il ne la possède pas, il la demande à la mémoire principale. On parle alors de défaut
de cache. La figure 3.13 (en bas) présente ce cas.
3. le microprocesseur traite la demande et renvoie la réponse au cache ;
4. le cache la stocke pour une utilisation ultérieure et la retransmet au microprocesseur au
besoin.

Figure 3.13 – Succès de cache - défaut de cache

Principe
Dans l’objectif d’accroître les performances d’un microprocesseur, la mèmoire cache utilise deux
principes :
— le principe de localité spatiale qui indique que l’accès à une donnée située à une adresse
x va probablement être suivi d’un accès à une zone très proche de x ;
— le principe de localité temporelle qui indique que l’accès à une zone mémoire à un
instant donné a de fortes chances de se reproduire dans la suite immédiate du programme.

Performances d’un microprocesseur

La puissance d’un microprocesseur est caractérisée par le nombre d’instructions qu’il est capable
de traiter par seconde.
Pour augmenter les performances d’un microprocesseur, on peut donc :
— soit augmenter la fréquence d’horloge (limitation matérielle) ;
— soit diminuer le CPI (choix d’un jeu d’instruction adapté).

35
3.6 Architecture des microprocesseurs

3.6.1 Architectures CISC et RISC


Actuellement l’architecture des microprocesseurs se compose de deux grandes familles :
— l’architecture CISC (Complex Instruction Set Computer) ;
— l’architecture RISC (Reduced Instruction Set Computer).

Architecture CISC

Un microprocesseur en CISC désigne un microprocesseur possédant un jeu d’instructions plus


complexe (comprenant plus de 200 instructions).
L’objectif de cette multitude d’instructions complexes est de faciliter la programmation en as-
sembleur. Néanmoins, cette méthode de programmation nécessite un cablage interne complexe
qui ralentit la vitesse globale du microprocesseur. De plus, ces instructions complexes sont rare-
ment utilisées par les programmeurs en pratique. Le 8088 (comme tous les premiers modèles)
sont des processeurs CISC.
On peut résumer l’architecture CISC dans les points suivants :
— le temps d’exécution des instructions est variable.
— elle peut effectuer plusieurs opérations élémentaires comme charger une valeur en mé-
moire, faire une opération arithmétique et ranger le résultat en mémoire.
— la vitesse typique : une instruction dure entre 3 et 10 cycles.

Parmi les avantages de cette architecture, on peut citer :


— réduction du nombre d’instructions d’un programme ;
— développement plus simple.

Et parmi les inconvénients :


— temps d’exécution (des instructions) plus long ;
— processeur plus complexe et donc plus lent.

Architecture RISC

Dans ce cas, le nombre d’instruction en assembleur est réduit, ce qui permet un cablage interne
plus rapide avec des architectures internes différentes comme pipeline et superpipeline.
On peut résumer l’architecture RISC dans les points suivants :
— généralement, 80% des traitements des langages de haut niveau faisaient appel à seule-
ment 20% des instructions du microprocesseur. De ce point de vue, les processeurs RISC
possèdent un jeu d’instructions plus simple et réduit (<100 instructions) ;
— chaque instruction effectue une seule opération élémentaire ;
— réduire le jeu d’instructions à celles le plus couramment utilisées ;
— donc améliorer la vitesse de traitement ;
— toutes les instructions sont codées sur la même taille ;
— vitesse typique : une instruction dure un cycle.

36
L’éxécution devient plus rapide avec cette architecture, mais au cout d’un compilateur plus
complexe.
La figure 3.14 présente une comparaison entre les architectures SISC et RISC.

Figure 3.14 – Architectures SISC et RISC

3.6.2 Architecture Pipeline


Pour le classic RISC pipeline, 5 étapes sont nécessaires pour traiter une instruction :
1. IF (Instruction Fetch) charge l’instruction à exécuter dans le pipeline.
2. ID (Instruction Decode) décode l’instruction et adresse les registres.
3. EX (Execute) exécute l’instruction (par la ou les unités arithmétiques et logiques).
4. MEM (Memory), dénote un transfert depuis un registre vers la mémoire dans le cas
d’une instruction du type STORE (accès en écriture) et de la mémoire vers un registre
dans le cas d’un LOAD (accès en lecture).
5. WB (Write Back) stocke le résultat dans un registre. La source peut être la mémoire
ou bien un registre.
En supposant que chaque étape met un cycle d’horloge pour s’exécuter, il faut normalement,
sans pipeline, 5 cycles pour exécuter une instruction, 15 pour 3 instructions.
La figure 3.15 présente un exemple n’utilisant pas l’architecture Pipeline, et la figure 3.16
présente le même exemple avec l’architecture Pipeline.

Figure 3.15 – Exemple sans Pipeline

En utilisant la technique du pipeline, notre processeur peut alors contenir plusieurs instructions,
chacune à une étape différente. Les 5 instructions s’exécuteront en 9 cycles, et le processeur
sera capable de terminer une instruction par cycle à partir de la cinquième, bien que chacune
d’entre elles nécessite 5 cycles pour s’exécuter complètement. Au 5eme cycle, tous les étages
sont en cours d’exécution.

37
Figure 3.16 – Exemple avec l’architecture Pipeline

3.7 Le processeur 8086 et la segmentation de la mémoire


L’espace adressable pour le micro-processeur 8086 étant de 220 = 1 Mo. Par conséquent, une
adresse doit être représentée par 20 bits. Or en mode réel, le processeur Intel 8086 possède des
registres sur 16 bits, ce qui l’empêchait de représenter directement une adresse sur 20 bits.
Afin de pallier ce manque, on utilise deux nombres pour adresser un octet quelconque de la
mémoire. Le premier est appelé adresse de segment, le second est appelé adresse d’offset,
et ils seront stockés séparément.
La mémoire est découpée en segments de 64 Ko chacun. Un segment est donc en quelque sorte
un gros bloc de mémoire auquel on peut accéder grâce à une adresse de segment qui désigne son
numéro. Par exemple, le premier segment est le segment 0000 (en hexadécimal), le deuxième
est le 0001, etc. Chaque numéro est codé sur 4 chiffres hexadécimal. Pour accéder à un octet
particulier dans un segment, il suffit de compter le décalage de cet octet par rapport au début
du segment. Ce décalage (offset) est obligatoirement inférieur ou égal à 65535 : il tient bien sur
16 bits lui aussi.
L’adresse logique d’un octet se note adresse-de-segment :offset.
Les segments ne sont pas situés les uns à la suite des autres. Autrement dit, le deuxième seg-
ment ne commence pas à l’octet 65536, mais à l’octet 16. Le troisième commence à l’octet 32,
etc.
La conséquence immédiate de ce que précéde est qu’un octet n’a pas une adresse logique unique.
En effet, l’octet numéro 66 peut être adressé par 0000 : 0042, mais aussi par 0001 : 0032, par
0002 : 0022, etc. Toutes ces adresses sont équivalentes.

Règle
adresse physique = 16× (registre de segment) + offset.

Exemple 1
L’adresse physique de 0B07 : 0100 est : 0B07 × 16 + 0100 = 0B070 + 0100 = 0B170
Exemple 2
Dans la figure 3.17, le programme commence à l’adresse 0B56 :0100. Le registre CS contient
l’adresse 0B56 et le registre IP contient l’adresse 0100.
Pour la mémoire, l’adressage va de 00000h à FFFFFh ce qui représente 1Mo. L’adresse physique
correspondant à l’adresse logique 0B56 :0100 est

0B56 × 16 + 0100 = 0B560 + 0100 = 0B660.

38
Figure 3.17 – Illustration de l’adressage segment :offset par l’emulateur emu8086

0B560 est le début du segment. La fin du segment est obtenu en ajoutant FFFFh, soit 64 Ko
ou 65536 octets. Nous avons donc 0B560+FFFF=1B55F qui représente la fin de notre segment
de code CS.
0B660 contient le premier octet correspondant aux instructions que doit exécuter le processeur,
dans l’exemple de la figure c’est B8.

3.8 Exercices

Exercice 1
1. Donner l’emplacement et la taille approximative des : DD, RAM, Cache, Registre ?
2. Quelles sont les atouts du processeur 80386 ?
3. Que prend en charge les calculs arithmétiques élémentaires ?
— L’UAL
— L’unité de contrôle
— Les registres

Exercice 2
1. Quel registre permet de stocker les données en cours de traitement par l’UAL ?
— Instruction
— Etat
— Accumulateur
2. Quel est la fonction du pipeline ?
— Il permet la transition de données à virgule.

39
— Il permet la récuperation de données.
— Il permet d’assembler temporellement les traitements a effectuer.

Exercice 3
A quoi correspondant 3 GHz d’un microprocesseur ?
— Le nombre de transistor.
— La finesse de gravure.
— La fréquence d’horloge.

Exercice 4
— Donner l’adresse physique de l’adresse suivante : A17C :022E
— Donner une adresse équivalente à l’adresse précédente.

40
Chapitre 4

Les bus

4.1 Définitions
Les informations échangées entre la mémoire et le processeur circulent sur des bus.
Un bus est un ensemble de n fils conducteurs, utilisés pour transporter n signaux binaires.
Chaque fil peut transporter un bit : il vaut 1 quand il est chargé et 0 quand il ne l’est pas.
Exemple : Un bus qui contient 8 fils électriques, peut transmettre 8 bits, c’est à dire un octet.
On trouve deux types de bus : les bus d’adresses et les bus de données.
— Le bus d’adresse est un bus unidirectionnel : seul le processeur envoie des adresses.
— Le bus de données est un bus bidirectionnel.
— Lors d’une lecture, c’est la mémoire qui envoie un mot sur le bus (le contenu de
l’emplacement demandé) ;
— lors d’une écriture, c’est le microprocesseur qui envoie la donnée.
La figure 4.1 présente les deux types de bus : bus de données et bus d’adresses.

Figure 4.1 – Présentation des bus

41
4.2 Largeur d’un bus
On appelle largeur d’un bus le nombre de fils qu’il contient. Plus un bus est plus large, plus
il peut transporter de bits en un seul transport.

Figure 4.2 – Exemple d’un bus de largeur 8 bits

La figure 4.2 présente un bus de largeur 8 bits transmettant le nombre binaire 01010110, qui est
équivaut à 86 en décimal. Dans cette figure, les traits noirs correspondent aux fils non chargés
et les traits jaunes au fils chargés.
Problème : Considèrons le nombre binaire 1010101011111111 (43775 en décimal) stocké par
exemple à l’adresse 3.
Comment peut-on transporter cette donnée avec un bus de données de largeur 16 bits ? et avec
un bus de données de largeur 8 bits ?

4.2.1 Transportation avec un bus de données plus large

Figure 4.3 – Un bus de données plus large que la donnée transportée

Considérons le nombre binaire 1010101011111111 stocké à l’adresse 3.


Avec un bus de données de largeur 16 bits, on a besoin d’un seul transport (accès mémoire).

42
La figure 4.3 présente un bus capable de transporter la donnée demandée en un seul transport.

4.2.2 Transportation avec un bus de données moins large


Avec un bus de données de largeur 8 bits, un seul transport ne suffit pas. On a besoin donc de
deux transports.
La figure 4.4 présente un bus nécessitant deux transports pour transmettre la donnée demandée.

Figure 4.4 – Un bus de données moins large que la donnée transportée

4.2.3 Transportation d’adresses


Contrairement aux bus de données, lorsqu’on veut transporter une adresse, il faut le faire en
une seule fois. Il est donc totalement impossible de transporter une adresse plus grande que
le bus d’adresse.
Exemple : avec un bus d’adresse de 20 bits, on ne pourra pas transporter une adresse plus
élevée que 1048575.
On aura donc la propriété importante suivante : Le nombre d’emplacements mémoire
utilisables est limité par la largeur du bus d’adresse.

4.2.4 Propriétés
De ce qui précède on peut déduire les deux résultats suivants :
— La largeur du bus d’adresse influe sur la capacité d’adressage, c’est à dire la
quantité de mémoire utilisable par l’ordinateur.
— La largeur du bus de données influe sur la vitesse de l’ordinateur. Plus il est large,
moins de transports sont nécessaires pour transmettre les données.

43
4.3 Fréquence et débit d’un bus
La fréquence d’un bus, exprimée en hertz, est le nombre de bits envoyés ou reçus par fil par
seconde. On parle de cycle pour désigner chaque envoi ou réception de données.
Le débit est la quantité de données qu’il peut transporter par unité de temps.
Le débit maximal du bus (ou taux de transfert maximal) est calculé en multipliant sa largeur
par sa fréquence.
Exemple
Calcul du débit (taux de transfert) d’un bus d’une largeur de 16 bits, cadencé à une fréquence
de 133 MHz.
Taux de transfert = 16 × 133 × 106 = 2128 × 106 bit/s,
ou encore le taux de transfert = 2128 × 106 /8 = 266 × 106 octets/s,
donc le taux de transfert est 266 Mo/s.

4.4 Fonctionnement : récapitulatif


Soit le code C suivant :

#i n c l u d e <s t d i o . h>
main ( ) {
i n t x = 1 , y = 5 , somme ;
somme = x + y ;
p r i n t f ( "%d" , somme ) ;
getch ( ) ;
}

Dans cette section on va expliquer, d’une manière simple, ce qui se passe entre le processeur,
la mèmoire et les bus.
Premièrement, le processeur envoie 1 à la mémoire via le bus de données. Il envoie aussi l’adresse
de x (supposons 1090) via le bus d’adresse, pour indiquer à la mémoire où il veut que 1 soit
stocké.
De même il va envoyer 5, l’adresse de y (supposons 1230) et l’adresse de somme (supposons
1500).
— Le processeur envoie 1090 à la mémoire via le bus d’adresse ;
— La mémoire envoie au processeur le nombre situé à l’adresse reçue, via le bus de données,
donc le processeur reçoit le nombre 1 ;
— A nouveau, le processeur envoie à la mémoire l’adresse 1230 ;
— La mémoire envoie le nombre situé à l’adresse reçue au processeur, en utilisant le bus de
données, donc le processeur reçoit le nombre 5 ;
— Le processeur fait le calcul x + y ;
— Le processeur envoie le résultat à la mémoire via le bus de données. Il envoie aussi
l’adresse 1500 via le bus d’adresse, pour indiquer à la mémoire où il veut que le résultat
soit stocké.

44
— L’emplacement mémoire à l’adresse 1500 contient donc le résultat 6.

45
Chapitre 5

Entrées sorties

5.1 Introduction
Les Entrées-Sorties (Input-Output en anglais) désigne l’ensemble des transferts de données
qui permettent au microprocesseur et à la mémoire de communiquer avec le monde extérieur.
Une entrée est une donnée (information) allant du monde extérieur vers le microprocesseur ;
et Une sortie est une donnée (information) allant du microprocesseur vers le monde extérieur.
La fonction de communication nécessite des périphériques d’entrées sorties. Un périphérique
est un dispositif matériel qui interagit avec le microprocesseur et la mémoire. Il permet d’assurer
les échanges d’informations en entrée et en sortie et de stocker de manière permanente des
informations.
On peut classer les périphériques en trois types :
— les périphériques d’entrée qui servent à obtenir des informations (ou données) pour le
système informatique. On peut citer dans ce type : le clavier (frappe de texte), la souris
(pointage), le scanner (numérisation de documents papier), etc.
— les périphériques de sortie qui servent à faire sortir des informations du système infor-
matique comme l’écran, l’imprimante, le haut-parleur, etc.
— Les périphériques d’entrée et de sortie qui opèrent aussi bien en écriture qu’en lecture.
Par exemple un disque dur, un lecteur/enregistreur de CD-ROM, une clé USB, etc.

Certains périphériques (comme les disques durs, la carte réseau, ...) sont branchés à l’intérieur
de l’ordinateur, alors que d’autres (comme le clavier, l’écran, la souris, etc.) sont branchés sur
des interfaces externes de l’ordinateur. On peut trouver aussi des périphériques branchés à un
réseau informatique et qu’ils communiquent avec un ordinateur central ou avec un serveur.
La figure 5.1 présente quelques interfaces des périphériques.

5.2 Contrôleur d’entrées sorties


Un périphérique se décompose habituellement en deux parties modulaires. La première est le
périphérique lui-même que l’on appelle composant physique. La seconde, électronique, est
appelée contrôleur de périphérique.

46
Figure 5.1 – 1. USB : connexion de périphériques. 2. RJ45 : connexion au réseau local filaire.
3. VGA : connexion de l’écran. 4. HDMI : connexion à un écran haute résolution

Les contrôleurs de périphériques sont des circuits électroniques conçus spécialement pour
gérer ces périphériques. Ils lisent les commandes envoyées par le processeur, les interpréter et
piloter le périphérique de façon à faire ce qui est demandé.
Un contrôleur d’entrée-sortie contient quatre types de registre : registre de données, registre de
contrôle, registre d’état et registre de commande.
Dans la façon la plus simple de procéder, le processeur envoie sur le bus l’adresse, les données et
les commandes à l’entrée-sortie. Ensuite, le processeur va devoir attendre et rester connecté au
bus tant que le périphérique n’a pas traité sa demande correctement, que ce soit une lecture, ou
une écriture. Mais, cette façon de faire a un inconvènient primordial. En effet, les périphériques
sont tellement lents que le processeur passe son temps à attendre le périphérique.
Pour résoudre ce problème, on utilise les registres d’interfaçage du contrôleur entre le proces-
seur et les entrées-sorties. Une fois que le processeur a écrit les informations à transmettre dans
ces registres, il peut faire autre chose.
Exemple d’une lecture classique
Afin de lire un périphérique (un disque dur par exemple) :
— le processeur place dans les registres de contrôle sa commande ;
— il attend ensuite l’interruption du périphérique et passe à autre chose.
— le périphérique lit ses registres de manière complètement indépendante puisqu’il est
indépendant du CPU ;
— il effectue la commande en plaçant le résultat dans sa mémoire interne ;
— il effectue les validations d’usage et déclenche ensuite une interruption.
— le processeur reprend le contrôle ;
— il lira mot par mot dans un registre du périphérique le bloc complet en effectuant une
boucle.

47
5.3 Types de périphériques
Le tableau 5.1 présente les principaux périphériques. Pour chaqu’un, on désigne s’il s’agit d’un
périphérique d’entrée ou de sortie et on donne la vitesse et le type de la donnée traitée.
Périphérique Type Vitesse Type de données
Clavier Entrée Très faible Caractères
Souris Entrée Faible Caractères
Ecran tactile Entrée Faible Caractères
Scanneur Entrée Moyenne Blocs
Voix Entrée Faible à moyenne Blocs
Son Entrée-sortie Moyenne Blocs ou continu
USB Entrée-sortie Faible à très élevée Blocs
Réseau Entrée-sortie Élevée à très élevée Blocs
Imprimante Sortie Faible à moyenne Blocs
Carte graphique Sortie Élevée Continu
Disque flash Stockage Moyenne Blocs
Disque magnétique Stockage Moyenne Blocs
SSD Stockage Moyenne à élevée Blocs
Disque optique Stockage Moyenne à élevée Blocs ou continu
Tape magnétique Stockage Faible à moyenne Blocs ou continu

Table 5.1 – Caractéristiques des principaux périphériques

Un périphérique caractère représente un dispositif matériel qui lit ou écrit en série un flux
d’octets. Les ports série et parallèle, les terminaux et les cartes son sont des exemples de
périphériques caractères.
Un périphérique bloc représente un dispositif matériel qui lit ou écrit des données sous forme
de blocs de taille fixe. Contrairement aux périphériques caractère, un périphérique bloc fournit
un accès direct aux données stockées sur le périphérique. Un lecteur de disque est un exemple
de périphérique bloc.

5.4 Gestion d’entrées sorties


En général, il existe trois méthodes de gestion d’entrées sorties : les entrées sorties programmées,
les entrées sorties pilotées par les interruptions et les entrées sorties avec DMA.

5.4.1 Les entrées sorties programmées


Cette technique consiste à laisser au processeur de traiter toute l’opération. Le processeur gère
toutes les étapes du transfert et dialogue continuellement avec le contrôleur de périphérique
pour vérifier s’il est en état de continuer. Le processeur ne soit libéré que lorsque l’entrée sortie
est terminée.
Cette technique est simple mais elle est lente. En effet, si le périphérique est lent, le processeur
sera monopolisé pendant toute la durée de l’échange.

48
Cette forme d’échange était la seule possible dans les premières générations de machines.

5.4.2 Les entrées sorties pilotées par les interruptions


Pour éviter la monopolisation du processeur pendant toute la durée de l’entrée sortie, une
première amélioration a été apportée grâce au mécanisme d’interruption.
Une interruption est un signal qui force le processeur à interrompre l’exécution du programme
en cours pour lancer une procédure spéciale appelée procédure d’interruption.
Cette technique est utilisée lorsque le processeur doit réagir rapidement à un changement d’état
d’un port d’entrée. Par exemple une entrée imprévue, une situation anormale, une instruction
illégale, etc.
— le périphérique prévient le processeur par une ligne d’interruption prévue à cet effet.
— le processeur interrompt la tâche en cours ;
— le processeur saute au sous-programme destiné à gérer la demande spécifique qui lui
est adressée ;
— à la fin du sous-programme, le processeur reprend l’exécution du programme principal
là où il l’avait laissée.
L’avantage est que avec cette technique, le microprocesseur n’a plus besoin d’attendre après
le périphérique, car c’est ce dernier qui dit au microprocesseur quand il est prêt. Cependant,
l’inconvènient réside toujours dans le fait que le microprocesseur doit gérer le transfert de
données lui-même. En effet, il ne peut faire autre chose durant ce temps.

5.4.3 Les entrées sorties avec DMA


Une alternative aux entrées sorties gérées par les interruptions est l’utilisation de l’accés direct
à la mémoire connue par le nom DMA (Direct Memory Access).
Le dispositif DMA est un composant matériel permettant un transfert de données direct entre
un périphérique et la mémoire ou vice versa, effectué sans intervention du microprocesseur.
Avec cette technique, le DMA se charge entièrement du transfert de données. Ce transfert se
fait par bloc de données, à des adresses contigues de la mémoire et provenant habituellement
du même périphérique.
La tache du processeur consiste seulement à initialiser l’échange entre le périphérique et la
mémoire en donnant l’identification du périphérique concerné, le sens du transfert, l’adresse en
mémoire centrale du premier mot à transférer et le nombre de mots concernés par l’échange.
Lorsque l’échange est terminée, le DMA signale au processeur que l’opération est terminée en
déclechant une interruption.
Le DMA est utilisé dans les ordinateurs, par exemple pour le transfert de données entre la
mémoire et le disque dur, ou pour le transfert de données vers la carte graphique ou la carte
de son, etc.
Le DMA fonctionne comme une entrée sortie programmée, sauf que le contrôleur DMA fait
le travail à la place du processeur.
L’avantage est de réduire le nombre d’interruptions. En effet, on passe d’une interruption par
caractère à une interruption par tampon.

49
Deuxième partie

Langage Assembleur

50
Chapitre 6

Introduction au Langage Assembleur

6.1 Introduction
Un programme est composé d’une suite d’instructions. L’ordinateur lit les instructions et les
applique dans l’ordre. Mais en général, il existe un nombre fixe d’instructions existantes. Elles
constituent ce qu’on appelle le jeu d’instructions.
Le nombre d’instructions disponibles peut varier d’une architecture à l’autre. Dans l’architecture
80x86, le jeu d’instructions contient environ une centaine d’instructions.

6.1.1 Principe
On représente chaque instruction par un symbole mnémonique écrit en lettres humaines. Par
exemple, add est le symbole mnémonique de l’instruction qui sert à faire l’addition.
Puis, on met en marche un logiciel (traducteur) nommé Assembleur. L’Assembleur remplace
chaque mnémonique par l’instruction binaire équivalente.
Pour un même microprocesseur, plusieurs langages assembleurs sont possibles. Ils se distinguent
surtout par les directives (.data ?, .code, ... par exemple) et les macros.
Les logiciels assembleurs les plus connus pour la famille Intel sont MASM (Microsoft ASseM-
bler), TASM (Turbo ASseMbler) et NASM (Netwide ASseMbler).
L’assembleur utilisé dans ce cours est MASM. On va utiliser la version utilisant la bibliothèque
Irvine32.

6.1.2 Premier exemple


Ci-après on exposera le code assembleur qui affiche le message ’Bonjour’ :
INCLUDE Irvine32.inc
.data
message BYTE "Bonjour",0
.code
main PROC

51
mov edx,OFFSET message
call WriteString
exit
main ENDP
END main
Ce code comme tout code Assembleur contient :
— des instructions ;
— des directives ;
— des macros ;
— ...

On a vu dans l’introduction de la première partie c’est quoi une instruction. Dans la section
suivante on étudiera les directives et les macros.

6.2 Directives et Macros

6.2.1 Les directives


Les directives sont des ordres que l’on donne à l’Assembleur (MASM) lui-même. On les
utilise pour demander certaines choses à MASM, notamment comment on veut qu’il assemble
le programme.
Les directives ne feront pas partie du programme en langage machine, puisqu’elles ne sont
destinées qu’à l’Assembleur et non au processeur.
Exemple :
INCLUDE Irvine32.inc est une directive qui indique à MASM d’utiliser la bibliothèque Ir-
vine32.

6.2.2 Les macros


Les macros sont en réalité eux-même composées d’instructions.
Lorsqu’on utilise une macro, elle est remplacée par la suite d’instructions préalablement prépa-
rée par les programmeurs.
Exemple :
exit est une macro pour quitter convenablement un programme.

6.3 Description du premier exemple


Sans utiliser la bibliothèque Irvine32, le code comportera plus d’instructions et des directives.
Le programme ci-après est le même que celui du premier exemple en utilisant la bibliothèque
Irvine32 mais sans utiliser des procédures (main PROC).

52
INCLUDE Irvine32.inc
.data
message BYTE "Bonjour",0
.code
salut :
mov edx,OFFSET message
call WriteString
exit
end salut

6.3.1 Description du programme


— On utilise le point virgule “ ;” pour les commentaires.
Par exemple l’instruction ; premier exemple est un commentaire en MASM.

— INCLUDE Irvine32.inc
est une directive qui indique à MASM d’utiliser la bibliothèque Irvine32.
“include” en MASM32 correspond à un “#include” en C. Les fichiers .inc contiennent
les signatures des fonctions à utiliser.

— .data
est une directive indiquant à MASM que dans cette partie qu’on déclare nos variables.

— message BYTE "Bonjour",0


Il s’agit d’une définition de données. Le mot message placé en début de ligne est un
label de données (variable). Il représente l’adresse du code ASCII du ’B’ (première lettre
du mot "Bonjour"). Le compilateur devra écrire les octets qui suivent tels qu’ils sont
dans notre code source. Il va donc écrire le code ASCII du ’B’, puis celui du ’o’, etc. Il
terminera par un NULL (c’est pourquoi on a mis un 0 à la fin).

— .code
est une directive indiquant à MASM qu’à partir d’ici nous commençons à écrire notre
programme proprement dit.

— ’salut :’ et ’end salut’


tout le code du programme doit être écrit entre les labels "salut :" et "end salut".

— mov edx,OFFSET message


est une instruction qui stocke le message dans le registre edx.

— call WriteString
est un appel d’une macro qui affiche une chaine de caractères.

— exit
est une macro qui sert à quitter le programme.

53
6.3.2 Compilation et exécution
Supposons que le code source est nommé : ’afficher.asm’
Pour la version qu’on va utiliser au travaux pratiques, on doit procéder comme suit.

— Copiez le fichier C :\Masm615\make32.bat dans votre répertoire de travail.


— Lancez l’invite de commande.
— Entrez dans votre répertoire et lancez la commande de compilation suivante :
make32 afficher (sans mettre l’extension .asm)
cette commande génère plusieurs fichiers parmi lesquels on trouve l’exécutable affi-
cher.exe.
— Pour lancer l’exécution il suffit de taper afficher suivi de Entrer.

54
Chapitre 7

Instructions de base

7.1 L’instruction mov et modes d’adressage


L’architecture Intel supporte plusieurs modes d’adressage. Dans cette section, on va décrire
ces modes en utilisant l’instruction mov qui copie le contenu d’un opérande source dans un
opérande destination.
La syntaxe de l’instruction mov est le suivant :

mov destination, source

— destination est l’espace de stockage où l’on veut mettre notre valeur (par exemple le
registre eax) ;
— source est la valeur à enregistrer.

7.1.1 Adressage immédiat


On écrit directement la valeur que l’on veut enregistrer.
Exemple :

mov eax, 15

qui veut dire que le registre eax contiendra 15.


— Si on ne precise rien, le nombre sera en décimal, c’est équivalent d’ajouter un ’d’ ;
— Si on veut un nombre en binaire, on doit ajouter un ’b’ ;
— Si on veut un nombre en hexadécimal, on doit ajouter ’h’.

Exemples :
mov eax, 15d ; 15 en décimal
mov eax, 101b ; 101 en binaire (5 en décimal)
mov eax, CD4h ; en hexadécimal (3284 en décimal)
Remarque :

55
La possibilité de stockage d’un registre est finie. Il faut que le nombre qu’on y met soit capable
d’y entrer.
Exemples :
mov eax, 2345236675 fonctionne parfaitement
mov eax, 5434955786 provoque une erreur car le nombre maximal stockable dans un registre
32 bits est 4294967295.

7.1.2 Adressage registre


On copie le contenu du registre source dans le registre destination.
Exemple :

mov eax, ebx

Dans cet exemple, on enregistre le contenu d’ebx dans eax.


On ne change pas ebx, on copie seulement ce qu’il contient dans eax.
Remarque importante : Les deux espaces de stockages, la destination et la source, doivent
obligatoirement être de la même taille.
Exemples :
mov dx, eax provoquera une erreur car eax mesure 32 bits et dx mesure seulement 16.
mov ebx, cx provoquera une erreur car ebx mesure 32 bits et cx mesure seulement 16.

7.1.3 Adressage direct


La source en mode d’adressage direct, contrairement au mode d’adressage immédiat, contient
l’adresse d’une donnée en mémoire au lieu de contenir la donnée.
Ce mode d’adressage provoque un temps d’exécution de l’instruction plus long. En effet, accéder
à la mémoire principale prend plus de temps que d’accéder à un registre.
Exemple :

mov eax, [0110]

Ceci signifie qu’on affecte au registre eax la valeur contenue dans l’emplacement mémoire
110h du segment de données DS.

7.1.4 Adressage indirect


Dans l’adressage indirect, la source contient le nom d’un registre qui contient l’adresse d’une
donnée en mémoire, dans le segment de données.
Ce type d’adressage est très utile pour parcourir un tableau par exemple (voir plus loin la
section concernant les tableaux).
Exemple :

56
mov eax, [ebx]

On affecte au registre eax la valeur contenue dans l’emplacement mémoire (dans le segment de
données) pointé par la valeur contenue dans le registre ebx.

7.1.5 Adressage indirect avec OFFSET


L’instruction

mov eax, [ebx + 8]

affecte au registre eax la valeur contenue dans l’emplacement mémoire pointé par la valeur
contenue dans le registre ebx plus un nombre de bytes (ici 8), dit décalage ou offset.
L’instruction

mov eax, [ebx + esi × k + 2]

affecte au registre eax la valeur contenue dans l’adresse pointée par la valeur de ebx+esi×k+2.

Ce mode d’adressage sert à gérer les structures de façon plus efficace. En effet, Pour sélectionner
une donnée dans une structure, le processeur doit calculer son adresse. Vu que cette donnée a
toujours une place bien précise dans la structure, on peut repérer celle-ci à partir de l’adresse
de début de notre structure en ajoutant le nombre de bytes (offset), qui séparent notre donnée
du début de la structure.

7.1.6 L’instruction movzx


On a vu dans la section 7.1.2 que les deux espaces de stockages, la destination et la source,
doivent être de la même taille.
Une façon de contourner ce problème est d’utiliser l’instruction movzx.
movzx pour mov Zero eXtend, c’est à dire "mov avec extention par zéro" va agrandir le contenu
du petit registre pour qu’il fasse la même taille que le grand, puis il va copier ce nombre dans
le grand registre.
Exemple :
Supposons qu’on a le nombre 4 = 0100 dans bx et qu’on veut copier cette valeur dans eax.
Solution :

movzx eax, bx

— bx est un registre de 16 bits, donc il contient la valeur 0000000000000100 ;


— movzx étend notre nombre de 16 bits à 32 bits : 00000000000000000000000000000100 ;
— movzx agit, enfin, comme un mov normal : Il copie cette valeur dans eax.

57
Exemple complet

INCLUDE Irvine32.inc
.code
salut :
mov bx,4 ; 4 = 0100
movzx eax,bx
call dumpregs ; affiche le contenu des registres
exit
end salut

Le résultat de ce code est comme ceci :


eax = 00000004 ; ebx = 7EF D0004 ; ...

7.2 La bibliothèque Irvine


La bibliothèque d’Irvine contient plusieurs routines utiles pour :
— entrer des données ;
— afficher des résultats ;
— et effectuer plusieurs tâches qui nécessite normalement de nombreux appels du système
d’exploitation pour les effectuer.

Cette bibliothèque s’appelle irvine.lib et doit être installée.


La puissance de la bibliothèque Irvine réside dans ses diverses procédures. Pour appeler une
procédure, on utilise le format suivant :
call nomProcedure
Nous ne couvrirons, dans cette partie, que quelques procédures que nous trouverons plus utiles.
Elle sont présentées dans le tableau 7.1.

7.3 Directives de base


Pour programmer en assembleur, on doit utiliser, en plus des instructions assembleur, des
directives ou pseudo-instructions, par exemple pour créer de l’espace mémoire pour des
variables ou bien pour définir des constantes, etc.

7.3.1 Syntaxe de déclaration d’une variable


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.

58
Procédure Description
Crlf écrit un retour chariot/ saut de ligne à l’écran.
Readchar lit un seul caractère de l’entrée standard et renvoie
le caractère dans le registre AL.
Readint Lit un entier décimal signé de 32 bits à partir de l’entrée standard, en s’arrêtant
lorsque la touche Entrée est enfoncée. L’entier sera stocké dans EAX.
Readstring lit une chaîne de caractères. Il met l’adresse du tampon d’entrée dans EDX ;
Il met le nombre maximum de caractères à lire dans ECX ; il remplit le tampon par
la chaîne d’entrée et définit EAX par la taille de la chaîne d’entrée.
Writechar écrit un seul caractère à l’emplacement actuel du curseur ; AL contient ce caractère.
Writeint écrit un nombre décimal signé de 32 bits sur la sortie standard au format décimal ;
stocké en EAX.
Writestring écrit une chaîne terminée par un caractère null sur la sortie standard ;
stocké en EDX.

Table 7.1 – Quelques procédures de la bibliothèque Irvine

Le nom d’une variable peut être une combinaison de chiffres, de lettres et du caractère “_”,
mais il doit respecter les contraintes suivantes :
— ne jamais commencer par un chiffre ;
— ne pas correspondre à un mot clé du langage Assembleur.

Syntaxe de déclaration d’une variable initialisée

Type Description
BYTE 8 bits non signés
SBYTE 8 bits signés
WORD 16 bits non signés
SWORD 16 bits signés
DWORD 32 bits non signés
SDWORD 32 bits signés
QWORD 64 bits non signés
TBYTE 80 bits signés
REAL4 réel 4 bytes IEEE
REAL8 réel 8 bytes IEEE
REAL10 réel 10 bytes IEEE

Table 7.2 – Types d’une variable

Les variables globales initialisées sont définies dans la directive .data comme ceci :
.data
nom type valeur
où :
— nom est le nom de la variable, il doit commencer par une lettre ;
— type est le type de la variable. Le tableau 7.2 présente quelques types d’une variable ;

59
— valeur est un nombre en hexadecimale, binaire, ou décimal.

Exemple

.data
a BYTE 5
b REAL4 5.22

Syntaxe de déclaration d’une variable non initialisée

Les variables globales non initialisées sont définies dans la directive .data ? comme ceci :
.data ?
nom type ?

Exemple

.data ?
a BYTE ?
b REAL4 ?

7.3.2 Exemple complet


Cet exemple présente un code qui calcule et affiche la somme des deux entiers.
Title Addition
include Irvine32.inc
.data ? ; déclaration des variables globales a et b non initialisées
a DWORD ?
b DWORD ?
.data ; déclaration des variables globales m1 et m2 initialisées par des chaines de caractères
m1 BYTE "Entrer un entier :",0
m2 BYTE "la somme est :",0
.code ; début du programme proprement dit
main PROC
mov edx,OFFSET m1 ; mettre le message m1 dans le registre edx
call writestring ; afficher le contenu de edx
call readint ; l’entier entré va être stocké dans eax
mov a,eax ; mettre ce dernier entier dans la variable a
mov edx,OFFSET m1 ; mettre le message m1 dans edx
call writestring ; afficher le contenu de edx
call readint ; le nouveau entier entré va être stocké dans eax
add eax,a ; ajouter a (le premier entier) au contenu de eax (deuxième entier) et stocker le
résultat dans eax
mov edx,OFFSET m2
call writestring
call writeint ; afficher le contenu de eax (la somme)

60
call crlf ; retourner à la ligne
main ENDP
END main

7.3.3 Les constantes


Les constantes sont comme des variables, mais elles n’existent que lorsque le programme soit
compilé (assemblé). Après la définition d’une constante, sa valeur ne peut pas être modifiée.
Pour définir les constantes, la directive EQU est utilisée.
Syntaxe pour constantes
nom EQU <expression>
Exemple :
k EQU 5
mov AX, k
ce code est équivalent à :
mov AX, 5

7.4 Les opérations mathématiques

7.4.1 Addition et soustraction : ADD et SUB


Les deux instructions ADD et SUB nécessitent deux opérandes : la source et la destination.
Syntaxe pour ADD :
ADD Destination, Source
ce qui donnera : Destination = Source + Destination
Exemple :
mov AX, 4
mov BX, 6
ADD AX, BX
movzx EAX, AX
Résultat :
BX = 6 et AX = 10.
Remarque : Pour utiliser l’afichage de la bibliothèque Irvine (call Writestring
et call Writeint par exemple), il faut utiliser les registres sur 32 bits (EAX,
EDX, etc.). les codes utilisant des registres de taille inférieure doivent ajouter
l’instruction movzx, par exemple movzx EAX, AX.
Syntaxe pour SUB :
SUB Destination, Source
ce qui donnera : Destination = Destination - Source

61
Exemple :
mov AX, 4
mov BX, 6
ADD AX, BX
SUB AX, 3
Résultat :
AX devient 7.
Remarque
Il est impossible d’utiliser des registres de tailles différentes pour faire l’addition ou la soustrac-
tion. Ajouter ou soustraire un 16 bits avec un 8 bits par exemple est impossible.
ADD BX, CL est aussi impossible !
Si vos besoins sont de ce type, vous devrez transférer un des registres par exemple dans un
autre registre à l’aide d’une instruction MOVZX.

7.4.2 Multiplication : MUL et IMUL


L’instruction MUL est utilisée pour les nombres non signés et IMUL pour les nombres signés.
Nous savons qu’en 16 bits en mode non signé les nombres vont de 0 à 65535 et en mode signé
ils vont de −32768 à 32767.
La multiplication n’a besoin que d’une seule opérande : la source qui est un registre.
La destination se trouvera toujours et obligatoirement dans AX (dans le cas de 16 bits).
Syntaxe pour MUL :
MUL source
ce qui donnera : eax = eax × source
Exemple :
mov AX, 4
mov BX, 6
MUL BX
Résultat :
AX = 4 × 6 = 24 et BX = 6.
Remarque
La multiplication de deux entiers codés sur 32 bits peut nécessiter 64 bits.
les quatre octets de poids faibles sont mis dans eax et les quatre octets de poids forts dans edx
(edx :eax).
Exemple
include irvine32.inc
mov eax, 12345678h
mov ebx, 256
MUL ebx
call dumpregs

62
Comme résultat on trouve : eax = 34567800 et edx = 00000012

7.4.3 Division : DIV et IDIV


L’instruction DIV est utilisée pour les nombres non signés et IDIV pour les nombres signés.
Syntaxe pour DIV :
DIV source
effectue la division entière : eax/source
Le quotient est mis dans eax et le reste est mis dans edx.
— Avec un octet, DIV divise la valeur de AL par la source, le reste sera dans AH.
— Avec un mot, DIV divise la valeur de AX par la source, le reste sera dans DX.
— Avec un double mot, DIV divise la valeur de EAX par la source, le reste sera dans
EDX.
Exemple :
include Irvine32.inc
.code
main PROC
mov EDX, 0
mov EAX, 18
mov EBX, 5
DIV EBX
call writeint ; affiche le contenu de EAX : le résultat de la division
call crlf
mov eax, edx
call writeint ; affiche le reste de la division
call crlf
main ENDP
END main
Résultat :
EAX = 3 et EDX = 3.
Remarques :
— La source peut être une adresse, une constante ou un registre.
— L’ajout de l’instruction mov EDX, 0 est important, car le dividende doit être stocké dans
EDX:EAX pour que le resultat soit dans EAX.

7.4.4 Incrémentation et décrémentation


Incrémentation

L’incrémentation se fait par l’instruction INC qui ajoute 1 au contenu de l’opérande.


Syntaxe pour INC :
INC source

63
effectue : source = source + 1
Exemple :
mov al, 3Fh
inc al
Résultat :
AL contient par suite la valeur 40h.

Décrémentation

La décrémentation se fait par l’instruction DEC qui retire 1 du contenu de l’opérande.


Syntaxe pour DEC :
DEC source
effectue : source = source − 1
Exemple :
mov al, 01h
dec al
Résultat :
AL contient par suite la valeur 00.

7.5 Tableau
Un tableau peut être vu comme une liste de variables. Une chaîne de caractères est un exemple
de tableau d’octets où chaque caractère est présenté comme une valeur de code ASCII.

7.5.1 déclaration d’un tableau


Un tableau se déclare dans le segment de données (DS). Deux types de tableaux existent :
— tableau d’octets (ou de caractères) déclaré par le mot clé BYTE ;
— tableau d’entiers déclaré par les mots clés WORD, DWORD, etc.
Exemples
a BYTE 48h, 65h, 6Ch, 6Ch, 6Fh, 00h
a est un tableau de type byte de 5 éléments.
b BYTE ’Hello’, 0
le tableau b est exactement une copie du tableau a (une chaine de caractères se termine par 0).
Le premier élément est initialisé avec le code ASCII de la lettre H majuscule, le deuxième
élément avec le code ASCII de la lettre e minuscule, ...
Ce tableau constitue une chaine de 5 caractères, les chaines de caractères étant simplement des
tableaux d’octets en assembleur.
La figure 7.1 donne une présentation de ces deux tableaux (a et b).

64
Figure 7.1 – Présentation des deux tableaux a et b

On peut accèder à n’importe quel élément en utilisant les indices.


mov AL, a[3]
enregistre le 4 élément du tableau a dans le registre AL.
On peut également utiliser l’un des registres BX, SI, DI, BP. Par exemple :
mov SI, 3
mov AL, a[SI]
ce code est équivalent au premier.
Remarque
c WORD ’Hello’, 0
le tableau c ne présente pas une chaîne de caractères du fait que les éléments du tableau ne
sont pas des octets, mais des mots.

7.5.2 L’opérateur DUP


Pour les tableaux, on peut utiliser l’opérateur DUP lorsqu’on veut faire des répétitions.
Syntaxe
nombre DUP (valeur(s))
où :
— nombre est une constante indiquant le nombre de répétition.
— valeur est l’expression que DUP doit répéter.
Exemples
c BYTE 5 DUP(9)
est l’alternatif de :
c BYTE 9, 9, 9, 9, 9
d BYTE 5 DUP(1, 2)
est l’alternatif de :
d BYTE 1, 2, 1, 2, 1, 2, 1, 2, 1, 2
Remarque
Bien sûr, vous pouvez utiliser WORD au lieu de BYTE s’il est nécessaire de conserver des
valeurs supérieures à 255 ou inférieures à −128. Cependant WORD ne peut pas être utilisé
pour déclarer des chaînes de caractères !

65
Exemple
include irvine32.inc
.data
a byte 48h,65h,6Ch,6Ch,6Fh
b byte "Hello", 0
.code
main PROC
mov edx, offset a[2]
call WriteString
call Crlf
exit
main endp
end main
Résultat :
lloHello
Nous n’avons pas terminé le premier tableau par 00h, c’est pourquoi que l’affichage commence
à a[2] et s’arrête au premier 0 rencontré. Donc le code affiche llo du tableau a suivi directement
de Hello du tableau b.

66
Chapitre 8

Contrôle de flux de programme

Contrôler le flux du programme est très important. C’est ici qu’on peut prendre des décisions
suite aux conditions.

8.1 Sauts inconditionnels


L’instruction de base qui transfère le contrôle à un autre point du programme est JMP.
Syntaxe de JMP
JMP label
Pour déclarer un label, il suffit de taper son nom suivi de deux points, exemple :
label1 :
Exemple
mov ax, 5 ; mettre 5 à AX
mov bx, 2 ; mettre 2 à BX
jmp calc ; aller à ’calc’
back : jmp stop ; aller à ’stop’
calc :
add ax, bx ; ajouter bx à ax.
jmp back ; aller à ’back’
stop :
ret ; retourner au système d’exploitation.

8.2 Saut conditionnel


Les instructions de saut conditionnel permettent d’effectuer un saut suivant une condition.
— Si celle-ci est réalisée, le processeur saute à l’instruction demandée ;
— Sinon, le processeur ignore cette instruction et passe automatiquement à l’instruction
d’après.
Les conditions pour chacune de ces instructions sont en fonction de l’état des registres spéci-
fiques appelés indicateurs (en anglais flag, ce qui signifie drapeau).

67
8.2.1 Indicateurs (Flags)
Les indicateurs sont des registres dont l’état est fixé par l’unité arithmétique et logique (UAL)
après certaines opérations. Ils font partie du registre d’état.
— CF (Carry Flag) : c’est l’indicateur de retenue. Il intervient lorsqu’il y a une retenue
après une addition ou une soustraction entre des entiers naturels. Lorsqu’il y a une
retenue il est positionné à 1, dans le cas contraire à 0.
— OF (Overflow Flag) : l’indicateur de débordement intervient lorsqu’il y a un débor-
dement, c’est-à-dire lorsque le nombre de bits sur lesquels les nombres sont codés n’est
pas suffisant et que le résultat d’une opération n’est pas codable avec le nombre de bits
spécifiés (il peut par exemple arriver dans ces conditions que la somme de deux nombres
positifs donne un nombre négatif). Dans ce cas l’indicateur OF est positionné à 1.
— SF (Sign Flag) : c’est l’indicateur de signe. SF donne tout simplement le signe du bit
de poids fort. Or, le bit de poids fort donne le signe du nombre (1 si le signe est négatif,
0 s’il est positif). Il simplifie le test du signe d’un entier relatif.
— ZF (Zero Flag) : l’indicateur de zéro permet de savoir si le résultat de la dernière
opération était nul. En effet, dans ce cas, l’indicateur ZF est positionné à 1 (0 dans
le cas contraire). Il permet notamment de déterminer si deux valeurs sont égales, en
effectuant leur soustraction, puis en observant l’état de l’indicateur de zéro.
Exemple 1
Les indicateurs après l’opération suivante 0110 + 0101 = 1011 sont comme suit :
— OF=1 : la somme de deux nombres positifs est négative.
— ZF=0 : le résultat 1011 n’est pas nul.
— SF=1 : le signe du résultat est négatif.
— CF=0 : il n’y a pas de retenue.
Exemple 2
Soit le code suivant :
include irvine32.inc
.code
main PROC
mov al,+127
add al,1
call DumpRegs
exit
main endp
end main
Résultat :
CF = 0 ; SF = 1 ; ZF = 0 ; OF = 1 ;
eax = 77603680
Exemple 3
Soit le code suivant :

68
include irvine32.inc
.code
main PROC
mov ax,0FFFFh
inc ax
call DumpRegs
exit
main endp
end main
Résultat :
CF = 0 ; SF = 0 ; ZF = 1 ; OF = 0
eax = 77600000.
Remarque :
Attention ! INC modifie les mêmes indicateurs que ADD sauf le CF.

8.2.2 Sauts conditionnels courts


Le tableau 8.1 présente quelques instructions de base pour faire le saut conditionnel.

Instruction Description
JE Saut si égal (Jump if equal : ZF = 1)
JNE Saut si non égal (Jump if not equal : ZF = 0)
JG Saut si supérieur (ZF = 0 ou SF = 0)
JGE Saut si supérieur ou égal (SF = 0)
JA Saut si supérieur (ZF = 0 ou CF = 0) (pour les nombres non signés)
JAE Saut si supérieur ou égal (CF = 0) (pour les nombres non signés)
JL Saut si inférieur (SF 6= 0)
JLE Saut si inférieur ou égal (ZF = 1 et SF 6= 0)
JB Saut si inférieur (CF = 1) (pour les nombres non signés)
JBE Saut si inférieur ou égal (CF = 1 o u ZF = 1) (pour les nombres non signés)

Table 8.1 – Quelques instructions de base pour le saut conditionnel

Généralement, ces instructions sont utilisées avec l’instruction CMP (compare) qui est simu-
laire à SUB (substract).

L’instruction CMP

Syntaxe de CMP
CMP Opérande Cible, Opérande Source
où :
— opérande Cible est le paramètre permettant d’indiquer l’opérande de base sur lequel
l’opération de comparaison est effectuée ;

69
— opérande Source est le paramètre permettant d’indiquer l’opérande supplémentaire
avec laquelle la comparaison est effectué.

L’instruction CMP offre la possibilité de comparer deux registres ou emplacements de mémoire.


Le résultat de la comparaison est indiqué par les indicateurs (flags). CMP affecte juste les
drapeaux (flags) et ne garde pas le résultat.

Exemples de Sauts conditionnels (CMP et JMP)

Exemple 1
include Irvine32.inc
.data
m1 BYTE "Non",0
m2 BYTE "Oui",0
.code
main PROC
mov al, 25 ; mettre 25 à AL
mov bl, 10 ; mettre 10 à BL
cmp al, bl ; comparer al − bl (zf = ?)
je egalite ; sauter vers egalite si al = bl (i.e si zf = 1)
mov edx,OFFSET m1 ; mettre le message m1 dans le registre edx
call writestring ; si tu arrive ici, alors al <> bl donc afficher ’non’
jmp stop ; aller à stop
egalite : ; si tu es là alors al = bl
mov edx,OFFSET m2 ; mettre le message m2 dans le registre edx
call writestring ; donc afficher ’oui’
stop :
ret
main ENDP
END main
Exemple 2
Considérons le code en C suivant :

if (AX == 3) BX = 5 ;
else BX = DX ;

Le code suivant est un équivalent en assembleur du code précédent :


CMP AX, 3
JNE elsee ; si pas égal, on saute au label elsee
mov BX, 5 ; sinon si égal, on affecte 5 à BX
JMP endiff ; on saute à la fin du if, donc au label endiff
elsee :
mov BX, DX
endiff :

70
8.3 Les boucles
Il n’existe pas de structure générale en assembleur pour coder une boucle. Cependant, à l’aide
des instructions vues précédemment pour réaliser des tests, on peut coder n’importe quel type
de boucle.
On verra également des instructions qui simplifient grandement le codage des boucles ’pour’.

8.3.1 Boucle Tant-Que


Le squelette d’une boucle Tant-Que, dans un langage de haut niveau, est le suivant :
Tant-Que (condition) Faire
action
Fin-TQ
Cela va se traduire en assembleur sous la forme suivante :
Tant-Que : calcul de la condition
Jcc Fin-TQ
action
...
JMP Tant-Que
Fin-TQ : ...

— Jcc est l’une des instructions de saut conditionnel vues plus haut dans le tableau 8.1 ;
— Tant-Que est un label indiquant le début de la boucle ;
— la boucle commence par une évaluation de la condition qui positionne les indicateurs du
registre FLAGS ;
— en fonction de la valeur des indicateurs (donc du résultat de la condition), un saut
conditionnel est effectué au label Fin-TQ indiquant la fin de la boucle ;
— le corps de la boucle se termine par une instruction de saut incondtionnel vers le début
de la boucle (JMP Tant-Que) qui permettra de réevaluer la condition d’arrêt après
chaque itération.
Exemple
Soit le code en C suivant :
int compteur = 0 ;
while ( compteur < 10)
++compteur ;
l’équivalent en assembleur du code précédent est le suivant :
mov ax, 0
debut :
cmp ax, 10
jae fin
inc ax ; incrementer ax
jmp debut
fin :

71
8.3.2 Boucle Répéter
Le squelette d’une boucle Répéter, dans un langage de haut niveau, est le suivant :
Repeter
action
Jusqu’à (condition vraie)
Cela va se traduire en assembleur sous la forme suivante :
Repeter :
action
...
calcul de la condition
Jcc Repeter
dans ce code on trouve :
— un label Repeter repère le début de la boucle et suivi du corps de la boucle ;
— à l’issue de l’exécution du corps, on trouve l’évaluation du test d’arrêt qui positionne les
indicateurs du registre FLAGS ;
— une instruction de saut conditionnel effectue un branchement au label Repeter pour
continuer les itérations si la condition d’arrêt n’est pas vérifiée.
Exemple
On veut écrire en assembleur un code permettant de décrémenter le registre ax jusqu’à qu’il
soit nul.
REPETE :
dec ax
cmp ax, 0
jne REPETE

8.3.3 Boucle Pour (LOOP)


Syntaxe du LOOP

LOOP label
où le paramètre ’label’ permet d’indiquer l’emplacement où doit se poursuivre l’exécution si la
condition est vérifiée.
Cette instruction de boucle permet de décrémenter le registre CX (compteur de boucle) de 1
et par la suite de donner le contrôle à une étiquette (un label) destinataire tant que le registre
CX ne vaut pas 0.
Le squelette d’une boucle “pour” s’écrit de la manière suivante :
POUR indice := 1 à bs FAIRE
action
Fin-Pour
Cependant en assembleur, il existe seulement des boucles ayant un indice variant d’une certaine
valeur bs à 1, en décrémentant sa valeur à chaque itération. La boucle précédente se transforme

72
à la boucle suivante :
POUR indice := bs à 1, pas := −1 FAIRE
action
Fin-Pour
Cette boucle se traduit en assembleur de la manière suivante :
mov cx, bs
BPOUR :
action
loop BPOUR
Exemple
mov AX, 0
mov CX, 5 ; CX est le compteur de la boucle
forr : ; label nommé forr
ADD AX, CX ; fait le calcul ax = ax + cx
LOOP forr ; décrémente CX. Si CX > 0 fait le saut à for.

C’est l’équivalent en C de :
for (cx=5 ; cx>0 ; cx- -)
ax = ax + cx ;

Exemple d’une boucle sans LOOP

Dans cet exemple on va donner l’équivalent en assembleur du code C précédent sans utiliser
LOOP :

mov AX, 0
mov CX, 0
forr :
CMP CX, 5
JGE endforr ; si CX >= 5, on sort de la boucle (vers le label endforr)
ADD AX, CX
INC CX ; CX est incrémenté de 1
JMP forr ; on reprend au début de la boucle
endforr :

73
Chapitre 9

Les sous-programmes

9.1 Introduction
Un sous-programme est une séquence d’instructions. Il joue, en assembleur, le role des procé-
dures et des fonctions dans les langages de haut niveau (tels que C et Java). L’objectif est
de structurer les programmes en donnant un nom à un traitement et en réduisant la taille des
programmes qui utilisent plusieurs fois la même séquence de code.
Appeler un sous-programme consiste à effectuer une rupture de séquence, poursuivre l’exécution
du sous-programme et reprendre ensuite l’exécution là où on a préalablement rompu la séquence.

9.2 Principe général de l’appel d’un sous-programme

9.2.1 Appel d’un sous-programme


Pour repérer un sous-programme, on utilise l’adresse de sa première instruction. Si dans
un langage de haut niveau on utilise le nom d’une procédure pour l’appeler, en assembleur,
on utilise son adresse.
L’instruction d’appel d’un sous-programme est :
call adresse
où adresse est un paramètre permettant d’indiquer l’adresse où est situé le sous-programme.
Cette instruction force le microprocesseur à exécuter les instructions du sous-programme indiqué
par l’adresse d’appel avant de continuer. Dès que la routine est terminé, l’exécution reprendra
son cours à l’instruction suivant le call.

9.2.2 Retour à l’appelant


Un sous-programme peut être appelé à différents endroits dans un programme.
Le registre IP contient à tout instant l’adresse de la prochaine instruction à exécuter.
Lorsque le processeur exécute l’instruction d’appel d’un sous-programme (l’instruction call),
le registre IP contient l’adresse de l’instruction où devra reprendre l’exécution de l’appelant.

74
Figure 9.1 – Ordre d’exécution d’instructions lors d’un appel d’un sous-programme

Cette adresse se nomme adresse de retour, c’est l’adresse x pour le premier appel dans la
figure 9.1, et c’est y pour le second appel.
Il suffit donc de mémoriser à l’instant où l’on exécute l’instruction d’appel de sous-programme
la valeur de ce registre. En restaurant la valeur du registre IP à l’issue de l’exécution du sous-
programme, l’exécution du programme reprendra naturellement, là où il faut, dans l’appelant.
Mais le problème réside dans la manière dont on doit mémoriser l’adresse de retour.
Dans les anciens ordinateurs, l’adresse de retour était conservée dans le sous-programme dans
un registre de sauvegarde, mais cette manière interdisait la récursivité. En effet, un second
appel à un sous-programme dont le premier appel n’était pas terminé, détruisait la première
adresse de retour en la remplaçant par la seconde. Même deux ou trois registres de sauvegarde
ne résolvent pas le problème puisque rien n’empêche un sous-programme qui appelle un sous-
programme d’appeler un autre sous-programme .... Il n’était alors plus possible de revenir à la
prochaine instruction du premier appel.
Pour éviter ce problème, les ordinateurs modernes utilisent une structure de données fonda-
mentale en informatique, c’est la pile.
L’utilisation de la pile permet à un même sous-programme d’empiler plusieurs adresses de
retour, rendant ainsi possibles les appels récursifs.

9.2.3 Les piles


La pile est une zone mémoire (une espèce de tableau) où le processeur range les données à
sauvegarder. Le sommet de la pile constitue son élément le plus important.
Les éléments de la pile sont situés dans une zone mémoire, ils sont placés les uns à la suite des
autres. Il nous suffit donc d’avoir un registre qui indique l’adresse du sommet de la pile pour
positionner les autres éléments. En effet, l’élément en-dessous du sommet de la pile se trouve
alors dans l’emplacement mémoire voisin, etc. Ce registre se nomme le pointeur de sommet
de la pile. C’est le registre SP (stack pointer).

75
Quand on empile ou on dépile une donnée, ce pointeur de pile doit être mis à jour pour indiquer
le nouveau sommet de pile. La figure 3.9 du chapitre 3 illustre le fonctionnement du registre
SP. Étant donnée l’importance de ces opérations, les instructions d’appel et de retour de sous-
programme modifient automatiquement le pointeur de pile pour que les appels et les retours
de sous-programmes fonctionnent correctement.
En utilisant la pile, l’appel d’un sous-programme est réalisé
— en empilant la valeur courante de IP (pour l’utiliser après pour le retour de sous-
programme) ;
— et en stockant dans IP l’adresse d’appel du sous-programme.

Le retour de sous-programme est réalisé


— en dépilant le sommet de la pile ;
— et en plaçant cette valeur dépilée dans le registre IP.

9.3 Appel du sous-programme sans passage de paramétres


Lorsque le sous-programme ne prend pas de paramètres en entrée, on utilise une instruction
CALL pour appeler le sous-programme et une instruction RET pour effectuer le retour à
l’appelant.
L’instruction call est déjà vue dans la section 9.2.1. Après l’appel de call :
— le contenu du registre IP est enregistré dans la pile ;
— la valeur du registre IP est mise à jour avec la valeur de l’opérande fournie à l’instruction
call ;
— la valeur du registre SP est bien entendu mise à jour après l’empilement de la valeur de
IP.

L’instruction RET (pour return) permet de quitter un sous-programme en retournant à l’ap-


pelant. Pour cela, le sommet de pile est dépilé dans le registre IP. L’adresse de retour doit donc
nécessairement se trouver en sommet de pile.
Le squelette d’un sous-programme en assembleur est comme suit :

nom-sous-programme proc
corps du sous-programme
ret
nom-sous-programme endp

Le sous-programme est identifié par la pseudo-instruction proc, son nom étant indiqué dans
le champ label (ici c’est nom-sous-programme). Ils viennent après les instructions du sous-
programme. La dernière instruction est RET qui indique le retour à l’appelant. Enfin, la
pseudo-instruction endp qui indique la fin du sous-programme.
Empilement et dépilement
Les opérations de la sauvegarde et de la restauration des contenus des registres se font en
utilisant la pile par les deux instructions PUSH et POP.

76
L’instruction PUSH a la syntaxe suivante :
PUSH opérande
où opérande peut être un registre, une adresse ou une valeur immédiate.
PUSH met le contenu de l’opérande dans la pile et décrémente la valeur de SP ( on parla de
l’empilement).
Ainsi, l’instruction PUSH BX empile le contenu du registre BX dans la pile.
La syntaxe de l’instruction POP est comme suit :
POP opérande
où opérande peut être un registre ou une adresse.
POP récupère le contenu de la pile, le stocke dans l’opérande et incrémente la valeur de SP
(on parle de dépilement).
Ainsi, l’instruction POP AX récupère le contenu du sommet de la pile et le transfère dans
AX.
La figure 3.9 du chapitre 3 illustre le fonctionnement de ces deux instructions.

9.4 Appel du sous-programme avec passage de paramétres


Un sous-programme prend souvent des paramètres en entrée. Dans les langages évolués, on
place généralement les noms de paramètres entre des parenthèses séparés par des virgules. En
assembleur, ceci ne fonctionne pas de cette manière.
Il existe toutefois deux façons de passer des paramètres à un sous-programme :
— le passage des paramètres par registre : on stocke les valeurs dans les registres utilisés
dans le sous-programme ;
— le passage des paramètres par pile : on stocke les valeurs dans la pile avant d’appeler le
sous-programme, puis on lit le contenu de la pile dans le sous-programme.

9.4.1 Passage de paramètres par registre


Le mode de passage de paramètres le plus simple consiste à passer la valeur dans un registre.
Dans ce cas, l’appelant charge un registre avec la valeur à passer et l’appelé récupère la valeur
dans ce même registre.
Pour illustrer le passage de paramètres par registre, nous donnons un exemple de sous-programme
calculant la factorielle d’un nombre dont la valeur est passée en entrée dans le registre AX.

Code de l’appelant
...
mov ax, 5
call factorielle
movzx eax, ax
call writeint

77
Code de l’appelé

factorielle proc
push cx
mov cx, ax
mov ax, 1
boucler :
mul cx
loop boucler
pop cx
ret
factorielle endp

Le passage des paramètres par registre est très simple à mettre en oeuvre mais elle est très
limitée, car on ne peut pas passer autant de paramètres que l’on désire, à cause du nombre
limité de registres. On lui préfèrera le passage des paramètres par pile.

9.4.2 Passage de paramètres par pile


Cette méthode de passage de paramètres consiste à stocker les valeurs des paramètres dans la
pile avant l’appel de procédure (grâce à l’instruction PUSH), puis de lire le contenu de la pile
grâce à un registre spécial (BP : Base pointer) qui permet de lire des valeurs dans la pile sans
les dépiler, ni modifier le pointeur de sommet de pile (SP).
Pour illustrer le passage de paramètres par pile, nous refaisons l’exemple de sous-programme
calculant la factorielle d’un nombre (le nombre 5 par exemple).

Code de l’appelant
...
mov eax, 5
push eax ; les valeurs des paramètres doivent être empilèes avant l’appel du sous-
programme (ici, nous avons empilé la valeur 5)
call factorielle ; la valeur de IP doit être empilée
add esp, 4 ; voir la remarque juste après
call writeint
exit

Remarque

Après l’exécution de toutes les instructions du sous-programme, on doit retourner à l’appelant.


La zone mémoire utilisée pour stocker des paramètres doit être libérée (ici nous avions un seul
paramètre). Pour cela, on modifie simplement la valeur du registre ESP en lui ajoutant la
valeur 4 qui est le nombre d’octets occupés sur la pile par la valeur empilée (si nous avons
utilisé AX au liu de EAX, alors on doit ajouter 2 à SP). On peut également libérer la pile
avec des instructions POP. Cependant, ces dernières peuvent modifier la valeur des registres,
ce qui peut amener à des effets indésirables.

78
Code de l’appelé

factorielle proc
push ebp ; la valeur du registre EBP est empilée
mov ebp, esp ; la valeur du registre de base EBP est initialisée avec la valeur
du sommet contenu dans ESP (la pile est vide maintenant : sommet = base). On
peut accéder maintenant à la valeur des paramètres via le registre EBP
push ecx
mov ecx, [ebp+8] ; on met dans ecx la valeur contenue dans [ebp+8] i.e 5
mov eax, 1
boucler :
mul ecx
loop boucler
pop ecx ; on dépile ecx
pop ebp ; on dépile ebp, comme ça le registre ESP pointe vers l’adresse de
retour
ret
factorielle endp

9.5 Sous-programme et variables locales


Les variables locales sont connues, uniquement, par le sous-programme où elles sont déclarées.
Par conséquent, la gestion des variables locales doit être réalisée entièrement dans l’appelé.
La solution la plus simple consiste, donc, à réserver de la place dans la pile pour y mettre les
valeurs des variables locales.
Le squelette d’un appel de sous-programme utilisant des variables locales est présenté comme
suit (coté appelé) :
push bp
mov bp, sp
push registres ; on empile des registres
sub sp, n ; on réserve n octets pour les variables locales
...
corps du sous-programme
...
add sp, n ; on libère la zone de pile utilisée par les variables locales
pop registres
pop bp
ret

Exemple

Supposons que notre code :


— passe un paramètre, stockés dans le registre AX ;
— enregistre la valeur du registre DI ;

79
— réserve 8 octets pour stocker des variables locales.

Code de l’appelant

push ax
call sous-prog
add sp, 4
...

Code de l’appelé

sous-prog proc
push bp
mov bp, sp
push di
sub sp, 8
...
traitement des variables locales
...
add sp, 8
pop di
pop bp
ret
sous-prog endp

La figure 9.2 illustre l’état de la pile après ces instructions.

Figure 9.2 – L’état de la pile durant l’exécution du code de l’exemple

80
Remarque

L’accès aux variables locales se fera par un adressage du genre [bp − 6] ... [bp + 4], selon la taille
des données.

9.6 Sous-programmes retournant une valeur : les fonctions


En assembleur, une fonction est un sous-programme qui retournera une valeur. Par convention,
la valeur renvoyée est placée dans un registre par exemple AX.
Voici un exemple qui calcule le carré d’un nombre entier (par exemple le nombre 5) en assem-
bleur.

Code de l’appelant

push 5
call carre
add esp, 4
call writeint
exit
; ici ax contient 25

Code de l’appelé

carre proc
push ebp
mov ebp, esp
mov eax, [ebp+8]
mul eax
pop ebp
ret
carre endp

Le résultat est maintenant stocké dans le registre AX. On peut l’utiliser (par exemple l’afficher)
dans le programme appelant.

81
Chapitre 10

Les interruptions

10.1 Introduction
Le microprocesseur ne peut exécuter qu’une seule instruction à la fois. Par conséquent, le
code du programme courant est exécuté de manière linéaire. Mais qu’est ce qu’il arrive si un
événement extérieur, par exemple la pression d’une touche du clavier, demande l’attention de
l’ordinateur ?
La machine doit pouvoir réagir immédiatement, sans attendre que le programme en cours
d’exécution se termine. Pour cela, elle interrompt ce dernier pendant un bref instant, le temps
de traiter l’événement survenu puis rend le contrôle au programme interrompu.
Une interruption est un appel d’une routine (espèce de sous-programme) spéciale présente
en mémoire appelée ISR ( Interrupt Service Routine).
L’appel se fait via l’instruction int 21h. Le registre ah contient un numéro qui référence la
fonctionnalité que l’on veut utiliser. Par exemple, on utilise le numèro 9 pour afficher une chaine
de caractères, le numèro 1 pour saisir la frappe d’un caractère au clavier, et le numèro 2 pour
écrire un caractère à l’écran.

10.2 Types d’interruptions


Les interruptions se divisent généralement en trois catégories :
— les interruptions électroniques, par exemple : le clavier ;
— les interruptions du BIOS, par exemple : l’accès aux disques ;
— les interruptions du DOS, par exemple : l’accès aux systèmes de fichiers.
Une autre façon de classifier les interruptions est la manière de son déclenchement. Dans ce
sens, on peut distinguer deux types :
— les interruptions matérielles qui sont déclenchées par notre matériel. Par exemple,
lorsque nous appuyons sur une touche du clavier. Dans ce cas, aucun logiciel n’intervient
et le contrôle est passé directement à la routine qui gère le clavier ;
— les interruptions logicielles qui sont appelées par des instructions en langage ma-
chine au sein d’un programme. Leur importance est capitale. En effet, contrairement au
langage C par exemple, l’assembleur ne dispose pas de fonction préprogrammée. Chaque

82
instruction doit être directement traduisible en langage machine.
Dans ce qui suit, nous intéréssons aux les interruptions logicielles du DOS.

10.3 Exemples d’interruptions du DOS


Pour déclencher un événement tel que : écrire une chaîne de caractères à l’écran, ou bien pour
lire un caractère entré au clavier, on procède de la même façon que le DOS lui-même !. En effet,
on déclenche les interruptions appropriées à l’aide de l’instruction INT du langage machine.
INT est une routine du DOS (ou parfois du BIOS) qui fera tout le travail. Les paramètres (ou
leurs adresses) sont passés dans les registres.
Il existe 256 interruptions, toutes sont notées en base hexadécimale. Il est très difficile de
mémoriser le rôle de chaque interruption, et a fortiori de chaque fonction ou sous-fonction.
C’est pourquoi qu’on fait recours à une liste des interruptions pour travailler, par exemple celle
de Ralph Brown.

10.3.1 Afficher un caractère à l’écran


Le code ci-après affiche le caractère ’A’ à l’écran.
mov dl, ’A’
mov ah, 02
int 21h

En effet,
— la première instruction demande au processeur de mettre dans le registre DL le code
ASCII de la lettre ’A’, c’est-à-dire 65, ou 41h ;
— la deuxième instruction mis le nombre 2 dans le registre AH ;
— la dernière instruction appelle l’interruption numéro 21h.
Dans le code précédent, la fonction numéro 2 de l’interruption 21h sert à écrire un caractère
à l’écran. Il faut pour cela écrire le code ASCII du caractère dans le registre DL et bien sûr
placer le nombre 2 dans AH.

10.3.2 Afficher un message à l’écran


Le code suivant affiche le message ’Bonjour tout le monde’ à l’écran.
.data
message db ’Bonjour tout le monde $’, 13, 10
.code
mov ah, 09
mov dx, offset message
int 21h
end

83
La chaine de caractères doit se terminer par le caractère ’$’, c’est le caractère de fin de chaine
sous DOS. Les caractères 13 et 10 sont les codes ASCII des deux caractères constituant un
retour-chariot.
L’instruction mov dx, offset message pointe vers l’adresse du premier caractère de la chaîne
de caractères message. Puis on appelle la fonction numèro 9 de l’interruption 21h et on donne
la main au DOS.

10.3.3 Saisir une frappe au clavier


Saisir un caractère

Le code suivant renvoie dans le registre al le code du caractère lu au clavier.


mov ah, 01
int 21h

Saisir une chaine de caractère

.data
message db 80 dup ( ?)
.code
mov ax, @data
mov ds, ax
mov bx, 0
mov cx, 80
lea dx, message
int 21h
mov ah, 3fh
int 21h
end

Dans le code précédent, on saisit une chaine de caractères, de taille maximale cx caractères, au
clavier et on la stocke dans la variable message.
Cette variable est déclarée comme un tableau de 80 octets (soit 80 caractères).
Le nombre de caractères lus est placé dans le registre ax.

10.3.4 Lire l’heure courante


La fonction 2ch de l’interruption 21h lit l’heure courante, telle qu’elle est stockée dans l’ordi-
nateur.
mov ah, 2ch
int 21h

Après ce code, les information de l’heure sont contenus dans les registres comme ceci :
— les heures dans ch ;

84
— les minutes dans cl ;
— les secondes dans dh ;
— les centièmes de secondes dans dl.

10.3.5 Lire la date courante


La fonction 2ah de l’interruption 21h lit la date courante, telle qu’elle est stockée dans l’ordi-
nateur.
mov ah, 2ah
int 21h

Après ce code, les information de l’heure sont contenus dans les registres comme ceci :
— le jour de la semaine dans al, il est codé comme ceci : 0 pour le dimanche, 1 pour le
lundi, etc. ;
— l’année dans cx ;
— le mois dans dh ;
— le jour dans dl.

85
Livres, documents et liens utiles

— Assembly Language for x86 Processors, KIP R. IRVINE, Florida International University
School of Computing and Information Sciences, 2015 (Seventh Edition).
— Assembleur i8086, Philippe Preux IUT Informatique du Littoral, 1995-1996.
— http ://vision.gel.ulaval.ca/ jflalonde/cours/1001/h15/notes/21. Entrees sorties program-
mees, interruptions, DMA.pdf

86

Vous aimerez peut-être aussi