Le mode 13h

 

Avant toute chose, je tiens à signaler ceci :

J'ai découvert la programmation graphique il y a seulement quelques mois. Cette rubrique n'est donc pas un magnifique cours sur la programmation graphique, mais plutôt le fruit de mes recherches personnelles (waaah...). Inutile donc d'espérer apprendre l'élaboration d'objets 3D avec modulation de texture, filtrage trilinéaire et Z-buffering le tout utilisant les derniers drivers OpenGL pour 3DFX Voodoo3 !!! Je m'arrêterai donc là où s'arrêtent mes connaissances (c à d pas très loin...)

A) Introduction

Le mode 13h est le mode graphique qui présente le meilleur rapport performances / simplicité. Sa résolution de 320 x 200 en 256 couleurs, sa mémoire linéaire,  sa vitesse appréciable et sa simplicité en font un mode graphique intéressant pour débuter la programmation graphique.

B) Détecter une carte VGA

Je crois que le mode 13h est disponible sur toutes les cartes video VGA. De nos jours toutes les cartes video supportent le VGA (waah... trop cool !!) mais il est tout de même plus raisonnable de vérifier la présence d'une carte VGA avant d'initialiser un mode graphique. Cela se fait grâce à la fonction 1Ah de l'interruption 10h (entrées/sorties video). Cette fonction nécessite la valeur 1Ah dans AH et 0 dans AL (1A00h dans AX quoi !). Elle renvoie 1Ah dans AL si une carte supportant le VGA est présente.

Ce qui donne en ASM :

mov  AX, 1A00h
int    10h
cmp  AL, 1Ah
jne    detect_error

ou en C :

int detect_VGA ()
{
  struct REGPACK registres;
  registres.r_ax=0x1A00;
  intr(0x10, &registres);
  if(registres.r_al==0x1A) return 0;
  return 1;
}

C) Initialiser le mode 13h

Une fois la carte détectée, on peut passer à l'initialisation. Pour cela, on utilise la fonction 0 de l'interruption 10h avec la valeur 13h dans AL.

Ce qui donne en ASM (préférable) :

mov  AX, 0013h
int    10h

ou en C :

void init_13h ()
{
  struct REGPACK registres;
  registres.r_ax=0x0013;
  intr(0x10, &registres);
}

D) Fermer le mode 13h

Avant la fin de votre programme, la fermeture du mode graphique est indispensable. Cette opération est similaire à l'initialisation, sauf qu'au lieu de spécifier 13h dans AL, on place 03 dans AL car 03 est le mode texte standard.

E) Afficher un point à l'écran

Avant toute chose, il est indispensable de connaître la disposition de la mémoire video en mode 13h. Elle débute à l'adresse A000:0000 et prend fin en A000:FA00. Un bref calcul mental vous indique qu'elle s'étend donc sur 64000 bytes. Chaque pixel de l'écran est sauvegardé en mémoire video dans 1 byte (256 couleurs). Avec une résolution de 320x200, il est donc logique d'avoir une mémoire de 64000 bytes !! Cette mémoire est dite linéaire, c à d : si on la représente comme un vecteur (ce qu'elle est !!), toutes les lignes de l'écran sont disposées séquentiellement, en partant de la première et en finissant par la 200eme. Mais alors, tout s'éclaircit !! Le calcul de l'offset d'un pixel dont les coordonnées sont X,Y se fait via la formule :

offset = 320 * Y + X

(Quoi !! Encore une formule à étudier !! Moi qui pensait pouvoir m'amuser...) Entre-nous, elle n'est pas mortelle...

Bref, pour afficher un point à l'écran en C (exemple : point rouge en X=10, Y=50)

unsigned offset;

offset=320*Y+X;
pokeb(0xA000,offset,4);  
/* couleur du rouge = 4 */

Remarque : La fonction pokeb place un byte à une adresse logique précise. Sa syntaxe est pokeb(unsigned segment, unsigned offset, char byte);
Le pokeb correspond donc à l'instruction MOV de l'Assembleur.

Encore une remarque : Vous avez compris que l'utilisation d'un entier non signé est ici préférable. En effet, un entier signé (int) n'utilise que 15 bits, à cause du bit signe, ce qui implique une limite maximum de 32767. C'est un peu juste pour placer des nombres qui peuvent atteindre 64000... Moralité : utilisez votre bit (signe) intelligemment...

L'affichage du même point en ASM donne :

;------- initialisation du segment de mémoire video dans ES
mov   ax, 0A000h
mov   es, ax
;------- calcul de l'offset
mov   ax, Y
mov   bx, ax
mov   cl, 8
shl     ax, cl
mov   cl, 6
shl     bx, cl
add    bx, ax
add    bx, X
;------- placement du byte en mémoire
mov   es:[bx], byte ptr 4

Cet algorithme nécessite tout de même une petite explication. Pour ne pas utiliser une multiplication (mul) TOUJOURS TROP gourmande en ressources (plus de 100 cycles machines !), j'ai préféré utiliser 2 shifts (décalages de bits). Un shift de 8 bits vers la gauche correspond à une multiplication par 2^8 soit 256. L'addition avec un deuxième shift de 6 bits vers la gauche (2^6 = 64) permet d'obtenir une multiplication par 320, pour un coût avoisinant les 75 cycles machines.

F) Un petit exercice

Il est désormais temps de mettre vos connaissances en pratique. Attardons-nous à la mise au point d'un petit programme en C qui afficherait un objet se déplaçant le long de l'écran. Ma version est disponible ici. L'objet en question serait déclaré en mémoire sous forme d'une bit-map (zone de points) de la manière suivante :

char alien[9][14]={{0,0,0,0,0,4,4,4,4,0,0,0,0,0},
        {0,0,4,4,4,4,4,4,4,4,4,4,0,0},
        {0,4,4,4,4,4,4,4,4,4,4,4,4,0},
        {0,4,4,7,7,4,4,4,4,7,7,4,4,0},
        {0,4,4,7,0,7,4,4,7,0,7,4,4,0},
        {0,4,4,4,4,4,4,4,4,4,4,4,4,0},
        {0,0,0,4,4,4,0,0,4,4,4,0,0,0},
        {0,0,4,0,0,0,4,4,0,0,0,4,0,0},
        {0,4,0,0,0,0,0,0,0,0,0,0,4,0}};

concrètement, ça ressemble à ça :

Gniark Gniark !!

 

<EOK> *

* (End Of Knowledges)

 

Si vous éprouvez une difficulté quelconque, si vous avez repéré une erreur dans un programme, si vous avez une idée à proposer ou une suggestion à faire, n'hésitez pas à me contacter !!! (ici : <SpiX>)

 

© Copyright Benjamin Fonzé 1999/2000