**LISTING 44.1 L44-1.C** /* Split screen VGA animation program. Performs page flipping in the top portion of the screen while displaying non-page flipped information in the split screen at the bottom of the screen. Compiled with Borland C++ in C compilation mode. */ #include #include #include #include #define SCREEN-SEG 0xA000 #define SCREEN-PIXWIDTH 640 /* in pixels */ #define SCREEN-WIDTH 80 /* in bytes */ #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 /* CRT Controller Index register */ #define CRTC-DATA 0x3D5 /* CRT Controller Data register */ #define OVERFLOW 0x07 /* index of CRTC reg holding bit 8 of the line the split screen starts after */ #define MAX-SCAN 0x09 /* index of CRTC reg holding bit 9 of the line the split screen starts after */ #define LINE-COMPARE 0x18 /* index of CRTC reg holding lower 8 bits of line split screen starts after */ #define NUM-BUMPERS (sizeof(Bumpers)/sizeof(bumper)) #define BOUNCER-COLOR 15 #define BACK-COLOR 1 /* playfield background color */ typedef struct { /* one solid bumper to be bounced off of */ int LeftX,TopY,RightX,BottomY; int Color; } bumper; typedef struct { /* one bit pattern to be used for drawing */ int WidthInBytes; int Height; unsigned char *BitPattern; } image; typedef struct { /* one bouncing object to move around the screen */ int LeftX,TopY; /* location */ int Width,Height; /* size in pixels */ int DirX,DirY; /* motion vectors */ int CurrentX[2],CurrentY[2]; /* current location in each page */ int Color; /* color in which to be drawn */ image *Rotation0; /* rotations for handling the 8 possible */ image *Rotation1; /* intrabyte start address at which the */ image *Rotation2; /* left edge can be */ 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); /* All bumpers in the playfield */ 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 for bouncing object when left edge is aligned with 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 for bouncing object when left edge is aligned with 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}; /* Initial settings for bouncing object. Only 2 rotations are needed because the object moves 4 pixels horizontally at a time */ 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; /* set display to 640x480 16-color mode */ int86(0x10, ®set, ®set); SetBIOS8x8Font(); /* set the pointer to the BIOS 8x8 font */ EnableSplitScreen(); /* turn on the split screen */ /* Display page 0 above the split screen */ ShowPage(PageStartOffsets[DisplayedPage = 0]); /* Clear both pages to background and draw bumpers in each 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(); /* draw the static split screen info */ BounceCount = 0; ShowBounceCount(); /* put up the initial zero count */ /* Draw the bouncing object at its initial location */ DrawImage(Bouncer.LeftX,Bouncer.TopY,&Bouncer.Rotation0, Bouncer.Color,PageStartOffsets[DisplayedPage],SCREEN-SEG); /* Move the object, draw it in the nondisplayed page, and flip the page until Esc is pressed */ Done = 0; do { NonDisplayedPage = DisplayedPage ^ 1; /* Erase at current location in the nondisplayed page */ 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); /* Move the bouncer */ MoveBouncer(&Bouncer, Bumpers, NUM-BUMPERS); /* Draw at the new location in the nondisplayed page */ DrawImage(Bouncer.LeftX,Bouncer.TopY,&Bouncer.Rotation0, Bouncer.Color,PageStartOffsets[NonDisplayedPage], SCREEN-SEG); /* Remember where the bouncer is in the nondisplayed page */ Bouncer.CurrentX[NonDisplayedPage] = Bouncer.LeftX; Bouncer.CurrentY[NonDisplayedPage] = Bouncer.TopY; /* Flip to the page we just drew into */ ShowPage(PageStartOffsets[DisplayedPage = NonDisplayedPage]); /* Respond to any keystroke */ if (kbhit()) { switch (getch()) { case 0x1B: /* Esc to end */ Done = 1; break; case 0: /* branch on the extended code */ switch (getch()) { case 0x48: /* nudge up */ Bouncer.DirY = -abs(Bouncer.DirY); break; case 0x4B: /* nudge left */ Bouncer.DirX = -abs(Bouncer.DirX); break; case 0x4D: /* nudge right */ Bouncer.DirX = abs(Bouncer.DirX); break; case 0x50: /* nudge down */ Bouncer.DirY = abs(Bouncer.DirY); break; } break; default: break; } } } while (!Done); /* Restore text mode and done */ regset.x.ax = 0x0003; int86(0x10, ®set, ®set); } /* Draws the specified list of bumpers into the specified page */ 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); } } /* Displays the current bounce count */ void ShowBounceCount() { char CountASCII[7]; itoa(BounceCount,CountASCII,10); /* convert the count to ASCII*/ TextUp(CountASCII,344,64,SPLIT-START-OFFSET,SCREEN-SEG); } /* Frames the split screen and fills it with various text */ 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("This is the split screen area...",8,8,SPLIT-START-OFFSET, SCREEN-SEG); TextUp("Bounces: ",272,64,SPLIT-START-OFFSET,SCREEN-SEG); TextUp("\033: nudge left",520,78,SPLIT-START-OFFSET,SCREEN-SEG); TextUp("\032: nudge right",520,90,SPLIT-START-OFFSET,SCREEN-SEG); TextUp("\031: nudge down",520,102,SPLIT-START-OFFSET,SCREEN-SEG); TextUp("\030: nudge up",520,114,SPLIT-START-OFFSET,SCREEN-SEG); TextUp("Esc to end",520,126,SPLIT-START-OFFSET,SCREEN-SEG); } /* Turn on the split screen at the desired line (minus 1 because the split screen starts *after* the line specified by the LINE-COMPARE register) (bit 8 of the split screen start line is stored in the Overflow register, and bit 9 is in the Maximum Scan Line reg) */ 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))); } /* Moves the bouncer, bouncing if bumpers are hit */ void MoveBouncer(bouncer *Bouncer, bumper *BumperPtr, int NumBumpers) { int NewLeftX, NewTopY, NewRightX, NewBottomY, i; /* Move to new location, bouncing if necessary */ NewLeftX = Bouncer->LeftX + Bouncer->DirX; /* new coords */ NewTopY = Bouncer->TopY + Bouncer->DirY; NewRightX = NewLeftX + Bouncer->Width - 1; NewBottomY = NewTopY + Bouncer->Height - 1; /* Compare the new location to all bumpers, checking for bounce */ for (i=0; iRightX) && (NewRightX >= BumperPtr->LeftX) && (NewTopY <= BumperPtr->BottomY) && (NewBottomY >= BumperPtr->TopY) ) { /* The bouncer has tried to move into this bumper; figure out which edge(s) it crossed, and bounce accordingly */ if (((Bouncer->LeftX > BumperPtr->RightX) && (NewLeftX <= BumperPtr->RightX)) || (((Bouncer->LeftX + Bouncer->Width - 1) < BumperPtr->LeftX) && (NewRightX >= BumperPtr->LeftX))) { Bouncer->DirX = -Bouncer->DirX; /* bounce horizontally */ 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; /* bounce vertically */ NewTopY = Bouncer->TopY + Bouncer->DirY; } /* Update the bounce count display; turn over at 10000 */ if (++BounceCount >= 10000) { TextUp("0 ",344,64,SPLIT-START-OFFSET,SCREEN-SEG); BounceCount = 0; } else { ShowBounceCount(); } } } Bouncer->LeftX = NewLeftX; /* set the final new coordinates */ Bouncer->TopY = NewTopY; }