128 lines
7.1 KiB
Markdown
128 lines
7.1 KiB
Markdown
#### Color Plane Manipulation {#Heading8}
|
|
|
|
The VGA provides a considerable amount of hardware assistance for
|
|
manipulating the four display memory planes. Two features illustrated by
|
|
the sample program are the ability to control which planes are written
|
|
to by a CPU write and the ability to copy four bytes—one from each
|
|
plane—with a single CPU read and a single CPU write.
|
|
|
|
The Map Mask register (SC register 2) selects which planes are written
|
|
to by CPU writes. If bit 0 of the Map Mask register is 1, then each byte
|
|
written by the CPU will be written to VGA memory plane 0, the plane that
|
|
provides the video data for the least significant bit of the palette RAM
|
|
address. If bit 0 of the Map Mask register is 0, then CPU writes will
|
|
not affect plane 0. Bits 1, 2, and 3 of the Map Mask register similarly
|
|
control CPU access to planes 1, 2, and 3, respectively. Any of the 16
|
|
possible combinations of enabled and disabled planes can be selected.
|
|
Beware, however, of writing to an area of memory that is not zeroed.
|
|
Planes that are disabled by the Map Mask register are not altered by CPU
|
|
writes, so old and new images can mix on the screen, producing unwanted
|
|
color effects as, say, three planes from the old image mix with one
|
|
plane from the new image. The sample program solves this by ensuring
|
|
that the memory written to is zeroed. A better way to set all planes at
|
|
once is provided by the set/reset capabilities of the VGA, which I'll
|
|
cover in Chapter 25.
|
|
|
|
The sample program writes the image of the colored ball to VGA memory by
|
|
enabling one plane at a time and writing the image of the ball for that
|
|
plane. Each image is written to the same VGA addresses; only the
|
|
destination plane, selected by the Map Mask register, is different. You
|
|
might think of the ball's image as consisting of four colored overlays,
|
|
which together make up a multicolored image. The sample program writes a
|
|
blank image to VGA memory by enabling all planes and writing a block of
|
|
zero bytes; the zero bytes are written to all four VGA planes
|
|
simultaneously.
|
|
|
|
The images are written to a nondisplayed portion of VGA memory in order
|
|
to take advantage of a useful VGA hardware feature, the ability to copy
|
|
all four planes at once. As shown by the image-loading code discussed
|
|
above, four different sets of reads and writes—and several **OUT**s as
|
|
well—are required to copy a multicolored image into VGA memory as would
|
|
be needed to draw the same image into a non-planar pixel buffer. This
|
|
causes unacceptably slow performance, all the more so because the wait
|
|
states that occur on accesses to VGA memory make it very desirable to
|
|
minimize display memory accesses, and because **OUT**s tend to be very
|
|
slow.
|
|
|
|
The solution is to take advantage of the VGA's write mode 1, which is
|
|
selected via bits 0 and 1 of the GC Mode register (GC register 5). (Be
|
|
careful to preserve bits 2-7 when setting bits 0 and 1, as is done in
|
|
Listing 23.1.) In write mode 1, a single **CPU** read loads the
|
|
addressed byte from all four planes into the VGA's four internal
|
|
latches, and a single **CPU** write writes the contents of the latches
|
|
to the four planes. During the write, the byte written by the **CPU** is
|
|
irrelevant.
|
|
|
|
The sample program uses write mode 1 to copy the images that were
|
|
previously drawn to the high end of VGA memory into a desired area of
|
|
display memory, all in a single block copy operation. This is an
|
|
excellent way to keep the number of reads, writes, and OUTs required to
|
|
manipulate the VGA's display memory low enough to allow real-time
|
|
drawing.
|
|
|
|
The Map Mask register can still mask out planes in write mode 1. All
|
|
four planes are copied in the sample program because the Map Mask
|
|
register is still 0Fh from when the blank image was created.
|
|
|
|
The animated images appear to move a bit jerkily because they are
|
|
byte-aligned and so must move a minimum of 8 pixels horizontally. This
|
|
is easily solved by storing rotated versions of all images in VGA
|
|
memory, and then in each instance drawing the correct rotation for the
|
|
pixel alignment at which the image is to be drawn; we'll see this
|
|
technique in action in Chapter 49.
|
|
|
|
Don't worry if you're not catching everything in this chapter on the
|
|
first pass; the VGA is a complicated beast, and learning about it is an
|
|
iterative process. We'll be going over these features again, in
|
|
different contexts, over the course of the rest of this book.
|
|
|
|
#### Page Flipping {#Heading9}
|
|
|
|
When animated graphics are drawn directly on the screen, with no
|
|
intermediate frame-composition stage, the image typically flickers
|
|
and/or ripples, an unavoidable result of modifying display memory at the
|
|
same time that it is being scanned for video data. The display memory of
|
|
the VGA makes it possible to perform page flipping, which eliminates
|
|
such problems. The basic premise of page flipping is that one area of
|
|
display memory is displayed while another is being modified. The
|
|
modifications never affect an area of memory as it is providing video
|
|
data, so no undesirable side effects occur. Once the modification is
|
|
complete, the modified buffer is selected for display, causing the
|
|
screen to change to the new image in a single frame's time, typically
|
|
1/60th or 1/70th of a second. The other buffer is then available for
|
|
modification.
|
|
|
|
As described above, the VGA has 64K per plane, enough to hold two pages
|
|
and more in 640x350 mode 10H, but not enough for two pages in 640x480
|
|
mode 12H. For page flipping, two non-overlapping areas of display memory
|
|
are needed. The sample program uses two 672x384 virtual pages, each
|
|
32,256 bytes long, one starting at A000:0000 and the other starting at
|
|
A000:7E00. Flipping between the pages is as simple as setting the start
|
|
address registers to point to one display area or the other—but, as it
|
|
turns out, that's not as simple as it sounds.
|
|
|
|
The timing of the switch between pages is critical to achieving
|
|
flicker-free animation. It is essential that the program never be
|
|
modifying an area of display memory as that memory is providing video
|
|
data. Achieving this is surprisingly complicated on the VGA, however.
|
|
|
|
The problem is as follows. The start address is latched by the VGA's
|
|
internal circuitry exactly once per frame, typically (but not always on
|
|
all clones) at the start of the vertical sync pulse. The vertical sync
|
|
status is, in fact, available as bit 3 of the Input Status 0 register,
|
|
addressable at 3BAH (in monochrome modes) or 3DAH (color).
|
|
Unfortunately, by the time the vertical sync status is observed by a
|
|
program, the start address for the next frame has already been latched,
|
|
having happened the instant the vertical sync pulse began. That means
|
|
that it's no good to wait for vertical sync to begin, then set the new
|
|
start address; if we did that, we'd have to wait until the *next*
|
|
vertical sync pulse to start drawing, because the page wouldn't flip
|
|
until then.
|
|
|
|
Clearly, what we want is to set the new start address, then wait for the
|
|
start of the vertical sync pulse, at which point we can be sure the page
|
|
has flipped. However, we can't just set the start address and wait,
|
|
because we might have the extreme misfortune to set one of the start
|
|
address registers before the start of vertical sync and the other after,
|
|
resulting in mismatched halves of the start address and a nasty jump of
|
|
the displayed image for one frame.
|