Skip to content
FreeDOSDeep Dive Published Updated 7 min readViews unavailable

The PC/AT 8042 Keyboard Controller: Status, Buffers, and Safe Ownership

Interpret the 8042 status and data ports, distinguish controller commands from keyboard bytes, and avoid stealing BIOS keyboard input in DOS.

The PC/AT keyboard controller is easy to misuse because two related protocols share the same ports. On the IBM AT, port 64h exposes controller status and accepts controller commands; port 60h carries data to or from the controller and keyboard. A byte read from 60h may be a keyboard scan code, a controller response, or another device’s data. Code that treats every byte as a character can consume input that the BIOS or a keyboard driver expected to process.

The IBM PC/AT technical reference documents the original interface. Modern chipsets often emulate portions of the 8042 programming model, and USB legacy emulation adds another layer. The details here apply to a documented AT-compatible controller, not automatically to every PC, virtual machine, or laptop. For ordinary DOS applications, use BIOS keyboard services such as INT 16h instead of taking ownership of controller I/O.

Read status before deciding what a byte means

The status register at port 64h includes an output-buffer-full indication and an input-buffer-full indication. In the AT-compatible model, bit 0 indicates that a byte is available for the host to read; bit 1 indicates that the controller is not yet ready to accept another host write. Other status bits describe additional conditions. A correct low-level routine checks these states instead of assuming that a previous command completed immediately.

read status from port 64h
if output-buffer-full is set:
    read one byte from port 60h and classify it by protocol state
before writing a command or data byte:
    wait, with a finite timeout, until input-buffer-full is clear

This is protocol pseudocode, not a portable DOS routine. It intentionally does not issue port writes. Direct I/O may be disallowed or virtualized under an extender, and a timeout policy must account for the target controller’s documented behavior. An unbounded wait can hang the entire machine if the controller is absent or stuck.

The command/data distinction matters. Writing a controller command to port 64h changes controller behavior; writing a data byte to 60h may send a command to the keyboard device or supply a parameter to a preceding controller command. Which interpretation applies depends on protocol state. Likewise, a response arriving in the output buffer must be associated with the outstanding command. A byte value alone is not sufficient to identify it.

The keyboard device has its own protocol

The keyboard can return scan-code bytes and command acknowledgements such as ACK or resend indications. Multi-byte scan codes, prefixes, and keyboard modes make one-byte-to-one-key assumptions unsafe. The controller may also perform translation between scan-code sets. Changing the controller command byte or keyboard scan-code mode can therefore alter what BIOS and DOS software sees.

A driver that speaks directly to the keyboard must track command state, expected responses, timeouts, and resends. It must also coordinate with the BIOS keyboard interrupt handler, which normally consumes controller output and places key information in the BIOS data area. If a foreground diagnostic reads port 60h first, the normal handler may never see that byte. If a handler drains output without preserving the proper protocol, it can break key input or leave the controller in a state that blocks subsequent commands.

The keyboard controller also historically multiplexes auxiliary-device traffic, including a PS/2 mouse on systems that implement that path. Status flags can help distinguish data sources, but a simple keyboard-only routine must not discard bytes just because they are not scan codes. Device routing and status semantics depend on the controller-compatible implementation.

Controller commands are not keystrokes

Controller commands can enable or disable interfaces, read or write the command byte, self-test the controller, or test an interface. These operations can disable keyboard delivery, change translation, or otherwise affect system input. A command-byte update should be performed as a read-modify-write against a known controller contract, preserving unrelated bits. Guessing a byte value from a code sample may disable an interface or change IRQ behavior.

The output buffer is a queue boundary, not an invitation to drain until empty. If the status indicates pending data, determine which device and command state owns the byte before reading it. A controller response such as a self-test result must not be handed to a scan-code parser; a keyboard prefix must not be mistaken for a complete key. State-machine code should track whether it expects a controller byte, keyboard ACK, resend, or scan sequence and apply a separate timeout to each expected response.

If a timeout occurs, stop sending further bytes and preserve the status snapshot for diagnosis. Retrying the same command immediately can compound the original desynchronization. A recovery procedure should first consult the target controller/device documentation, then return the system to ordinary BIOS input without overwriting unrelated controller configuration. In a user utility, that often means reporting the raw status and exiting rather than attempting an automatic reset.

On virtual machines, compare behavior with the configured chipset and input-device model. A VM can emulate an 8042 for boot compatibility while delivering the actual keyboard through a host integration channel; the emulated status bits may not reflect the host keyboard’s physical state. A hardware-level diagnostic should therefore say it inspected the emulated controller, not claim that it tested the keyboard hardware attached to the host.

Avoid resetting the keyboard or controller as a first-line diagnostic. Reset commands disrupt the user’s input path and may require waiting for device initialization. A stuck keyboard in a DOS boot can be caused by a driver conflict, interrupt masking, USB emulation, or an actual controller issue; probing by changing global state can make evidence worse.

For FreeDOS applications, the normal layering is:

  1. Use INT 16h for BIOS keyboard input when its semantics are sufficient.
  2. Use the DOS keyboard/driver interface supplied by the environment for richer support.
  3. Write an 8042-level driver only when the target hardware and ownership model are explicit.

The BIOS function’s key buffer and scan-code semantics are covered by a separate interface. Do not conflate that higher-level API with raw controller bytes.

Concurrency and ownership

The controller is a single stateful device shared among interrupt handlers, BIOS code, keyboard drivers, and sometimes a mouse driver. A sequence that polls status, sends a command, and reads a response must not be interleaved with another controller client. DOS does not automatically serialize arbitrary I/O-port transactions. A driver must use an ownership mechanism appropriate to its environment, keep critical sections short, and never block while holding global interrupt state longer than necessary.

The safest ownership design is to centralize controller access in one driver. Applications communicate through that driver’s documented interface rather than competing for the ports. If a diagnostic must inspect the controller, prefer read-only status sampling and do not consume pending output. Record the status and timing context; do not drain buffers just to make a status bit look clear.

Failure modes and diagnostics

The classic failure is an infinite wait for input-buffer-empty. Use a bounded timeout and report which operation failed. Another is a lost key caused by reading output-buffer data intended for the BIOS. A third is a protocol desynchronization after an unexpected response or resend; reset the state machine only through a documented recovery procedure. Finally, a USB keyboard may depend on firmware legacy support that disappears or behaves differently after a mode transition.

Test with a real PS/2 keyboard and the exact BIOS/emulator target if the program depends on raw ports. Exercise an absent device, delayed response, unexpected byte, resend, and multi-byte scan code. Verify ordinary BIOS key input still works after the diagnostic exits. Do not perform destructive self-tests or controller reconfiguration on a production machine without an explicit recovery plan.

Practical rule

An 8042-compatible interface is a protocol endpoint, not a character port. Check status, distinguish controller commands from keyboard-device bytes, wait with a timeout, track the outstanding exchange, and respect BIOS/driver ownership. For nearly all application-level keyboard input, use the BIOS or DOS interface. Raw port access is appropriate only for a driver or controlled diagnostic that can own the hardware contract end to end.

Related:

Sources:

Comments