123 lines
5 KiB
Markdown
123 lines
5 KiB
Markdown
In 640x400, 16-color mode, page 0 runs from offset 0 to offset 31,999
|
|
(7CFFH), and page 1 runs from offset 32,000 (7D00H) to 63,999 (0F9FFH).
|
|
Page 1 is selected by programming the Start Address registers (CRTC
|
|
registers 0CH, the high 8 bits, and 0DH, the low 8 bits) to 7D00H.
|
|
Actually, because the low byte of the start address is 0 for both pages,
|
|
you can page flip simply by writing 0 or 7DH to the Start Address High
|
|
register (CRTC register 0CH); this has the benefit of eliminating a
|
|
nasty class of potential synchronization bugs that can arise when both
|
|
registers must be set. Listing 45.3 illustrates simple 640x400 page
|
|
flipping.
|
|
|
|
**LISTING 45.3 L45-3.C**
|
|
|
|
/* Sample program to exercise VGA 640x400 16-color mode page flipping, by
|
|
drawing a horizontal line at the top of page 0 and another at bottom of page 1,
|
|
then flipping between them once every 30 frames. Tested with Borland C++,
|
|
in C compilation mode. */
|
|
|
|
#include <dos.h>
|
|
#include <conio.h>
|
|
|
|
#define SCREEN_SEGMENT 0xA000
|
|
#define SCREEN_HEIGHT 400
|
|
#define SCREEN_WIDTH_IN_BYTES 80
|
|
#define INPUT_STATUS_1 0x3DA /* color-mode address of Input Status 1
|
|
register */
|
|
/* The page start addresses must be even multiples of 256, because page
|
|
flipping is performed by changing only the upper start address byte */
|
|
#define PAGE_0_START 0
|
|
#define PAGE_1_START (400*SCREEN_WIDTH_IN_BYTES)
|
|
|
|
void main(void);
|
|
void Wait30Frames(void);
|
|
extern void Set640x400(void);
|
|
|
|
void main()
|
|
{
|
|
int i;
|
|
unsigned int far *ScreenPtr;
|
|
union REGS regset;
|
|
|
|
Set640x400(); /* set to 640x400 16-color mode */
|
|
|
|
/* Point to first line of page 0 and draw a horizontal line across screen */
|
|
FP_SEG(ScreenPtr) = SCREEN_SEGMENT;
|
|
FP_OFF(ScreenPtr) = PAGE_0_START;
|
|
for (i=0; i<(SCREEN_WIDTH_IN_BYTES/2); i++) *ScreenPtr++ = 0xFFFF;
|
|
|
|
/* Point to last line of page 1 and draw a horizontal line across screen */
|
|
FP_OFF(ScreenPtr) =
|
|
PAGE_1_START + ((SCREEN_HEIGHT-1)*SCREEN_WIDTH_IN_BYTES);
|
|
for (i=0; i<(SCREEN_WIDTH_IN_BYTES/2); i++) *ScreenPtr++ = 0xFFFF;
|
|
|
|
/* Now flip pages once every 30 frames until a key is pressed */
|
|
do {
|
|
Wait30Frames();
|
|
|
|
/* Flip to page 1 */
|
|
outpw(0x3D4, 0x0C | ((PAGE_1_START >> 8) << 8));
|
|
|
|
Wait30Frames();
|
|
|
|
/* Flip to page 0 */
|
|
outpw(0x3D4, 0x0C | ((PAGE_0_START >> 8) << 8));
|
|
} while (kbhit() == 0);
|
|
|
|
getch(); /* clear the key press */
|
|
|
|
/* Return to text mode and exit */
|
|
regset.x.ax = 0x0003; /* AL = 3 selects 80x25 text mode */
|
|
int86(0x10, ®set, ®set);
|
|
}
|
|
|
|
void Wait30Frames()
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<30; i++) {
|
|
/* Wait until we're not in vertical sync, so we can catch leading edge */
|
|
while ((inp(INPUT_STATUS_1) & 0x08) != 0) ;
|
|
/* Wait until we are in vertical sync */
|
|
while ((inp(INPUT_STATUS_1) & 0x08) == 0) ;
|
|
}
|
|
}
|
|
|
|
After I described 640x400 mode in a magazine article, Bill Lindley, of
|
|
Mesa, Arizona, wrote me to suggest that when programming the VGA to a
|
|
nonstandard mode of this sort, it's a good idea to tell the BIOS about
|
|
the new screen size, for a couple of reasons. For one thing, pop-up
|
|
utilities often use the BIOS variables; Bill's memory-resident screen
|
|
printer, EGAD Screen Print, determines the number of scan lines to print
|
|
by multiplying the BIOS "number of text rows" variable times the
|
|
"character height" variable. For another, the BIOS itself may do a poor
|
|
job of displaying text if not given proper information; the active text
|
|
area may not match the screen dimensions, or an inappropriate graphics
|
|
font may be used. (Of course, the BIOS isn't going to be able to display
|
|
text anyway in highly nonstandard modes such as Mode X, but it will do
|
|
fine in slightly nonstandard modes such as 640x400 16-color mode.) In
|
|
the case of the 640x400 16-color model described a little earlier, Bill
|
|
suggests that the code in Listing 45.4 be called immediately after
|
|
putting the VGA into that mode to tell the BIOS that we're working with
|
|
25 rows of 16-pixel-high text. I think this is an excellent suggestion;
|
|
it can't hurt, and may save you from getting aggravating tech support
|
|
calls down the road.
|
|
|
|
**LISTING 45.4 L45-4.C**
|
|
|
|
/* Function to tell the BIOS to set up properly sized characters for 25 rows of
|
|
16 pixel high text in 640x400 graphics mode. Call immediately after mode set.
|
|
Based on a contribution by Bill Lindley. */
|
|
|
|
#include <dos.h>
|
|
|
|
void Set640x400()
|
|
{
|
|
union REGS regs;
|
|
|
|
regs.h.ah = 0x11; /* character generator function */
|
|
regs.h.al = 0x24; /* use ROM 8x6 character set for graphics */
|
|
regs.h.bl = 2; /* 25 rows */
|
|
int86(0x10, ®s, ®s); /* invoke the BIOS video interrupt
|
|
to set up the text */
|
|
}
|