codefordumm!ies: the cool logo

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 :

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 :

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.