The Intermediate Step

At first glance, it might appear that the original DOS fish tank cannot be ported to Windows because it accesses off-screen video memory and uses page flipping, both of which Windows won't tolerate. We must obviously eliminate this behavior if we want to port the fish tank to Windows.

Starting with version 4.0, virtual buffers are also available in Fastgraph for DOS. This makes it rather easy to create an "intermediate" version of a DOS program that serves as a good first step when converting to Windows. The intermediate version will use virtual buffers instead of off-screen video pages and will not write to video memory except when copying the contents of a virtual buffer to the screen. Here is the source code for an intermediate version of the fish tank program:


/****************************************************************************\
*                                                                            *
*  Fish2.c                                                                   *
*                                                                            *
*  This is the DOS version of the Fastgraph Fish Tank that uses virtual      *
*  buffers.                                                                  *
*                                                                            *
\****************************************************************************/
#include 
#include 
#include 
#include 
void main(void);
void GetFish(void);
void GoFish(void);
void PutFish(int,int,int,int);
#define MAX(x,y) ((x) > (y)) ? (x) : (y)
#define MIN(x,y) ((x) < (y)) ? (x) : (y)
#define NFISH 11
int hVB1, hVB2;
/****************************************************************************\
*                                                                            *
*  main program                                                              *
*                                                                            *
\****************************************************************************/
void main()
{
#ifdef FG32
   char *Buffer1;
   char *Buffer2;
#else
   char far *Buffer1;
   char far *Buffer2;
#endif
   // in case we're compiling for protected mode
   fg_initpm();
   // make sure the system supports video mode 19
   if (fg_testmode(19,1) == 0)
   {
      printf("This program requires VGA graphics.\n");
      exit(0);
   }
   // initialize the video environment
   fg_setmode(19);
   // create two 320x200 virtual buffers
   fg_vbinit();
#ifdef FG32
   Buffer1 = (char *)malloc(320*200);
   Buffer2 = (char *)malloc(320*200);
#else
   Buffer1 = (char far *)farmalloc(320L*200L);
   Buffer2 = (char far *)farmalloc(320L*200L);
#endif
   hVB1 = fg_vbdefine(Buffer1,320,200);
   hVB2 = fg_vbdefine(Buffer2,320,200);
   // display the coral background in virtual buffer #2 (which
   // will always contain a clean copy of the background image)
   fg_vbopen(hVB2);
   fg_showpcx("CORAL.PCX",2);
   // copy the background to the visual page
   fg_vbpaste(0,319,0,199,0,199);
   // get the fish
   GetFish();
   // make the fish go
   GoFish();
   // free the virtual buffer memory
   fg_vbclose();
#ifdef FG32
   free(Buffer1);
   free(Buffer2);
#else
   farfree(Buffer1);
   farfree(Buffer2);
#endif
   // restore the original video state
   fg_setmode(3);
   fg_reset();
}
/****************************************************************************\
*                                                                            *
*  GetFish()                                                                 *
*                                                                            *
*  Fill the fish bitmap arrays.                                              *
*                                                                            *
\****************************************************************************/
char Fish1[56*25];
char Fish2[54*38];
char Fish3[68*26];
char Fish4[56*30];
char Fish5[62*22];
char Fish6[68*36];
int FishX[]      = {  0, 64,128,200,  0, 80}; /* location of fish x & y */
int FishY[]      = {199,199,199,199,150,150};
int FishWidth[]  = { 56, 54, 68, 56, 62, 68}; /* size of fish: width */
int FishHeight[] = { 25, 38, 26, 30, 22, 36}; /* size of fish: height */
char *Fishes[] = {Fish1, /* for convenience, an array of pointers to fishes */
                  Fish2,
                  Fish3,
                  Fish4,
                  Fish5,
                  Fish6};
