J'apprends la programmation avec l'assembleur x86, partie 4
Dans la partie 3 nous avons écrit l'alphabet caractère par caractère en appelant 26 fois l'interruption 0x21.
Ne serait-il pas plus efficace de stocker notre alphabet en mémoire pour pouvoir l'afficher en une seule interruption ?
Bin si, bien sûr, et c'est ce que nous allons voir tout de suite.
Mes vêtements, je les mets moires...
Pour écrire un caractère en mémoire, nous allons utiliser l'instruction stosb qui va placer la valeur de al à l'adresse es:di.
Mais à quoi correspond l'adresse es:di ?
En assembleur x86, la mémoire est divisée en segment et en offset.
Le couple segment:offset correspond à l'adresse d'un octet en mémoire.
Chaque segment de mémoire peut contenir 65768 offsets, la valeur maximale que peut contenir un mot.
Nous avions déjà vu précédemment l'octet qui est une valeur de 8 bits qui peut contenir des chiffres de 0 à 255.
Le mot est en fait un double octet, une valeur de 16 bits, qui va de 0 à 65767.
Pour plus de détails, nous vous invitons à lire cette petite explication sur les bits qui n'a rien d'obscène.
Le format de programme exécutable .COM que nous avons choisi pour débuter ce tuto de programmation, est limité, pour ses instructions et ses données à un seul segment, soit 65768 octets.
Ainsi, l'adresse des données du programme (section .data) partage le même segment que celle du code, à savoir les instructions (section .text).
Monsieur Seg ment, Madame Off sait
es et di sont deux registres qui correspondent respectivement à un segment et à un offset.
Au démarrage du programme les valeurs de es et di sont inconnues.
Pour que es est la valeur de notre segment de code (et donc, aussi de données), il va prendre la valeur du registre cs, qui correspond, ça tombe bien, au segment de code (code sgment comme disent les grands bretons).
Mais, petit caprice du processeur, on ne peut pas écrire :
mov es, cs ; oh non ! ne fais pas ça !
car on il est impossible d'affecter un registre de segment à un autre.
Il faut passer par un intermédiaire :
mov ax, cs ; notez que l'on prend un registre sur 16 bits
mov es, ax
Ou, plus élégant et qui est plus rapide à l'exécution :
push cs ; et hop !
pop es ; ! poh te la même chose à l'envers...
Les instructions push et pop servent à placer et retirer des valeurs sur un espèce de tas qui s'appelle la pile.
Cette pile sera nettement plus exploitée dans nos futurs tutos, et son fonctionnement bien plus détaillé.
Pour l'instant, il suffit de retenir que je pose une valeur d'un registre au sommet de la pile (push), et je récupere cette valeur dans un autre registre (pop).
Pour mettre l'offset de nos données dans di, c'est beaucoup plus facile :
mov di, alphabet ; alphabet est l'endroit où on veut stocker... l'alphabet
Réserve voir, Doug !
Quand on va exécuter l'instruction stosb, le processeur va faire deux choses :
- placer à l'adresse es:di la valeur contenu dans al
- incrémenter di de 1 pour passer à l'offset suivant
Ainsi, si on répète 26 fois l'instruction stosb comme nous le voulons pour l'alphabet, 26 valeurs seront placées en mémoire, les unes à la suite de l'autre. Il faut s'assurer que nous aurons la place pour stocker ces valeurs. Et cette place nous pouvons la réserver dans la section .data :
section .data
alphabet: times 26 db 0 ; on réserve 26 octets pour stocker l'alphabet
fin db '$' ; suivi de l'indispensable '$' de fin de chaîne
En détaillant :
- alphabet : le label de nos données
- times 26 : on réserve 26 valeurs...
- db : ... de type octet (dw pour word)...
- 0 : ... qui valent 0 pour l'instant
Le goût des stocks
Pour effectuer notre boucle d'écriture, nous n'utiliserons pas l'instruction loop. Comme on a besoin que al qui passe de 'A' à 'Z', nous allons répéter notre boucle tant que al est différent ou égal à 'Z' :
On utilise l'instruction cmp soit compare :
cmp al, 'Z'
qui va signaler au processeur le résultat de la comparaison. Il met à 1 ou à 0 un des drapeaux (flag) du processeur.
Pour exploiter ce flag, on utilisera l'instruction
jbe stocke
qui veut dire :
rejoint le label stocke si le résultat de la comparaison est plus petit ou égal
On ne sautera/rejoindra plus stocke dès que al sera supérieur à 'Z'.
Le code source de memory.asm
; ======
; memory
; ======
;
; C:\nasm -fbin memory.asm -o memory.com
org 0x100
global main
section .text
main:
push cs ; cs est le segment mémoire du programme
pop es ; es a la valeur de cs
mov di, alphabet ; di est l'offset de l'alphabet
mov al, 'A' ; on commence à la valeur 'A'
stocke:
stosb ; on stocke al à l'adresse es:di et di est incrémenté
inc al ; al est incrémenté
cmp al, 'Z' ; on compare al à 'Z'
jbe stocke ; si al est plus petit (below) ou egale (equal) on rejoint stocke
mov ah, 0x09 ; on affiche notre alphabet
mov dx, alphabet
int 0x21
mov ax, 0x4c00
int 0x21
section .data
alphabet: times 26 db 0 ; on réserve 26 octets pour stocker l'alphabet
fin db '$' ; suivi de l'indispensable '$' de fin de chaîne
On compile :
C:\nasm -fbin memory.asm -o memory.com
On exécute :
C:\memory
Et voici le résultat :
ABCDEFGHIJKLMNOPQRSTUVWXYZ
C:\_
Pour m'entrainer
Modifier le programme pour afficher les nombres de 0 à 9... en sens inverse ! (jge correspond à saute si plus grand ou égal)
Solution
Et après...
Dans la partie 5 on parlera fonction et pile.