Reading — step 1 of 5
Read
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:
- You press a key → controller latches a scancode, raises IRQ 1 (→ vector 33 after the PIC remap from the Interrupts lesson).
- The CPU vectors to your keyboard handler (ring 0, IDT gate you installed).
- 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). - 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…