Skip to content
Lesson 20 of 24

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: 0x02 to 0x0B are 1 2 3 4 5 6 7 8 9 0, 0x10 to 0x19 are q w e r t y u i o p, 0x1E to 0x26 are a s d f g h j k l, 0x2C to 0x32 are z x c v b n m.
  • A break code is sent when it is released: the make code with bit 7 set (0x1E for a down, 0x9E for 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 0x1D is right Ctrl pressed, 0xE0 0x9D released. A keyboard may also send a fake 0xE0 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:

RowScancodesNormalShifted
digits0x02-0x0D1234567890-=!@#$%^&*()_+
top0x10-0x1Bqwertyuiop[]QWERTYUIOP{}
home0x1E-0x29asdfghjkl;' followed by a backtickASDFGHJKL:"~
bottom0x2B-0x35a backslash then zxcvbnm,./`
space0x39spacespace

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 Enter 0xE0 0x1C) completes the line and hands it to the reader;
  • Ctrl-C discards the line and sends SIGINT to 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.

ByteMeaningLine buffer
2Aleft Shift down
23h key, shiftedH
A3h up
AAleft Shift up
17i keyHi
97i up
1CEnterline Hi delivered

Common mistakes

  • Emitting a character for break codes, so every key types twice.
  • Treating 0xE0 0x2A as a Shift press, or processing the byte after 0xE0 as 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.

Up nextFile SystemsFile Systems & Beyond

Discussion

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

Sign in to post a comment or reply.

Loading…