Copy Link
Add to Bookmark
Report

2d Chapitre 1 - Mode VGA 320x200x256

eZine's profile picture
Published in 
2d3dfx
 · 20 Feb 2021

CHAPITRE 1 - Mode VGA 320x200x256

Ce premier chapitre traite des notions de base de la programmation graphique : la carte VGA et sa programmation, comment activer un mode graphique, afficher des pixels à l'écran et le chaînage linéaire en mode 13h. Le tout sera démontrer avec un exemple d'application, un champ d'étoiles défilantes. Il s'agit d'un effet très simple réalisable à partir des fonctions vues dans ce chapitre.

NOTIONS DE BASE

Tout d’abord, comme tous les domaines de la programmation système, il faut comprendre que la programmation graphique est relativement dépendante du compilateur utilisé. On ne parle pas ici des concepts algorithmiques mais bien des façons dont le langage utilisé adresse la mémoire vidéo. Dans notre cas, nous utilisons Borland Turbo C++, un compilateur DOS fonctionnant en mode réel. Ce mode ne permets pas directement l’accès à plus de 64k à la fois, et on ne peut dépasser la fameuse barrière des 640k de mémoire conventionnelle. Cette mémoire est divisée en 10 segments de 64k, et chaque segment possède 65535 offsets. C’est un peu comme une adresse légale : un nom de rue (le segment) et une adresse de porte (l’offset).

Borland nous offre avec leurs compilateur les BGI, pour Borland Graphics Interface. Ces fonctions ne sont pas très optimisées et ne conviennent que très rarement à des projets où la vitesse est impérative. Nous allons donc programmer tout au cours de cette série les diverses fonctions graphiques selon nos besoins. Mais avant tout, il serait utile de connaître la carte VGA (Video Graphic Array).


LA CARTE VGA ET SES COMPOSANTES

Par leurs possibilités d'affichage graphique et texte, les cartes EGA/VGA surclassent largement ses prédécesseurs. Lorsque IBM a introduit cette carte en 1987, ils ont offert du même coup une carte souple et facilement programmable, et un mode confortable pour la programmation graphique, le 320x200 en 256 couleurs. Ce mode porte le numéro 13h, c'est à dire 13 en hexadécimal (19 en décimal). Voici la liste de quelques résolutions offertes:

  
Numéro du mode Résolution Couleurs
0Dh 320x200 16
0Eh 640x200 16
0Fh 640x350 2
10h 640x350 16
11h 640x480 2
12h 640x480 16
13h 320x200 256


Cette liste ne comporte que les modes standards. Il est intéressant de savoir qu'il existe des modes VESA, qui eux poussent encore plus loin les résolutions, jusqu'à 1600x1200, et offre encore plus de couleurs. Le mode 13h, que nous utiliserons, peut se programmer lui aussi. On peut obtenir ainsi des résolutions de 320x400, simuler un mode de couleur 16-bit et écrire sur plusieurs pages de mémoires en même temps. Nous ne verrons pas ces fameux modes, dit « mode x ». Les cartes EGA/VGA se composent de 4 contrôleurs qui se répartissent les tâches liées à la génération du signal vidéo. Concrètement, il s'agit de:

  • Contrôleur CRT
  • Contrôleur d'attributs
  • Contrôleur graphiques
  • Convertisseur digital en analogique (DAC) digital-analog converter

Pour compléter le tout, quelques registres généraux envoient eux aussi des informations sur l'état de la carte. Ces registres sont presque tous programmables, bien que certains soient extrêmement délicats à manipuler. En particulier, le contrôleur CRT, pour Cathode Ray Tube, pilotant la création des signaux vidéo et de synchronisation pour le retour de balayage horizontal et vertical du canon à électrons. Un mauvais usage pourrait causer des problèmes et même endommager le moniteur.

LE BIOS ET LES REGISTRES DU PROCESSEUR

Pour ceux qui ne connaisse pas ce qu'est le BIOS, disons simplement qu'il permet d'accéder directement au matériel (carte vidéo, disquettes, disques durs, interfaces série/parallèles, clavier, horloge, etc), et qu'il est composé d'un éventail de fonctions standardisées.

Maintenant qu'on sait que la carte VGA se compose de différents contrôleurs et qu'on connaît quels modes vidéos le standard VGA accepte, il est temps de concrètement utiliser ces registres pour se positionner en mode graphique. Pour ce faire, il existe des fonctions gravés dans le BIOS de notre ROM qui nous permet d'activer le mode désiré. Ces fonctions utilisent naturellement les registres du processeur. L'architecture centrale d'un ordinateur se compose d'un microprocesseur. Ce processeur se compose de trois types de registres : Les registres généraux (AX,BX,CX,DX), les registres de segment et le registre de flag. Dans la programmation système, et surtout en assembleur, la connaissance de ces registres est impérative. Je n'entrerai pas dans les détails maintenant, mais ceux qui désirent plus d'informations peuvent aller lire la section Assembleur.


