J'apprends la programmation avec l'assembleur x86, partie 6
Afficher des lettres et des chiffres c'est passionnant 5 minutes, mais ne sentez-vous pas le besoin d'exprimer votre pleine créativité et votre joie de vivre en affichant un ... écran vert !
Et oui, cette splendide réalisation sera le premier pas pour dompter le sauvage animal qui se terre qu sein de votre PC : la carte graphique !
On vit des hauts
La carte graphique de nos PC peut afficher de moultes manières de beaux dessins, avec une définition plus ou moins grande et un nombre varié de couleur. Comme on s'intéresse ici à l'assembleur pour une émulation MS-DOS, on va se contenter de travailler dans le mode video VGA, et plus spécifiquement le mode 0x13 :
- définition de 320 x 200 pixels (chaque pixel étant un point de l'affichage)
- le nombre incroyable de 256 couleurs parmis un choix de 16,77 millions !
Pour accéder à ce mode vidéo, il va falloir utiliser une interruption du BIOS, l'interruption 0x10 qui permet, entre autre, de lancer ou de quitter le mode graphique. (http://www.ctyme.com/intr/int.htm)
Pour changer le mode vidéo, on utilise le service 0x00 de l'interruption 0x10
mov ah, 0x00 ; le service 0x00 de l'int 0x10 change le mode vidéo
mov al, 0x13 ; pour celui de al
int 0x10 ; ici al = 0x13 soit VGA 320x200x256 couleurs
Mais une fois qu'on a changé le mode video de notre PC comment restaurer le barbant mode texte précédent ? On va ainsi procéder à une sauvegarde du mode vidéo en cours avec le service 0x0f de l'interruption 0x10 qui nous renvoie la valeur du mode vidéo dans le registre al.
Pour lire le mode vidéo en cours :
mov ah, 0x0f ; le service 0x0f de l'int 0x10 récupère le mode vidéo en cours
int 0x10 ; dans al
Pour le sauvegarder dans un variable old_mode déclarée en temps que byte (db) :
mov [old_mode], al ; on conserve cette valeur dans old_mode
Avec beaucoup d'adresse
Une fois notre mode graphique changé, comment on accède à nos mignons petits pixels ?
Et bien, avec le mode VGA 0x13, c'est très simple : chaque pixel correspond à un octet de la mémoire vidéo et cette mémoire vidéo commence au segment 0xa000 de notre PC. Donc notre premier pixel, en haut à gauche de l'écran, est à 0xa000:0000, le second à 0xa000:0001 et ainsi de suite...
Un pixel correspond à un octet, soit une valeur de 0 à 255, et contient l'index de couleur de la palette de 256 couleurs disponible dans le mode VGA 0x13.
Par exemple, la valeur 0 correspond à la couleur noire, soit dans l'arithmètique des couleurs informatique :
- une valeur de 0 pour le rouge,
- de 0 pour le vert,
- de 0 pour le bleu.
On verra dans une partie ultérieure de notre extraordinaire tutorial comment on peut choisir et modifier les couleurs de la palette ; on se contente ici de la palette définie par défaut pour le mode VGA.
Dessin, mais des gros
Pour afficher notre magnifique couleur vert kaki uniforme, on va remplir l'espace mémoire correspondant à notre mode 0x13 avec la valeur 240, une des couleurs de la palette.
Notre mode vidéo fait 320 x 200 pixels soit 64000 pixels, nous allons donc stocker avec une boucle notre valeur à partir de l'adresse 0xa000:0000 :
mov ax, 0xa000
mov es, ax
xor di, di ; es:di = 0xa000:0000 -> début de la mémoire VGA
mov cx, 64000 ; 320 x 200 = 64000 répétitions
mov al, 0xf0 ; couleur 240 sur 256, un vert kaki
rep stosb ; on remplie l'écran avec les 64000 pixels
Christian, j'ai une touche !
Pour que l'on puisse admirer ce vert kaki sublime, il faut qu'on puisse faire une pause entre le changement de mode vidéo et sa restauration. Pour cela, on va attendre que l'utilisateur (en l'occurence vous, parce que ce serait tout de même étonnant que d'autres personnes exécute ce programme...) appuie avec conviction sur une touche du clavier.
C'est ainsi que l'on définit la petite procédure wait_key qui fait appel à l'interruption 0x16 et son service 0x01 :
wait_key:
mov ah, 0x01 ; le service 0x01 de l'int 0x16
int 0x16 ; vérifie si une touche a été appuyée
jz wait_key ; si ZF est activé, aucune touche n'a été appuyée
ret
wait_key boucle sur lui-même tant que le flag ZF est activé ; un appui, même très discret, désactivera ce flag et hop, on quittera cette pourtant confortable répetition.
Le code source de green.asm
Remarque à caractère informatif : pour que le code soit plus lisible, nous avons déclarer des constantes - c'est un peu comme des variables qui ne peuvent pas être modifiée - pour l'adresse de la mémoire vidéo et ses dimensions.
; =====
; green
; =====
;
; C:\nasm -fbin green.asm -o green
org 0x100
global main
VGA_MEMORY_SEGMENT equ 0xa000 ; segment mémoire de la carte VGA
VGA_WIDTH equ 320 ; nombre de pixels horizontaux en VGA
VGA_HEIGHT equ 200 ; nombre de pixels verticaux en VGA
section .text
main:
call enter_vga ; on entre dans le mode VGA
call draw_something ; on dessine un truc
call wait_key ; on attend une touche au clavier
call quit_vga ; on quitte le mode VGA
mov ax, 0x4c00 ; on quitte sans erreur
int 0x21
enter_vga:
mov ah, 0x0f ; le service 0x0f de l'int 0x10 récupère le mode vidéo en cours
int 0x10 ; dans al
mov [old_mode], al ; on conserve cette valeur dans old_mode
mov ah, 0x00 ; le service 0x00 de l'int 0x10 change le mode vidéo
mov al, 0x13 ; pour celui de al
int 0x10 ; ici al = 0x13 soit VGA 320x200x256 couleurs
ret
quit_vga:
mov ah, 0x00 ; on restaure le mode vidéo
mov al, [old_mode] ; conservé dans old_mode
int 0x10
ret
wait_key:
mov ah, 0x01 ; le service 0x01 de l'int 0x16
int 0x16 ; vérifie si une touche a été appuyée
jz wait_key ; si ZF est activé, aucune touche n'a été appuyée
ret
draw_something:
mov ax, VGA_MEMORY_SEGMENT
mov es, ax
xor di, di ; es:di = 0xa000:0000 -> début de la mémoire VGA
mov cx, VGA_WIDTH * VGA_HEIGHT ; 320 x 200 = 64000 répétitions
mov al, 0xf0 ; couleur 240 sur 256, un vert kaki
rep stosb ; on remplie l'écran avec les 64000 pixels
ret
section .data
old_mode: db 0 ; variable pour conserver le mode vidéo précédent
Pour m'entrainer
En modifiant uniquement la fonction draw_something essayez d'obtenir l'écran suivant :
On affiche des pixels dont la couleur de départ 0 est incrémentée jusqu'à 255, puis retourne à 0, et ainsi de suite.
(si un registre de 8bits valant 255 est incrémenté, il passe de nouveau à 0)
Solution
Et après...
Dans notre partie 7 on va un peu jouer avec notre palette de couleur !