135 lines
5.5 KiB
Markdown
135 lines
5.5 KiB
Markdown
------------------------ --------------------------------- --------------------
|
||
[Previous](45-04.html) [Table of Contents](index.html) [Next](45-06.html)
|
||
------------------------ --------------------------------- --------------------
|
||
|
||
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 */
|
||
}
|
||
|
||
------------------------ --------------------------------- --------------------
|
||
[Previous](45-04.html) [Table of Contents](index.html) [Next](45-06.html)
|
||
------------------------ --------------------------------- --------------------
|
||
|
||
* * * * *
|
||
|
||
Graphics Programming Black Book © 2001 Michael Abrash
|