123 lines
7.8 KiB
Markdown
123 lines
7.8 KiB
Markdown
Listing 44.1 is written in C. It could equally well have been written in
|
|
assembly language, and would then have been somewhat faster. However,
|
|
wanted to make the point (as I've made again and again) that assembly
|
|
language, and, indeed, optimization in general, is needed only in the
|
|
most critical portions of any program, and then only when the program
|
|
would otherwise be too slow. Only in a highly performance-sensitive
|
|
situation would the performance boost resulting from converting Listing
|
|
44.1 to assembly justify the time spent in coding and the bugs that
|
|
would likely creep in—and the sample program already updates the screen
|
|
at the maximum possible rate of once per frame even on a 1985-vintage
|
|
8-MHz AT. In this case, faster performance would result only in a longer
|
|
wait for the page to flip.
|
|
|
|
#### Write Mode 3 {#Heading5 align="center"}
|
|
|
|
It's possible to update the bitmap very efficiently on the VGA, because
|
|
the VGA can draw up to 8 pixels at once, and because the VGA provides a
|
|
number of hardware features to speed up drawing. This article makes
|
|
considerable use of one particularly unusual hardware feature, write
|
|
mode 3. We discussed write mode 3 back in Chapter 26, but we've covered
|
|
a lot of ground since then—so I'm going to run through a quick refresher
|
|
on write mode 3.
|
|
|
|
Some background: In the standard VGA's high-resolution mode, mode 12H
|
|
(640x480 with 16 colors, the mode in which this chapter's sample program
|
|
runs), each byte of display memory controls 8 adjacent pixels on the
|
|
screen. (The color of each pixel is, in turn, controlled by 4 bits
|
|
spread across the four VGA memory planes, but we need not concern
|
|
ourselves with that here.) Now, there will often be times when we want
|
|
to change some but not all of the pixels controlled by a particular byte
|
|
of display memory. This is not easily done, for there is no way to write
|
|
half a byte, or two bits, or such to memory; it's the whole byte or none
|
|
of it at all.
|
|
|
|
You might think that using AND and OR to manipulate individual bits
|
|
could solve the problem. Alas, not so. ANDing and ORing would work if
|
|
the VGA had only one plane of memory (like the original monochrome
|
|
Hercules Graphics Adapter) but the VGA has four planes, and ANDing and
|
|
ORing would work only if we selected and manipulated each plane
|
|
separately, a process that would be hideously slow. No, with the VGA you
|
|
must use the hardware assist features, or you might as well forget about
|
|
real-time screen updates altogether. Write mode 3 will do the trick for
|
|
our present needs.
|
|
|
|
Write mode 3 is useful when you want to set some but not all of the
|
|
pixels in a single byte of display memory *to the same color.* That is,
|
|
if you want to draw a number of pixels within a byte in a single color,
|
|
write mode 3 is a good way to do it.
|
|
|
|
Write mode 3 works like this. First, set the Graphics Controller Mode
|
|
register to write mode 3. (Look at Listing 44.2 for code that does
|
|
everything described here.) Next, set the Set/Reset register to the
|
|
color with which you wish to draw, in the range 0-15. (It is not
|
|
necessary to explicitly enable set/reset via the Enable Set/Reset
|
|
register; write mode 3 does that automatically.) Then, to draw
|
|
individual pixels within a single byte, simply read display memory, and
|
|
then write a byte to display memory with 1-bits where you want the color
|
|
to be drawn and 0-bits where you want the current bitmap contents to be
|
|
preserved. (Note well that *the data actually read by the CPU doesn't
|
|
matter;* the read operation latches all four planes' data, as described
|
|
way back in Chapter 24.) So, for example, if write mode 3 is enabled and
|
|
the Set/Reset register is set to 1 (blue), then the following sequence
|
|
of operations:
|
|
|
|
mov dx,0a000h
|
|
mov es,dx
|
|
mov al,es:[0]
|
|
mov byte ptr es:[0],0f0h
|
|
|
|
will change the first 4 pixels on the screen (the left nibble of the
|
|
byte at offset 0 in display memory) to blue, and will leave the next 4
|
|
pixels (the right nibble of the byte at offset 0) unchanged.
|
|
|
|
Using one **MOV** to read from display memory and another to write to
|
|
display memory is not particularly efficient on some processors. In
|
|
Listing 44.2, I instead use **XCHG**, which reads and then writes a
|
|
memory location in a single operation, as in:
|
|
|
|
mov dx,0a000h
|
|
mov es,dx
|
|
mov al,0f0h
|
|
xchg es:[0],al
|
|
|
|
Again, the actual value that's read is irrelevant. In general, the
|
|
**XCHG** approach is more compact than two **MOV**s, and is faster on
|
|
386 and earlier processors, but slower on 486s and Pentiums.
|
|
|
|
If all pixels in a byte of display memory are to be drawn in a single
|
|
color, it's not necessary to read before writing, because none of the
|
|
information in display memory at that byte needs to be preserved; a
|
|
simple write of 0FFH (to draw all bits) will set all 8 pixels to the
|
|
set/reset color:
|
|
|
|
mov dx,0a000h
|
|
mov es,dx
|
|
mov byte ptr es:[di],0ffh
|
|
|
|
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
 *If you're familiar with VGA programming, you're no doubt aware that everything that can be done with write mode 3 can also be accomplished in write mode 0 or write mode 2 by using the Bit Mask register. However, setting the Bit Mask register requires at least one **OUT** per byte written, in addition to the read and write of display memory, and **OUT**s are often slower than display memory accesses, especially on 386s and 486s. One of the great virtues of write mode 3 is that it requires virtually no **OUT**s and is therefore substantially faster for masking than the other write modes.*
|
|
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
In short, write mode 3 is a good choice for single-color drawing that
|
|
modifies individual pixels within display memory bytes. Not
|
|
coincidentally, the sample application draws only single-color objects
|
|
within the animation area; this allows write mode 3 to be used for all
|
|
drawing, in keeping with our desire for speedy screen updates.
|
|
|
|
#### Drawing Text {#Heading6 align="center"}
|
|
|
|
We'll need text in the sample application; is that also a good use for
|
|
write mode 3? Sometimes it is, but not in this particular case.
|
|
|
|
Each character in a font is represented by a pattern of bits, with
|
|
1-bits representing character pixels and 0-bits representing background
|
|
pixels. Since we'll be using the 8x8 font stored in the BIOS ROM (a
|
|
pointer to which can be obtained by calling a BIOS service, as
|
|
illustrated by Listing 44.2), each character is exactly 8 bits, or 1
|
|
byte wide. We'll further insist that characters be placed on byte
|
|
boundaries (that is, with their left edges only at pixels with X
|
|
coordinates that are multiples of 8); this means that the character
|
|
bytes in the font are automatically aligned with display memory, and no
|
|
rotation or clipping of characters is needed. Finally, we'll draw all
|
|
text in white.
|