Right, a somewhat cryptic title perhaps, but don’t worry. It’s just the usual 8088-retroprogramming talk again. I want to talk about how some values in PC hardware are latched, and how you can use that to your advantage.
Latched values in this context basically mean values that are ‘buffered’ in an internal register, and will not become active right away, but after a certain event occurs.
As you might recall in my discussion of 8088 MPH’s sprite part, I used the fact that the start offset register is latched in the scrolling of the background image. You can write a new start offset to the CRTC at any time, and it won’t become active until the frame is finished.
This has both downsides and upsides. The downside is that you can’t change the start offset anywhere on the screen, to do C64/Amiga like bitmap stretching effects and such (although as you may know, reenigne found a way around that, which is how he pulled off the Kefrens bars in 8088 MPH, among other things). The upside is that you don’t have to explicitly write the value during the vertical blank interval when you want to perform smooth scrolling or page flipping. Which is what I did in the sprite part: I only had to synchronize the drawing of the sprite to avoid flicker, and I could fire off the new scroll offset immediately.
Another interesting latched register is in the 8253 Programmable Interval Timer. As you know, we don’t have any raster interrupt on the IBM PC. And as you know, we’ve tried to work around that by exploiting the fact that the timer and the CGA card are running off the same base clock signal, and then carefully setting up a timer interrupt at 60 Hz (19912 ticks), synchronized to vsync.
In the sprite part, I just left the timer running. The music was played in the interrupt handler. The sprite routine just had to poll the counter values to get an idea of the beam position on screen. In the final version of 8088 MPH, reenigne used a slightly more interesting trick to adjust the brightness of the title screen as it rolled down, because the polling wasn’t quite accurate enough.
The trick exploits the fact that when the 8253 is in ‘rate generator’ or ‘square wave’ mode, if you write a new ‘initial count’ value to the 8253, without sending it a command first, it will latch this value, but continue counting down the old value. When the value reaches 0, it will use the new ‘initial count’ that was latched earlier.
The advantage here is that there are no cycles spent on changing the rate of the timer, which means there is no jitter, and the results are completely predictable. You can change the timer value any number of times during a frame, and you know that as long as the counts all add up to 19912 ticks, you will remain in sync with the screen.
Here is a demonstration of this trick:
What you see here is that the timer is fired a number of times per screen, changing the background colour everytime, to paint some raster bars. The timing of the start of the red bar is modified every frame, adding 76 ticks (one scanline), shifting it downward, which makes the cyan bar grow larger. To compensate, the timing for the purple bar is modified as well, subtracting 76 ticks, to compensate.
The result is that all the bars remain perfectly stable and in sync with the screen. If you reinitialized the timer everytime, you would get some drift, because it’s difficult to predict the amount of cycles it takes to send the new commands to the timer, and to compensate for that.
In 1991 donut, I was not aware of the latching trick yet, so I reinitialized the timer every time. As a result, there is a tiny bit of jitter depending on how fast your system is. This is mostly hidden by black scanlines in the area where the jitter would occur, but on very slow systems, you may see that the palette changes on the scroller are a scanline off, or more.
Because 1991 donut is aimed at VGA systems, I would have to reinitialize the timer at the end of every frame anyway, because I have to re-sync it to the VGA card, which runs on its own clock. But with the latch trick, I could at least have made the palette changes independent of CPU-speed (and there are various other tricks I’ve picked up since then, which could speed up 1991 donut some more).
Always think one step ahead
The catch with this trick is that you always have to be able to think one step ahead: the value will not become active until the timer reaches 0. So you can’t change the current interval, only the next one. In most cases that should not be a problem though, such as with drawing sprites, raster bars and such tricks.
The actual inspiration for this article was actually not because of anything graphics-related, but rather because of analog joysticks. That may be another application of this trick. Namely, to determine the position on a joystick axis, you have to initialize the joystick status register to 1, and then poll it to see how long it takes until it turns into a 0. This ties up the CPU completely.
So, my idea was to set up a system where you’d use a few timer interrupts to poll the joystick during each frame. If you don’t need a lot of accuracy (eg only ‘digital’ movement), you’d only have to fire it a few times, theoretically 3 would be enough to get left-middle-right or top-middle-bottom readings. But in practice you’d probably want to fire a few more. But still it would probably cost less CPU time than polling.
Another application that may be interesting is related to music/sound effects. Instead of having just a single rate, you can multiplex multiple frequencies this way, by modifying the counter value at each interrupt, and keeping track of how often it has fired to determine which state you are currently in (more or less like the coloured bars representing different ‘states’ in the above example).
To finish off for today, I also want to share another timer-related trick. This trick came to us because someone showed interest in 8088 MPH, and had some suggestions for improvements. Now this is exactly the kind of thing we had been hoping for! We wanted to inspire other people to also do new and exciting things on this platform, and push the platform further and further. Hopefully we will be seeing more PC demos (as in original IBM PC specs, so 8088+CGA+PC speaker).
This particular trick is the automatic end-of-interrupt functionality in the 8259 Programmable Interrupt Controller. This functionality is not enabled by default on the PC, which means that you have to manually send an EOI-command to the 8259 in your interrupt handler everytime. So, if you reinitialize the 8259 and enable the auto-EOI bit in ICW4, you no longer have to do this, which saves a few instructions and cycles everytime. Interrupts on 8088 are quite expensive, so we can use any help we can get. The above rasterbar example is actually using this trick.