Skip to content
Keyboard Input & Scancodes
step 1/5

Reading — step 1 of 5

Read

~3 min readSystem Calls & I/O

Keyboard Input & Scancodes

The Interrupts lesson built the delivery machinery — a device raises a line, the CPU vectors into your handler. Now meet the device everyone tests first: the keyboard. It's the perfect first driver because it's interrupt-driven, stateful, and produces something you can see. And it teaches a humbling truth about hardware: the keyboard does not send letters. It sends numbers about physical keys, and turning those into text is entirely your job.

Scancodes, not characters

Press the A key and the keyboard controller doesn't send 'a' (0x61). It sends 0x1E — the scancode for "the key in that physical position." Release it and it sends 0x9E (0x1E + 0x80) — the break code. The keyboard reports physical events — this key went down, this key came up — with zero notion of what letter is painted on the key (which is why remapping to Dvorak or another language is a software table swap, not new hardware).

The flow, per keystroke:

  1. You press a key → controller latches a scancode, raises IRQ 1 (→ vector 33 after the PIC remap from the Interrupts lesson).
  2. The CPU vectors to your keyboard handler (ring 0, IDT gate you installed).
  3. Your handler reads the scancode from port 0x60 (in al, 0x60 — port I/O, a privileged instruction, which is why this happens in the kernel).
  4. You decode scancode → character and feed it to whatever's reading input.

Step 4 is the driver's brain, and your exercise.

Make, break, and why break codes matter

  • Make code (< 0x80): a press. Usually → emit a character.
  • Break code (>= 0x80, i.e. make + 0x80): a release. For a letter, you ignore it — you already emitted the character on press.

So why track releases at all? Modifier keys. Shift isn't a character; it's a state. Press Left-Shift (make 0x2A) → Shift is now ON. Release it (break 0xAA) → Shift OFF. The letters typed in between are uppercase. A keyboard driver is therefore a little state machine: a shift_held flag flipped by make/break of the modifier keys, consulted every time a normal key is pressed:

on scancode s:
    if s is a shift make:   shift = true
    elif s is a shift break: shift = false
    elif s is a break:       ignore        (normal key release)
    else:                    emit lookup(s), upper/shifted if shift

This is why you can't decode a keystroke in isolation — 0x1E means a or A depending on history. The same pattern extends to Ctrl (for Ctrl-C → signals, the shell course's SIGINT), Caps Lock (a toggle — flips on each press, ignores release, subtly different from Shift), and the 0xE0-prefixed extended codes for arrow keys. Your exercise nails the core: Shift + letters + digits + Enter.

Two layers, like the console

The console lesson split "poke the hardware" from "the state machine"; the keyboard splits the same way. Reading port 0x60 in an interrupt handler is three instructions. The decoding — scancode tables, modifier state, shifted symbols, the eventual line-buffering and echo that make a usable terminal — is where a keyboard becomes input. And note the top-half/bottom-half discipline from the Interrupts lesson applies perfectly: the handler's top half just grabs the byte from 0x60 (fast, can't block); the decoding and delivery to a waiting reader is bottom-half work. Your simulator is that decoder, fed the raw scancode stream the top half would produce.

Your exercise: Scancode Decoder

Given a stream of hex scancodes, produce the typed text — tracking Shift across make/break, uppercasing letters and shifting digits to symbols, treating Enter as newline, and ignoring the release codes of ordinary keys. The graded subtleties are the stateful ones: Shift held across several keys (all uppercase until release), Shift released mid-stream (the boundary is exact), shifted digits mapping to !@#$…, and — the one that trips people — remembering that a normal key's break code must be silently dropped, or every letter prints twice. Build the flag-and-lookup machine, and you've written the front half of every terminal you've ever typed into.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…

Keyboard Input & Scancodes — Build an OS Kernel