Définition de l’assembleur
L’assembleur est un langage de programmation bas niveau qui permet de convertir un fichier texte contenant des instructions lisibles en un programme exécutable directement par le processeur, appelé langage machine.
Le langage machine est particulièrement difficile à manipuler, car il est constitué uniquement de valeurs numériques, généralement représentées en hexadécimal (base 16). Programmer directement à ce niveau serait complexe et peu pratique.
L’assembleur agit donc comme une couche intermédiaire au-dessus du langage machine. Il offre des instructions symboliques compréhensibles par l’humain, qui sont ensuite traduites en langage machine par l’assembleur. Cela rend la programmation beaucoup plus accessible, tout en restant très proche du fonctionnement interne du processeur.
Le fichier texte dans lequel sont écrites ces instructions est appelé le code source.
Le langage hexadécimal
L’un des concepts fondamentaux en programmation assembleur est la représentation des nombres, en particulier le système hexadécimal. Ce système de numérotation repose sur la base 16 et utilise à la fois des chiffres (0 à 9) et des lettres (A à F) pour représenter les valeurs.
Tu connais déjà le système décimal, qui fonctionne en base 10. En assembleur, les nombres décimaux peuvent être explicitement indiqués avec le suffixe d (par exemple 1000d), même si, dans la plupart des assembleurs, le décimal est utilisé par défaut.
Pour mieux comprendre le principe des bases, imaginons un système en base 5 (comme les cinq doigts de la main). On compte ainsi :
0, 1, 2, 3, 4.
Arrivé à la limite de la base, il devient impossible de représenter le nombre 5 avec un seul chiffre. On ajoute alors un chiffre à gauche, exactement comme en base 10.
Ainsi :
-
5 devient
10 -
7 devient
12
Chaque fois que l’on atteint la fin d’un cycle, on incrémente le chiffre de poids supérieur.
Application à la base 16
En hexadécimal, après le chiffre 9, on continue avec des lettres :
-
10 (décimal) →
0A -
11 →
0B -
15 →
0F
Une fois F atteint, on incrémente le chiffre de gauche :
-
16 →
10 -
255 →
FF -
256 →
0100h
Le suffixe h (et parfois un zéro en tête) permet d’indiquer qu’un nombre est écrit en hexadécimal.
Par exemple :
-
257d→101h -
65535d→FFFFh
Ce système est très utilisé en assembleur car il correspond naturellement à la représentation binaire des données. Pour convertir facilement des nombres décimaux en hexadécimal, tu peux utiliser la calculatrice Windows en mode scientifique.
Le calcul binaire
Les ordinateurs fonctionnent en base 2, appelée binaire. Ce système n’utilise que deux valeurs : 0 et 1.
Chaque 0 ou 1 est appelé un bit, et 8 bits forment un octet.
Un nombre binaire est composé de positions ayant chacune un poids précis :
Par exemple :
-
00000001b→ 1 -
00000010b→ 2 -
00000011b→ 3 (1 + 2) -
11111111b→ 255
Plus il y a de bits disponibles, plus les valeurs représentables sont grandes. Nous verrons plus tard que cela dépend de la taille des registres utilisés.
Le processeur et ses registres
Le processeur est composé de plusieurs éléments internes. Pour le programmeur assembleur, les plus importants sont les registres.
Un registre peut être vu comme une petite zone de stockage très rapide, capable de contenir des valeurs de tailles différentes selon son type.
On distingue plusieurs catégories de registres :
-
registres généraux
-
registres d’état
-
registres de segment
Dans ce cours, nous nous concentrerons principalement sur les registres généraux et les registres de segment.
Registres généraux
Les registres généraux les plus utilisés sont :
-
AX
-
BX
-
CX
-
DX
Ce sont des registres de 16 bits, chacun pouvant être divisé en deux parties de 8 bits :
-
AX → AH (partie haute) et AL (partie basse)
-
BX → BH et BL
-
CX → CH et CL
-
DX → DH et DL
À partir des processeurs plus récents, ces registres peuvent être étendus à 32 bits en ajoutant le préfixe E :
-
AX → EAX
-
BX → EBX
-
CX → ECX
-
DX → EDX
Les parties hautes et basses n’existent pas directement pour les registres 32 bits. Il n’y a pas de EAL ou EAH. La partie haute d’un registre 32 bits ne peut pas être manipulée directement et nécessite des instructions spécifiques.
En termes de capacité :
-
un registre 8 bits peut contenir jusqu’à 255
-
un registre 16 bits jusqu’à 65535
-
un registre 32 bits des valeurs bien plus grandes
Les processeurs 286 et antérieurs ne supportent pas les registres 32 bits. Ceux-ci sont apparus avec les processeurs 386.
Registres pointeurs
Il existe également des registres pointeurs, utilisés pour des opérations spécifiques :
-
SI (Source Index)
-
DI (Destination Index)
-
BP (Base Pointer)
-
SP (Stack Pointer)
-
IP (Instruction Pointer)
Ces registres peuvent eux aussi être étendus à 32 bits :
-
ESI, EDI, EBP, ESP, EIP
Contrairement aux registres généraux, ils n’ont pas de version 8 bits.
-
SI et DI sont souvent utilisés pour manipuler des données en mémoire et les instructions de chaîne.
-
SP sert à gérer la pile (stack) et est généralement réservé à des usages spécifiques que nous verrons plus tard.
Le processeur et ses registres de segment
Pour accéder à la mémoire, un ordinateur utilise ce que l’on appelle une adresse mémoire. En assembleur, cette adresse est généralement composée de deux parties :
-
le segment
-
l’offset (déplacement)
La taille de ces éléments dépend du mode de fonctionnement du processeur (16 bits ou 32 bits).
Exemple d’adresse mémoire
Cette adresse correspond à la mémoire vidéo en mode 16 bits.
L’adressage en mode réel
L’exemple précédent utilise un adressage 16 bits, appelé mode réel.
Le mode réel est un mode de fonctionnement historique du processeur dans lequel :
-
les segments sont limités à 64 Ko (65536 octets)
-
l’accès à la mémoire est restreint à des blocs de taille fixe
Le processeur 286 a introduit le mode protégé, qui permet de dépasser cette limitation et d’accéder à une plus grande partie de la mémoire. Ce mode sera largement amélioré à partir du 386, rendant possible l’accès à quasiment toute la RAM.
Dans ce cours, nous commencerons par le mode réel, car il est plus simple à comprendre et idéal pour apprendre les bases de l’assembleur, même s’il présente certaines limites. Les segments ont été conservés sur les processeurs plus récents afin de garantir une compatibilité avec les anciens systèmes.
Les registres de segment
Les registres de segment servent à indiquer quelle zone de la mémoire le processeur doit utiliser.
Ils sont toujours manipulés sur 16 bits, quel que soit le processeur.
Sur les processeurs 386 et plus récents, l’offset peut être codé sur 32 bits, sauf en mode réel où il reste limité à 16 bits.
Il existe six registres de segment :
-
CS (Code Segment) : contient le segment du code en cours d’exécution
-
SS (Stack Segment) : pointe vers le segment de la pile
-
DS (Data Segment) : utilisé pour les données principales
-
ES (Extra Segment) : segment supplémentaire pour les données
-
FS (Extra Segment) : disponible à partir du processeur 386
-
GS (Extra Segment) : disponible à partir du processeur 386
Ces registres permettent au processeur de localiser précisément le code, les données et la pile en mémoire.