118 lines
6 KiB
Markdown
118 lines
6 KiB
Markdown
The 640x400 mode I've described here isn't exactly earthshaking, but it
|
||
can come in handy for page flipping and CGA emulation, and I'm sure that
|
||
some of you will find it useful at one time or another.
|
||
|
||
### Another Interesting Twist on Page Flipping {#Heading9}
|
||
|
||
I've spent a fair amount of time exploring various ways to do animation.
|
||
I thought I had pegged all the possible ways to do animation:
|
||
exclusive-ORing; simply drawing and erasing objects; drawing objects
|
||
with a blank fringe to erase them at their old locations as they're
|
||
drawn; page flipping; and, finally, drawing to local memory and copying
|
||
the dirty (modified) rectangles to the screen, as I've discussed in this
|
||
chapter.
|
||
|
||
To my surprise, someone threw me an interesting and useful twist on
|
||
animation not long ago, which turned out to be a cross between page
|
||
flipping and dirty-rectangle animation. That someone was Serge Mathieu
|
||
of Concepteva Inc., in Rosemere, Quebec, who informed me that he designs
|
||
everything "from a game *point de vue*."
|
||
|
||
In normal page flipping, you display one page while you update the other
|
||
page. Then you display the new page while you update the other. This
|
||
works fine, but the need to keep two pages current can make for a lot of
|
||
bookkeeping and possibly extra drawing, especially in applications where
|
||
only some of the objects are redrawn each time.
|
||
|
||
Serge didn't care to do all that bookkeeping in his animation
|
||
applications, so he came up with the following approach, which I've
|
||
reworded, amplified, and slightly modified in the summary here:
|
||
|
||
**1.** Set the start address to display page 0.
|
||
|
||
**2.** Draw to page 1.
|
||
|
||
**3.** Set the start address to display page 1 (the newly drawn page),
|
||
then wait for the leading edge of vertical sync, at which point the page
|
||
has flipped and it's safe to modify page 0.
|
||
|
||
**4.** Copy, via the latches, from page 1 to page 0 the areas that
|
||
changed from the previous screen to the current one.
|
||
|
||
**5.** Set the start address to display page 0, which is now identical
|
||
to page 1, then wait for the leading edge of vertical sync, at which
|
||
point the page has flipped and it's safe to modify page 1.
|
||
|
||
**6.** Go to step 2.
|
||
|
||
The great benefit of Serge's approach is that the only page that is ever
|
||
actually drawn to (as opposed to being block-copied to) is page 1. Only
|
||
one page needs to be maintained, and the complications of maintaining
|
||
two separate pages vanish entirely. The performance of Serge's approach
|
||
may be better or worse than standard page flipping, depending on whether
|
||
a lot of extra work is required to maintain two pages or not. My guess
|
||
is that Serge's approach will usually be slower, owing to the
|
||
considerable amount of display-memory copying involved, and also to the
|
||
double page-flip per frame. There's no doubt, however, that Serge's
|
||
approach is simpler, and the resultant display quality is every bit as
|
||
good as standard page flipping. Given page flipping's fair degree of
|
||
complication, this approach is a valuable tool, especially for
|
||
less-experienced animation programmers.
|
||
|
||
An interesting variation on Serge's approach doesn't page flip nor wait
|
||
for vertical sync:
|
||
|
||
**1.** Set the start address to display page 0.
|
||
|
||
**2.** Draw to page 1.
|
||
|
||
**3.** Copy, via the latches, the areas that changed from the last
|
||
screen to the current one from page 1 to page 0.
|
||
|
||
**4.** Go to step 2.
|
||
|
||
This approach totally eliminates page flipping, which can consume a
|
||
great deal of time. The downside is that images may shear for one frame
|
||
if they're only partially copied when the raster beam reaches them. This
|
||
approach is basically a standard dirty-rectangle approach, except that
|
||
the drawing buffer is stored in display memory, rather than in system
|
||
memory. Whether this technique is faster than drawing to system memory
|
||
depends on whether the benefit you get from the VGA's hardware, such as
|
||
the Bit Mask, the ALUs, and especially the latches (for copying the
|
||
dirty rectangles) is sufficient to outweigh the extra display-memory
|
||
accesses involved in drawing and copying, since display memory is
|
||
notoriously slow.
|
||
|
||
Finally, I'd like to point out that in any scheme that involves changing
|
||
the display-memory start address, a clever trick can potentially reduce
|
||
the time spent waiting for pages to flip. Normally, it's necessary to
|
||
wait for display enable to be active, then set the two start address
|
||
registers, and finally wait for vertical sync to be active, so that you
|
||
know the new start address has taken effect. The start-address registers
|
||
must never be set around the time vertical sync is active (the new start
|
||
address is accepted at either the start or end of vertical sync on the
|
||
EGAs and VGAs I'm familiar with), because it would then be possible to
|
||
load a half-changed start address (one register loaded, the other not
|
||
yet loaded), and the screen would jump for a frame. Avoiding this
|
||
condition is the motivation for waiting for display enable, because
|
||
display enable is active only when vertical sync is not active and will
|
||
not become active for a long while.
|
||
|
||
Suppose, however, that you arrange your page start addresses so that
|
||
they both have a low-byte value of 0 (page 0 starts at 0000H, and page 1
|
||
starts at 8000H, for example). Page flipping can then be done simply by
|
||
setting the new high byte of the start address, then waiting for the
|
||
leading edge of vertical sync. This eliminates the need to wait for
|
||
display enable (the two bytes of the start address can never be
|
||
mismatched); page flipping will often involve less waiting, because
|
||
display enable becomes inactive long before vertical sync becomes
|
||
active. Using the above approach reclaims all the time between the end
|
||
of display enable and the start of vertical sync for doing useful work.
|
||
(The steps I've given for Serge's animation approach assume that the
|
||
single-byte approach is in use; that's why display enable is never
|
||
waited for.)
|
||
|
||
In the next chapter, I'll return to the original dirty-rectangle
|
||
algorithm presented in this chapter, and goose it a little with some
|
||
assembly, so that we can see what dirty-rectangle animation is really
|
||
made of. (Probably not dog hair....)
|