356 lines
14 KiB
HTML
356 lines
14 KiB
HTML
<HTML>
|
|
<HEAD>
|
|
<META name=vsisbn content="1576101746">
|
|
<META name=vstitle content="Michael Abrash's Graphics Programming Black Book, Special Edition">
|
|
<META name=vsauthor content="Michael Abrash">
|
|
<META name=vspublisher content="The Coriolis Group">
|
|
<META name=vspubdate content="07/01/97">
|
|
<META name=vscategory content="Web and Software Development: Game Development,Web and Software Development: Graphics and Multimedia Development">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<TITLE>Michael Abrash's Graphics Programming Black Book Special Edition: Split Screens Save the Page Flipped Day</TITLE>
|
|
|
|
<!-- HEADER -->
|
|
<!-- Empty Reference Subhead -->
|
|
|
|
<!--ISBN=1576101746//-->
|
|
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
|
<!--AUTHOR=Michael Abrash//-->
|
|
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
|
<!--CHAPTER=44//-->
|
|
<!--PAGES=820-825//-->
|
|
<!--UNASSIGNED1//-->
|
|
<!--UNASSIGNED2//--></HEAD><BODY LINK=#0000FF ALINK=#000099 VLINK=#0000FF BGCOLOR=#FFFFFF>
|
|
|
|
<CENTER>
|
|
<TABLE BORDER>
|
|
<TR>
|
|
<TD><A HREF="44-01.html">Previous</A></TD>
|
|
<TD><A HREF="index.html">Table of Contents</A></TD>
|
|
<TD><A HREF="44-03.html">Next</A></TD>
|
|
</TR>
|
|
</TABLE>
|
|
</CENTER>
|
|
<P><BR></P>
|
|
<P><B>LISTING 44.1 L44-1.C</B></P>
|
|
<!-- CODE //-->
|
|
<PRE>
|
|
/* 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;
|
|
}
|
|
</PRE>
|
|
<!-- END CODE //-->
|
|
<P><BR></P>
|
|
<CENTER>
|
|
<TABLE BORDER>
|
|
<TR>
|
|
<TD><A HREF="44-01.html">Previous</A></TD>
|
|
<TD><A HREF="index.html">Table of Contents</A></TD>
|
|
<TD><A HREF="44-03.html">Next</A></TD>
|
|
</TR>
|
|
</TABLE>
|
|
</CENTER>
|
|
|
|
<hr width="90%" size="1" noshade>
|
|
<div align="center">
|
|
<font face="Verdana,sans-serif" size="1">Graphics Programming Black Book © 2001 Michael Abrash</font>
|
|
</div>
|
|
<!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
|
<!-- reference_subfoot = footer -->
|
|
<!-- reference_footer = subfoot -->
|
|
|
|
<!-- BEGIN SUB FOOTER -->
|
|
</BODY>
|
|
</HTML>
|
|
|
|
<!-- END FOOTER -->
|
|
|
|
|