codefordumm!ies: the cool logo

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 !

notre écran vert

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 :

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 :

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 :

plein de couleurs

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 !