7.1 KiB
| title | author | date | isbn | publisher | category | chapter | pages |
|---|---|---|---|---|---|---|---|
| Michael Abrash's Graphics Programming Black Book, Special Edition | Michael Abrash | 1997-07-01 | 1576101746 | The Coriolis Group | Web and Software Development: Game Development,Web and Software Development: Graphics and Multimedia Development | 24 | 458-460 |
Logical function 0, which writes the CPU data unmodified, is the standard mode of operation of the ALUs. In this mode, the CPU data is combined with the latched data by ignoring the latched data entirely. Expressed as a logical function, this could be considered CPU data ANDed with 1 (or ORed with 0). This is the mode to use whenever you want to place CPU data into display memory, replacing the previous contents entirely. It may occur to you that there is no need to latch display memory at all when the data unmodified function is selected. In the sample program, that is true, but if the bit mask is being used, the latches must be loaded even for the data unmodified function, as I'll discuss in the next chapter.
Logical functions 1 through 3 cause the CPU data to be ANDed, ORed, and XORed with the latched data, respectively. Of these, XOR is the most useful, since exclusive-ORing is a traditional way to perform animation. The uses of the AND and OR logical functions are less obvious. AND can be used to mask a blank area into display memory, or to mask off those portions of a drawing operation that don't overlap an existing display memory image. OR could conceivably be used to force an image into display memory over an existing image. To be honest, I haven't encountered any particularly valuable applications for AND and OR, but they're the sort of building-block features that could come in handy in just the right context, so keep them in mind.
Notes on the ALU/Latch Demo Program
VGA settings such as the logical function select should be restored to their default condition before the BIOS is called to output text or draw pixels. The VGA BIOS does not guarantee that it will set most VGA registers except on mode sets, and there are so many compatible BIOSes around that the code of the IBM BIOS is not a reliable guide. For instance, when the BIOS is called to draw text, it's likely that the result will be illegible if the Bit Mask register is not in its default state. Similarly, a mode set should generally be performed before exiting a program that tinkers with VGA settings.
Along the same lines, the sample program does not explicitly set the Map Mask register to ensure that all planes are enabled for writing. The mode set for mode 10H leaves all planes enabled, so I did not bother to program the Map Mask register, or any other register besides the Data Rotate register, for that matter. However, the profusion of compatible BIOSes means there is some small risk in relying on the BIOS to leave registers set properly. For the highly safety-conscious, the best course would be to program data control registers such as the Map Mask and Read Mask explicitly before relying on their contents.
On the other hand, any function the BIOS provides explicitly—as part of the interface specification—such as setting the palette RAM, should be used in preference to programming the hardware directly whenever possible, because the BIOS may mask hardware differences between VGA implementations.
The code that draws each vertical box in the sample program reads from
display memory immediately before writing to display memory. The read
operation loads the VGA latches. The value that is read is irrelevant as
far as the sample program is concerned. The read operation is present
only because it is necessary to perform a read to load the latches, and
there is no way to read without placing a value in a register. This is a
bit of a nuisance, since it means that the value of some 8-bit register
must be destroyed. Under certain circumstances, a single logical
instruction such as XOR or AND can be used to perform both the
read to load the latches and then write to modify display memory without
affecting any CPU registers, as we'll see later on.
All text in the sample program is drawn by VGA BIOS function 13H, the write string function. This function is also present in the AT's BIOS, but not in the XT's or PC's, and as a result is rarely used; the function is always available if a VGA is installed, however. Text drawn with this function is relatively slow. If speed is important, a program can draw text directly into display memory much faster in any given display mode. The great virtue of the BIOS write string function in the case of the VGA is that it provides an uncomplicated way to get text on the screen reliably in any mode and color, over any background.
The expression used to load DX in the TEXT_UP macro in the sample
program may seem strange, but it's a convenient way to save a byte of
program code and a few cycles of execution time. DX is being loaded with
a word value that's composed of two independent immediate byte values.
The obvious way to implement this would be with
MOV DL,VALUE1
MOV DH,VALUE2
which requires four instruction bytes. By shifting the value destined
for the high byte into the high byte with MASM's shift-left operator,
SHL (*100H would work also), and then logically combining the
values with MASM's OR operator (or the ADD operator), both
halves of DX can be loaded with a single instruction, as in
MOV DX,(VALUE2 SHL 8) OR VALUE1
which takes only three bytes and is faster, being a single instruction.
(Note, though, that in 32-bit protected mode, there's a size and
performance penalty for 16-bit instructions such as the MOV above;
see the first part of this book for details.) As shown, a macro is an
ideal place to use this technique; the macro invocation can refer to two
separate byte values, making matters easier for the programmer, while
the macro itself can combine the values into a single word-sized
constant.
A minor optimization tip illustrated in the listing is the use of
INC AXandDEC AXin theDrawVerticalBoxsubroutine when only AL actually needs to be modified. Word-sized register increment and decrement instructions (or dword-sized instructions in 32-bit protected mode) are only one byte long, while byte-size register increment and decrement instructions are two bytes long. Consequently, when size counts, it is worth using a whole 16-bit (or 32-bit) register instead of the low 8 bits of that register forINCandDEC—if you don't need the upper portion of the register for any other purpose, or if you can be sure that theINCorDECwon't affect the upper part of the register.
The latches and ALUs are central to high-performance VGA code, since
they allow programs to process across all four memory planes without a
series of OUTs and read/write operations. It is not always easy to
arrange a program to exploit this power, however, because the ALUs are
far more limited than a CPU. In many instances, however, additional
hardware in the VGA, including the bit mask, the set/reset features, and
the barrel shifter, can assist the ALUs in controlling data, as we'll
see in the next few chapters.
