139 lines
No EOL
7.4 KiB
Markdown
139 lines
No EOL
7.4 KiB
Markdown
---
|
|
title: Michael Abrash's Graphics Programming Black Book, Special Edition
|
|
author: Michael Abrash
|
|
date: '1997-07-01'
|
|
isbn: '1576101746'
|
|
publisher: The Coriolis Group
|
|
category: 'Web and Software Development: Game Development,Web and Software Development:
|
|
Graphics and Multimedia Development'
|
|
chapter: '34'
|
|
pages: 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 `OUT`s.
|
|
|
|
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 {#Heading8}
|
|
|
|
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. |