II. DEUXIEME EXEMPLE : LES FICHIERS EXE
Le programme suivant est un programme EXE qui fait exactement la même
chose que l’exemple précédent.
Examinons-le !
Tout d’abord, la directive
org 100h
a disparu.
En effet, lors de l’exécution, CS pointera vers notre
segment de code et non pas vers le PSP. Il est donc inutile de
décaler les offsets de 256.
Remarquons que notre programme dispose de deux segments
supplémentaires :
-
Le segment “data” est destiné à
contenir les données, c’est-à-dire les
variables.
-
Le segment “pile” sera notre segment de pile.
Notre directive “assume” devient donc :
assume cs:code, ds:data, ss:pile
Ainsi, le compilateur est informé que DS pointera vers le segment
“data” et SS vers “pile”.
Les deux lignes suivantes,
mov ax, data
mov ds, ax
servent à initialiser le registre DS. Celui-ci pointe vers le PSP au
début du programme mais nous voulons le faire pointer vers notre
segment de données appelé “data”.
Cela est nécessaire puisque la fonction 9 de l’interruption
21h attend l’adresse de la chaîne dans le couple DS:DX et
que notre message se trouve dans le segment de données.
La première instruction charge l’adresse du segment “data”
dans AX. La seconde transfère cette valeur de AX dans DS.
Mais pourquoi diable utiliser AX comme intermédiaire ? Après
tout, on pourrait écrire :
mov ds, data
Eh bien non ! Pour la simple raison que DS
est un registre de segment et qu’en tant que tel on ne peut pas
lui charger de valeur immédiate.
On appelle « valeur immédiate »
toute constante tapée directement dans l’instruction
elle-même.
Exemples de chargement de valeurs immédiates :
mov ax, 135 ;charge 135 dans AX
mov bx, offset message ;charge
l’offset de message dans BX
mov bx, offset fin – offset debut ;charge
le nombre d’octets entre fin et debut dans BX
mov es, 10 ;instruction
illicite car ES est un registre de segment !
Remarque :
une autre possibilité aurait été d'écrire
: “PUSH AX” (empiler AX) puis “POP DS”
(dépiler le dernier nombre empilé et le placer dans
DS).
Les trois lignes suivantes :
mov ah, 09h
mov dx, offset message
int 21h
n’ont pas changé.
Il nous faut également terminer le programme par un appel de la
fonction 4ch de l’interruption 21h. C’est ainsi que se
terminent les programmes EXE.
mov ah, 4ch
int 21h
Remarque :
on aurait également pu écrire :
mov ax, 4c00h
int 21h
La seule différence est que AL est mis à zéro, ce qui
indique au programme à qui on rend le contrôle (ici le
DOS) que notre programme s’est terminé normalement. Les
fichiers COM peuvent également utiliser la fonction 4ch.
Le segment de code se termine :
code ends
et le segment de données commence à sa suite :
data segment use16
message db “Bonjour, monde !”, ‘$’
data ends
Il nous reste à écrire le segment de pile. Dans ce
programme, il n’était pas absolument indispensable de le
séparer du segment de code. Mais c’est une bonne
habitude de le faire.
pile segment stack
remplissage db 256 DUP (?)
pile ends
Le mot-clé “stack” indique que ce segment est le
segment de pile.
Les mots “db 256 DUP (?)” déclarent 256 octets
non initialisés. C’est la « matière »
de notre pile.
Sachez que tout appel d’interruption se traduit par l’empilage
des flags et de CS:IP. Il est donc indispensable d’avoir une
pile, même si celle-ci peut éventuellement partager le
même segment que le code, comme dans un fichier COM. Mais dans
un programme EXE, il vaut mieux réserver un segment à
la pile. Les 256 octets que nous déclarons ici indiquent
seulement que la pile contient 256 octets. Ainsi, au début de
l’exécution, SS:SP pointera vers la fin de ces octets.
Nous verrons ce que signifie le point d’interrogation dans la
troisième partie.
La fin du fichier et le point d’entrée sont signalés
par :
end debut
Et voilà ! Vous avez découvert l’allure d’un
programme en assembleur. Nous pouvons donc à présent
nous pencher sur l’étude du langage.
Sommaire
Suite
|