/********************************************************************** * S 3 2 2 0 C . C * **-------------------------------------------------------------------** * Task : Demonstrates sprites in 320x200 VGA graphic * * mode, using 256 colors and four screen pages. * * This program requires the assembly language * * modules V3220CA.ASM and S3220CA.ASM. * **-------------------------------------------------------------------** * Author : Michael Tischer * * Developed on : 09/09/90 * * Last update : 02/17/92 * **-------------------------------------------------------------------** * Memory model : SMALL * **-------------------------------------------------------------------** * (MICROSOFT C) * * Compilation : CL /AS /c /W0 s3220c.c * * LINK s3220c v3220ca s3220ca; * **-------------------------------------------------------------------** * (BORLAND TURBO C) * * Compilation : Create a project file containing the following: * * s3220c.c * * v3220ca.asm * * s3220ca.asm * **-------------------------------------------------------------------** * Call : s3220c * **********************************************************************/ #include #include #include #include #include #include /*-- Compiler-dependent declarations --------------------------------*/ #ifdef __TURBOC__ #include #else #include #define random(x) ( rand() % (x+1) ) /* Random function */ #endif /*-- Type declarations ----------------------------------------------*/ typedef unsigned char BYTE; typedef struct { /* Sprite design */ BYTE twidth, /* Total width */ theight, /* Height in pixel lines */ ppage, /* Placed in page ... */ *bmskp, /* Pointer to bit mask */ msklen; /* Entry length */ int pxlin; /* Pixel lines for sprite */ } SPLOOK; /* in its page */ typedef struct { /* Sprite descriptor (ID) */ BYTE bkgpage; /* Background page */ int x[2], y[2], /* Coordinates: pp. 0 & 1 */ bkx, bky; /* Background buffer */ SPLOOK * splookp; /* Pointer to sprite design */ } SPID; /*-- External references to assembler routines ----------------------*/ extern void init320200( void ); extern void setpix( int x, int y, unsigned char pcolor); extern BYTE getpix( int x, int y ); extern void setpage( BYTE page ); extern void showpage( BYTE page ); extern void far * getfontptr( void ); extern void waitvsync( void ); extern void blockmove( BYTE frompage, int fromx, int fromy, BYTE topage, int tox, int toy, BYTE pwidth, BYTE pheight, BYTE *bmskp ); /*-- Constants ------------------------------------------------------*/ #define NOBITMASK (BYTE *) 0 #define MAXX 319 /* Maximum X- and Y-coordinates */ #define MAXY 199 #define OUT_LEFT 1 /* For collision documentation in SpriteMove() */ #define OUT_TOP 2 #define OUT_RIGHT 4 #define OUT_BOTTOM 8 #define OUT_NO 0 /* None */ /********************************************************************** * IsVga: Determines whether a VGA card is installed. * **-------------------------------------------------------------------** * Input : None * * Output : 0 if no VGA exists, otherwise <0 * **********************************************************************/ BYTE IsVga( void ) { union REGS Regs; /* Processor registers for interrupt call */ Regs.x.ax = 0x1a00; /* Function 1AH applies to VGA only */ int86( 0x10, &Regs, &Regs ); return ( Regs.h.al == 0x1a ); /* Is this function available? */ } /********************************************************************** * Line: Draws a line based on the Bresenham algorithm. * **-------------------------------------------------------------------** * Input : X1, Y1 = Starting coordinates (0 - ...) * * X2, Y2 = Ending coordinates * * PCOLOR = Color of line pixels * **********************************************************************/ /*-- Function for swapping two integer variables --------------------*/ void SwapInt( int *i1, int *i2 ) { int dummy; dummy = *i2; *i2 = *i1; *i1 = dummy; } /*-- Main part of function ------------------------------------------*/ void Line( int x1, int y1, int x2, int y2, BYTE pcolor ) { int d, dx, dy, aincr, bincr, xincr, yincr, x, y; if ( abs(x2-x1) < abs(y2-y1) ) /* X- or Y-axis overflow? */ { /* Check Y-axis */ if ( y1 > y2 ) /* y1 > y2? */ { SwapInt( &x1, &x2 ); /* Yes --> Swap X1 with X2 */ SwapInt( &y1, &y2 ); /* and Y1 with Y2 */ } xincr = ( x2 > x1 ) ? 1 : -1; /* Set X-axis increment */ dy = y2 - y1; dx = abs( x2-x1 ); d = 2 * dx - dy; aincr = 2 * (dx - dy); bincr = 2 * dx; x = x1; y = y1; setpix( x, y, pcolor ); /* Set first pixel */ for (y=y1+1; y<= y2; ++y ) /* Execute line on Y-axes */ { if ( d >= 0 ) { x += xincr; d += aincr; } else d += bincr; setpix(x, y, pcolor); } } else /* Check X-axis */ { if ( x1 > x2 ) /* x1 > x2? */ { SwapInt( &x1, &x2 ); /* Yes --> Swap X1 with X2 */ SwapInt( &y1, &y2 ); /* and Y1 with Y2 */ } yincr = ( y2 > y1 ) ? 1 : -1; /* Set Y-axis increment */ dx = x2 - x1; dy = abs( y2-y1 ); d = 2 * dy - dx; aincr = 2 * (dy - dx); bincr = 2 * dy; x = x1; y = y1; setpix(x, y, pcolor); /* Set first pixel */ for (x=x1+1; x<=x2; ++x ) /* Execute line on X-axes */ { if ( d >= 0 ) { y += yincr; d += aincr; } else d += bincr; setpix(x, y, pcolor); } } } /********************************************************************** * PrintChar : Writes a character to the screen while in graphic mode.* **-------------------------------------------------------------------** * Input : THECHAR = Character to be written * * X, Y = X- and Y-coordinates of upper-left corner * * FG = Foreground color * * BK = Background color * * Info : Character is created in an 8x8 matrix, based on the * * 8x8 ROM font. * **********************************************************************/ void PrintChar( char thechar, int x, int y, BYTE fg, BYTE bk ) { typedef BYTE FDEF[256][8]; /* Font array */ typedef FDEF far *TPTR; /* Pointer to font */ BYTE i, k, /* Loop counter */ BMask; /* Bit mask for character design */ static TPTR fptr = (TPTR) 0; /* Pointer to font in ROM */ if ( fptr == (TPTR) 0 ) /* Pointer to font already set? */ fptr = getfontptr(); /* No --> Use the assembler function to load */ /*- Generate character pixel by pixel ------------------------------*/ if ( bk == 255 ) /* Drawing transparent characters? */ for ( i = 0; i < 8; ++i ) /* Yes --> Set foreground pixels only */ { BMask = (*fptr)[thechar][i]; /* Get bit pattern for one line */ for ( k = 0; k < 8; ++k, BMask <<= 1 ) /* Execute column */ if ( BMask & 128 ) /* Pixel set? */ setpix( x+k, y+i, fg ); /* Yes */ } else /* No --> Consider background as well */ for ( i = 0; i < 8; ++i ) /* Execute lines */ { BMask = (*fptr)[thechar][i]; /* Get bit pattern for one line */ for ( k = 0; k < 8; ++k, BMask <<= 1 ) /* Execute columns */ setpix( x+k, y+i, ( BMask & 128 ) ? fg : bk ); } } /********************************************************************** * GrfxPrintf: Displays a formatted string on the graphic screen. * *---------------------------------------------------------------------* * Input : X, Y = Starting coordinates (0 - ...) * * FG = Foreground color * * BK = Background color (255 = transparent) * * STRING = String with format information * * ... = Arguments similar to printf * **********************************************************************/ void GrfxPrintf( int x, int y, BYTE fg, BYTE bk, char * string, ... ) { va_list parameter; /* Parameter list for VA_... macros */ char stngbuf[255], /* Buffer for formatted string */ *cp; va_start( parameter, string ); /* Convert parameter */ vsprintf( stngbuf, string, parameter ); /* Format */ for ( cp = stngbuf; *cp; ++cp, x+= 8 ) /* Formatted string */ PrintChar( *cp, x, y, fg, bk ); /* Display using PrintChar */ } /********************************************************************** * CreateSprite: Creates a sprite based on a user-defined * * pixel pattern. * **-------------------------------------------------------------------** * Input : SPLOOKP = Pointer the data structure from CompileSprite()* * BKGPAGE = Screen page in which sprite background should * * be stored * * BKX, = bkpage coordinates at which sprite background * * BKY is stored * * Output : Pointer to created sprite structure * * Info : The sprite background requires two areas the same size * * as the corresponding sprite. * **********************************************************************/ SPID *CreateSprite( SPLOOK *splookp, BYTE bkgpage, int bkx, int bky ) { SPID *spidp; /* Pointer to created sprite structure */ spidp = (SPID *) malloc( sizeof(SPID) ); /* Allocate sprite struc. */ spidp->splookp = splookp; /* Pass data to the */ spidp->bkgpage = bkgpage; /* sprite structure */ spidp->bkx = bkx; spidp->bky = bky; return spidp; /* Return pointer to the sprite structure */ } /********************************************************************** * CompileSprite: Creates a sprite's pixel and bit patterns, based on * * the sprite's definition at runtime. * **-------------------------------------------------------------------** * Input : BUFP = Pointer to array contains string pointers * * controlling sprite's pattern * * SHEIGHT = Sprite height (and number of strings needed) * * GPAGE = Graphic page for sprite design * * Y = Pixel lines needed for sprite * * FB = ASCII cahracters for the smallest color * * FGCOLOR = First color code for FB * * Info : Sprite structure of pixel lines starts at left margin. * **********************************************************************/ SPLOOK *CompileSprite( char **bufp, BYTE sheight, BYTE gpage, BYTE y, char fb, BYTE fgcolor ) { BYTE swidth, /* String width */ c, /* Get character from c sprite array */ i, k, l, /* Loop variables */ pixc, /* Pixel counter for creating the bit mask */ pixm, /* Pixel mask */ *lspb; /* Floating pointer in sprite buffer */ int spacing, /* Spacing from start of sprite to start of sprite */ lx, ly; /* Floating coordinates */ SPLOOK *splookp; /* Pointer to created sprite structure */ /*-- Create SpriteLook structure and fill with data ----------------*/ splookp = (SPLOOK *) malloc( sizeof(SPLOOK) ); swidth = strlen( *bufp ); splookp->twidth = spacing = ( ( swidth + 3 + 3 ) / 4 ) * 4; splookp->bmskp = (BYTE *) malloc( (spacing*sheight+7)/8*4 ); splookp->theight = sheight; splookp->pxlin = y; splookp->ppage = gpage; splookp->msklen = (spacing*sheight+7)/8; /*-- Fill sprite background in home page with --------------------*/ /*-- codes for transparent character background --------------------*/ setpage( gpage ); /* Set page for drawing sprite */ for ( ly = y + sheight-1, lx = 4 * spacing - 1; ly >= (int) y; --ly) Line( 0, ly, lx, ly, 255 ); /*-- Draw four matching sprites in the home page -------------------*/ for ( l = 0, lx = 0; l < 4; ++l, lx+=spacing+1 ) for ( i = 0; i < sheight; ++i ) for ( k = 0; k < swidth; ++k ) setpix( lx+k, y+i, ( c = *(*(bufp+i)+k) ) == ' ' ? 255 : fgcolor+(c-fb)); /*-- Execute the four sprites and create bit masks -----------------*/ /*-- for copying the sprites into the bitplanes -----------------*/ for ( l = pixm = pixc = 0, lx = 0; l < 4; ++l, lx+=spacing ) { lspb = splookp->bmskp + splookp->msklen * l; for ( i = 0; i < sheight; ++i ) for ( k = 0; k < spacing; ++k ) { pixm >>= 1; /* Shift pixel mask 1 bit right */ if ( getpix( lx+k, y+i ) != 255 ) /* Background pixel? */ pixm |= 128; /* No --> Set bit for mask */ if ( ++pixc == 8 ) /* Eight pixels already handled? */ { /* Yes --> Place bit mask in sprite buffer */ *lspb++ = pixm; pixc = pixm = 0; /* Set pixel counter and mask to 0 */ } } if ( pixc ) /* Last nibble still not in buffer? */ { /* No --> Move high nibble to */ *lspb = pixm >> 4; /* low nibble and store them */ pixc = pixm = 0; /* Set pixel counter and mask to 0 */ } } return splookp; /* Return pointer to sprite buffer */ } /********************************************************************** * PrintSprite : Displays sprite in a specified page. * **-------------------------------------------------------------------** * Input : SPIDP = Pointer to the sprite structure * * SPRPAGE = Page in which sprite should be drawn (0 or 1) * **********************************************************************/ void PrintSprite( register SPID *spidp, BYTE sprpage ) { BYTE twidth; /* Total width of sprite */ int x; /* X-coordinate of sprite in its page */ SPLOOK *splookp; /* Pointer to sprite's appearance */ twidth = (splookp = spidp->splookp)->twidth; x = spidp->x[sprpage]; blockmove( splookp->ppage, twidth * (x % 4), splookp->pxlin, sprpage, x & ~3, spidp->y[sprpage], twidth, splookp->theight, splookp->bmskp + (x % 4) * splookp->msklen ); } /********************************************************************** * GetSpriteBg: Gets a sprite background and specifies the position. * **-------------------------------------------------------------------** * Input : SPIDP = Pointer to the sprite structure * * SPRPAGE = Page from which the background should be taken * * (0 or 1) * **********************************************************************/ void GetSpriteBg( register SPID *spidp, BYTE sprpage ) { SPLOOK *splookp; /* Pointer to sprite graphic */ splookp = spidp->splookp; blockmove( sprpage, spidp->x[sprpage], spidp->y[sprpage], spidp->bkgpage, spidp->bkx + ( splookp->twidth * sprpage ), spidp->bky, splookp->twidth, splookp->theight, NOBITMASK ); } /********************************************************************** * RestoreSpriteBg: Restores sprite background from original graphic * * page. * **-------------------------------------------------------------------** * Input : SPIDP = Pointer to the sprite structure * * SPRPAGE = Page from which the background should be copied* * (0 or 1) * **********************************************************************/ void RestoreSpriteBg( register SPID *spidp, BYTE sprpage ) { SPLOOK *splookp; /* Pointer to sprite graphic */ splookp = spidp->splookp; blockmove( spidp->bkgpage, spidp->bkx + ( splookp->twidth * sprpage ), spidp->bky, sprpage, spidp->x[sprpage], spidp->y[sprpage], splookp->twidth, splookp->theight, NOBITMASK ); } /********************************************************************** * MoveSprite: Copy sprite within background to original graphic page.* **-------------------------------------------------------------------** * Input : SPIDP = Pointer to the sprite structure * * SPRPAGE = Page to which the background should be copied * * (0 or 1) * * DELTAX = Movement counter in X- * * DELTAY and Y-directions * * Output : Collision marker (see OUT_ constants) * **********************************************************************/ BYTE MoveSprite( SPID *spidp, BYTE sprpage, int deltax, int deltay ) { int newx, newy; /* New sprite coordinates */ BYTE out; /* Display collision with border */ /*-- Move X-coordinates and test for border collision --------------*/ if ( ( newx = spidp->x[sprpage] + deltax ) < 0 ) { newx = 0 - deltax - spidp->x[sprpage]; out = OUT_LEFT; } else if ( newx > 319 - spidp->splookp->twidth ) { newx = 640-newx-2*(spidp->splookp->twidth); out = OUT_RIGHT; } else out = OUT_NO; /*-- Move Y-coordinates and test for border collision --------------*/ if ( ( newy = spidp->y[sprpage] + deltay ) < 0 ) /* Top border? */ { /* Yes --> Deltay must be negative */ newy = 0 - deltay - spidp->y[sprpage]; out |= OUT_TOP; } else if ( newy + spidp->splookp->theight > 199+1 ) /* Bottom border? */ { /* Yes --> Deltay must be positive */ newy = 400-newy-2*(spidp->splookp->theight); out |= OUT_BOTTOM; } /*-- Set new position only if different from old position ----------*/ if ( newx != spidp->x[sprpage] || newy != spidp->y[sprpage] ) { /* If there's a new position */ RestoreSpriteBg( spidp, sprpage ); /* then reset background and */ spidp->x[sprpage] = newx; /* store new coordinates */ spidp->y[sprpage] = newy; GetSpriteBg( spidp, sprpage ); /* Get new background */ PrintSprite( spidp, sprpage ); /* Draw sprite in specified page */ } return out; } /********************************************************************** * SetSprite: Sets sprite at a specific position. * **-------------------------------------------------------------------** * Input : SPIDP = Pointer to the sprite structure * * x0, y0 = Sprite coordinates for page 0 * * x1, y1 = Sprite coordinates for page 1 * * Info : This function call should be made the first time that * * MoveSprite() is called. * **********************************************************************/ void SetSprite( SPID *spidp, int x0, int y0, int x1, int y1 ) { spidp->x[0] = x0; /* Store coordinates in sprite structure */ spidp->x[1] = x1; spidp->y[0] = y0; spidp->y[1] = y1; GetSpriteBg( spidp, 0 ); /* Get sprite backgrounds */ GetSpriteBg( spidp, 1 ); /* in pages 0 and 1 */ PrintSprite( spidp, 0 ); /* Draw sprite in */ PrintSprite( spidp, 1 ); /* pages 0 and 1 */ } /********************************************************************** * RemoveSprite: Removes a sprite from its current position and makes * * it invisible. * **-------------------------------------------------------------------** * Input : SPIDP = Pointer to the sprite structure * * Info : After calling this function the SetSprite() function * * must be called before the sprite can be moved using the * * MoveSprite() function. * **********************************************************************/ void RemoveSprite( SPID *spidp ) { RestoreSpriteBg( spidp, 0 ); /* Reset sprite backgrounds */ RestoreSpriteBg( spidp, 1 ); /* in pages 0 and 1 */ } /********************************************************************** * Demo: Demonstrates these functions. * **********************************************************************/ void Demo( void ) { static char *StarShipUp[20] = { " AA ", " AAAA ", " AAAA ", " AA ", " GGBBGG ", " GBBCCBBG ", " GBBBCCBBBG ", " GBBBBBBBBBBG ", " GBBBBBBBBBBG ", " G GBBBBBBBBBBBBG G ", "GCG GGDBBBBBBBBBBDGG GCG", "GCG GGBBBDBBB BBBDBBBGG GCG", "GCBGGGBBBBBDBB BBDBBBBBGGGBCG", "GCBBBBBBBBBBDB BDBBBBBBBBBBCG", "BBBBBBBBBBBBDB BB BDBBBBBBBBBBBB", "GGCBBBBBBBDBBBBBBBBBBDBBBBBBBCG ", " GGCCBBBDDDDDDDDDDDDDDBBBCCG ", " GGBBDDDDDGGGGGDDDDDDBBG ", " GDDDDGGG GGGDDDDG ", " DDDD DDDD " }; static char *StarShipDown[20] = { " DDDD DDDD ", " GDDDDGGG GGGDDDDG ", " GGBBDDDDDGGGGGDDDDDDBBG ", " GGCCBBBDDDDDDDDDDDDDDBBBCCG ", "GGCBBBBBBBDBBBBBBBBBBDBBBBBBBCG ", "BBBBBBBBBBBBDB BB BDBBBBBBBBBBBB", "GCBBBBBBBBBBDB BDBBBBBBBBBBCG", "GCBGGGBBBBBDBB BBDBBBBBGGGBCG", "GCG GGBBBDBBB BBBDBBBGG GCG", "GCG GGDBBBBBBBBBBDGG GCG", " G GBBBBBBBBBBBBG G ", " GBBBBBBBBBBG ", " GBBBBBBBBBBG ", " GBBBCCBBBG ", " GBBCCBBG ", " GGBBGG ", " AA ", " AAAA ", " AAAA ", " AA " }; #define SPRNUM 6 /* Number of sprites */ #define CWIDTH 38 /* Width of copyright message in characters */ #define CHEIGHT 6 /* Message height in rows */ #define SX (MAXX-(CWIDTH*8)) / 2 /* Starting X-coordinate */ #define SY (MAXY-(CHEIGHT*8)) / 2 /* Starting Y-coordinate */ struct { /* For sprite management */ SPID *spidp; /* Pointer to sprite ID */ int deltax[2], /* X-movement for pages 0 and 1 */ deltay[2]; /* Y-movement for pages 0 and 1 */ } sprites[ SPRNUM ]; BYTE page, /* Current page */ out; /* Get flags for page collision */ int x, y, i, /* Loop counter */ dx, dy; /* Movement value */ char lc; SPLOOK *starshipupp, *starshipdnp; /* Sprite pointer */ srand( *(long far *) 0x0040006c ); /* Initialize random */ /* number generator */ /*-- Fill the first two graphic pages with characters --------------*/ for ( page = 0; page < 2; ++ page ) { setpage( page ); for ( lc = 0, y = 0; y < 200-8; y += 12 ) for ( x = 0; x < 320-8; x += 8 ) GrfxPrintf( x, y, lc % 255, 255, "%c", lc++ & 127 ); /*-- Display copyright message -----------------------------------*/ Line( SX-1, SY-1, SX+CWIDTH*8, SY-1, 15 ); Line( SX+CWIDTH*8, SY-1, SX+CWIDTH*8, SY+CHEIGHT*8,15 ); Line( SX+CWIDTH*8, SY+CHEIGHT*8, SX-1, SY+CHEIGHT*8, 15 ); Line( SX-1, SY+CHEIGHT*8, SX-1, SY-1, 15 ); GrfxPrintf( SX, SY, 15, 4, " " ); GrfxPrintf( SX, SY+8, 15, 4, " S3220C.C (c) 1992 by Michael Tischer " ); GrfxPrintf( SX, SY+16, 15, 4, " " ); GrfxPrintf( SX, SY+24, 15, 4, " Sprite demo for 320x200 mode " ); GrfxPrintf( SX, SY+32, 15, 4, " on VGA cards " ); GrfxPrintf( SX, SY+40, 15, 4, " " ); } /*-- Create patterns for the different sprites ---------------------*/ starshipupp = CompileSprite( StarShipUp, 20, 2, 0, 'A', 1 ); starshipdnp = CompileSprite( StarShipDown, 20, 2, 40, 'A', 1 ); /*-- Create different sprites --------------------------------------*/ for ( i = 0; i < SPRNUM ; ++ i) { sprites[ i ].spidp = CreateSprite( starshipupp, 3, ( i % 3 ) * 100, (i / 3) * 30 ); do /* Select movement value for sprites */ { dx = 0; dy = random(8) - 4; } while ( dx==0 && dy==0 ); sprites[ i ].deltax[0] = sprites[ i ].deltax[1] = dx * 2; sprites[ i ].deltay[0] = sprites[ i ].deltay[1] = dy * 2; x = ( 320 / SPRNUM * i ) + (320 / SPRNUM - 40) / 2 ; y = random( 200 - 40 ); SetSprite( sprites[ i ].spidp, x, y, x - dx, y - dy ); } /*-- Move sprites and bounce them off the page borders -------------*/ page = 1; /* Start with page 1 */ while ( !kbhit() ) /* Press a key to end the loop */ { showpage( 1 - page ); /* Display other page */ for ( i = 0; i < SPRNUM; ++ i) /* Execute sprites */ { /* Move sprite and check for page collision */ out = MoveSprite( sprites[i].spidp, page, sprites[i].deltax[page], sprites[i].deltay[page] ); if ( out & OUT_TOP || out & OUT_BOTTOM ) /* Top/bottom */ /* collision? */ { /* Yes --> Change direction of movement and sprite graphic */ sprites[i].deltay[page] = 0 - sprites[i].deltay[page]; sprites[i].spidp->splookp = ( out & OUT_TOP ) ? starshipdnp : starshipupp; } if ( out & OUT_LEFT || out & OUT_RIGHT ) sprites[i].deltax[page] = 0 - sprites[i].deltax[page]; } page = (page+1) & 1; /* Toggle between 1 and 0 */ } } /*********************************************************************/ /** M A I N P R O G R A M **/ /*********************************************************************/ void main( void ) { union REGS regs; if ( IsVga() ) /* VGA card installed? */ { /* Yes --> Go ahead */ init320200(); /* Initialize graphic mode */ Demo(); getch(); /* Wait for a key */ regs.x.ax = 0x0003; /* Shift into text mode */ int86( 0x10, ®s, ®s ); } else printf( "S3220C.C - (c) 1992 by Michael Tischer\n\n"\ "This program requires a VGA card\n\n" ); } ÿ