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; 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.
Note
If anyone has a ROM dump of a newer 8042-style keyboard controller, please leave a comment…
Hello, seeing that you “know too much” about the keyboard controller, perhaps you could give me a hint with a little problem I’m having 🙂 I’ve been doing POS low-level software for years; now I have a PS/2 problem I can’t solve, and documentation is not helping me.
Normally my (relevant) setup is the 8042 with a POS keyboard (not a standard PC keyboard), sometimes also a barcode reader, which is a scanner that comes with a keyboard wedge to have both the scanner and the keyboard on the same PS/2 port. My PS/2 code works flawlessly with most scanners, but I have one in particular that is getting me nightmares. During initialization, interrupts are still not enabled, I program the keyboard and read answers by polling. Nevertheless, with this particular scanner, when I send commands to the keyboard (read keyboard ID, set mode 3, set LEDs, etc.) almost always, instead of ACK, I receive 0xF4. An example: I want to change to mode 3; I send 0xF0 (change mode), I get first an extraneous 0xF4, and if I continue reading, I finally get my ACK. Then I proceed to send 0x03 (mode 3); the keyboard does not send a response and eventually the controller informs read timeout in its status.
This happens with any keyboard command I try; if it has an argument, I never get the ACK for it. I am not really sure what to do with the timeout bit on the keyboard controller status either; when that happens I read the next data and I get 0xFF (that’s how it should behave) but the status timeout bit is still set.
No documentation I have read says what to do with the timeout condition, and there is no 0xF4 documented as a possible response either. I studied several keyboard drivers; all of them (even the Linux one) ignore the timeout condition; if waiting for ACK it appears any other value, some drivers take it as a scancode, some other drivers abort the ACK wait and inform an error condition, and some other drivers ignore it and continue waiting for the ACK. Any of those three behaviours won’t help me. To add to the mystery, if I connect a POS keyboard with this problematic scanner to a Linux box, both behave themselves, but naturally the initialization process is not the same, the 8042 chip is not the same.
Can you give me any idea of what is happening, or how to deal with this?
Thank you very much for listening!
I don’t suppose you have an oscilloscope or something that would allow you to track what’s happening on the wire? I can’t see any hint that a keyboard (or controller) would send 0xF4 either, so I wonder if it could be some kind of a bit error. It’s not a scan code and shouldn’t be translated by the KBC.
Are you resetting the POS keyboard first, just in case (command 0xFF)? Also, is the problem specific to one KBC/device combination, or have you tried your code with the same POS keyboard but a different KBC? Is it possible that the device just doesn’t respond quickly enough and would like some delays inserted?
Do you have any documentation for the POS keyboard, in case it uses some non-standard extensions or requires some unusual programming sequence? There is unfortunately a huge amount of variation between various keyboards and KBCs; they definitely weren’t all created equal.
Thank you very much for your answer. In fact, it’s a combination problem: the keyboard alone works OK, the scanner alone works OK, together they fail. I do start the programming sequence resetting the keyboard; in case of timeout, I tried resetting again but after a few commands there’s the same failure. The problem arises specifically with commands that have an argument; seems like the argument or the answer to it gets lost. I think just like you that there is a timing problem, I don’t know if it is between the 8042 and the keyboard wedge, or between the keyboard wedge and the keyboard. The same keyboard alone or with different keyboard wedges works fine, so it doesn’t seem to be a programming sequence problem specific to the keyboard.
On the other hand, in the PS/2 documentation the timeouts seem long enough, but somehow they are not, or someone is not following the rules close enough. I did try inserting some delays, but the protocol itself make delays unnecessary: you have to ask the 8042 when it’s allowed to write and when to read, and you can’t send an argument for a command before the command opcode has been ACKed. In any case I put delays to no avail. I assume it’s a hardware failure, either is an unfortunate combination of buggy implementations for one or both devices (keyboard wedge/keyboard), or perhaps the 8042 is involved as well, I don’t know.
Anyway, thank you very much for your insight!
My guess would be that both the keyboard and the wedge are slightly broken, but the problem only shows up when they’re combined together. Is the wedge a passive device or does it have its own logic? if it’s anything like a KVM switch then it can easily introduce problems.
This is where a scope might be helpful. I have no experience with analyzing the PS/2 protocol on the wire, but I’ve used a USB protocol analyzer and it was extremely helpful in understanding the behavior of various slightly broken devices and buggy software. Of course sometimes the only conclusion is that a device is really broken and there’s not much that can be done about it, other than avoiding it.
I’ve got a ROM dump of a slightly more recent keyboard controller: the 8042 from a Compaq Portable III. It has various extra features, including autosensing whether the keyboard is AT or XT, and using the appropriate protocol.
The Portable III probably didn’t support PS/2 mice? I think I now understand AT style keyboard controllers reasonably well, but I’ve not seen any ROM for either IBM’s PS/2 or any Phoenix or AMI clone.
Correct — the Portable III behaves like an AT controller rather than a PS/2.
I’ve tried dumping the Mega-KB controller on some 386-era motherboards, but its ROM seems to be protected; the programmer just reads zeroes.
I’ve now managed to dump the 8742 from a PS/2 model 50 motherboard, if you’re interested.
Definitely interested! How did you manage to dump it? Or was the trick just finding the right motherboard/controller?
I think it was just a case of finding a controller where the manufacturer had decided not to protect the contents. So far, the only IBM 804x controller I haven’t been able to read has been the 8048 from the original XT keyboard.
For now, I’ve put the PS/2 8042 image at http://www.seasip.info/tmp/72×8455.zm82.bin
Thank you so much. Disassembling… First impression: they kept a lot of the code from the PC/AT KBC ROM. This was clearly not developed from scratch (and why should it?).
Pingback: IBM PS/2 Model 50 Keyboard Controller | OS/2 Museum
I recall that the PC’s ROM BIOS had the ability to have a test program “toggled into” RAM via the keyboard port. Has anyone actually done this?
As far as I know, IBM used this for manufacturing tests (since it only needed the motherboard with a CPU and a bit of RAM, no disks or other devices). I’ve never heard of anyone else using the feature, probably because it’s not that useful in a typical system.
may i get 8048 dump? lightning got my computer. i have board and software to program intel prom’s. i may be able to program 8048 and change other 2 chips and get keyboard to work. any help would be greatly appreciated. thanks paul
sorry i should have mentioned this is for the pc or xt not at. thanks paul
0xf4 means restart kbd scanning
No. That is to say, not on the keyboard controller — it’s a keyboard command. Completely different (but deceptively similar) command set.
I would like to mention, that the Command ACh IS INDEED DOCUMENTED by IBM in the PC/AT Technical Reference released March 1984. A PDF of this document can be found here: https://archive.org/details/bitsavers_ibmpcat150ferenceMar84_26847525
You’re right, the ACh command is documented in the AT Tech Ref (the three editions I checked). It is not documented in the XT Model 286 Tech Ref or any of the subsequent references, which is what I was working with. It is not clear to me if the XT Model 286 and PS/2 machines really removed the command or not.
Just a sidenote, nothing directly related to this article, but anyway…
Years ago, when I just started with 8051 microcontrollers, I realized, that it’s businterface is similar and I could do a skeleton-program that allows byte-communications vbetween ISA bus and a micro. IIRC I used an AT89c51AC3.
I got stuck on the Connection/Communication. Due to lack of a Logic analyzer there was no way for me to find errors.
I think the Problem was a too fast ISA Bus and a 11.0592MHz Osc for the 8051.
Unfortunately I can’t find the sheets with drawings as well as Code anymore 🙁
These days I’m trying to build a more-or-less PC compatible with an AM186. It would be very handy to have a Controller attached to Bus….
It was some day in 2006/2007, before this dump showed up. Maybe I’ll start again with that disassembly for looking on how it’s done…
Nice post.
I’m trying to implement a s2 initialization on my operating system.
It’s not easy.
Thanks for the help 🙂