/* Programme d'animation par division d'écran VGA. Réalise la permutation de pages dans la partie supérieure de l'écran alors que s'affiche l'information de page non permutée dans la division d'écran en bas de l'écran. Testé avec Borland C++ 4.02 en modèle small par Jim Mischel 12/16/94. */ #include #include #include #include #define SCREEN_SEG 0xA000 #define SCREEN_PIXWIDTH 640 /* en pixels */ #define SCREEN_WIDTH 80 /* en octets*/ #define SPLIT_START_LINE 339 #define SPLIT_LINES 141 #define NONSPLIT_LINES 339 #define SPLIT_START_OFFSET 0 #define PAGE0_START_OFFSET (SPLIT_LINES*SCREEN_WIDTH) #define PAGE1_START_OFFSET ((SPLIT_LINES+NONSPLIT_LINES)*SCREEN_WIDTH) #define CRTC_INDEX 0x3D4 /* registre Index CRT Controller */ #define CRTC_DATA 0x3D5 /* registre Data CRT Controller */ #define OVERFLOW 0x07 /* index du registre CRTC ayant le bit 8 de la ligne après laquelle commence la division de l'écran */ #define MAX_SCAN 0x09 /* index du registre CRTC ayant le bit 9 de la ligne après laquelle commence la division de l'écran */ #define LINE_COMPARE 0x18 /* index du registre CRTC ayant les 8 bits inférieurs de la ligne après laquelle commence la division de l'écran */ #define NUM_BUMPERS (sizeof(Bumpers)/sizeof(bumper)) #define BOUNCER_COLOR 15 #define BACK_COLOR 1 /* couleur de fond du terrain de jeu */ typedef struct { /* un objet solide sur lequel rebondir */ int LeftX,TopY,RightX,BottomY; int Color; } bumper; typedef struct { /* un motif de bits utilisé pour afficher */ int WidthInBytes; int Height; unsigned char *BitPattern; } image; typedef struct { /* un objet rebondissant à déplacer dans l'écran */ int LeftX,TopY; /* emplacement */ int Width,Height; /* taille en pixels */ int DirX,DirY; /* vecteurs du déplacement */ int CurrentX[2],CurrentY[2]; /* emplacement courant dans chaque page */ int Color; /* couleur d'affichage */ image *Rotation0; /* rotations pour traiter les 8 adresses */ image *Rotation1; /* intraoctet de départ possibles sur lesquelles */ image *Rotation2; /* le coté gauche peut se trouvé */ image *Rotation3; image *Rotation4; image *Rotation5; image *Rotation6; image *Rotation7; } bouncer; void main(void); void DrawBumperList(bumper *, int, unsigned int); void DrawSplitScreen(void); void EnableSplitScreen(void); void MoveBouncer(bouncer *, bumper *, int); extern void DrawRect(int,int,int,int,int,unsigned int,unsigned int); extern void ShowPage(unsigned int); extern void DrawImage(int,int,image **,int,unsigned int,unsigned int); extern void ShowBounceCount(void); extern void TextUp(char *,int,int,unsigned int,unsigned int); extern void SetBIOS8x8Font(void); /* Tous les objets dans le terrain de jeu */ bumper Bumpers[] = { {0,0,19,339,2}, {0,0,639,19,2}, {620,0,639,339,2}, {0,320,639,339,2}, {60,48,79,67,12}, {60,108,79,127,12}, {60,168,79,187,12}, {60,228,79,247,12}, {120,68,131,131,13}, {120,188,131,271,13}, {240,128,259,147,14}, {240,192,259,211,14}, {208,160,227,179,14}, {272,160,291,179,14}, {228,272,231,319,11}, {192,52,211,55,11}, {302,80,351,99,12}, {320,260,379,267,13}, {380,120,387,267,13}, {420,60,579,63,11}, {428,110,571,113,11}, {420,160,579,163,11}, {428,210,571,213,11}, {420,260,579,263,11} }; /* Image d'un objet rebondissant lorsque le coté gauche est aligné avec le bit 7 */ unsigned char _BouncerRotation0[] = { 0xFF,0x0F,0xF0, 0xFE,0x07,0xF0, 0xFC,0x03,0xF0, 0xFC,0x03,0xF0, 0xFE,0x07,0xF0, 0xFF,0xFF,0xF0, 0xCF,0xFF,0x30, 0x87,0xFE,0x10, 0x07,0x0E,0x00, 0x07,0x0E,0x00, 0x07,0x0E,0x00, 0x07,0x0E,0x00, 0x87,0xFE,0x10, 0xCF,0xFF,0x30, 0xFF,0xFF,0xF0, 0xFE,0x07,0xF0, 0xFC,0x03,0xF0, 0xFC,0x03,0xF0, 0xFE,0x07,0xF0, 0xFF,0x0F,0xF0}; image BouncerRotation0 = {3, 20, _BouncerRotation0}; /* Image d'un objet rebondissant lorsque coté gauche est aligné avec le bit 3 */ unsigned char _BouncerRotation4[] = { 0x0F,0xF0,0xFF, 0x0F,0xE0,0x7F, 0x0F,0xC0,0x3F, 0x0F,0xC0,0x3F, 0x0F,0xE0,0x7F, 0x0F,0xFF,0xFF, 0x0C,0xFF,0xF3, 0x08,0x7F,0xE1, 0x00,0x70,0xE0, 0x00,0x70,0xE0, 0x00,0x70,0xE0, 0x00,0x70,0xE0, 0x08,0x7F,0xE1, 0x0C,0xFF,0xF3, 0x0F,0xFF,0xFF, 0x0F,0xE0,0x7F, 0x0F,0xC0,0x3F, 0x0F,0xC0,0x3F, 0x0F,0xE0,0x7F, 0x0F,0xF0,0xFF}; image BouncerRotation4 = {3, 20, _BouncerRotation4}; /* Paramètre initial d'un objet rebondissant. Seules 2 rotations sont nécessaires car l'objet se déplace horizontalement 4 pixels à la fois */ bouncer Bouncer = {156,60,20,20,4,4,156,156,60,60,BOUNCER_COLOR, &BouncerRotation0,NULL,NULL,NULL,&BouncerRotation4,NULL,NULL,NULL}; unsigned int PageStartOffsets[2] = {PAGE0_START_OFFSET,PAGE1_START_OFFSET}; unsigned int BounceCount; void main() { int DisplayedPage, NonDisplayedPage, Done, i; union REGS regset; regset.x.ax = 0x0012; /* paramètre l'affichage en mode 640x480 en 16 couleurs */ int86(0x10, ®set, ®set); SetBIOS8x8Font(); /* paramètre le pointeur sur la police 8x8 BIOS */ EnableSplitScreen(); /* active la division de l'écran */ /* Affiche la page 0 au-dessus de la division de l'écran */ ShowPage(PageStartOffsets[DisplayedPage = 0]); /* Efface les pages du fond et affiche les objets dans chaque page */ for (i=0; i<2; i++) { DrawRect(0,0,SCREEN_PIXWIDTH-1,NONSPLIT_LINES-1,BACK_COLOR, PageStartOffsets[i],SCREEN_SEG); DrawBumperList(Bumpers,NUM_BUMPERS,PageStartOffsets[i]); } DrawSplitScreen(); /* affiche l'info statique de la division de l'écran */ BounceCount = 0; ShowBounceCount(); /* installe le compteur zéro initial */ /* Affiche l'objet rebondissant à son emplacement initial */ DrawImage(Bouncer.LeftX,Bouncer.TopY,&Bouncer.Rotation0, Bouncer.Color,PageStartOffsets[DisplayedPage],SCREEN_SEG); /*Déplace l'objet, l'affiche dans la page non affichée, et permute la page jusqu'à ce que Esc soit pressé */ Done = 0; do { NonDisplayedPage = DisplayedPage ^ 1; /* Efface à l'emplacement courant dans la page non affichée */ DrawRect(Bouncer.CurrentX[NonDisplayedPage], Bouncer.CurrentY[NonDisplayedPage], Bouncer.CurrentX[NonDisplayedPage]+Bouncer.Width-1, Bouncer.CurrentY[NonDisplayedPage]+Bouncer.Height-1, BACK_COLOR,PageStartOffsets[NonDisplayedPage],SCREEN_SEG); /* Déplace la balle */ MoveBouncer(&Bouncer, Bumpers, NUM_BUMPERS); /* Affiche au nouvel emplacement dans la page non affichée */ DrawImage(Bouncer.LeftX,Bouncer.TopY,&Bouncer.Rotation0, Bouncer.Color,PageStartOffsets[NonDisplayedPage], SCREEN_SEG); /* Mémorise là où est la balle dans la page non affichée */ Bouncer.CurrentX[NonDisplayedPage] = Bouncer.LeftX; Bouncer.CurrentY[NonDisplayedPage] = Bouncer.TopY; /* Permute la page dans laquelle nous venons d'afficher*/ ShowPage(PageStartOffsets[DisplayedPage = NonDisplayedPage]); /* Répond à une touche clavier */ if (kbhit()) { switch (getch()) { case 0x1B: /* Esc pour finir */ Done = 1; break; case 0: /* branchement sur le code étendu */ switch (getch()) { case 0x48: /* pousse en haut */ Bouncer.DirY = -abs(Bouncer.DirY); break; case 0x4B: /* pousse à gauche */ Bouncer.DirX = -abs(Bouncer.DirX); break; case 0x4D: /* pousse à droite */ Bouncer.DirX = abs(Bouncer.DirX); break; case 0x50: /* pousse en bas */ Bouncer.DirY = abs(Bouncer.DirY); break; } break; default: break; } } } while (!Done); /* Restaure le mode texte et fini */ regset.x.ax = 0x0003; int86(0x10, ®set, ®set); } /* Affiche la liste spécifiée d'objets dans la page spécifiée */ void DrawBumperList(bumper * Bumpers, int NumBumpers, unsigned int PageStartOffset) { int i; for (i=0; iLeftX,Bumpers->TopY,Bumpers->RightX, Bumpers->BottomY,Bumpers->Color,PageStartOffset, SCREEN_SEG); } } /* Affiche le compteur courant des rebonds */ void ShowBounceCount() { char CountASCII[7]; itoa(BounceCount,CountASCII,10); /* convertit le compteur en ASCII */ TextUp(CountASCII,344,64,SPLIT_START_OFFSET,SCREEN_SEG); } /* affiche la division de l'écran et la remplit de divers textes */ void DrawSplitScreen() { DrawRect(0,0,SCREEN_PIXWIDTH-1,SPLIT_LINES-1,0,SPLIT_START_OFFSET, SCREEN_SEG); DrawRect(0,1,SCREEN_PIXWIDTH-1,4,15,SPLIT_START_OFFSET, SCREEN_SEG); DrawRect(0,SPLIT_LINES-4,SCREEN_PIXWIDTH-1,SPLIT_LINES-1,15, SPLIT_START_OFFSET,SCREEN_SEG); DrawRect(0,1,3,SPLIT_LINES-1,15,SPLIT_START_OFFSET,SCREEN_SEG); DrawRect(SCREEN_PIXWIDTH-4,1,SCREEN_PIXWIDTH-1,SPLIT_LINES-1,15, SPLIT_START_OFFSET,SCREEN_SEG); TextUp("Ceci est la zone de la division de l'écran...",8,8,SPLIT_START_OFFSET, SCREEN_SEG); TextUp("rebond: ",272,64,SPLIT_START_OFFSET,SCREEN_SEG); TextUp("\033: à gauche",520,78,SPLIT_START_OFFSET,SCREEN_SEG); TextUp("\032: à droite",520,90,SPLIT_START_OFFSET,SCREEN_SEG); TextUp("\031: en bas",520,102,SPLIT_START_OFFSET,SCREEN_SEG); TextUp("\030: en haut",520,114,SPLIT_START_OFFSET,SCREEN_SEG); TextUp("Esc pour finir",520,126,SPLIT_START_OFFSET,SCREEN_SEG); } /* Active la division de l'écran (moins 1 car la division de l'écran commence *après * la ligne spécifiée par le registre LINE_COMPARE) (le bit 8 de la division de l'écran commence à la ligne qui est stockée dans le registre Overflow, et le bit 9 est dans le registre Maximum Scan Line) */ void EnableSplitScreen() { outp(CRTC_INDEX, LINE_COMPARE); outp(CRTC_DATA, (SPLIT_START_LINE - 1) & 0xFF); outp(CRTC_INDEX, OVERFLOW); outp(CRTC_DATA, (((((SPLIT_START_LINE - 1) & 0x100) >> 8) << 4) | (inp(CRTC_DATA) & ~0x10))); outp(CRTC_INDEX, MAX_SCAN); outp(CRTC_DATA, (((((SPLIT_START_LINE - 1) & 0x200) >> 9) << 6) | (inp(CRTC_DATA) & ~0x40))); } /*Déplace la balle, rebondit si un objet est touché */ void MoveBouncer(bouncer *Bouncer, bumper *BumperPtr, int NumBumpers) { int NewLeftX, NewTopY, NewRightX, NewBottomY, i; /*Déplace au nouvel emplacement, rebondit si nécessaire */ NewLeftX = Bouncer->LeftX + Bouncer->DirX; /* nouvelles coordonnées */ NewTopY = Bouncer->TopY + Bouncer->DirY; NewRightX = NewLeftX + Bouncer->Width - 1; NewBottomY = NewTopY + Bouncer->Height - 1; /* Compare le nouvel emplacement à tous les objets, pour rechercher un rebond */ for (i=0; iRightX) && (NewRightX >= BumperPtr->LeftX) && (NewTopY <= BumperPtr->BottomY) && (NewBottomY >= BumperPtr->TopY) ) { /* La balle a essayé de rentrer dans cet objet; cherche quel coté(s)elle a touché, et rebondit en fonction*/ if (((Bouncer->LeftX > BumperPtr->RightX) && (NewLeftX <= BumperPtr->RightX)) || (((Bouncer->LeftX + Bouncer->Width - 1) < BumperPtr->LeftX) && (NewRightX >= BumperPtr->LeftX))) { Bouncer->DirX = -Bouncer->DirX; /* rebondit à l'horizontale */ NewLeftX = Bouncer->LeftX + Bouncer->DirX; } if (((Bouncer->TopY > BumperPtr->BottomY) && (NewTopY <= BumperPtr->BottomY)) || (((Bouncer->TopY + Bouncer->Height - 1) < BumperPtr->TopY) && (NewBottomY >= BumperPtr->TopY))) { Bouncer->DirY = -Bouncer->DirY; /* rebondit à la verticale */ NewTopY = Bouncer->TopY + Bouncer->DirY; } /*Met à jour l'affichage du compteur de la balle; passe à 10000 */ if (++BounceCount >= 10000) { TextUp("0 ",344,64,SPLIT_START_OFFSET,SCREEN_SEG); BounceCount = 0; } else { ShowBounceCount(); } } } Bouncer->LeftX = NewLeftX; /* paramètres les nouvelles coordonnées de fin* / Bouncer->TopY = NewTopY; }