x86 Assembly/Programmable Interval Timer
The Programmable Interval Timer (PIT) is an essential component of modern computers, especially in a multi-tasking environment. The PIT chip can be made ‒ by setting various register values ‒ to count up or down, at certain rates, and to trigger interrupts at certain times. The timer can be set into a cyclic mode, so that when it triggers it automatically starts counting again, or it can be set into a one-time-only countdown mode.
On newer hardware, a HPET (High Precision Event Timer), which is an evolution of the PIT concept, is likely to be available.
The PIT contains a crystal oscillator which emits a signal 1193182 hz. This output frequency is divided by three different values to provide three output channels to the CPU. Channel 0 is used as a system timer by most operating systems. Channel 1 was used to refresh the DRAM, but is no longer used and may not even be accessible. Channel 2 is used to control the PC speaker. Of these, channel 0 is the most frequently encountered.
To make the PIT fire at a certain frequency f, you need to figure out an integer x, such that 1193182 / x = f. This is a trivially solved problem which results in the formula:
- x = 1193182 / f
How this division actually works is that each divisor is saved in an internal register. On every clock pulse, the register is decremented. Only when it reaches 0 is the clock pulse allowed to continue on to the CPU. Higher divisors result in lower frequencies, and vice versa.
Note that because the divisor is 16 bits, and a value of 0 is interpreted as 65536, there are limits on the producible frequencies:
- max = 1193182 / 1 = 1193182 hz
- min = 1193182 / 65536 ≈ 18.2065 hz
This final value is also the resolution of the frequency, that is, each consecutive possible frequency differs by 18.2065 hz.
The PIT is accessed via four ports, three for the three channels and one for commands:
One commonly performed task is setting the frequency of the channel 0, the system timer. If a frequency of 100 hz is desired, we see that the necessary divisor is 1193182 / 100 = 11931. This value must be sent to the PIT split into a high and low byte.
mov al, 0x36 out 0x43, al ;tell the PIT which channel we're setting mov ax, 11931 out 0x40, al ;send low byte mov al, ah out 0x40, al ;send high byte