Make tests public. Rewrite most tests. Add tests.
[pintos-anon] / src / devices / kbd.c
1 #include "devices/kbd.h"
2 #include <ctype.h>
3 #include <debug.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include "devices/intq.h"
7 #include "threads/interrupt.h"
8 #include "threads/io.h"
9
10 /* Keyboard data register port. */
11 #define DATA_REG 0x60
12
13 /* Shift state bits. */
14 #define LSHIFT  0x01            /* Left Shift. */
15 #define RSHIFT  0x02            /* Right Shift. */
16 #define LALT    0x04            /* Left Alt. */
17 #define RALT    0x08            /* Right Alt. */
18 #define LCTRL   0x10            /* Left Ctrl. */
19 #define RCTRL   0x20            /* Right Ctrl. */
20 #define CAPS    0x40            /* Caps Lock. */
21
22 /* Current shift state. */
23 static unsigned shift_state;
24
25 /* Keyboard buffer. */
26 static struct intq buffer;
27
28 /* Number of keys pressed. */
29 static int64_t key_cnt;
30
31 static intr_handler_func keyboard_interrupt;
32
33 /* Initializes the keyboard. */
34 void
35 kbd_init (void) 
36 {
37   intq_init (&buffer);
38   intr_register_ext (0x21, keyboard_interrupt, "8042 Keyboard");
39 }
40
41 /* Retrieves a key from the keyboard buffer.
42    If the buffer is empty, waits for a key to be pressed. */
43 uint8_t
44 kbd_getc (void) 
45 {
46   enum intr_level old_level;
47   uint8_t key;
48
49   old_level = intr_disable ();
50   key = intq_getc (&buffer);
51   intr_set_level (old_level);
52   
53   return key;
54 }
55
56 /* Prints keyboard statistics. */
57 void
58 kbd_print_stats (void) 
59 {
60   printf ("Keyboard: %lld keys pressed\n", key_cnt);
61 }
62 \f
63 /* Maps a set of contiguous scancodes into characters. */
64 struct keymap
65   {
66     uint8_t first_scancode;     /* First scancode. */
67     const char *chars;          /* chars[0] has scancode first_scancode,
68                                    chars[1] has scancode first_scancode + 1,
69                                    and so on to the end of the string. */
70   };
71   
72 /* Keys that produce the same characters regardless of whether
73    the Shift keys are down.  Case of letters is an exception
74    that we handle elsewhere.  */
75 static const struct keymap invariant_keymap[] = 
76   {
77     {0x01, "\033"},
78     {0x0e, "\b"},
79     {0x0f, "\tQWERTYUIOP"},
80     {0x1c, "\n"},
81     {0x1e, "ASDFGHJKL"},
82     {0x2c, "ZXCVBNM"},
83     {0x37, "*"},
84     {0x39, " "},
85     {0, NULL},
86   };
87
88 /* Characters for keys pressed without Shift, for those keys
89    where it matters. */
90 static const struct keymap unshifted_keymap[] = 
91   {
92     {0x02, "1234567890-="},
93     {0x1a, "[]"},
94     {0x27, ";'`"},
95     {0x2b, "\\"},
96     {0x33, ",./"},
97     {0, NULL},
98   };
99   
100 /* Characters for keys pressed with Shift, for those keys where
101    it matters. */
102 static const struct keymap shifted_keymap[] = 
103   {
104     {0x02, "!@#$%^&*()_+"},
105     {0x1a, "{}"},
106     {0x27, ":\"~"},
107     {0x2b, "|"},
108     {0x33, "<>?"},
109     {0, NULL},
110   };
111
112 static bool map_key (const struct keymap[], unsigned scancode, uint8_t *);
113
114 static void
115 keyboard_interrupt (struct intr_frame *args UNUSED) 
116 {
117   /* Status of shift keys. */
118   bool shift = (shift_state & (LSHIFT | RSHIFT)) != 0;
119   bool alt = (shift_state & (LALT | RALT)) != 0;
120   bool ctrl = (shift_state & (LCTRL | RCTRL)) != 0;
121   bool caps = (shift_state & CAPS) != 0;
122
123   /* Keyboard scancode. */
124   unsigned code;
125
126   /* False if key pressed, true if key released. */
127   bool release;
128
129   /* Character that corresponds to `code'. */
130   uint8_t c;
131
132   /* Read scancode, including second byte if prefix code. */
133   code = inb (DATA_REG);
134   if (code == 0xe0)
135     code = (code << 8) | inb (DATA_REG);
136
137   /* Bit 0x80 distinguishes key press from key release
138      (even if there's a prefix). */
139   release = (code & 0x80) != 0;
140   code &= ~0x80u;
141
142   /* Interpret key. */
143   if (code == 0x3a) 
144     {
145       /* Caps Lock. */
146       if (!release)
147         shift_state ^= CAPS; 
148     }
149   else if (map_key (invariant_keymap, code, &c)
150            || (!shift && map_key (unshifted_keymap, code, &c))
151            || (shift && map_key (shifted_keymap, code, &c)))
152     {
153       /* Ordinary character. */
154       if (!release) 
155         {
156           /* Handle Ctrl, Shift.
157              Note that Ctrl overrides Shift. */
158           if (ctrl && c >= 0x40 && c < 0x60) 
159             {
160               /* A is 0x41, Ctrl+A is 0x01, etc. */
161               c -= 0x40; 
162             }
163           else if (shift == caps)
164             c = tolower (c);
165
166           /* Handle Alt by setting the high bit.
167              This 0x80 is unrelated to the one used to
168              distinguish key press from key release. */
169           if (alt)
170             c += 0x80;
171
172           /* Append to keyboard buffer. */
173           if (!intq_full (&buffer)) 
174             {
175               key_cnt++;
176               intq_putc (&buffer, c); 
177             }
178         }
179     }
180   else
181     {
182       /* Table of shift keys.
183          Maps a keycode into a shift_state bit. */
184       static const unsigned shift_keys[][2] = 
185         {
186           {  0x2a, LSHIFT},
187           {  0x36, RSHIFT},
188           {  0x38, LALT},
189           {0xe038, RALT},
190           {  0x1d, LCTRL},
191           {0xe01d, RCTRL},
192           {0, 0},
193         };
194   
195       const unsigned (*key)[2];
196
197       /* Scan the table. */
198       for (key = shift_keys; (*key)[0] != 0; key++) 
199         if ((*key)[0] == code)
200           {
201             if (release)
202               shift_state &= ~(*key)[1];
203             else
204               shift_state |= (*key)[1];
205             break;
206           }
207     }
208 }
209
210 /* Scans the array of keymaps K for SCANCODE.
211    If found, sets *C to the corresponding character and returns
212    true.
213    If not found, returns false and C is ignored. */
214 static bool
215 map_key (const struct keymap k[], unsigned scancode, uint8_t *c) 
216 {
217   for (; k->first_scancode != 0; k++)
218     if (scancode >= k->first_scancode
219         && scancode < k->first_scancode + strlen (k->chars)) 
220       {
221         *c = k->chars[scancode - k->first_scancode];
222         return true; 
223       }
224
225   return false;
226 }