135 lines
8.3 KiB
Markdown
135 lines
8.3 KiB
Markdown
------------------------ --------------------------------- --------------------
|
||
[Previous](44-03.html) [Table of Contents](index.html) [Next](44-05.html)
|
||
------------------------ --------------------------------- --------------------
|
||
|
||
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.
|
||
|
||
------------------------ --------------------------------- --------------------
|
||
[Previous](44-03.html) [Table of Contents](index.html) [Next](44-05.html)
|
||
------------------------ --------------------------------- --------------------
|
||
|
||
* * * * *
|
||
|
||
Graphics Programming Black Book © 2001 Michael Abrash
|