2d Chapitre 4 - Les écrans virtuels
CHAPITRE 4 - Les écrans virtuels
Le principe des écrans virtuels est très simple et il permet de gérer plusieurs surface de travail à la fois, permettant ainsi la réalisation des animations. La technique présentée aujourd’hui est à la base de tous les jeux qui existe. Plutôt qu’aller écrire directement en mémoire vidéo toutes les trames d’une animation, nous la dessinons tout d’abord sur un écran virtuel, et nous copions le résultat sur l’écran physique. En fait, c’est l’équivalent informatique des petits calepins où sont dessiné des centaines d’images, que l’on doit faire défiler rapidement une à la suite de l’autre pour constituer une animation.
Qu'est-ce qu'un écran virtuel et pourquoi en utiliser? Si vous avez déjà essayer de faire des animations avec la méthode dessine-efface-dessine, vous avez du remarquer que cela créait non seulement du beaucoup de statique sur l’écran, mais aussi un dédoublement du dessin. C'est que ce procédé demande beaucoup de la part de la carte vidéo (dessine la figure, redessine-la en noir, dessine la figure, etc). La solution se trouve justement dans l'utilisation des écrans virtuels. Proprement dit, on se crée un espace de travail similaire à notre mémoire vidéo et une fois le travail effectué, on transfert le tout sur la mémoire VGA. Pour créé notre "tampon" nous utiliserons de la RAM.
Pour initialiser notre espace de travail, il faut savoir combien d'espace nous avons besoin au total. Si on se souviens bien, l'écran VGA mesure 64000 octets, car nous avons 320x200 lignes et chaque pixels occupe 1 octet. L'opérateur "new" crée un nouvel objet, de taille (sizeof(objet)), dans zone de mémoire appelé le tas (heap):
char *virtuel = new unsigned char[64000];
À présent, il faudra réécrire nos fonction pour qu’elles puissent écrire sur l’écran virtuel. Vous avez le choix : soit que vous leurs attribuer un paramètre destination qui spécifie si nous allons écrire en mémoire vidéo ou sur l’écran virtuel, ou soit que vous envoyez tout sur l’écran virtuel. Dans le code source, c’est cette dernière technique qui est utilisée :
void putpixel(int x, int y, unsigned char coul)
{
virtuel[(y*320)+x] = coul;
}
Cependant, il nous reste à voir comment afficher le pixel virtuel, et comment échanger les deux écrans pour nous permettre de faire de l'animation. C’est une simple question de déplacer la mémoire de l’écran virtuel vers la mémoire de l’écran physique.
COPIER L'ÉCRAN VIRTUEL SUR L'ÉCRAN PHYSIQUE
Quand nous dessinons sur l'écran virtuel, le contenu de la mémoire vidéo n'est aucunement affecté. C'est pourquoi que lorsqu'il est temps d'effectuer un échange entre les deux écrans, on utilisera la fonction memcpy, qui copie une partie de mémoire vers un autre:
void cpyvirt()
{
memcpy(ecran,virtuel,64000);
}
Avec cpyvirt, nous copions les 64000 octets de l’écran virtuel, vers la mémoire de l’écran physique. Souvent, après ce transfert, nous aurons besoin de "nettoyer" notre écran, c'est donc pourquoi la fonction cls devra être mise à jour pour tenir compte des écrans virtuels...
void clsvirt()
{
memset(virtuel,0,64000);
}
Il y a certain cas ou l’opérateur new, toujours en mode réel, ne suffira pas pour allouer un bloc de mémoire. Aussitôt que votre écran virtuel dépasse 65535 octets, new n’allouera pas la mémoire suffisante, mais ne retournera pas de message d’erreur! Par exemple, pour un écran de 256x256, vous auriez besoin de 65536 octets. La solution est d’utiliser farmalloc, une fonction de la librairie standard alloc.h, pour allouer le segment :
char *virtuel = (char *) farmalloc(65536);
Dans tous les cas, c’est une très bonne pratique de programmation de dé-allouer la mémoire que vous aviez réservé. Avec l’opérateur new, on utilise delete, et si vous avez utiliser farmalloc, il faudra utiliser farfree :
New : delete[] virtuel;
Farmalloc : farfree(virtuel);
Utiliser les écrans virtuels est aussi simple que cela! Vous pouvez déclarer plus qu'un seul écran virtuel, si jamais vos besoins sont plus grand. Cependant, souvenez-vous qu'en mode réel, un PC ne peut franchir la barrière des 640K. Merci à notre ami Bill Gates pour cela. Éventuellement, vous devriez jeter un coup d'œil sur Visual C++ ou DJGPP pour avoir accès au mode protégé, qui mets fin à cette limite. Dans ce chapitre, nous avons vu :
- Comment mettre sur pied un écran virtuel à l’aide de l’opérateur new
- Comment adapter nos fonctions pour utiliser les écrans virtuels
- Utiliser farmalloc pour réserver des blocs de mémoire de plus de 64K
- L’opérateur delete et la function farfree pour libérer la mémoire
- Copier d’un écran virtuel vers un écran physique
2dchap4.cpp
//----------------------------------------------------------------------//
// FICHIER : 2DCHAP4.CPP //
// AUTEUR : Shaun Dore //
// DESCRIPTION : Animations et ecrans virtuels //
// DATE DE MODIFICATION : 28-01-98 //
// COMPILATEUR : Borland Turbo C++ Real Mode 16-bit compiler //
// NOTES : Compiler avec modele memoire Compact //
//----------------------------------------------------------------------//
//----------------------------------------------------------------------//
// Fichiers include //
//----------------------------------------------------------------------//
#include <mem.h>
#include <stdio.h>
#include <conio.h>
//----------------------------------------------------------------------//
// Constantes //
//----------------------------------------------------------------------//
#define MIN_Y 20
#define MAX_Y 160
//----------------------------------------------------------------------//
// Variables globales //
//----------------------------------------------------------------------//
char *ecran = (char *) (0xA0000000L); // ptr sur RAM Video
char *virtuel = new unsigned char[64000L]; // ptr sur ecran virtuel
//----------------------------------------------------------------------//
// setmode - Appelle le mode passer en parametre //
//----------------------------------------------------------------------//
void setmode(unsigned int mode)
{
asm {
MOV AX, [mode]
INT 0x10
}
}
//----------------------------------------------------------------------//
// clsvirt - vide l'ecran //
//----------------------------------------------------------------------//
void clsvirt()
{
memset(virtuel,0,64000L);
}
//----------------------------------------------------------------------//
// cpyvirt - copie un ecran vers un autre //
//----------------------------------------------------------------------//
void cpyvirt()
{
memcpy(ecran,virtuel,64000L);
}
//----------------------------------------------------------------------//
// putpixel - Affiche un pixel //
//----------------------------------------------------------------------//
void putpixel(int x, int y, unsigned char col)
{
virtuel[(y*320)+x] = col;
}
//----------------------------------------------------------------------//
// hline - Dessine une ligne horizontale //
//----------------------------------------------------------------------//
void hline(int x1, int x2, int y, unsigned char coul)
{
memset(virtuel+x1+(y*320),coul,(x2-x1));
}
//----------------------------------------------------------------------//
// setpal - fixe les attributs r,g,b //
//----------------------------------------------------------------------//
void setpal(unsigned char col,unsigned char r, unsigned char g, unsigned char b)
{
outp (0x03C8,col); // Envoie les informations au DAC
outp (0x03C9,r);
outp (0x03C9,g);
outp (0x03C9,b);
}
//----------------------------------------------------------------------//
// preparepal - modifie la palette a mon gout //
//----------------------------------------------------------------------//
void preparepal()
{
for (int i=0;i<256;i++) setpal(i,0,0,0);
for (i=1;i<=10;i++) setpal(i,0,0,i*3);
for (i=11;i<=20;i++) setpal(i,(i-10)*3,0,0);
for (i=21;i<=30;i++) setpal(i,0,(i-20)*3,0);
}
//----------------------------------------------------------------------//
// Barre - Anime trois barres sur l'ecran //
//----------------------------------------------------------------------//
void barre(int &y, unsigned char coul, int &sens)
{
int ligne,i,x;
for(ligne=0;ligne<=10;ligne++)
{
hline(0,319,y+ligne,coul);
if(ligne < 5) coul++;
if(ligne > 5) coul--;
if(y>=MAX_Y) sens=-1;
if(y<=MIN_Y) sens=1;
}
y+=sens;
}
//----------------------------------------------------------------------//
// Fonction MAIN //
//----------------------------------------------------------------------//
void main()
{
int sensg = 1;
int sensb = 1;
int sensr = -1;
int yb = MIN_Y;
int yr = MIN_Y + 25;
int yg = MIN_Y + 25;
setmode(0x13);
preparepal();
do
{
clsvirt();
barre(yr,15,sensr);
barre(yb,5, sensb);
barre(yg,25,sensg);
cpyvirt();
} while (!kbhit());
delete []virtuel; // Ne pas oublier de liberer la memoire!
setmode(0x03);
printf("Shaun Dore\ndores@videotron.ca\n http://pages.infinit.net/shaun ");
}