7.4 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 | 34 | 648-650 |
The big question is, How does Listing 34.1 cycle colors? Via the BIOS or directly? With interrupts enabled or disabled? Et cetera?
However you like, actually. Four equates at the top of Listing 34.1
select the sort of color cycling performed; by changing these equates
and CYCLE_SIZE, you can get a feel for how well various approaches
to color cycling work with whatever combination of computer system and
VGA you care to test.
The USE_BIOS equate is simple. Set USE_BIOS to 1 to load the
DAC through the block-load-DAC BIOS function, or to 0 to load the DAC
directly with OUTs.
If USE_BIOS is 1, the only other equate of interest is
WAIT_VSYNC. If WAIT_VSYNC is 1, the program waits for the
leading edge of vertical sync before loading the DAC; if WAIT_VSYNC
is 0, the program doesn't wait before loading. The effect of setting or
not setting WAIT_VSYNC depends on whether the BIOS of the VGA the
program is running on waits for vertical sync before loading the DAC.
You may end up with a double wait, causing color cycling to proceed at
half speed, you may end up with no wait at all, causing cycling to occur
far too rapidly (and almost certainly with hideous on-screen effects),
or you may actually end up cycling at the proper one-cycle-per-frame
rate.
If USE_BIOS is 0, WAIT_VSYNC still applies. However, you will
always want to set WAIT_VSYNC to 1 when USE_BIOS is 0;
otherwise, cycling will occur much too fast, and a good deal of
continuous on-screen garbage is likely to make itself evident as the
program loads the DAC non-stop.
If USE_BIOS is 0, GUARD_AGAINST_INTS determines whether the
possibility of the DAC loading process being interrupted is guarded
against by disabling interrupts and setting the write index once for
every location loaded and whether the DAC's autoincrementing feature is
relied upon or not.
If GUARD_AGAINST_INTS is 1, the following sequence is followed for
the loading of each DAC location in turn: Interrupts are disabled, the
DAC Write Index register is set appropriately, the RGB triplet for the
location is written to the DAC Data register, and interrupts are
enabled. This is the slow but safe approach described earlier.
Matters get still more interesting if GUARD_AGAINST_INTS is 0. In
that case, if NOT_8088 is 0, then an autoincrementing load is
performed in a straightforward fashion; the DAC Write Index register is
set to the index of the first location to load and the RGB triplet is
sent to the DAC by way of three LODSB/OUT DX,AL pairs, with LOOP
repeating the process for each of the locations in turn.
If, however, NOT_8088 is 1, indicating that the processor is a 286
or better (perhaps AT_LEAST_286 would have been a better name),
then after the initial DAC Write Index value is set, all 768 DAC
locations are loaded with a single REP OUTSB. This is clearly the
fastest approach, but it runs the risk, albeit remote, that the loading
sequence will be interrupted and the DAC registers will become garbled.
My own experience with Listing 34.1 indicates that it is sometimes
possible to load all 256 locations cleanly but sometimes it is not; it
all depends on the processor, the bus speed, the VGA, and the DAC, as
well as whether autoincrementation and REP OUTSB are used. I'm not
going to bother to report how many DAC locations I could successfully
load with each of the various approaches, for the simple reason that I
don't have enough data points to make reliable suggestions, and I don't
want you acting on my comments and running into trouble down the pike.
You now have a versatile tool with which to probe the limitations of
various DAC-loading approaches; use it to perform your own tests on a
sampling of the slowest hardware configurations you expect your programs
to run on, then leave a generous safety margin.
One thing's for sure, though—you're not going to be able to cycle all 256 DAC locations cleanly once per frame on a reliable basis across the current generation of PCs. That's why I said at the outset that brute force isn't appropriate to the task of color cycling. That doesn't mean that color cycling can't be used, just that subtler approaches must be employed. Let's look at some of those alternatives.
Color Cycling Approaches that Work
First of all, I'd like to point out that when color cycling does work,
it's a thing of beauty. Assemble Listing 34.1 so that it doesn't use the
BIOS to load the DAC, doesn't guard against interrupts, and uses
286-specific instructions if your computer supports them. Then tinker
with CYCLE_SIZE until the color cycling is perfectly clean on your
computer. Color cycling looks stunningly smooth, doesn't it? And this is
crude color cycling, working with the default color set; switch over to
a color set that gradually works its way through various hues and
saturations, and you could get something that looks for all the world
like true-color animation (albeit working with a small subset of the
full spectrum at any one time).
Given that, how can we take advantage of color cycling within the limitations of loading the DAC? The simplest approach, and my personal favorite, is that of cycling a portion of the DAC while using the rest of the DAC locations for other, non-cycling purposes. For example, you might allocate 32 DAC locations to the aforementioned sunset, reserve 160 additional locations for use in drawing a static mountain scene, and employ the remaining 64 locations to draw images of planes, cars, and the like in the foreground. The 32 sunset colors could be cycled cleanly, and the other 224 colors would remain the same throughout the program, or would change only occasionally.
That suggests a second possibility: If you have several different color sets to be cycled, interleave the loading so that only one color set is cycled per frame. Suppose you are animating a night scene, with stars twinkling in the background, meteors streaking across the sky, and a spaceship moving across the screen with its jets flaring. One way to produce most of the necessary effects with little effort would be to draw the stars in several attributes and then cycle the colors for those attributes, draw the meteor paths in successive attributes, one for each pixel, and then cycle the colors for those attributes, and do much the same for the jets. The only remaining task would be to animate the spaceship across the screen, which is not a particularly difficult task.
The key to getting all the color cycling to work in the above example, however, would be to assign each color cycling task a different part of the DAC, with each part cycled independently as needed. If, as is likely, the total number of DAC locations cycled proved to be too great to manage in one frame, you could simply cycle the colors of the stars after one frame, the colors of the meteors after the next, and the colors of the jets after yet another frame, then back around to cycling the colors of the stars. By splitting up the DAC in this manner and interleaving the cycling tasks, you can perform a great deal of seemingly complex color animation without loading very much of the DAC during any one frame.
