The IBM PC/XT Keyboard Interface
The XT keyboard interface, as it will be referred to from now on, is one of the most frequently misunderstood systems on the IBM PC, confusing both emulator authors and retro-programmers alike. It is easy to write a program that uses the keyboard in a way that works fine on an AT (or in DosBox), but fails on a more accurate XT emulator or real XT system. This is due to some peculiarities of the XT interface, which will be explained here.
The Host Keyboard Interface
The low-level, host-facing interface to the keyboard is implemented via the 8255 PPI. The keyboard is controlled via two bits on Port B, 61h, and the scancodes themselves are read out of Port A, 60h. These particular ports became somewhat standard, and so are often implemented on systems which do not even have an 8255 at all.
| Bits | Name | Description |
|---|---|---|
| 0:5 | Other functions | Other functions - see PPI section |
| 6 | PB6 | 0: Pull keyboard clock line LOW 1: Allow keyboard to drive clock |
| 7 | PB7 | 0: Normal operation 1: Clear KSR + Clear IRQ1 [5150 only: Present SW1 settings to port A] |
The Hardware Keyboard Interface
PB6 is typically used to reset the keyboard. It is wired up to a driver that will connect the keyboard clock line to ground. The control pin of the driver is active-low, which is why setting PB6 to 0 will perform this function, and setting PB6 to 1 allows the keyboard to operate normally.
Here is a simplified schematic:
See the Model F section for more specific details, but if the keyboard clock line is held low for 20ms the keyboard will perform a reset/self-test. The BIOS sometimes does this, but applications usually do not.
The keyboard interface is a typical two-wire serial connection with a clock and a data line. Both of these are pulled high, with either the PC or the keyboard able to drive the lines low. The PC drives the lines low with dedicated drivers; the keyboard takes advantage of the open-collector outputs of its 8048 microcontroller.
This scheme allows simple hardware flow control - when the PC has the clock or data line pulled low, the keyboard cannot send data. The keyboard’s microcontroller can detect when the PC has either line held low, and knows to wait as the PC must either be busy, or requesting a reset. Keys pressed while the data line is held low by the PC will be stored in the keyboard’s internal FIFO until they can be sent. If this FIFO is full, then any additional keystrokes will be lost.
Each scancode is sent as a series of 9 bits. A rising edge of the keyboard clock line triggers the PC to sample the data line, shifting the resulting bit into a 74LS322 shift register, U24. The first bit, called a start bit, is always 1. This is used to good effect - when the Qh output of U24 goes high, this triggers various circuitry that clamps down on the keyboard data line, pulling it low after an additional clock. At this point, the shift register contains the entire 8-bit scancode, which can then be read out of PPI Port A.
Bits are sent over the wire from least-significant (bit 0 of scancode) to most-significant. Here is a logic analyzer capture of the ‘G’ key being pressed (scancode 0x22):
Keyboard scancode 0x22 timing trace (Click to zoom)
Here is a simplified schematic:
Note that a complete scancode being received directly triggers IRQ1, and only writing 1 to PB7 will lower IRQ1 again.
Setting PB7 to 1 does several things:
- It clears the contents of the keyboard shift register U24, making its
Qhoutput low for at least the next 8 clocks. - It disables the parallel outputs of the keyboard shift register U24, making it impossible to read a scancode.
- It resets the flip-flop controlling IRQ1 and the keyboard data line. This forces IRQ1 low, and allows the keyboard to drive the data line.
Therefore, to read the keyboard, software must set PB7 to 1 and then reset it back to 0 after each scancode is read.