+ /* Status of shift keys. */
+ bool shift = left_shift || right_shift;
+ bool alt = left_alt || right_alt;
+ bool ctrl = left_ctrl || right_ctrl;
+
+ /* Keyboard scancode. */
+ unsigned code;
+
+ /* False if key pressed, true if key released. */
+ bool release;
+
+ /* Character that corresponds to `code'. */
+ uint8_t c;
+
+ /* Read scancode, including second byte if prefix code. */
+ code = inb (DATA_REG);
+ if (code == 0xe0)
+ code = (code << 8) | inb (DATA_REG);
+
+ /* Bit 0x80 distinguishes key press from key release
+ (even if there's a prefix). */
+ release = (code & 0x80) != 0;
+ code &= ~0x80u;
+
+ /* Interpret key. */
+ if (code == 0x3a)
+ {
+ /* Caps Lock. */
+ if (!release)
+ caps_lock = !caps_lock;
+ }
+ else if (map_key (invariant_keymap, code, &c)
+ || (!shift && map_key (unshifted_keymap, code, &c))
+ || (shift && map_key (shifted_keymap, code, &c)))
+ {
+ /* Ordinary character. */
+ if (!release)
+ {
+ /* Handle Ctrl, Shift.
+ Note that Ctrl overrides Shift. */
+ if (ctrl && c >= 0x40 && c < 0x60)
+ {
+ /* A is 0x41, Ctrl+A is 0x01, etc. */
+ c -= 0x40;
+ }
+ else if (shift == caps_lock)
+ c = tolower (c);
+
+ /* Handle Alt by setting the high bit.
+ This 0x80 is unrelated to the one used to
+ distinguish key press from key release. */
+ if (alt)
+ c += 0x80;
+
+ /* Append to keyboard buffer. */
+ if (!input_full ())
+ {
+ key_cnt++;
+ input_putc (c);
+ }
+ }
+ }
+ else
+ {
+ /* Maps a keycode into a shift state variable. */
+ struct shift_key
+ {
+ unsigned scancode;
+ bool *state_var;
+ };
+
+ /* Table of shift keys. */
+ static const struct shift_key shift_keys[] =
+ {
+ { 0x2a, &left_shift},
+ { 0x36, &right_shift},
+ { 0x38, &left_alt},
+ {0xe038, &right_alt},
+ { 0x1d, &left_ctrl},
+ {0xe01d, &right_ctrl},
+ {0, NULL},
+ };
+
+ const struct shift_key *key;
+
+ /* Scan the table. */
+ for (key = shift_keys; key->scancode != 0; key++)
+ if (key->scancode == code)
+ {
+ *key->state_var = !release;
+ break;
+ }
+ }
+}
+
+/* Scans the array of keymaps K for SCANCODE.
+ If found, sets *C to the corresponding character and returns
+ true.
+ If not found, returns false and C is ignored. */
+static bool
+map_key (const struct keymap k[], unsigned scancode, uint8_t *c)
+{
+ for (; k->first_scancode != 0; k++)
+ if (scancode >= k->first_scancode
+ && scancode < k->first_scancode + strlen (k->chars))
+ {
+ *c = k->chars[scancode - k->first_scancode];
+ return true;
+ }
+
+ return false;