Anyone trying to use the classic 8042-style keyboard controller (KBC) found in the IBM PC/AT and nearly all later PCs typically runs into a problem with lack of accurate documentation. The 8042 (or 8742, or any number of compatible parts built into later Super I/O chips) is actually quite well documented. The catch is that the 8042 is a programmable micro-controller with its own control software in (usually) ROM. Until recently, no one outside a few companies (IBM, AMI, Phoenix) knew exactly what the control software did.
IBM documented a number of commands the host can send to the KBC. It should be understood that all those commands are a pure software construct, with nothing about the 8042 hardware dictating that the commands need to follow any specific format, function, or that they even need to be there at all. Therefore understanding the 8042 ROM code is the only way towards understanding exactly what the commands are and what they do, with the caveat that different controllers may and do have somewhat different code in their ROM.
It’s almost unbelievable that it took until 2009(?) for a dump of the original PC/AT’s 8042 ROM to be available. In late 2010, Attila Tarpai disassembled, commented and analyzed the ROM. The following text concentrates on one part of the ROM—the commands the host can send to the controller (via port 64h). The PC/AT Technical Reference lists most of the commands, and other literature such as The Undocumented PC lists almost all the rest. However, some aspects of the command operation are not fully documented anywhere.
Complete List of KBC Commands
The commands listed as “ignored” perform no function.
- 00h-1Fh: Read KBC RAM indirect. Not documented.
- 20h-3Fh: Read KBC RAM at offset 20h-3Fh. Only command 20h is documented by IBM.
- 40h-5Fh: Write KBC RAM indirect. Not documented.
- 60h-7Fh: Write KBC RAM at offset 20h-3Fh. Only command 60h is documented by IBM. The byte at offset 20h is the command byte and is treated specially.
- 80h-A8h: Ignored.
- AAh: Self test. This command is documented, but its side effects are not.
- ABh: Interface test.
- ACh: Diagnostic dump. Mentioned by third parties, but not documented by IBM.
- ADh: Disable keyboard.
- AEh: Enable keyboard.
- AFh-BFh: Ignored.
- C0h: Read input port.
- C1h: Continuous input port poll, high nibble. Mentioned by third parties, but not documented by IBM.
- C2h: Continuous input port poll, low nibble. Mentioned by third parties, but not documented by IBM.
- C3h-CFh: Ignored.
- D0h: Read output port.
- D1h: Write output port.
- D2h-DEh: Ignored.
- E0h: Read test inputs.
- E1h-EFh: Ignored.
- F0h-FFh: Pulse output bits.
Additional Command Detail
Most of the KBC commands are adequately documented by IBM and others, but not all are. The following section provides some detail on undocumented or poorly documented commands.
00h-1Fh and 40h-5Fh, Read/Write Indirect
The indirect read and write commands behave much like the normal KBC RAM read/write command, but use the contents of RAM location 2Bh as a base. That is, the value at KBC RAM address 2Bh is added to the low 5 bits of the command and the result is used as an address of the KBC RAM location to read or write. After initialization, location 2Bh contains the value 20h, which means that command 05h behaves exactly like 25h, etc. However, the user can use the KBC RAM write command to change the contents of location 2Bh and thus access any KBC RAM location. Note that writes to the command byte (location 20h) are treated specially regardless of whether direct or indirect addressing is used.
AAh, Self Test
The Self Test command performs tests of the KBC and on success, sends 55h to the host on success; that much is documented by IBM and others. However, the self test command also effectively resets the KBC and puts it into a known state. That means, among other things, that the A20 address line is enabled, keyboard interface is disabled, and scan code translation is enabled.
Furthermore, after the system is powered on, the keyboard controller does not start operating until the self test command is sent by the host and successfully completed by the KBC.
ACh, Diagnostic Dump
The diagnostic dump command is not documented by IBM, although it is mentioned in The Undocumented PC and some other references. The Undocumented PC mentions that the dump contains 16 bytes of the KBC’s RAM, the current input and output port state, test inputs, and the program status word (20 bytes total), which is correct. However, the dump format is not explained, and it’s not entirely obvious.
For each byte of information dumped, the KBC sends three bytes to the host: two ASCII hex digits in scan set 1 format followed by an ASCII space. The idea is clearly to initiate the diagnostic dump command and let the host’s keyboard and console drivers display the information “typed in” by the KBC.
Before sending data to the host, the dump command stores the current P1 and P2 states in locations 30h and 31h, respectively. Location 32h is set to contain the T0 and T1 input line states in bits 0 and 1 (with the top bits zeroed), and the program status word is stored in location 33h. The contents of locations 20h-33h are then sent to the host in the format explained above.
While processing the dump command, the KBC is not accepting keyboard input. The KBC waits potentially indefinitely for the host to read the bytes (from port 60h), but any byte (new command) written to the KBC (port 64h) aborts the dump.
C1h and C2h, Continuous Input Port Poll
IBM does not document these commands, but The Undocumented PC does. The low (command C1h) or high (command C2h) nibble of the P1 status port is placed into the high nibble of the status register, which the host can read via port 64h.
Same as with the dump command, the KBC does not accept any keyboard input and the poll command is terminated when the host writes a new KBC command (to port 64h).
D1h, Write Output Port
This command is actually well documented, and IBM even warns that bit 0 should never be written as zero, because it would reset the processor. But it’s worse than that—the processor goes into reset state and stays in reset. Because the CPU is inoperable, there’s no one to stop the KBC from keeping the CPU in reset, and the system must be powered off.
Enabling and Disabling the Keyboard Interface
Many programmers misunderstand what “enabling” and “disabling” the keyboard means. There are in fact several ways to do that. The keyboard interrupt can be disabled, in the host’s interrupt controller and/or in the keyboard controller itself (clearing bit 0 of the controller’s command byte). In a typical interrupt-driven system, that will effectively block keyboard input.
The keyboard itself can be disabled by sending the Default Disable (F5h) command to the keyboard. In this state, the keyboard controller can communicate with the keyboard, but the keyboard does not scan for key presses or releases. This method isn’t commonly used; among other things, it risks losing key up events for keys pressed at the time the scanning was disabled.
The most common method is disabling the interface between the KBC and the keyboard. Bit 4 in the controller’s command byte determines whether the keyboard interface is enabled (bit clear) or disabled (bit set). The bit can be set either by the Enable/Disable Keyboard KBC commands (AEh/ADh) or writing the command byte directly via the Write RAM commands.
In addition to those methods, at least two other events affect the state of the keyboard interface. As noted earlier, the Self Test command (AAh) KBC command disables the keyboard interface. Additionally, writing a byte to port 60h in order to send it to the keyboard (i.e. a KBC command is not expecting input) causes the keyboard interface to be enabled. This allows the host to skip explicitly enabling the keyboard interface in some situations. Neither of these details is documented by IBM.
It is important to note that when the keyboard interface is disabled, the KBC still functions normally and can interrupt the host (if interrupts are enabled). The keyboard likewise functions normally and can store keystrokes in its internal buffer. However, the keyboard controller does allow the keyboard to send any data. This method is typically used by keyboard interrupt service routines to avoid keyboard input interfering with the host’s communication with the KBC. Because the keyboard itself is scanning, keystrokes will not be lost unless the keyboard interface is disabled for extended periods of time.
If anyone has a ROM dump of a newer 8042-style keyboard controller, please leave a comment…