ACTIVATION DU MODE VIDÉO 13h

Nous allons maintenant nous lancer dans le vif du sujet, c'est à dire la programmation. Comme nous avons vu, les registres et le BIOS font le travail pour nous en ce qui concerne le mode graphique. Pour avertir le microprocesseur que nous appelons une fonction du BIOS, on utilise une interruption, qui signale au CPU de tout arrêter et d’écouter le périphérique. Donc tout le travail est fait par les circuits imprimé de notre BIOS! Il suffit d'inscrire le mode vidéo dans le registre AL et d'actionner l'interruption 10h! Le registre AL fait partie du registre AX (composé de AH et AL). Voici le code source qui active le mode vidéo passé en paramètre:

 void setmode(unsigned int mode)   
{
asm{
MOV AX, mode
INT 0x10
}
}


Le mot réservé "asm" signifie que ce qui suit sera interprété comme de l'assembleur (et non du code C). On appelle ce genre de code du "inline asm", car ce code s'insère directement dans le code C, par opposition à un module assembleur externe (plus d'informations dans la section assembleur). Pour l'instant vous n'avez qu'a comprendre que "MOV AX, mode" met dans le registre AX la valeur de mode, et que "INT 0x10" appelle l'interruption vidéo, la 10(hexa).

setmode(0x13);

Nous voilà en mode graphique 320x200 par 256 couleurs. Rien de très impressionnant pour l'instant surtout avec la technologie d'aujourd'hui qui nous permet des affichages en 1280x1024 en milliards de couleurs. Mais comme il faut commencer quelque part, nous nous contenterons pour l'instant de cette modeste résolution. N’oubliez pas que les fonctions vues dans ce cours peuvent s'adapter à des modes hautes résolutions avec quelques modifications mineures. De la même façon, nous retournons en mode texte avec l'appel de fonction suivant:

setmode(0x03);

A présent, il est temps d'afficher quelque chose. Commençons par un pixel, c'est à dire un point. Pour ce faire, il existe, comme dans le cas précédant, une fonction dans le ROM pour afficher un point graphique. Cependant, ces fonctions ont recours aux interruptions qui sont très lentes. C'est pourquoi nous allons écrire directement dans la mémoire.


SURVOL DE LA MÉMOIRE VIDÉO

Pour pouvoir écrire directement dans la mémoire vidéo, nous allons avoir besoin de son adresse. Les pointeurs servent justement à cela! Donc, nous allons initialiser un pointeur sur le début de notre mémoire vidéo. La mémoire d'un PC en mode réel est organisée sous forme de segment et d'offset, et une adresse mémoire contient un octet(char). Cette adresse est un nombre entre 0000 et FFFF (hexa). Par exemple, une adresse mémoire pourrait être 7456:0D90. Nous allons donc déclarer un pointeur de type char sur cette adresse :

char *ecran = (char *) (0xA0000000L);

Nous devons effectuer une conversion de type devant l’adresse mémoire pour indiquer au compilateur que nous adressons la mémoire avec un pointeur de char. En mode 13h, l'écran se compose de 320 colonnes et de 200 rangés, numérotée respectivement de 0 à 319, et de 0 à 199. Donc, il existe 64000 positions possibles pour un pixel, et chaque pixel peut se composer de 256 couleurs (donc 8 bit par pixel ou bpp). La mémoire alloué pour une page est donc de 64000 octets. Les pixels n'y sont pas représenté dans un plan cartésien, mais plutôt chaîné de façon linéaire, un après les autres. Pour l'ordinateur, il n'y a pas de position (11,1), mais plutôt la somme de 1 rangée de 320 colonnes, plus 11 colonnes. Donc, la formule pour déterminer la position d'un pixel est la suivante:

position = x + (y*320)

Pixels en mémoire:

  
0 1 2 (...) 309 310 311 312 313 314 315 316 317 318 319
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334


Dans le cas précédent, le pixel se situe à la position 331, ce qui équivaut à notre (11,1) dans notre plan cartésien. Maintenant, voyons examiner le code nécessaire à une telle opération:

 void putpixel (int x, int y, unsigned char coul)   
{
ecran[(y*320)+x] = coul;
}


La fonction putpixel reçoit en paramètre x et y, nos coordonnées, ainsi que coul, la couleur désiré qui elle est représenté par un caractère non signé. Un char signé se situe entre -127 et 128, non signé entre 0 et 255, qui correspond au nombre de couleurs en mode 13h, 256 (ne pas oublier le 0). Dans notre exemple, "ecran" est en fait un pointeur sur l'adresse de départ de la mémoire VGA, qui se situe juste au dessus de la mémoire conventionnelle, à l'adresse A000:0000. Donc, en partant du début de la mémoire VGA, on se déplace de x+(y*320), car pour chaque y, on doit multiplier par 320, pour descendre sur la rangée voulue. Ensuite, on ajoute x pour se déplacer vers la droite sur la rangée. Le bit en question est assigné une valeur de couleur coul.


VIDER L'ÉCRAN

Une autre application qui nous provient de ce principe : vider l'écran. En se positionnant au tout début de la mémoire vidéo, et en allumant tous les pixels d'une couleur, nous vidons l'écran. La fonction memset permet de mettre une certaine quantité de mémoire à une certaine valeur. Dans notre cas, la valeur est 0 (noir en général) et la quantité est 64000 octets:

 Void cls () 
{
memset(ecran,0,64000L);
}


De la même façon l’écran pourrait être rempli d’une autre couleur que noir (0). Regarder à présent le code source. Il est abondamment commenté et devrait être très simple à comprendre. Il englobe l’ensemble des notions que nous avons vues aujourd’hui, soit :

  • Les appels aux BIOS pour sélectionner un mode vidéo
  • Affecter un pointeur sur la mémoire vidéo (A000 0000)
  • La structure linéaire de la mémoire
  • Afficher un pixel en mode 13h
  • Nettoyer l’écran


2dchap1.cpp

  
//----------------------------------------------------------------------//
// FICHIER : 2DCHAP1.CPP //
// AUTEUR : Shaun Dore //
// DESCRIPTION : Starfield horizontal pour demontrer putpixel //
// DATE DE MODIFICATION : 30-09-97 //
// COMPILATEUR : Borland Turbo C++ Real Mode 16-bit compiler //
// NOTES : Compiler avec modele memoire COMPACT //
//----------------------------------------------------------------------//

//----------------------------------------------------------------------//
// Fichiers include //
//----------------------------------------------------------------------//

#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

//----------------------------------------------------------------------//
// Declaration des constantes //
//----------------------------------------------------------------------//

#define MAX_ETOILES 500

//----------------------------------------------------------------------//
// Types personalises //
//----------------------------------------------------------------------//

typedef struct type_etoile // Pour contenir les infos sur chaque etoiles
{ // Coordonnees x,y et "z"
int x,y,z;
};

//----------------------------------------------------------------------//
// Variables globales //
//----------------------------------------------------------------------//

char *ecran = (char *) (0xA0000000L); // Pointeur sur RAM video

//----------------------------------------------------------------------//
// setmode - Appelle le mode passer en parametre //
//----------------------------------------------------------------------//
void setmode(unsigned int mode)
{
asm {
MOV AX, mode
INT 0x10
}
}

//----------------------------------------------------------------------//
// putpixel - Affiche un pixel directement dans la memoire //
//----------------------------------------------------------------------//
void putpixel (int x, int y, unsigned char coul)
{
ecran[(y*320)+x] = coul;
}

//----------------------------------------------------------------------//
// Fonction MAIN //
//----------------------------------------------------------------------//
void main()
{
// Rappel mathematique: % = modulo, le reste de la division entiere
// x mod y donne toujours entre 0 et y ex: x % 15 donne toujours
// un nombre entre 0 et 15. Bon moyen pour ramener des valeurs dans
// un cadre defini. On s'en sert en adressage disperse.
int et;
unsigned char coul;
type_etoile etoiles[MAX_ETOILES]; // Tableau d'etoiles du type etoiles
// definie plus haut
setmode(0x13);
randomize(); // Generateur de nombres aleatoires
for (int i=0;i<=MAX_ETOILES;i++) // Initialise la structure a 0
{
etoiles[i].x = 0;
etoiles[i].y = 0;
etoiles[i].z = 0;
}
do
{
for (et=0;et<MAX_ETOILES;et++)
{
for (unsigned int ralenti=0; ralenti<=1000; ralenti++) {} // Ralentir!
putpixel(etoiles[et].x,etoiles[et].y,0);
//efface ancienne position
etoiles[et].x = etoiles[et].x - (etoiles[et].z % 15 + 1);
// la vitesse de deplacement en fonction du z
if (etoiles[et].x <= 0) // si l'etoile est a gauche...
{
etoiles[et].x = 319; // on la remet a droite,
etoiles[et].y = random (200); // a une nouvelle hauteur,
etoiles[et].z = random (256); // sur un nouveau z.
}
coul = etoiles[et].z % 15 + 17; // 17-32 = tons de gris
putpixel(etoiles[et].x,etoiles[et].y,coul); // affiche nouvelle position
}
} while (!kbhit());

setmode(0x03);
printf("Shaun Dore\ndores@videotron.ca\nhttp://pages.infinit.net/shaun/");
}

← previous
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT