°cours 1 Architecture Des Ordinateurs SMI4 FSO OUAJDA
°cours 1 Architecture Des Ordinateurs SMI4 FSO OUAJDA
°cours 1 Architecture Des Ordinateurs SMI4 FSO OUAJDA
Cours pour :
Filière : SMI
Semestre : 4
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
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
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
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).
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
#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 ) ;
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
9
Chapitre 1
Systèmes de numération
où ai ∈ {0, 1, ..., 9} et ak 6= 0
Exemple :
(124)10 = 1 × 102 + 2 × 101 + 4 × 100
.
où ai ∈ {0, 1} et ak 6= 0.
10
Exemple :
(101)2 = 1 × 22 + 0 × 21 + 1 × 20
où ai ∈ {0, 1, ..., 7} et ak 6= 0.
Exemple :
(124)8 = 1 × 82 + 2 × 81 + 4 × 80
où ai ∈ {0, 1, ..., 9, , A, B, C, D, E, F } et ak 6= 0.
Exemple :
(124)16 = 1 × 162 + 2 × 161 + 4 × 160
(101)2 = 1 ∗ 22 + 0 ∗ 21 + 1 ∗ 20 = (5)10
(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
(124)8 = 1 × 82 + 2 × 81 + 4 × 80 = (84)10
(23)10 = (27)8
La figure 1.2 présente une méthode de conversion du système décimal au système octal.
(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
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
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
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.
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.
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.
#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 ?
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.
18
Figure 2.4 – Représentation des adresses dans une mémoire
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.
Conversion en binaire
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
Exemple 1
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.
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.
23
Chapitre 3
Processeur
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).
25
Figure 3.3 – Familles de micro-processeurs x86
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.
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
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).
Registres généraux
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.
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.
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
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
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.
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
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.
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.
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
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.
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
Architecture CISC
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.
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
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
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.
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.
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 ?
42
La figure 4.3 présente un bus capable de transporter la donnée demandée en un seul transport.
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.
#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.
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
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.
48
Cette forme d’échange était la seule possible dans les premières générations de machines.
49
Deuxième partie
Langage Assembleur
50
Chapitre 6
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.
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.
52
INCLUDE Irvine32.inc
.data
message BYTE "Bonjour",0
.code
salut :
mov edx,OFFSET message
call WriteString
exit
end salut
— 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.
— .code
est une directive indiquant à MASM qu’à partir d’ici nous commençons à écrire notre
programme proprement dit.
— 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.
54
Chapitre 7
Instructions de base
— destination est l’espace de stockage où l’on veut mettre notre valeur (par exemple le
registre eax) ;
— source est la valeur à enregistrer.
mov eax, 15
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.
Ceci signifie qu’on affecte au registre eax la valeur contenue dans l’emplacement mémoire
110h du segment de données DS.
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.
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
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.
movzx eax, bx
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
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.
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.
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
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
Les variables globales non initialisées sont définies dans la directive .data ? comme ceci :
.data ?
nom type ?
Exemple
.data ?
a BYTE ?
b REAL4 ?
60
call crlf ; retourner à la ligne
main ENDP
END main
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.
62
Comme résultat on trouve : eax = 34567800 et edx = 00000012
63
effectue : source = source + 1
Exemple :
mov al, 3Fh
inc al
Résultat :
AL contient par suite la valeur 40h.
Décrémentation
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.
64
Figure 7.1 – Présentation des deux tableaux a et b
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ôler le flux du programme est très important. C’est ici qu’on peut prendre des décisions
suite aux conditions.
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.
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)
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é.
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 ;
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’.
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
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 ;
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.
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.
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.
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.
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.
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
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
Exemple
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
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.
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.
82
instruction doit être directement traduisible en langage machine.
Dans ce qui suit, nous intéréssons aux les interruptions logicielles du DOS.
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.
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.
.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.
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.
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