programmation système
3. Structure d’un programme en Assembleur 8
Un programme en Assembleur se compose en général de 3 parties définissant les différents segments :
1. le segment de code (pointé par CS), 2. le segment de données (pointé par DS), 3. le segment de pile (pointé par SS).
3.1. Déclaration des segments
La déclaration d’un segment se fait selon la syntaxe suivante : Nom_Segment SEGMENT PARA ;Début du segment
<<*** Instructions ou données appartenant au segment ***>> Nom_Segment ENDS ;Fin du segment
NB. : Tout ce qui se trouve après le point-virgule (;) est considéré comme commentaire Exemple : Déclaration d’un segment de code
MonCode SEGMENT PARA
MOV AX,1200h
ADD AX,[5E00h]
MOV AX,4C00h
INT 21h
MonCode ENDS
3.2. Déclaration des données et de la pile
Le segment de données est réservé aux données que le programme utilise pendant son exécution. Ces données sont représentées sous forme d’octets, de mots, de tableaux ou de chaînes de caractères et peuvent être traîtés comme des constantes ou des variables. Chaque donnée est stockée en mémoire sous forme hexadécimale.
Les variables peuvent recevoir une valeur initiale attribuée par le programme comme elles peuvent rester sans initialisation et c’est pendant l’exécution qu’elles reçoivent des valeurs.
La syntaxe de définition des données au sein du segment de données dépend de leur nature : 3.2.1. Définition d’un octet
Nombre DB 18h
Label Valeur initiale
Les valeurs initiales peuvent être exprimées en décimal, en hexadécimal (h) ou en binaire (b). Le compilateur se charge de faire la conversion. Ainsi les déclarations suivantes sont identiques :
Décimal Hexadécimal Binaire Nombre DB 24 Nombre DB 18h Nombre DB 00011000b
Pour définir un octet sans lui affecter de valeur initiale, on utilise le symbole ? Nombre DB ?
3.2.2. Définition d’un mot (16 bits) Prod DW 5C0Fh Label Valeur initiale 3.2.3. Définition d’un tableau d’octets
Tab DB 15h,5Ah,21h,7Ch,82h
Dans cet exemple, si le label Tab désigne l’adresse 45F0h, La case mémoire 45F0h contient 15h, la case 45F1h la valeur 5Ah, ... etc
Pour définir un tableau de 10 octets sans initialisation, on écrit : Tab DB 10 DUP (?)
Pour définir un tableau de 7 octets contenant des valeurs identiques (2Ah par exemple), on écrit : Tab1 DB 7 DUP (2Ah)
Cette définition est parfaitement identique à la définition suivante : Tab1 DB 2Ah, 2Ah, 2Ah, 2Ah, 2Ah, 2Ah, 2Ah 3.2.4. Définition d’un tableau de mots
TabM DW 15EEh, 5A00h, 12FFh, 8B00h
Si l’adresse pointée par le label TabM est 1200h, le contenu de la mémoire sera:
1200h 1201h 1202h 1203h 1204h 1205h 1206h 1207h EEh 15h 00h 5Ah FFh 12h 00h 8Bh
3.2.5. Définition d’une chaîne de caractères MaChn DB 'Ceci est un exemple' Chn2 DB 'FIN',0Dh,0Ah
Une chaîne de caractères est stockée sous forme d’octets représentant le code ASCII de chaque caractère. La chaîne est écrite entre guillemets et c’est le compilateur qui se charge de convertir chaque caractère en code ASCII.
On peut définir des chaînes de caractères contenant des caractères spéciaux (caractères non imprimables mais qui ont une signification particulière pour l’ordinateur). Ces caractères sont représentés par leur code ASCII dans la définition de la chaîne. Ainsi dans le deuxième exemple, la chaîne de caractère "FIN" est suivie de deux caractères spéciaux de code ASCII : 0Dh (retour chariot) et 0Ah (retour à la ligne).
Si le label Chn2 désigne l’adresse 8000h, le contenu de la mémoire sera le suivant : 8000h 8001h 8002h 8003h 8004h
46h 49h 4Eh 0Dh 0Ah "F" "I" "N"
L’affichage de cette chaîne sur l’écran provoquera un retour à ligne après l’apparition du mot "FIN".
3.2.6. Déclaration du segment de données
La syntaxe est identique à celle du segment du code. Dans un segment de données on regroupe toutes les données utilisées par le programme.
Les variables déclarées sont placées l'une après l'autre dans la mémoire. Chacune prend l'espace mémoire nécessaire.
Exemple
Donnees SEGMENT PARA ; Début du segment
Var1 DB 12h ; 1 octet
Mot1 DW 15E4h ; 2 octets (mot de 16 bits) Table DB 5 DUP(1Ch) ; 5 octets (taille du tableau) Chaine DB 'Test',0 ; 5 octets (longueur de la chaîne) Donnees ENDS ; Fin du segment
Si le premier octet du segment de données se trouve à l'offset 0000, le contenu de la mémoire sera:
0000h 0001h 0002h 0003h 0004h 0005h 0006h 0007h 12h E4h 15h 1Ch 1Ch 1Ch 1Ch 1Ch
Var1 Mot1 Table
0008h 0009h 000Ah 000Bh 000Ch 54h 65h 73h 74h 00h
Chaine
Les labels qui identifient chaque variable reçoivent l'offset de celle-ci dans le segment de données : Label Offset attribué
Var1 0000h Mot1 0001h Table 0003h Chaine 0008h
3.2.7. Utilisation des données dans le programme
Comme on vient de voir, chaque donnée est repérée par un label. Celui-ci reçoit l'adresse logique (sur 16 bits) de la donnée dans la mémoire.
Pour faire référence à une donnée définie dans le segment de données, on utilise tout simplement son label.
Exemple 1
Soit la variable Var1 de type octet définie précédemment dans le segment de données (voir ci- dessus):
Var1 DB 12h
Pour charger le contenu de la variable Var1 dans le registre AL, on écrit : MOV AL,Var1
Pour écraser le contenu de Var1 et la charger par la valeur 45h, on écrit : MOV Var1,45h
Remarque
Var1 représente l'adresse logique de la variable, en réalité, Var1 désigne l'adresse logique 0000h. Au moment de la compilation, le compilateur remplace l'instruction MOV AL,Var1 par MOV AL,[0000h].
Exemple 2
Soient deux tables de 5 valeurs dont le début est désigné par les labels XVect et YVect. La deuxième table n'est pas initialisé.
XVect DB 12h,3Ah,15h,4C,2Dh YVect DB 5 DUP (?)
Le programme suivant permet de copier dans la deuxième table le contenu de la première en utilisant SI et DI comme registres d'index :
LEA SI,XVect ;Charger SI par l'adresse désignée par XVect LEA DI,YVect ;Charger DI par l'adresse désignée par YVect
MOV AH,5
suite: MOV AL,[SI] MOV [DI], AL INC SI INC DI DEC AH CMP AH,0 JNE suite
L'instruction LEA (Load Effective Adress) permet de charger dans un registre l'adresse désignée par un label.
Exemple
Si XVect désigne l'adresse 2B07h :
• LEA BX,XVect aura pour résultat BX=2B07h,
• MOV BX,XVect aura pour résultat BX=3A12h (premières valeurs de la table XVect). Remarque
LEA BX,XVect est équivalente à MOV BX,offset XVect (charger BX par l'offset de XVect) 3.2.8. Déclaration du segment de pile
La pile est une zone mémoire qui doit être réservée pour le programme (gestion des appel aux sous-programmes et aux interruptions, ...), et pour d'autres usages généraux (sauvegarde de données temporaires, passage de paramètres à une procédure, ...)
Dans le plupart des cas, la déclaration d'un segment de pile est obligatoire surtout en présence de sous-programmes.
La taille de la pile varie selon les applications. Un programme récursif aura besoin d'une pile de taille beaucoup plus importante qu'un programme ordinaire.
La réservation d'une zone mémoire pour le segment de pile se fait de la manière suivante : MaPile SEGMENT PARA STACK
DB 256 DUP (?) MaPile ENDS
Ici, la taille de la pile est de 256 octets. Ce qui est suffisant pour des petits programmes en assembleur.
3.3. Exemple de programme
Pile SEGMENT PARA STACK ; Définition du segment de pile DB 256 DUP ('P') ; Pile de 256 octets remplie par
Pile ENDS ; le caractère 'P'
Donnees SEGMENT PARA ; Définition du segment de données Table DB 11 DUP (00) ; Table de 11 octets initialisée par 0 Donnees ENDS
Code SEGMENT PARA ; Définition du segment de code ASSUME CS : Code ; Affectation des registres ASSUME DS : Donnees ; de segments
ASSUME SS : Pile
Debut: MOV AX,Donnees ; Initialisation du registre DS
MOV DS,AX
LEA BX,Table ; BX reçoit l'adresse logique de Table
XOR AL,AL ; AL = 0
Boucle: MOV Byte Ptr [BX],AL ; Byte Ptr [BX] veut dire octet ...
INC BX ; ... pointé par BX
INC AL
CMP AL,0Bh ; Teste la fin de la boucle
JNE Boucle ; AL ≠ 0Bh ⇒ aller à Boucle
MOV AH, 4Ch ; Appel à la fonction 4C de
INT 21h ; l'interruption 21h(Retour au DOS) Code ENDS
END Debut ; END indique la fin du programme ; Debut désigne le label de la ; première instruction du programme Remarque
Les registres CS et SS sont automatiquement affectés aux segments Code et Pile respectivement. Par contre, le registre DS doit être initialisé dans le programme
3.4. Appel aux fonctions du DOS et du BIOS
L'appel des fonctions du DOS et du BIOS se fait par le biais des interruptions logicielles. Avant d'appeler l'interruption, on charge les éventuels paramètres dans les registres du microprocesseur. Certaines fonctions renvoient des résultats après l'exécution de l'interuption. Ceux-ci sont en général stockés dans les registres du microprocesseur.
Comme le nombre des fonctions disponibles est très grand, il n'est pas possible de donner des explications détaillées dans le cadre de ce document (voir à ce sujet les ouvrages spécialisés).
Exemple : Affichage d'un caractère sur l'écran Interruption : 21h
Paramètres :
• AH = 02h (fonction n°2)
• DL = Code ASCII du caractère à afficher
Le programme suivant permet d'afficher le caractère "A" sur l'écran : MOV DL,41h ;Code ASCII du caractère "A" MOV AH,2 ;Fonction n°2
INT 21h ;Appel de l'interruption 21h