void GetFish()
{
   register int FishNum;
   // get the fish bitmaps from a PCX file
   fg_vbopen(hVB1);
   fg_showpcx("FISH.PCX",2);
   for (FishNum = 0; FishNum < 6; FishNum++)
   {
      fg_move(FishX[FishNum],FishY[FishNum]);
      fg_getimage(Fishes[FishNum],FishWidth[FishNum],FishHeight[FishNum]);
   }
}
/****************************************************************************\
*                                                                            *
*  GoFish()                                                                  *
*                                                                            *
*  Make the fish swim around.                                                *
*                                                                            *
\****************************************************************************/
/*  There are 11 fish total, and 6 different kinds of fish. These
*   arrays keep track of what kind of fish each fish is, and how each
*   fish moves:
*
*   Fish[]   -- which fish bitmap applies to this fish?
*   xStart[] -- starting x coordinate
*   yStart[] -- starting y coordinate
*
*   xMin[]   -- how far left (off screen) the fish can go
*   xMax[]   -- how far right (off screen) the fish can go
*   xInc[]   -- how fast the fish goes left and right
*   Dir[]    -- starting direction for each fish
*
*   yMin[]   -- how far up this fish can go
*   yMax[]   -- how far down this fish can go
*   yInc[]   -- how fast the fish moves up or down
*   yTurn[]  -- how long fish can go in the vertical direction
*               before stopping or turning around
*   yCount[] -- counter to compare to yTurn
*/
int Fish[]   = {   1,   1,   2,   3,   3,   0,   0,   5,   4,   2,   3};
int xStart[] = {-100,-150,-450,-140,-200, 520, 620,-800, 800, 800,-300};
int yStart[] = {  40,  60, 150,  80,  70, 190, 180, 100,  30, 130,  92};
int xMin[]   = {-300,-300,-800,-200,-200,-200,-300,-900,-900,-900,-400};
int xMax[]   = { 600, 600,1100,1000,1000, 750, 800,1200,1400,1200, 900};
int xInc[]   = {   2,   2,   8,   5,   5,  -3,  -3,   7,  -8,  -9,   6};
int Dir[]    = {   0,   0,   0,   0,   0,   1,   1,   0,   1,   1,   0};
int yMin[]   = {  40,  60, 120,  70,  60, 160, 160,  80,  30, 110,  72};
int yMax[]   = {  80, 100, 170, 110, 100, 199, 199, 120,  70, 150, 122};
int yTurn[]  = {  50,  30,  10,  30,  20,  10,  10,  10,  30,   20, 10};
int yCount[] = {   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0};
int yInc[]   = {   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0};
void GoFish()
{
   register int i;
   unsigned char Key, aux;
   // make the fish swim around
   do
   {
      // copy the background to the workspace
      fg_vbcopy(0,319,0,199,0,199,hVB2,hVB1);
      // put all the fish in their new positions
      for (i = 0; i < NFISH; i++)
      {
         yCount[i]++;
         if (yCount[i] > yTurn[i])
         {
            yCount[i] = 0;
            yInc[i] = (rand() % 3) - 1;
         }
         yStart[i] += yInc[i];
         yStart[i] = MIN(yMax[i],MAX(yStart[i],yMin[i]));
         if (xStart[i] >= -72 && xStart[i] < 320)
            PutFish(Fish[i],xStart[i],yStart[i],Dir[i]);
         xStart[i] += xInc[i];
         if (xStart[i] <= xMin[i] || xStart[i] >= xMax[i])
         {
            xInc[i] = -xInc[i];
            Dir[i] = 1 - Dir[i];
         }
      }
      // copy this frame to the screen
      fg_vbpaste(0,319,0,199,0,199);
      // intercept a keystroke, if it is escape exit the program
      fg_intkey(&Key,&aux);
   }
   while (Key != 27);
}
/****************************************************************************\
*                                                                            *
*  PutFish()                                                                 *
*                                                                            *
*  Draw one of the six fish anywhere you want.                               *
*                                                                            *
\****************************************************************************/
void PutFish (int FishNum, int x, int y, int FishDir)
{
   // move to position where the fish will appear
   fg_move(x,y);
   // draw a left- or right-facing fish, depending on FishDir
   if (FishDir == 0)
      fg_flpimage(Fishes[FishNum],FishWidth[FishNum],FishHeight[FishNum]);
   else
      fg_clpimage(Fishes[FishNum],FishWidth[FishNum],FishHeight[FishNum]);
}

First, and perhaps most importantly, notice that a great deal of the code in this version of the fish tank is the same as in the original version. The only real differences lie in the use of virtual buffers and the elimination of the page flipping, which of course were the goals of the intermediate version.

The main program creates two 320x200 virtual buffers that correspond to video pages 1 and 2 in the original version. We allocate the virtual buffer memory with farmalloc() or malloc(), depending on if we're compiling for a 16-bit or 32-bit platform, and then use fg_vbdefine() to create the virtual buffers. We then load the coral background in the second virtual buffer, replacing the fg_setpage() call with a call to fg_vbopen(). Next, we use fg_vbpaste() instead of fg_copypage() to display the coral background on the screen. The GetFish() function loads the fish sprites into the first virtual buffer instead of video page 1.

The GoFish() function no longer performs page flipping, so we don't need the VisualPage and WorkPage variables. Instead, it constructs each frame by first copying the background from the second virtual buffer to the first and then applying the fish sprites to the first virtual buffer. Once the frame is ready, we use fg_vbpaste() to display it on the screen rather than page flip; this is the only place where we write to video memory. As before, GoFish() continues until the user presses the Escape key.

The main program has two additional tasks to perform: it must call fg_vbclose() to shut down the virtual buffer environment (otherwise the call to fg_setmode() will not restore the original video mode), and it must release the virtual buffer memory. We use conditional compilation to distinguish 16-bit and 32-bit platforms, calling either farfree() or free() as appropriate.

Before we wrap up this section, there's one important issue we should address. In 320x200 256-color graphics modes, each video page requires 64,000 bytes. Each virtual buffer likewise takes up 64,000 bytes, so we need to be able to allocate 128,000 bytes of conventional memory for the two virtual buffers. That's not too unreasonable, but if we were using a SuperVGA graphics mode, each virtual buffer would require more space. For example, suppose we wanted to create an intermediate version of a program that ran in the 800x600 256-color SVGA graphics mode (Fastgraph's mode 26) and used two video pages. In this mode, each video page requires 480,000 bytes, so we would need a 480,000-byte virtual buffer to take the place of the second video page. That's a large chunk of the 640K limit of real mode DOS, so it's entirely possible that we wouldn't be able to create such a large virtual buffer. The solution in such cases is to use Fastgraph's protected mode libraries instead of real mode. Protected mode provides a minimum 16MB address space, as opposed to the 640K real mode limit.

<< Prev

Next >>

Contents
Fastgraph Home Page

 

copyright 2001 Ted Gruber Software, Inc.