push 0
call exit # Sortie du programme principal (arrêt)
Les variables
En ASM, les variables sont déclarées dans la zone indiquée par la directives .data. La
syntaxe de déclaration est :
variable .type : valeur initiale
Le nom d’une variable commence toujours par une lettre.
Les différents types utilisés sont :
byte : entiers sur un octet (8 bits)
word : entiers sur un mot (16 bits)
int : entiers sur un double-mot (32 bits)
ascii : chaînes de caractère
asciz : chaînes de caractère terminées par l’octet 0
space <rep>,<val> : répète <rep> fois la valeur <val> codée sur 1 octet
fill <rep>,<taille>,<val> : répète <rep> fois la valeur <val> codée sur <taille> octets
Pour utiliser la variable n, il faut écrire tout simplement n. Mais pour utiliser l’adresse de
n, on peut écrire offset n ou [n].
Les commentaires
Un commentaire commence par le caractère ‘#’ suivi du texte.
Les étiquettes
Les étiquettes sont les noms que l’on donne à une instruction permettant de faire un saut à
cette instruction à partir de n’importe quel endroit du programme. Dans un programme, une
étiquette est utilisée pour nommer une seule instruction. La syntaxe de déclaration est :
nom_étiquette :
Les registres
Ce sont des zones internes au processeur permettant de stocker de manière temporaire les
données. Le programmeur peut utiliser les registres EAX, EBX, ECX, EDX, EDI, ESI, ESP et
EBP dans un programme.
Les registres EAX, EBX, ECX et EDX peuvent être utilisés par le programmeur. Il peut y
affecter n’importe quelle donnée. La taille de ces registres est de 4 octets. La partie basse de
ces registres peut être utilisé pour les données de 16 bits. Pour le faire, il faut simplement
supprimer la lettre E. Le registre de 16 bits ainsi obtenu peut être aussi divisé en deux parties
et chaque partie utilisée pour les données de 8 bits. Par AX=AH+AL.
Les registres EDI et ESI peuvent aussi être utilisés pour stocker n’importe quelle donnée,
mais le programmeur n’a pas le droit de les diviser.
Les registres ESP et EBP sont utilisés pour manipuler la pile. ESP désigne toujours le sommet
de la pile. EBP est utilisé dans des sous programmes pour parcourir la pile.
La pile
C’est la partie de la mémoire de l’ordinateur, permettant de :
sauvegarder temporairement des informations,
passer des paramètres aux sous-programmes,
créer des variables locales,
sauvegarder le contexte lors de l’appel aux sous-programmes,
La pile est manipulée avec les instructions PUSH (empiler) et POP (dépiler). On la manipule
aussi avec les registres ESP et EBP.
Les instructions
La plupart des instructions assembleur (aussi appelées mnémoniques) ont 0, 1 ou 2
arguments (opérandes).
Ces opérandes sont soit un label (pour les instructions de saut uniquement), soit un
registre, soit un accès à la mémoire (suivant les différents modes d’adressage).
En général pour une même instruction, il ne peut y avoir plus d’un accès à la mémoire.
En général les opérandes doivent être de même taille (1, 2 ou 4 octets).
.intel_syntax noprefix
.data
a : .int
b : .int 10
c : .int 5
.global main
main:
mov eax, b
add eax, c
mov a, eax
push 0
call exit
Forme 2 (avec sinon) : ‘si a<b alors inst1 sinon inst2 finsi’ se traduit par
Cmp a,b
Jge sinon
Inst1
Jmp finsi
Sinon :
inst2
finsi
cmp x,z;
jg sinon
cmp y,z
jg sinon
inst 1
jmp finsi
sinon:
inst 2
finsi:
cmp x,z;
jle alors
cmp y,z;
jg sinon
alors:
inst 1
jmp finsi;
sinon :
inst 2
finsi:
cmp lettre,’a’;
je cas_a;
cmp lettre,’b’;
je cas_b;
cmp lettre,’c’;
je cas_c;
jmp default;
cas_a:
mov code,2;
jmp fin_cas;
cas_b:
mov code,5;
jmp fin_cas;
cas_c:
mov code,3;
jmp fin_cas;
default:
mov code,0;
fin_cas:
Instructions de boucle (for, while, . . . )
Mov ecx,1
Pour : cmp ecx, valeur_finale
jg finpour
inst
inc ecx
jmp pour
Finpour :
mov ecx, i
tantque:
cmp ecx,i
jge fintantque
inst
jmp tantque
fintantque:
Repeter :
Inst
Cmp a,b
Jle repeter
Jusqua :
Les procédures
Le code d’une procédure commence par une étiquette et s’achève par l’instruction ret. Ce
code peut être écrit avant ou après le programme principal main. L’appel de la procédure se
fait par l’instruction call procedure. L’instruction call empile la valeur du registre EIP
(compteur ordinal)
Variables locales
Contrairement au langage du haut niveau, pour créer les variables dans une procédure, on
utilise :
1. Les registres
2. La pile : pour créer une variable, il faut simplement augmenter le registre ESP de la
taille de la variable. Pour une variable de type int, il faut faire ADD ESP, 4. Avec la
pile, il faut toujours supprimer ces variables à la fin de la procédure en diminuant ESP
de la taille (en octets) des variables créées. L’accès aux variables créées se fait avec le
registre EBP.
Le corps d’une procédure aura toujours la forme :
Procedure:
Push ebp
Mov ebp, esp
Corps de la procedure
Pop ebp
Ret.
Les macros
Les macros ne sont pas réellement des sous-programmes : ils permettent de simplifier
l’écriture d’un programme et disparaissent après la compilation. Le contenu d’une macro est
recopié à l’endroit ou on l’appelle. La macro peut être placée n’importe où dans le code, mais
doit précéder l’endroit où on l’appelle. Le code de la macro disparaît après la compilation
Une macro possède un nom, et accepte des arguments. Forme générale d’une macro :
.macro nom_macro arg1,arg2,...,argn
.........
Instructions
.........
.endm
Exemple : lire une chaîne de caractères au clavier (stdin) d’au plus 100 caractères.
.data
buffer: .space 100,0
nbcar: .int 0
main:
mov eax,3 # fonction READ
mov ebx,0 # 0=stdin
mov ecx,offset buffer
mov edx,100 #nb max de car.
int 0x80
mov nbcar,eax
La fonction write()
Cette fonction écrit un certain nombre de caractères depuis un buffer en mémoire vers un
fichier.
Paramètres :
eax = 4
ebx = descripteur de fichier
ecx = adresse du buffer
edx = nombre de caractères à écrire
Retour :
eax = nombre de caractères écrits
La fonction Close()
Cette fonction ferme l’accès à un fichier.
Paramètres :
eax = 6
ebx = descripteur de fichier
Retour : aucun
En cas d’erreur sur l’ouverture, la création, etc. d’un fichier : en sortie de l’appel à
l’interruption, le registre eax contient non pas le résultat attendu (nombre d’octets lus, etc.)
mais un code d’erreur négatif.
###
### print_int : affiche un entier
###
### arguments sur la pile :
### 1) l'entier à afficher
###
print_int:
push ebp # sauvegarder ebp
mov ebp,esp # sommet de pile dans ebp
pusha # sauvegarder tous les registres
push [ebp+8] # empiler l'argument 1 de print_int
push offset descripteur_int # empiler l'adresse de la chaîne descripteur
call printf # appel de la fonction C printf
add esp,8 # dépiler les deux arguments de printf
popa # récupérer les registres
pop ebp # recupérer ebp
ret # retour du sous-programme
###
### print_string : affiche une chaîne de caractères
###
### arguments sur la pile :
### 1) l'adresse de la chaîne à afficher
###
print_string:
push ebp # sauvegarder ebp
mov ebp,esp # sommet de pile dans ebp
pusha # sauvegarder tous les registres
push [ebp+8] # empiler l'argument 1 de print_string
call printf # appel de la fonction C printf
add esp,4 # dépiler l'argument de printf
popa # récupérer les registres
pop ebp # recupérer ebp
ret # retour du sous-programme
###
### print_string : affiche une chaîne de caractères
###
### arguments sur la pile :
###
print_endl:
push ebp
pusha # sauvegarder tous les registres
push '\n'
call putchar
add esp,4
popa # récupérer les registres
pop ebp
ret