Académique Documents
Professionnel Documents
Culture Documents
Accéder à Open-Campus
Contact @SUPINFO
ACCUEIL CURSUS COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS Naviguer sur la page
ACCUEIL CURSUS COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS
Cyril-Alexandre PACHON
PLAN DU CHAPITRE
Un CPU exécute des instructions qui sont la résultante des programmes écrits LES VARIABLES EN ASSEMBLEUR 8086
avec des langages de programmation. Ces instructions contenues dans la
mémoire composent le langage d’assemblage (assembleur). Pour qu’un LE SYSTEME VIDEO DU 8086
programme fonctionne, le CPU lit une part une les instructions. Donc pour les
LES INSTRUCTIONS DE SAUTS EN ASSEMBLEUR 8086
atteindre, les instructions sont rangées dans la mémoire via des adresses. Au
chargement d’un programme, le CPU dispose de l’adresse d’origine et contient LES PROCEDURES EN ASSEMBLEUR 8086
dans son Compteur Ordinal l’adresse de la future instruction à exécuter.
LES INSTRUCTIONS D’INTERRUPTIONS EN ASSEMBLEUR
Les instructions disposent de toutes les données pour que combinées entre
8086
elles et exécutées sur le CPU l’algorithme original traduit en langage de
programmation fonctionne. Les données des instructions se retrouveront soit LES PROGRAMMES EN ASSEMBLEUR 8086
dans les registres du CPU soit dans la mémoire centrale.
L’OUTIL EMU8086
Il existe 2 familles d’architecture de CPU, les CISC ( Complex Instruction Set
Computer ) et les RISC ( Reduced Instruction Set Computer ). Leurs différences L’ADRESSAGE
résident dans la construction des instructions. Un ensemble d’instructions est
appelé jeux d’instructions ou ISA ( Instruction Set Architecture ).
Le CISC dispose de jeux d’instructions pouvant effectuer plusieurs opérations, comme les opérations arithmétiques, les opérations
logiques, le chargement et la récupération de données en mémoire. Ils sont présents dans les x86, les pentiums, d’Intel, les 68xx de
Motorola.
Le RISC dispose d’instructions relativement petites, unitaires, codées sur les mêmes tailles de mot avec une exécution cadencée sur les
mêmes cycles d’horloge. Les instructions RISC sont possibles grâce à ses nombreux registres, caches (et aux mémoires) ne limitant pas la
taille des demandes. Les puces RISC sont conçues pour exécuter ces instructions très rapidement.
Pour rappel, un registre est un circuit ou composant de mémorisation interne au CPU et permet de stocker adresse et donnée utiles au
bon fonctionnement d’un programme en cours d’exécution. Dans un CPU, les registres sont toujours en nombre limités et pour un
meilleur contrôle chaque registre dispose d’un nom.
Ce chapitre va vous permettre d’appréhender la programmation en assembleur 8086. Il vous propose d’apprendre l’organisation interne
d’un processeur 80x86 16bits (base de toute une lignée de micro-processeur INTEL), de comprendre l’utilité des différents registres de
son CPU et de découvrir les bases du langage assembleur 8086 au travers de l’émulateur EMU 8086.
Toutes les parties de ce chapitre sont guidées par notre mode de programmation en 8086 car nous nous plaçons dans le cas où une
machine actuelle est trop difficile d’approche sans connaitre certains fondamentaux. Même si cela parait un peu vieillissant, il est de
bonne augure de concevoir les principes de base de façon simples. Cette étude permettra ensuite d’aborder dans d’autres cours des
parties plus actuelles voire futuristes de nos architectures. Il ne faut pas se méprendre sur les architectures actuelles, elles sont toutes
fondées sur des premiers principes, elles évoluent, elles se transforment mais les modèles eux restent toujours un fondement pour aller
utiliser, appréhender et comprendre les nouveautés. Nous allons donc décrire étapes par étapes les principes de la programmation en
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 1/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
assembleur 8086 tout en modélisant et étudiant la machine du même type dont elle dépend. L’émulateur 8086 sera notre outil de
Contact @SUPINFO
simulation lorsque nous en aurons besoin. L’émulateur sera bien sûr étudié pour mieux l’utiliser.
ACCUEIL CURSUS COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS Naviguer sur la page
L’assembleur est un langage de programmation proche du langage d’un ordinateur n’utilisant pas directement une notation
exclusivement faite avec des 0 et des 1. Il dispose de mnémoniques (de plus haut niveau) pour construire des programmes avec
structures de données et structures de traitements. L’assembleur dépend fortement du type de processeur. Donc il n’y a pas de langage
d’assembleur unique et chaque constructeur a son assembleur. Par conséquent, il est obligatoire de maitriser le fonctionnement d'un
processeur pour pouvoir aborder cette partie.
L'assembleur contrôle directement le CPU, il a une maîtrise du système et peut proposer des programmes rapides. Bien que les langages
de plus niveau comme le C, C++, C#, Python, … permettent de faire des programmes facilement, ils n'optimisent pas le code d'exécution.
L’impact est d’avoir des programmes plus volumineux pour faire la même demande.
Le microprocesseur 80x86 se présente sous la forme d'un boîtier DIP ( Dual Inline Package ). En électronique un DIP est un boitier
contenant des circuits intégrés qui dispose de sorties pour se connecter à un environnement externe (de nouveau un circuit intégré). Il
est à noter que les DIP sont lié à un PCB ( Printed Circuit Board ) pour faciliter la manipulation tout en limitant les détériorations. Ainsi le
boitier peut s’insérer facilement sans risque. Par conséquent la PCB ( ou carte électronique ) est une plaque de soutènement de
composants permettant les liens entre les composants. Ce processeur a un bus d'adresses et un bus de données multiplexés (certaines
pattes transmettent à certains moments un bit d'adresse et à d'autres moments un bit de donnée). Il est organisé autour d’un bus
interne de données de 16 bits, il comporte:
20 broches pour véhiculer les adresses (A 0 ... A 19 ). Il peut adresser 2 20 (= 1048576) positions mémoire différentes contenant
chacune 1 octet (8 bits) : la mémoire a au plus une capacité de 1 Mo.
de registres, 16 bits pour stocker des données.
de registres, 16 bits pour stocker des adresses (codées sur 20 bits).
de registres temporaires directement pour UAL.
un bloc pour former des adresses, 20 bits à partir de morceaux d’adresse codés sur 16 bits.
Un registre est un composant de mémoire interne au microprocesseur dont sa capacité est calibrée sur une taille des instructions. Ainsi
nous pouvons avoir des tailles de registres de 8 bits, 16 bits, 32 bits, 64 bits, … selon le modèle et la structure du micro-processeur. Le
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 2/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
rôle du registre est de stocker des adresses ou des résultats intermédiaires des calculs en cours. Les commandes de l'assembleur
Contact @SUPINFO
manipulent les registres. Pour les contrôler et les utiliser chaque registre dispose d’un nom.
ACCUEIL CURSUS COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS Naviguer sur la page
Comme tout CPU le 8086 dispose d'un certain nombre de type de registres:
les registres généraux destinés au traitement des valeurs. Ils permettent d’effectuer des additions, des multiplications, des calculs
logiques, nommés A ( Accumulateur ), B ( Base ), C ( Compteur ), D ( Donnée ).
les registres d'adressages (registres de segments, pointeurs et index) utilisés pour pointer, lire ou écrire un endroit en mémoire,
nommés D ( Donnée ), S ( Stack - Pile ), C ( Code ), E ( Extra ). L’IP ( Instruction Pointer ) sera aussi nommé CO ( Compteur Ordinal ).
les registres des états (Flags - Indicateurs) pour indiquer l'état du CPU. Ils donnent une indication sur le résultat de la dernière
instruction exécutée, par exemple ZF ( Zero Flag ) qui indique zéro, CF ( Carry Flag ) qui indique une retenue, NF ( Negative Flag ) ou SF
( Sign Flag ) qui indique si le résultat est négatif ou positif, OF ( Overflow Flag ) qui indique un dépassement de capacité de registre, PF
( Parity Flag ) qui indique si les 8 bits de poids fort sont constitué d’un nombre pair de 1 ….
Les flags du registre d’état de la figure ci-dessus contiendront alors les valeurs mis à 0 ou 1 par le processeur (à la suite de l'exécution
d'une instruction) ou par le programmeur pour modifier le mode de fonctionnement du processeur.
La gestion des registres est faite avec des instructions processeurs. Ces instructions sont formées avec le code binaire des programmes à
exécuter. Il est possible de programmer le processeur avec des langages de plus bas niveau et pour rendre plus facile la manipulation, la
lecture et l’écriture de telles instructions binaires, le programmeur utilise des symboles (mots clés, ou mnémoniques) à la place de des
codes binaires et fait appel à l’assembleur. Les symboles en assembleur sont souvent la compression d'un mot ou d'une expression
réalisant une action. Par exemple MOV (MOVe), MUL (MULtiply), … . Les mnémoniques sont suivies de registres, de valeurs, … .
L’architecture du 80x86 est little-endian (le Motorola 68000 et le PowerPC sont par exemple big-endian). Par exemple pour l’entier
F1AB 16 en mémoire, il y a 2 possibilités :
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 3/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
Si nous manipulons des nombres codés sur 16 bits (1 mot mémoire), nous utilisons 4 registres généraux :
Contact @SUPINFO
AX, l‘Accumulateur, utilisé pour stocker les résultats de certains calculs arithmétiques et logiques.
ACCUEIL CURSUS COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS Naviguer sur la page
BX, la Base, utilisé comme registre de base pour des données dans le segment pointé par le registre de segment ES. Un segment
est un ensemble d’octets consécutifs dont un octet désigne le couple (numéro du segment, déplacement dans le segment).
CX, le Compteur, utilisé pour les boucles.
DX, le registre de Données, contient l'adresse des ports d'entrée/sortie (pour les instructions IN et OUT) et sert également
d'extension à AX pour manipuler les données sur 32 bits.
Si nous manipulons des nombres codés sur 8 bits (1 octet), nous utilisons les 4 registres généraux en mode 8 bits pour en produire 8
registres AH, AL, BH, BL, CH, CL, DH et DL avec comme convention, H est mis pour "High" et L pour "Low" :
Les 2 registres d'index, notés SI ( Source Index ) et DI ( Destination Index ), sont utilisés pour indexer les éléments d'un tableau. Dans les
instructions de mouvements de chaînes d'octets, ils sont utilisés simultanément :
Une instruction dans le CPU est composée de plusieurs segments. Un segment qui désigne le code du programme, un segment qui
désigne les données, un segment qui désigne l’organisation des appels, ….. Une instruction contient donc tous ses segments, mais sous
forme d’adresse pointant vers la mémoire. Pour accéder à sa mémoire centrale, le 80x86 dispose de registres de segment suivants:
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 4/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
Contact @SUPINFO
ACCUEIL
( Code Segment
CS CURSUS ) pointe sur la base du segment qui contient le code (les instructions que doit exécuter le processeur).
COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS Naviguer sur la page
DS ( Data Segment ) pointe sur la base du segment contenant les données (variables, tableaux ...).
SS ( Stack Segment ) pointe sur la base du segment qui contient la pile gérée par les registres SP et BP.
ES ( Extra Segment ) pointe sur la base d'un segment supplémentaire qui est généralement utilisé pour compléter le segment de
données.
Ces segments sont situés selon la place disponible dans la mémoire. Parfois, ces segments peuvent se chevaucher partiellement. Les
adresses des segments sont données et gérées par le système d’exploitation lors du mécanisme de chargement du programme à
exécuter. Comme les adresses sont ensuite contrôlées de façon automatique, normalement il n’y a pas de risque de confusions d’accès
(des systèmes de gestion d’erreurs peuvent se mettre en place comme les blue Sreen). Par contre, les déplacements sont liés aux
instructions contenues dans le programme en se base sur une adresse d’origine.
La pile est une zone de mémoire qui permet de conserver de manière temporaire des données (par exemple, l’état des registres lors d’un
appel de procédure). Nous utilisons 2 registres pointeurs :
SP ( Stack Pointer ) pointe sur le sommet de la pile et se met à jour automatiquement par les instructions d'empilement et de
dépilement.
BP ( Base Pointer ) pointe la base de la région de la pile contenant les données accessibles (variables locales, paramètres,...) à
l'intérieur d'une procédure. Il doit être mis à jour par le programmeur.
La pile n'est pas gérée avec des registres, elle utilise la mémoire (RAM). Dans cette pile, les données sont naturellement stables et seuls
les pointeurs SP et BP sont utilisés pour atteindre les données. Cette pile est de type LIFO et elle est manipulée par :
PUSH pour empiler une valeur de 16 bits (le pointeur SP est décrémenté automatiquement de 2).
POP pour dépiler une valeur de 16 bits (SP est incrémenté automatiquement de 2).
Le bus d’adresse de la mémoire réelle est de 20 bits. Or le 80x86 n’a pas de registre de 20 bits mais que de 16 bits. Un registre de 16 bits
référence 64Ko de mémoire, soit 2 16 = 65 536. Pour répondre à cette problématique le 80x86 en mode réel combine un des neuf
registres généraux avec l'un des quatre registres de segments pour former une adresse de 20 bits.
Le registre IP ( Instruction pointer ) est appelé pointeur d'instruction ou compteur ordinal. La valeur contenue dans ce registre aiguille
instruction par instruction les déplacements le micro-processeur. Il propose toujours la prochaine adresse de l’instruction à exécuter par
le micro-processeur. Le registre IP est constamment modifié après chaque fin instruction pour qu'il puisse pointer sur l'instruction
suivante. Ce registre permet de pointer une case mémoire dans le segment de code afin que le 80x86 puisse charger la prochaine
instruction à exécuter.
À chaque type d'accès en mémoire, il faut faire correspondre un registre de segment et parfois un registre général, par exemple :
CS et IP sont combinés pour accéder à la prochaine instruction à exécuter sous la forme CS:IP.
SS et SP sont combinés en SS:SP pour toutes les opérations concernant la pile.
II est possible d'utiliser un registre de segment autre que celui utilisé par défaut en le spécifiant explicitement. Mais les valeurs de CS, DS
et SS sont produites par le système d’exploitation dès le lancement du programme. Ces valeurs de segments étant implicites, il sera
possible de les utiliser juste en spécifiant les offset de déplacements.
Pour adresser 1 méga-octet, il faut 4 bits, en plus des 16 bits d'un registre général. Cette combinaison est obtenue en décalant le registre
de segment de 4 bits et en ajoutant le résultat au contenu du registre général pour obtenir l'adresse sur 20 bits.
Le résultat est appelé EA ( Effective Address ). L'EA est placée sur le bus d'adresses afin de pouvoir accéder à l'emplacement mémoire
correspondant. L’EA est constituée de deux parties, le registre de segment définissant une zone mémoire de 64Ko, et le registre général
spécifiant un déplacement à partir de l'origine de ce segment de 64Ko (c'est-à-dire une adresse sur 16 bits à l'intérieur de ce segment).
L’EA est exprimée en donnant le registre segment et le registre général séparés par deux points par exemple: DS:SI. Le registre DS est le
segment de 64Ko et SI contient le déplacement dans ce segment. Si nous avons par exemple 1A8B:0010, l'adresse du segment est 1A8B
et le déplacement dans le segment est 0010 en hexadécimal. Pour obtenir l’EA il faut faire : 1A8B * 10 16 + 10 = 1A8C0.
Les nombres sont codés sur 1 ou 2 octets selon la taille de la valeur est l’utilisation des registres (une extension est possible pour 4 octet,
mais les opérations seront spécifiques). Il y a 4 types de codages des nombres aussi bien en base 2, 8, 10, 16, …. :
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 5/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
Un microprocesseur exécute un jeu d'instructions relatif au programme créé par le concepteur. Une instruction réalise une action simple
sur le microprocesseur, comme "récupérer une nombre en mémoire", "additionner deux nombres et placer le résultat en mémoire".
L'assembleur aura pour rôle de convertir le fichier source contenant les instructions; en mnémoniques (indication sous forme de lettre
de l’opération à effectuer), en actions sur les registres et mémoire. Le fichier exécutable produit contient les codes binaires de chacune
des instructions, compréhensible uniquement par le microprocesseur associé. L'assembleur est donc qu’une traduction d’un fichier
source éditer en langage de haut niveau vers un langage binaire (de bas niveau).
Le codage d’une instruction en langage de programmation est constitué d’un code-opérateur (CodeOp) ou mnémonique (codé sur 1 ou 2
octets) suivi d’opérandes (valeur immédiate, registre, adresse). Par exemple, pour additionner 60 10 avec 15 10 , le code en assembleur
sera le suivant:
MOV AX, 60
ADD AX, 15
Ces instructions sont codées en hexadécimal, car il y a une correspondance numérique entre les mnémoniques, registres et valeurs,
nous obtenons :
B83C00 16
050F00 16
101110000011110000000000 2
000001010000111100000000 2
En lisant les valeurs binaires, les seules valables pour le microprocesseur, l’assembleur en utilisant les noms des registres, les
mnémoniques,… devient bien un langage de programmation, et même de haut niveau dû à sa lisibilité par rapport au binaire.
Pour des raisons de souplesse, d’explication et de manipulation une instruction sera constituée de l’opérateur (ADD, MOV,….) suivi des
adressages (données participant à l’opération). C’est ainsi nous aurons besoin d’avoir pour les adressages de nommer et manipuler:
Le 8086 permet d’effectuer des opérations binaires classiques grâce au mnémoniques AND, OR, NOT et XOR. Ces opérateurs peuvent
être utilisés avec :
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 6/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
ACCUEIL CURSUS COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS Naviguer sur la page
MOV AL, 01100001b
AND AL, 11011111b --> Nous obtenons 01000001b
OR AL, 01110110b --> Nous obtenons 01110111b
NOT AL --> Nous obtenons 10001000b
XOR AL, 01010101b --> Nous obtenons 11011101b
XOR BX, BX --> Nous obtenons 00000000b
Le 8086 permet d’effectuer des opérations de décalage sur des nombres signés ou non signés. Les bits qui sont expulsés sont stockés
dans le bit CF :
La différence entre les 2 types de décalage est que dans la seconde version le bit de signe n’est pas concerné par ce décalage. Cette
opération est plus rapide à exécuter que l’opération de multiplication ou de division par 2. Le nombre de bits de décalage est indiqué
dans la seconde opérande.
Le 8086 permet de réaliser des opérations de rotation sur des nombres signés ou non signés. Les bits expulsés sont replacés dans les
trous formés par ce décalage :
La différence entre les 2 types de décalage est que dans la seconde version le bit CF est utilisé en plus des bits à modifier (lors d’un
mouvement, le trou est rempli avec une copie du bit CF et l’autre bit, qui est expulsé, est placé dans CF). Le nombre de bits de décalage
est indiqué dans la seconde opérande.
Comme pour tous calculateurs et microprocesseurs les unités de traitements arithmétiques font les opérations +, -, * et /. Pour les
utiliser et le décrire, nous passons toujours par des symboles. Par contre pour effectuer des calculs dits non de bases, il faudra faire des
programmes et enchainer les calculs, décalages et rotations.
Les mnémoniques ADD ( ADDition ) et SUB ( SUBtraction ) permettent d’effectuer des additions et des soustractions. Ils sont construit par :
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 7/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
ACCUEIL CURSUS COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS Naviguer sur la page
ADD AX, BX ; effectue l’opération AX <-- AX + BX
SUB AX, BX ; effectue l’opération AX <-- AX – BX
Si une addition ou une soustraction sont codées sur 16 bits, alors leurs résultats seront codés sur au plus 16 + 1 bits soit 16 bits de
résultat + 1 bit de retenue (le bit CF). Les opérations ADD et SUB sont effectuées sans tenir compte de l’état initial du bit CF du registre
d’état mais l’état de ce bit peut être modifié à l’issue de l’opération.
Par exemple :
INC AX incrémente la valeur stockée dans AX. INC AX donne le même résultat que ADD AX, 1 mais le premier mnémonique
correspond à un codeop de 1 octet alors que le codeop correspondant au second mnémonique en occupe 3.
DEC BX décrémente la valeur stockée dans BX. DEC BX donne le même résultat que SUB BX, 1 mais le premier mnémonique
correspond à un codeop de 1 octet alors que le codeop correspondant au second mnémonique en occupe 3.
Pour effectuer des additions sur 32 bits avec des registres limités à 16 bits, nous devons scinder les nombres en 2 parties :
Pour demander au processeur d’effectuer des additions qui tiennent compte du bit CF, nous devons utiliser un nouveau mnémonique :
ADC ( ADdition with Carry ).
Il n’existe pas de mnémonique SUBC mais un mnémonique appelé SBB ( SuBtract with Borrow ) qui effectue le même type d’opération
mais en sens inverse.
Comme les opérations ADC et SBB modifient l’état du bit CF, il sert à effectuer des opérations sur 48 bits, 64 bits … en stockant les
résultats intermédiaires en mémoire, l’opération se fera par bloc de 16 bits.
Le mnémonique MUL ( MULtiply ) permet les multiplications, mais son mode de fonctionnement est très différent par rapport à celui de
l'addition et la soustraction.
La multiplication MUL avec deux données de 8 bits donne comme résultat une valeur codée sur 16 bits, donc de façon directe les valeurs
données ne peuvent pas dépasser les bits de poids faibles. Si les valeurs dépassent les 8 bits, il faut alors utiliser plusieurs registres.
Pour multiplier un nombre N par un nombre M, nous devons d'abord copier le nombre N dans le registre AL (ou AX) puis invoquer
l’instruction MUL M :
si l’opérande est un octet, nous calculons AL * opérande et le résultat est stocké dans AX.
si l’opérande est un mot, nous calculons AX * opérande et le résultat est stocké dans les registres DX et AX (DX contenant la partie
de poids fort).
Le 8086 propose un autre mnémonique pour effectuer les multiplications entre 2 nombres signés. Le mnémonique IMUL est utilisé de la
même manière que le mnémonique MUL à la différence que les opérandes sont considérés comme des nombres signés (codés en
complément à 2) et que le résultat est un nombre signé.
Le mnémonique de la division euclidienne est DIV et son mode de fonctionnement est voisin de celui de MUL.
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 8/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
Pour diviser un nombre N par un nombre M, nous devons d'abord copier le nombre N dans le registre AX (ou AX et DX) puis invoquer
Contact @SUPINFO
l’instruction DIV M :
ACCUEIL CURSUS COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS Naviguer sur la page
si l’opérande est un octet, nous calculons AX / opérande : le quotient est stocké dans AL et le reste dans AH.
dans le cas d’un mot, nous calculons (AX DX) / opérande : le quotient est stocké dans AX et le reste dans DX.
Le 8086 propose un autre mnémonique pour effectuer les divisions euclidiennes entre 2 nombres signés. Le mnémonique IDIV est utilisé
de la même manière que le mnémonique DIV. Les différences sont que les opérandes sont considérées comme des nombres signés
(codés en complément à 2) et que le résultat est un nombre signé.
Le 8086 offre 4 instructions pour modifier directement l’état des bits DF (direction) et IF (interruptions externes) :
Les mnémoniques STI et CLI sont utiles pour implanter au niveau assembleur des procédures qui ne peuvent pas être interrompues par
des événements externes.
Les mnémoniques STD et CLD sont utiles pour aiguiller le sens de manipulation des chaines d’octets. Si le flag DF est à 0, le traitement
est de gauche à droite, si DF est à 1 le traitement va de droite à gauche.
Comme pour tous les langages, avant d’utiliser une variable, il faut initialement la déclarer. Les variables sont considérées comme des
emplacements mémoire qui peuvent être manipulés par l’intermédiaire de leur adresse ou de leur nom.
Le nom d’une variable peut être une combinaison de chiffres, de lettres et du caractère « _ », mais il doit respecter les contraintes
suivantes :
Lorsque l’association entre le nom et la variable est créée, ce nom peut être utilisé pour désigner l’emplacement mémoire contenant
l’information. L’assembleur traduit le nom en une adresse lors du processus d’assemblage.
L'association entre le nom et l'adresse s'effectue en utilisant les instructions DB ( Define Byte – 1 octet pour 8086) ou DW ( Define Word –
2 octets pour 8086) selon que la variable contient des octets ou des mots.
La valeur associée à la variable peut être un nombre (hexadécimal, décimal ou binaire) ou le symbole ? (si la variable n’est pas initialisée),
par exemple :
var1 DB 50h
var2 DB ?
var3 DW 1110h
Il peut être intéressant de connaître l’adresse d’une variable pour utiliser cette dernière comme argument des interruptions logicielles.
Celle-ci se décompose en 2 parties :
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 9/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
1. SEG nom remplacé par le nom du registre associé au segment contenant la donnée.
ACCUEIL CURSUS COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS Naviguer sur la page
2. OFFSET nom remplacé par la valeur du déplacement.
var1 DB 50h
L’exemple ci-dessus sera interprété par, un placement de pointer suivi d’un déplacement (valeur dépendant du nombre d’instruction
contenu dans le fichier) :
MOV AX, CS
MOV BX, 00100h
Une constante numérique est définie grâce à la directive EQU qui indique à l’assembleur que le nom de cette constante doit être
remplacée par sa valeur au moment de la création de l’exécutable.
L’exemple ci-dessus, définit une constante nommée var1 dont sa valeur est 50h et sera interprété par :
Il faut noter la différence entre une constante initialisée par EQU et une variable initialisée par DB ou DW. EQU construit une constante
dont la valeur est immédiate (et normalement invariante) alors que les autres déclarations construisent des variables dont les valeurs se
situent aux adresses de stockage des valeurs. C’est donc pour cela que DB et DS pourront ne pas avoir de valeurs initialement données
et pourront récupérer des valeurs au cours de l’exécution du programme.
4.2 Tableaux
Un tableau peut être vu comme une liste de variables qui sont associées à un même nom. La création d’un tel tableau s’effectue alors de
la manière suivante :
Il est alors possible d’accéder à une case précise du tableau en utilisant un opérateur d’indexation : nous écrivons le nom du tableau
suivi du numéro de la case placé entre crochet (les cases étant numérotées de 0 à N-1 pour un tableau comportant N éléments) :
Si un tableau est construit en répétant une ou plusieurs valeurs, il peut être utile d’utiliser l’opérateur DUP, par exemple :
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 10/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
ACCUEIL CURSUS COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS Naviguer sur la page
Une chaîne de caractères peut être vue comme un tableau contenant une suite de code ASCII séparés par une virgule (correspondant
chacun à une lettre). Dans tous les cas, et pour faciliter les parcours et les recherches, les chaines de caractères seront terminées par le
symbole $ (pour marquer la fin de la chaîne de caractères).
Cette écriture est fastidieuse car il faut connaître le code ASCII de chaque caractère. Nous pouvons opter pour une séquence dissociée
de caractères notés chacun entre symboles ' ' et séparés par une virgule.
Ou pour une séquence concaténée de caractères avec un symbole ' en début et en fin.
var6 DB 'HELLO$'
5.1 Le moniteur
La présentation reste adapter à notre mode de programmation assembleur. Nous allons rester dans des modes simples, qui permettent
de mieux appréhender l’apprentissage voulu ici. Pour décrire le balayage et la formation d’une image couleur, le moniteur reçoit de la
part du contrôleur d'écran des signaux analogiques correspondant à l'image à afficher :
1. le moniteur reçoit un premier signal de synchronisation qui ordonne au moniteur de baisser très fortement l'intensité des canons
(de manière à faire disparaître le spot) puis à modifier l'alimentation des bobines de déviation de manière à placer le spot dans le
coin supérieur gauche de l'écran (côté spectateur).
2. le moniteur reçoit un deuxième signal de synchronisation qui lui indique de prendre en compte les signaux de couleur à
destination des canons (il s’agit d’un spot).
3. les fils transportant les signaux de couleur et le signal de balayage vertical sont envoyés de manière synchrone vers le moniteur.
Le spot se déplace de la gauche vers la droite et les luminophores de la ligne sont bombardés chacun leur tour avec une intensité
dépendant de la couleur à produire.
4. le moniteur reçoit un signal lui indiquant d'éteindre le spot et de le ramener très rapidement vers la gauche de l'écran, un cran
plus bas pour former la ligne suivante.
Les étapes 3 et 4 sont répétées jusqu'à former l'image complète. Lorsque l'image est totalement formée, nous retournons à la première
étape.
Le contrôleur d'écran est souvent désigné sous le terme de carte vidéo ou encore de CRTC ( Cathode Ray Tube Controler ). Il existe de très
nombreuses cartes vidéo capables de gérer les dessins en 3D, les textures ... Cependant, comme nous nous intéressons au processeur
80x86, nous allons plutôt décrire une carte plus ancienne, la carte CGA ( Color Graphic Adaptator ), dont le fonctionnement sera plus aisé
à comprendre. Construite autour d'un circuit appelée le CRTC 6845 de Motorola. Cette carte peut fonctionner selon différents modes
texte:
40 x 25 caractères en monochrome.
40 x 25 caractères en 16 couleurs.
80 x 25 caractères en monochrome.
80 x 25 caractères en 16 couleurs. Cette carte peut fonctionner selon différents modes graphiques:
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 11/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
La carte CGA dispose d'une palette de 16 couleurs d’où sont tirées 2 sous-palettes utilisées pour le mode 320x200 en 4 couleurs:
La carte CGA dispose de registres internes du CRTC 6845 suffisants pour les anciennes cartes graphiques telles que la carte MDA
( Monochrome Display Adaptator ) qui équipait l'IBM PC de 1981. La carte CGA ayant des possibilités supplémentaires, d'autres registres
sont additionnés, comme par exemple :
Le registre de sélection de mode est sur 1 octet. Il va permettre de distinguer plusieurs modes tels que :
02 = 40 * 25
A chage de caractères bit 0
12 = 80 * 25
02 = mode texte
Mode vidéo bit 1
12 = mode graphique
02 = sortir
Signal de couleur bit 2
12 = inhiber
02 = non
Production d'un signal bit 3
12 = oui
Le registre de sélection de couleur est sur 1 octet. Il va permettre de distinguer plusieurs couleurs tels que :
Pour le système d'affichage, il existe la mémoire vidéo. Elle est accédée à la fois par le processeur (le plus souvent en écriture) et par le
Motorola 6845 qui constitue le cœur de la carte vidéo CGA.
Cette zone de mémoire a une capacité de 16 Ko et commence à l'adresse B800:0000. Les 16 Ko correspondent à 16 * 1024 octets, soit
16384 emplacements qui sont organisés de différentes manières selon le mode vidéo choisi. Dans cette présentation, nous allons nous
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 12/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
limitons au mode texte, plus facile à appréhender. En mode texte, un caractère est codé par 2 octets :
Contact @SUPINFO
Le mode texte en 80*25 nécessite 80*25*2 octets soit 4000 octets. Comme la mémoire vidéo a une capacité de 16 Ko, nous pouvons
mettre en place 4 pages :
L'intérêt de ce système de pagination est de pouvoir construire une page (que le processeur accède en écriture) pendant qu'une autre
page est affichée (le Motorola 6845 accède en lecture à une autre zone mémoire). Cela permet une plus grande fluidité au niveau de
l'affichage. Lorsque la page est prête, il suffit de modifier les registres 0Ch 0Dh du Motorola 6845 pour qu'il affiche la bonne page. Pour
déterminer la valeur du déplacement (offset) à l'intérieur d'une page, nous pouvons appliquer la formule : offset (colonne, ligne) = ligne *
160 + colonne * 2.
Après cette longue description, nous allons enfin afficher des caractères à l'écran. Pour commencer, nous allons d'abord afficher le
message «Bonjour,monde!!!» en écrivant les lettres dans les bonnes cases de la mémoire vidéo grâce à l'instruction MOV.
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 13/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
Contact @SUPINFO
ACCUEIL CURSUS COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS Naviguer sur la page
Les différents affichages ont été faits avec une vision interne de l’architecture. Nous verrons dans la suite qu’il est possible de faire
intervenir des fonctions prédéfinies (notion d’interruptions) pour faire des affichages. Pour cela il faut introduire les notions de
navigation dans le code et ensuite nous construirons les interruptions.
Les étiquettes (nommées aussi labels) permettent de spécifier au programme à quel endroit continuer une exécution. Il s’agit d’une
ancre de programmation, comme un point d’entrée pour commencer des instructions. Pour accéder à une étiquette, il est possible
d’utiliser un mnémonique de contrôle du flot d'instructions : JMP (pour jump). Nous parlons ainsi de programmation par Go To non
conditionnelle. Le branchement inconditionnel consiste simplement à sauter d'une position dans le code à une autre position pour
continuer l'exécution. Ce type de branchement est opposé aux branchements conditionnels qui réalisent des sauts en fonction des tests
effectués sur les bits du registre d'état. Le saut inconditionnel est JMP suivi d'un nom de l'étiquette (ou d'un nombre représentant
l'adresse de destination codée sur 4 octets mais ce n'est pas conseillé). Pour que les instructions s’exécutent, l’étiquette atteinte est
convertie en une adresse au moment de l'assemblage des instructions de saut.
Pour ajouter une étiquette dans le programme assembleur, il suffit de déclarer un nom (qui ne commence pas par un chiffre) suivi des 2
points ( : ).
Par exemple :
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 14/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
Contact @SUPINFO
etiquette1: MOV AX, BX
JMP etiquette1
ACCUEIL CURSUS COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS Naviguer sur la page
La partie du programme ci-dessus provoque une boucle entre l’instruction JMP etiquette1 et l’ancre etiquette1. Lors de l’exécution de
l’instruction le JMP décodée instruit le saut vers l’étiquette1. Pour cet exemple comme le programme reste une séquence vers JMP, il y a
une boucle de créée.
A l'instar de l'instruction JMP, les instructions de branchement conditionnel s'utilisent avec un label. Elles doivent être placées juste après
l'instruction (CMP, DEC, INC ...) qui modifient l'état du registre d'état sinon les autres mnémoniques placées entre cette instruction et le
branchement conditionnel pourraient altérer les bits du registre d'état. L'instruction CMP (pour compare) affecte les drapeaux du
registre d'état (instructions de branchement conditionnel afin d'implanter des boucles, des tests ...). La comparaison de 2 nombres
s'effectue en faisant une pseudo-soustraction qui affecte les drapeaux dans le registre d’état :
OF : Overflow Flag
SF : Sign Flag
ZF : Zero Flag
AF : Auxiliary Carry Flag
PF : Parity Flag
CF : Carry Flag
Compte tenu de la ressemblance avec l'instruction SUB, les types des opérandes traités sont :
L'amplitude des sauts conditionnels est limitée à 127 octets vers l'avant et 128 octets vers l'arrière. Pour contourner ce problème, nous
pouvons coupler ce saut conditionnel avec un saut inconditionnel, par exemple :
CMP AX, BX
JZ grandSaut
JMP plusLoin
plusLoin: ….
plusLoin2: ….
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 15/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
Il est à noter, lors de l’émulation des branchements par saut, l’émulateur modifiera votre programme si il y a un saut plus simple en
Contact @SUPINFO
termes de mise à jour des flags conforme à la demande du programme écrit.
ACCUEIL CURSUS COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS Naviguer sur la page
La première manière d'implanter des boucles en utilisant les mnémoniques de sauts conditionnels (JNZ, JMP ...) : cela se rapproche du
while et du do ... while.
La seconde méthode (qui se rapproche plutôt du for) consiste à utiliser le registre CX comme un compteur par l’intermédiaire d’une des
mnémoniques suivantes :
Instruction Description
LOOP Décrémente CX et va à l’étiquette si CX != 0
JCXZ va à l’étiquette si CX = 0
L’exemple de code suivant propose une itération avec un loop dont les tours de décrémentation sont gérés par le registre CX :
MOV CX, 5
boucle: INC AX
...
LOOP boucle ; Si CX != 0, nous rebouclons
...
Il peut être utile de placer dans des procédures distinctes des sections de code qui sont appelées plusieurs fois de manière à :
7.1 La construction
A l’instar de sauts, la mise en place d’une procédure s’effectue en respectant quelques contraintes :
nous devons d’abord écrire son nom (une étiquette mais sans les 2 points) suivi de la directive PROC pour signaler à l’assembleur
qu'il s'agit d'une procédure.
nous plaçons ensuite les instructions assembleur correspondant à l'implantation de la procédure (la dernière étant l’instruction
RET).
nous terminons le code de la procédure par une ligne comportant le nom de la procédure suivi de la directive ENDP.
Name PROC
….
RET
Name ENDP
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 16/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
ACCUEIL CURSUS COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS Naviguer sur la page
Comme le montre le programme de la figure ci-dessous, lorsqu’une procédure est implantée, elle peut être appelée par l'instruction
CALL suivie du nom de la procédure. Le mnémonique sauvegarde l’IP dans la pile, indiquant ainsi l'adresse de retour. La procédure est
déroulée jusqu’à l’instruction RET qui restaure l’IP depuis la pile et qui entraîne l’exécution de l’instruction qui suit CALL.
Lorsque nous quittons une procédure appelante pour entrer dans une procédure appelée, nous devons sauvegarder le contexte
d’exécution de la procédure appelante afin de pouvoir reprendre son exécution lors du retour de la procédure appelée. Ce contexte
d’exécution de la procédure appelante est défini par le contenu de l’ensemble des registres qu’elle utilise :
Si nous ne conservons pas l’état de ces registres et si ces derniers sont modifiés par la procédure appelée, le fonctionnement de la
procédure appelante est corrompu. Le moyen le plus couramment utilisé pour conserver ces données est la pile (le segment SS) que
nous manipulons grâce aux instructions suivantes :
L’instruction PUSH permet de conserver une donnée à l’adresse SS:[SP] et de décrémenter SP de 2 (octets). Nous pouvons réaliser 3 types
de PUSH :
L’instruction PUSHA permet d’empiler le contenu des registres AX, CX, DX, BX, SP, BP, SI et DI. Nous avons donc une équivalence entre
l’instruction PUSHA et l’ensemble des instructions pris dans l’ordre PUSH AX, PUSH CX, PUSH DX, ….
L’instruction PUSHF permet de conserver le contenu du registre d’état à l’adresse SS:[SP]. Comme ce registre a une taille de 16 bits SP est
décrémentée de 2 (octets) à l’issue de cette opération.
L’instruction POP permet de récupérer une donnée à l’adresse SS:[SP] et d’incrémenter SP de 2 (octets). Nous pouvons réaliser 2 types de
POP :
L’instruction POPA effectue le traitement inverse de PUSHA. POPA récupère les informations stockée dans la pile pour les placer dans les
registres DI, SI, BP, SP, BX, DX, CX, et AX. Comme précédemment, nous avons une équivalence entre l’instruction POPA et l’ensemble des
instructions pris dans l’ordre (ordre inversé de PUSHA). Il est à noter que POP SP est ignoré pour ne pas perturber le fonctionnement de
la pile.
L’instruction POPF effectue le traitement inverse de PUSHF. POPF permet de copier le contenu stocké à l’adresse SS:[SP] vers le registre
d’état. Comme ce registre a une taille de 16 bits SP est incrémentée de 2 (octets) à l’issue de cette opération.
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 17/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
Lors d’une utilisation normale d’un ordinateur, il y a en permanence l’exécution d’instructions. Toutes ces instructions ne dépendent pas
Contact @SUPINFO
d’un même programme. En effet, il y a plusieurs programmes qui se partagent le CPU, les entrées/sorties et donc pour exécuter les
instructions, il yCOURS
ACCUEIL CURSUS a un procédé
ADMISSIONScyclique
CAMPUS d’interruption
DOCUMENTATION /ANCIENS
sauvegarde / exécution
ENTREPRISES / restauration.
OPEN CAMPUS PUBLICATIONSIl existe un ensemble d’interruptions
Naviguer sur la: page
Les interruptions matérielles permettent au processeur de réagir aux actions des périphériques externes comme le clavier, la souris …
Le processeur exécute un programme stocké dans le segment de code de la mémoire. Il doit être capable de réagir à des événements
externes en interrompant le programme en cours pour exécuter une procédure de traitement de l’événement. Ce mécanisme nécessite
l’adjonction d’un composant appelée contrôleur d’interruption sur lequel sont branchés les périphériques susceptibles de demander au
processeur d’effectuer des traitements en réaction à leur fonctionnement (le clavier, la souris, le disque dur …).
Si nous rentrons dans les détails, le contrôleur d’interruption est composé de 2 circuits 8259A mis en cascade. Dont chaque fil, appelé
IRQ ( Interrupt ReQuest ) est raccordé à un périphérique (ou un ensemble de périphériques).
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 18/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
Contact @SUPINFO
ACCUEIL CURSUS COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS Naviguer sur la page
Si nous prenons l’exemple du clavier, celui-ci est raccordé à l’IRQ 1. Lorsque le clavier sollicite l’attention du processeur, celui-ci envoie
un signal sur cette patte. Le numéro de l’interruption est alors mis sur le bus de données pour être transmis vers le processeur. Lorsque
le bus de données est stable, le contrôleur d’interruption envoie un signal IRQ Request vers le processeur pour lui demander de tenir
compte du clavier. Le processeur indique par un INT ACKNOWLEDGE qu’il a pris en compte le signal d’interruption : le contrôleur
d’interruption désactive alors ce signal pour pouvoir en reprendre un en compte ultérieurement.
Figure 1.15. IRQ clavier
Avant le traitement de l’IRQ, le processeur exécutait un programme, il existe un contexte d’exécution correspondant à l’état des
différents registres au moment de cette interruption (en particulier le compteur ordinal qui pointe la prochaine instruction à exécuter).
Le processeur lit d’abord une zone mémoire appelée la table des vecteurs d’interruption, afin de connaître l’adresse de la procédure de
traitement en fonction du numéro de l’interruption. Le processeur exécute alors la première instruction de la procédure. Les premières
instructions consistent généralement à sauvegarder le contexte dans la pile avant d’exécuter la procédure de traitement de l’interruption
proprement dite. Lorsque le traitement proprement dit est terminé, nous rechargeons le contexte d’exécution depuis la pile (le compteur
ordinal pointe sur l’instruction du programme principal dont l’exécution pourra reprendre) et nous exécutons l’instruction IRET ( Interrupt
RETurn ). Ce procédé de sauvegarde est illustré par les schémas de la figure ci-dessous.
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 19/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
Certaines des interruptions matérielles (par exemple, le clavier) peuvent être masquées (le processeur peut ne pas en tenir compte) alors
Contact @SUPINFO
que d’autres (défaut de circuit RAM) ne peuvent pas l’être.
ACCUEIL CURSUS COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS Naviguer sur la page
Ce mécanisme est utilisé lorsque le processeur doit effectuer un traitement critique. Cela concerne par exemple la commutation entre 2
processus dans un système d’exploitation multitâches (des informations critiques doivent être sauvegardées et le processeur ne doit en
aucun cas être dérangé pendant cette phase).
8.2.1 L’écran
Ce principe appliqué au traitement de signaux électroniques externes a été étendu au traitement des signaux logiciels internes. Le
programmeur a en effet la possibilité d’invoquer l’exécution de procédures toute faites (nous parlons d’interruption par abus de langage)
en utilisant l’instruction INT suivi du numéro de l’interruption à exécuter, par exemple :
INT 10H
Le processeur interrompt alors le programme principal, sauvegarde le contexte d’exécution courante, et se branche sur la procédure de
traitement selon le principe évoqué précédemment. Les interruptions logicielles sont en réalité des procédures toutes faites, mises à la
disposition des programmeurs pour faciliter le contrôle des organes de l’ordinateur (disque dur, clavier …).
Nous distinguons :
les interruptions du BIOS (Basic Input Output System) qui sont implantées par le constructeur de la carte mère (les 32 premières
interruptions).
les interruptions système (DOS, Windows, Linux …) qui sont chargées en mémoire lors du chargement du système d’exploitation
(les 32 suivantes,). Nous allons en étudier 2 d’entre elles qui gèrent le clavier et l’écran.
L’écran peut être contrôlé par l’interruption n°10h (BIOS) ou l’interruption n°21h (DOS).
L’interruption 21h cache un ensemble de fonctions. Le numéro de la fonction utilisée et les paramètres de la fonction doivent être placés
dans certains registres avant l’instruction INT 21h.
Si nous souhaitons afficher un caractère à l’écran, nous devons utiliser la fonction 06h. Pour cela, nous devons placer la valeur 06h dans
le registre AH et le code ASCII du caractère à afficher dans le registre DL avant d’appeler l’interruption 21h, par exemple :
MOV AH, 6H
MOV DL, ‘A’
INT 21h
La routine de traitement lit d’abord le contenu de AH pour déterminer qu’il s’agisse de la fonction 06h puis elle lit le registre DL pour
déterminer le caractère qui doit être affiché à la position courante du curseur sur l’écran.
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 20/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
Pour l’affichage des caractères, nous utilisons la fonction 09h. Pour cela, il faut effectuer les affectations ci-dessous avant d’appeler
Contact @SUPINFO
l’interruption 21h :
ACCUEIL CURSUS COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS Naviguer sur la page
placer la valeur 09h dans le registre AH.
l’offset de cette même chaîne de caractères dans DX.
La chaîne de caractères est alors affichée à la position courante du curseur sur l’écran.
ORG 100h
INT 21h
RET
L’affichage d’une chaine de caractères peut de faire aussi avec une interruption. Pour cela, nous utilisons la fonction 40h. Il faut effectuer
les affectations ci-dessous avant d’appeler l’interruption 21h :
La chaîne de caractères est alors affichée à la position courante du curseur sur l’écran.
ORG 100H
Il est toujours possible de se positionner ou nous voulons dans l’écran d’affichage, et pour cela il faut positionner le curseur. Nous
utilisons la fonction 02h en effectuent les affectations ci-dessous avant d’appeler l’interruption 10h :
ORG 100H
MOV AH,02h
MOV DH,05h
MOV DL,20h
INT 10H
RET
8.2.2 Le clavier
Le clavier peut être contrôlé par l’interruption de la fonction 16h du BIOS ou par l’interruption de la fonction 21h du DOS. L’interruption
16h est également composée de plusieurs fonctions mais 2 nous intéressent plus particulièrement :
la fonction 00h qui permet de récupérer le code de la touche qui a été frappée au clavier.
la fonction 01h qui permet de vérifier si une touche a été frappée au clavier.
Pour lire la touche qui a été frappée sur le clavier, il faut utiliser la fonction 0h.
Pour cela, nous écrivons la valeur 0h dans AH puis nous exécutons l’instruction INT 16h.
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 21/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
Lors de son exécution, cette routine retire du buffer du clavier, le code de la touche qui a été tapé, pour le mettre à notre disposition :
Contact @SUPINFO
Pour tester si une touche a été frappée sur le clavier, il faut utiliser la fonction 1h.
Comme précédemment, nous écrivons la valeur 1h dans AH puis nous exécutons l’instruction INT 16h.
Si un caractère est présent dans le buffer, cette routine met le drapeau ZF à 0 (sinon il est à 1) puis elle copie depuis le buffer du clavier,
le code de la touche qui a été tapé, pour le mettre à notre disposition :
Si un programme contient une boucle qui teste de façon incessante si une touche a été frappée sur le clavier :
Ce type de programme fait de l’attente active. Par conséquent, il teste de façon répétée si un événement est survenu pour déclencher
une action au lieu d’être activée par l’événement lui-même. Il est généralement conseillé d’éviter ce type de programmation car elle
conduit à gaspiller du temps CPU et par voie de conséquence à dégrader les performances d’un programme Assembleur.
Il est préférable d’adopter une technique de programmation événementielle où le programme réagit à des événements externes.
Pour réaliser ce type de programmation en assembleur, il est nécessaire de dérouter les interruptions.
Ce déroutement s’effectue simplement en modifiant l’adresse contenue dans la table des vecteurs d’interruption.
Si nous prenons l’exemple du clavier, celui-ci est d’abord géré par l’interruption 9h qui est appelée par le contrôleur d’interruption :
le traitement par défaut consiste à copier les codes concernant la touche qui a été frappée dans un buffer.
il est possible d’implanter notre propre procédure de traitement puis de modifier l’adresse de la procédure associée à l’interruption
9h de manière à ce que notre procédure soit appelée lorsqu’une touche est saisie.
Cette technique était utilisée dans les anciens jeux vidéo pour gagner en fluidité dans le contrôle du jeu.
Il est possible de faire du masquage d’interruption grâce à l’instruction CLI ( CLear Interrupt ) qui a pour effet de mettre à 0 le bit IF du
registre d’état.
L’instruction STI ( SeT Interruption ) permet de rendre le processeur sensible aux interruptions masquables.
Pour construire un fichier en ASM, il faut adopter la rigueur de programmation habituelle, car il subira ensuite une transformation pour
devenir un programme exécutable (Pour un programme assembleur, cette transformation est nommée assemblage). Une fois
l’assemblage fait sans erreur, nous avons un fichier dit objet (écrit exclusivement en binaire sur des octets consécutifs en mémoire,
découpé en segment de code). Si pour fonctionner, le programme a besoin d’autres instructions provenant d’un autre programme, il
faudra lier tous les codes objets entre eux avec un éditeur de lien.
Les instructions (codage du code-opérateur comprenant le mnémonique suivi du ou des valeurs immédiates, registres, adresses, …) sont
écrites les unes en dessous des autres permettant à l'assembleur de différencier les commandes à faire. Nous distinguons 2 types de
format, les fichiers pour faire des programmes COM et des fichiers pour faire des programmes EXE.
ORG 100H
programme:
MOV AH,02h
MOV DH,05h
MOV DL,20h
INT 10H
Contact @SUPINFO
INT 21H
ACCUEIL
RET CURSUS COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS Naviguer sur la page
segmentprogramme ends
end programme
Un fichier COM ne comporte qu’un segment nous le voyons sur la figure ci-dessus par l’instruction : segmentprogramme segment use16.
Cette instruction Indique au compilateur le début de l’adresse du segment nommé segmentprogramme. use16 indique que les adresses
de segment et de l’offset sont codées sur 16 bits et non sur 8 bits. Le segment est pointé initialement par CS, DS et SS. Il faut rappeler
dans le programme ces pointages et l’instruction assume permet de faire un espace de nom sur DS. L’espace de nom permet d’éviter
d’écrire systématiquement les variables avec l’expression ds:. Ensuite pour construire le code objet, le compilateur cherche quel registre
l’utilisateur utilise, il veut un registre de segment. Il récupère le segment pointé par assume. Les instructions segmentprogramme ends et
end programme permettent de donner les indications nécessaire de fin pour construire l’ensemble des codes objets.
Pour un fichier de type EXE, il faut distinguer les partie des code objets produits. Il se base sur le comportement dynamique d’un
programme et le construit comme un processus et sépare les données du programme et de la pile des appels.
programme:
MOV AH,02h
MOV DH,05h
MOV DL,20h
INT 21H
INT 21H
RET
segmentprogramme ends
Un fichier EXE comporte une distinction entre le programme, ses données et la pile des appels.
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 23/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
Contact @SUPINFO
ACCUEIL CURSUS COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS Naviguer sur la page
Les segments de pile, de données, de code, … sont dans leur zone mémoire. Les registres CS DS SS et ES sont les pointeurs d’adresse des
segments d'instructions. ASSUME indique à l'assembleur où les segments sont situés.
10 L’OUTIL EMU8086
Lorsque nous démarrons l’outil Emu 8086, l'application nous propose une interface.
Si nous appuyons sur le bouton "Code Examples", nous avons accès à une liste d'exemple. Le programme Hello World étant trop
complexe à ce stade de l’apprentissage (il faut d'abord décrire le fonctionnement de la carte vidéo pour comprendre cet exemple), nous
allons nous orienter vers un programme de calcul avec des "ADD/ SUB". Pour utiliser les opérations, nous devons au préalable charger
dans les registres des valeurs, et nous le ferons avec l’instruction MOV et les registres de bases AX et BX.
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 24/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
Contact @SUPINFO
ACCUEIL CURSUS COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS Naviguer sur la page
Pour une création d’édition d’un programme, nous le faisons en 3 étapes, New, Empty workspace et valider.
Chaque instruction assembleur (mnémonique) possède une correspondance en langage machine, appelée code opérateur (CodeOp), qui
peut être codé sur un ou plusieurs octets. Cette correspondance est spécifiée par le constructeur - Intel - dans son databook ce qui
permet au programmeur de développer leur assembleur et/ou leur émulateur.
Les instructions opération registre, valeur sont dites à adressage immédiat car nous indiquons "en dur" la valeur qui doit être chargée
dans le registre :
Les instructions opération registre, registre sont dites de registres car les données, sur lesquelles les opérations sont effectuées, sont
contenues dans les registres :
Dans ces 2 cas, il n'y a pas de construction d'adresses sur 20 bits puisque nous ne faisons pas intervenir la mémoire, ces instructions
sont rapides à exécuter.
Pour générer et commencer à exécuter le programme de la figure ci-dessus, il faut cliquer sur EMULATE. L'assembleur attribue aux
instructions traduite en langage machine des adresses relatives. Le processus d’émulation respecte les conditions architecturales car les
adresses sont attribuées de façon séquentielle à partir du début du programme. C’est la raison pour laquelle une origine doit être
définie; fait par l’instruction ORG 100h.
Un programme, pour être exécuté est chargé en mémoire par le système d’exploitation. Le DOS distingue 2 modèles de programmes :
1. les fichiers exécutables COM (utilisant un seul segment dans la mémoire de taille 64 Ko).
2. les fichiers exécutables EXE (limités que par la mémoire disponible dans l’ordinateur).
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 25/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
Le DOS charge le fichier COM et lui alloue toute la mémoire disponible. Mais si la mémoire est insuffisante, il annule le chargement en
Contact @SUPINFO
l’indiquant à l’utilisateur.
ACCUEIL CURSUS COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS Naviguer sur la page
Dès que le chargement se fait, le DOS crée un PSP ( Program Segment Prefix ) du programme lui-même, au début du bloc de mémoire,
représentant une taille 100h d’où l’obligation de placer org 100h dans les programmes pour ne pas corrompre le PSP.
Un PSP contient des informations comme le nom et le type d’extension du programme, des paramètres d’entrées, …
Pour désigner une instruction contenue dans une case de la mémoire de 1 Mo, nous avons besoin d'une adresse codée sur 20 bits (cela
dépasse les capacités des registres internes du 80x86).
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.
L'adresse d'un octet (en hexadécimale) se note AAAA:BBBB où AAAA est l'adresse de segment et BBBB est l’offset. Par exemple, le 17ième
octet de la RAM (le numéro 16) est situé à l'adresse 0000:0010. L’octet 0000:0100 est l'octet numéro 256.
Si l'adresse de l'octet est 12F3:0230, alors son adresse effective est : 12F3 16 * 10 16 + 0230 16 = 13160 16 . Un octet n'a pas une adresse
unique. Par exemple, l'octet numéro 8810 peut être adressé par :
1. 0000:0058
2. 0001:0048
3. 0002:0038
4. 0003:0028
5. 0004:0018
6. 0005:0008
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 26/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
Contact @SUPINFO
ACCUEIL CURSUS COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS Naviguer sur la page
Appuyons sur le bouton "Single Step" afin de lancer une exécution pas-à-pas du programme. Chaque clik effectué représente une
progression dans les instructions.
La première instruction (MOV AX, 5) est chargée, décodée et exécutée par le processeur :
La prochaine instruction pointée par IP (MOV BX, 10) est surlignée dans les différents écrans.
Appuyons sur le bouton "Single Step" pour exécuter la deuxième instruction (MOV BX, 10) :
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 27/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
Contact @SUPINFO
ACCUEIL CURSUS COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS Naviguer sur la page
la fenêtre ALU affiche le contenu des 2 registres temporaires d'entrées et le résultat de l'opération.
le registre AX contient la valeur 000Fh (0005h + 000Ah = 000Fh).
le registre IP contient la valeur 0108h (IP passe de 0106h à 0108h car ADD AX, BX est codé sur 2 octets).
la fenêtre ALU affiche le contenu des 2 registres temporaires d'entrées et le résultat de l'opération.
le registre AX contient la valeur 000Fh (000Fh - 0001h = 000Eh).
le registre IP contient la valeur 0010Bh (IP passe de 0108h à 0010Bh car SUB AX, 1 est codé sur 3 octets).
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 28/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
Contact @SUPINFO
ACCUEIL CURSUS COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS Naviguer sur la page
Nous exécutons la cinquième instruction (RET). Cette instruction est équivalente à effectuer POP IP :
nous dépilons la valeur stockée dans la pile à la position SS:SP (adresse : SS * 16 10 + SP) pour la charger dans le registre IP. SS:SP (=
0B56h:FFFEh) et le mot (2 octets consécutifs) stocké à cette adresse valent 0000h.
puis IP = 0000h, la prochaine instruction exécutée est à l'adresse CS:IP = 0B56h:0000h.
nous modifions la valeur de SP car nous avons dépilé un élément. SP = FFFEh + 0002h = 10000h soit 0000h car SP est un registre 16
bits.
Nous appuyons sur single step et nous exécutons une instruction non saisie dans notre programme (INT 020h) qui a été
automatiquement ajoutée par le système d'exploitation (de même que la valeur qui était empilée et la valeur des registres SS et SP).
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 29/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
Contact @SUPINFO
ACCUEIL CURSUS COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS Naviguer sur la page
INT 020h donne l'ordre au processeur d'exécuter l'interruption (routine ou procédure) numéro 20h (l’ordre de INT 020h est de quitter le
système d’exploitation). Le processeur modifie le contenu de ses registres car il saute dans une autre zone mémoire puis il exécute cette
instruction qui consiste simplement à terminer le programme et à rendre la main au système d'exploitation (ici une émulation du DOS).
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 30/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
Contact @SUPINFO
11 L’ADRESSAGE
ACCUEIL CURSUS COURS ADMISSIONS CAMPUS DOCUMENTATION ANCIENS ENTREPRISES OPEN CAMPUS PUBLICATIONS Naviguer sur la page
Nous avons vu 2 modes d'adressages. Ces 2 modes ne font pas intervenir la mémoire centrale : Le mode immédiat où un opérande est
un nombre codé sur 8 ou 16 bits directement dans le codeop :
Les instructions d'un programme sont pointées par le registre IP qui contient une adresse relative au segment de code (Code Segment ou
CS) avec :
L’adressage des données dans DS peut s’effectuer de différentes manières (en utilisant différentes combinaisons) et prenons DEP est un
nombre qui correspond à un décalage en mémoire, exprimé en octets :
DS : DEP, DS : BX, DS : SI, DS : DI, DS : BX+DEP, DS : SI+DEP, DS : DI+DEP, DS : BX+SI, DS : BX+DI, DS : BX+SI+DEP, DS : BX+DI+DEP
Nous pouvons adresser des données dans la pile (Stack Segment ou SS) ou dans le segment supplémentaire ( Extra Segment ou ES). Nous
avons les combinaisons supplémentaires suivantes et prenons DEP est un nombre qui correspond à un décalage en mémoire, exprimé
en octets :
ES : DEP, ES : BX, ES : SI, ES : DI, ES : BX+DEP, ES : SI+DEP, ES : DI+DEP, ES : BX+SI, ES : BX+DI, ES : BX+SI+DEP, ES : BX+DI+DEP
D’un point de vue programmation, ces adressages des données peuvent être utilisés à l’aide de l’instruction MOV.
D’un point de vue syntaxique, nous distinguons ce type d’adressage de ceux évoqués précédemment grâce à l’utilisation des crochets [ ]
qui entoure la seconde partie de l’adresse, par exemples :
MOV DS:[BX], 10 signifie que nous stockons 10 dans la case dont l’adresse est DS:BX (soit DS * 16 10 + BX). le segment concerné est
DS, il n’est pas nécessaire de l’indiquer car il est sous-entendu et donc MOV [BX], 4 est équivalent à MOV DS:[BX], 4
MOV AX, DS:[BX+DI+0x10h] signifie qu'il faut copier le contenu de la case mémoire d’adresse DS:BX+DI+0x10h dans le registre AX.
MOV ES:[BX+SI], DX permet de stocker le contenu du registre DX dans la case mémoire d’adresse ES:[BX+SI].
MOV CX, SS:[BP+0x02h] permet de copier, dans le registre CX, le contenu dans la case mémoire d’adresse SS:[BP+0x02h].
Nous avons utilisé les instructions ADD, SUB et ADC en respectant l’une des 2 syntaxes (qui correspondent respectivement au mode
immédiat et au mode par registre) :
Les instructions MUL, IMUL, DIV et IDIV ne supportent pas le mode immédiat. Ces instructions s’utilisent uniquement en mode par
registre et en mode en mémoire. Nous avons les syntaxes suivantes :
MUL/IMUL/DIV/IDIV registre
MUL/IMUL/DIV/IDIV mémoire
Pour spécifier si le pointeur manipule un mot ou un octet (utile pour opération de division et de multiplication). Nous devons ajoutons
une information supplémentaire devant le pointeur :
DIV b.ES:[BX+DI+0x10h]
Il est important de noter qu’il n’existe pas d’instruction avec un MOV pour transférer le contenu d’un registre de segment vers un autre
registre de segment. Donc, il est indispensable de passer par un registre de données pour transférer les registres de segment.
Adressage direct : l'opérande est une case mémoire (registre DS par défaut) :
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 31/32
16/03/2020 Chapitre 09 - L'assembleur x86 | SUPINFO, École Supérieure d'Informatique
MOV AL,[15h]
Contact @SUPINFO
Adressage basé : l'opérande est une case mémoire dont l'adresse est donnée par BX (avec DS par défaut) ou BP (avec SS par défaut) :
MOV AH,[BX]
MOV AL,[BP]
Adressage indexé : l'opérande est une case mémoire dont l'adresse est donnée par SI ou DI (avec DS par défaut, sauf mnémonique
spécifique) :
MOV AH,[SI]
MOV AH,[BX+DI]
MOV [BP+SI],AH
MOV AH,[BX+15h]
MOV AH,[DI+15h]
MOV AH,[BX+SI+15h]
Le support de cours 1CPA Essentiel proposé dans la formation SUPINFO est maintenant terminé. Il a proposé une vision Hardware
Hardware (UAL, registres, mémoires et bus basés sur une architecture pédagogique) et Hardware Software (Assembleur).
A propos de SUPINFO | Contacts & adresses | Enseigner à SUPINFO | Presse | Conditions d'utilisation & Copyright | Respect de la vie privée | Investir
https://www.supinfo.com/cours/1CPA/chapitres/09-assembleur-x86 32/32