2) The timer interrupt (int 08h)
I'll assume you've all played old games, back in the days where gameplay and plot were far more important that fancy graphics and nice box-sets... (although these concepts are all important and shouldn't be mutually exclusive one can't help but notice that priorities shifted to uncanny and greedy grounds). Game programmers of the period, often one-man teams with lots of imagination, were sometimes confronted with the problem of implementing certain delays in the game (damn, that enemy plane is closing in too fast... evasive maneuvers... I'll ne...arghhhhhhh). Often, dummy loops of the following form were employed :
This seemed to work. However, this kind of work-around has a significant disadvantage : It relies on processor speed. What a surprise to find out that the powerful hero Kill Them All of our favourite platform game, so deft and gracious on our 20MHZ i386, now, with a 600MHZ Pentium III running a GeForce beast, helplessly dashes against every kind of inoffensive and pitiful obstacle before you even can operate a single key!
Summarizing, we need a way to generate exactly defined time intervals. And what better way than by hardware? Thus, the PC's designers have implemented one (PC/XT and most ATs) or sometimes two (some new ATs or EISA) Programmable Interval Timers (PITs).
The PIT 8253/8254 generates programmable time intervals from an external clock signal of a crystal oscillator that are defined independently from the CPU. It's very flexible and has six modes of operation in all (these modes will not be explained in this tutorial, maybe in a future less generic one).
The 8253/8254 chip comprises three independently and separately programmable counters 0-2, each of which is 16 bits wide. Each counter, or channel, is supplied with its own clock signal (CLK0-CLK2) which serves as the time base for each counter. Each channel is responsible for a different task on the PC :
Channel 0 : This channel is responsible for updating the system clock, generating interrupts every 55ms (approximately). This is about once every 1/18.2 seconds. Sometimes this is called the "eighteenth second clock" but we will refer to it as the timer interrupt. We'll discuss this channel in more detail in a little while.
Channel 1 : This channel controls DMA memory refreshing, instructing all 18 CLK cycles on a DMA chip to carry out a dummy read cycle. In the course of this dummy cycle, data is read from memory onto the data bus and the address buffers, and address decoders and sense amplifiers in the memory chips are activated to refresh one memory cell row. But the data is not read by any peripheral. Instead, it's discarded in the next bus cycle. This is done because DRAM's memory cells must be periodically refreshed or they quickly lose their charge. One interesting thing that should be noted is that most system designers lay out the memory refresh rather carefully, that is, the memory is refreshed more often that is really necessary. This so-called refresh overhead can reach 10% or more. Reprogramming channel 1 to refresh memory at a slower rate can sometimes speed up system performance, but don't overdue it or data losses might incur (giving rise to parity errors upon reading main memory).
Channel 2 : This channel is dedicated to the tone frequency generation for the installed speaker. It's normally programmed to generate a square wave so a continuous tone is heard. Reprogramming it for "Interrupt on Terminal Count" mode is a nifty trick which can be used to play 8-bit samples from the PC speaker. You may generate various frequencies with it. The audible range of tones lies between about 16Hz and 16kHz. Frequencies above and below this range are called infra- or supersonic: Your Pc's amplifier is probably unable to generate such tones.
The timer interrupt vector (channel 0) is probably the most commonly patched interrupt in the system. However, it turns out there are two of these vectors in the system. The first one, int 08h, is the hardware vector associated with the timer interrupt. Unless you're willing to taunt fate, it's not a good idea to patch this interrupt. If you want to build a timer handler, go for the second interrupt, interrupt 1ch. The BIOS' timer ISR (int 08h) always executes an int 1ch instruction before it returns. Catching it, assuming control and chain back to the old ISR is the best way to design your timer handler. Unless you're willing to duplicate the BIOS and DOS timer code, you should never completely replace the existing timer ISR with one of your own. Twiddling with int 1ch can be very dangerous and misuse can cause your system to crash or otherwise malfunction.
Finally, without entering into too much detail, I'll leave you with the port addresses of the various 8253/8254 PIT registers (the control register loads the counters and controls the various operation modes) :