Previous | Table of Contents | Next

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 <stdio.h>
    #include <conio.h>
    #include <dos.h>
    #include <math.h>

    #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, &regset, &regset);
       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, &regset, &regset);
    }

    /* Draws the specified list of bumpers into the specified page */
    void DrawBumperList(bumper * Bumpers, int NumBumpers,
          unsigned int PageStartOffset)
    {
       int i;

       for (i=0; i<NumBumpers; i++,Bumpers++) {
          DrawRect(Bumpers->LeftX,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; i<NumBumpers; i++,BumperPtr++) {
          /* If moving puts the bouncer inside this bumper, bounce */
          if (  (NewLeftX <= BumperPtr->RightX) &&
                (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;
    }

Previous | Table of Contents | Next

Graphics Programming Black Book © 2001 Michael Abrash