Step 1 of 5 · Reading · ~4 min
Read
System Calls & I/O
Keyboard Input & Scancodes
A key press does not arrive as the letter a. It arrives as a number that means "the key in the third position of the home row went down", and turning that into text is the kernel's job. In this lesson your program is the keyboard driver plus the console's line discipline: it decodes a stream of raw PS/2 scancodes into characters, tracks Shift, Ctrl and Caps Lock, lets Backspace edit the line being typed, and handles Ctrl-C and Ctrl-D the way a Unix terminal does.
From key to byte
The keyboard controller exposes two I/O ports: 0x64 (status) and 0x60 (data). Each key event raises IRQ 1 (lesson 17); the handler reads one byte from port 0x60 and must do so before the next event, or it is lost. The bytes are scancodes in set 1 (the default after the controller's translation):
- A make code is sent when a key goes down. A-Z and the digits live in fixed positions:
0x02to0x0Bare1 2 3 4 5 6 7 8 9 0,0x10to0x19areq w e r t y u i o p,0x1Eto0x26area s d f g h j k l,0x2Cto0x32arez x c v b n m. - A break code is sent when it is released: the make code with bit 7 set (
0x1Eforadown,0x9Efor up). - While a key is held, the hardware repeats the make code (typematic).
- Some keys send a two-byte sequence beginning with the extended prefix
0xE0: arrows, right Ctrl, keypad Enter.0xE0 0x1Dis right Ctrl pressed,0xE0 0x9Dreleased. A keyboard may also send a fake0xE0 0x2A(a Shift) around some keys; it is not a real Shift.
Scancodes name keys, not characters. The mapping to characters is the keyboard layout, a table in the driver. The US layout rows are:
| Row | Scancodes | Normal | Shifted |
|---|---|---|---|
| digits | 0x02-0x0D | 1234567890-= | !@#$%^&*()_+ |
| top | 0x10-0x1B | qwertyuiop[] | QWERTYUIOP{} |
| home | 0x1E-0x29 | asdfghjkl;' followed by a backtick | ASDFGHJKL:"~ |
| bottom | 0x2B-0x35 | a backslash then zxcvbnm,./ | ` |
| space | 0x39 | space | space |
Modifiers are state
Shift (0x2A left, 0x36 right), Ctrl (0x1D, and 0xE0 0x1D) and Caps Lock (0x3A) produce no character. They change how later keys are read, so the driver keeps state: Shift is on while either shift key is down; Caps Lock toggles on each press; a letter is upper-case when exactly one of Shift and Caps Lock is active, while digits and punctuation depend on Shift alone.
The line discipline
A terminal in the default cooked mode does not hand every keystroke to the program. The kernel edits a line buffer and delivers whole lines:
- ordinary characters are appended (and echoed);
- Backspace (
0x0E) removes the last character; - Enter (
0x1C, or keypad Enter0xE0 0x1C) completes the line and hands it to the reader; - Ctrl-C discards the line and sends
SIGINTto the foreground process (lesson 14); - Ctrl-D on an empty line means end of file (the reader sees a zero-length read);
- other Ctrl combinations are not text.
This is what xv6's console.c does with a 128-byte buffer.
A worked trace
Typing Hi and Enter: 2A 23 A3 AA 17 97 1C.
| Byte | Meaning | Line buffer |
|---|---|---|
2A | left Shift down | |
23 | h key, shifted | H |
A3 | h up | |
AA | left Shift up | |
17 | i key | Hi |
97 | i up | |
1C | Enter | line Hi delivered |
Common mistakes
- Emitting a character for break codes, so every key types twice.
- Treating
0xE0 0x2Aas a Shift press, or processing the byte after0xE0as an ordinary key. - Letting Caps Lock affect digits and punctuation, or ignoring that Shift+Caps gives lower case.
- Releasing Shift when only one of the two Shift keys is released.
Your exercise
The starter reads the bytes and prints your output. You write the decoder: modifier state, the key table and the line editing.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…