Use bool vars instead of bits for shift keys.
[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 /* Current state of shift keys.
14    True if depressed, false otherwise. */
15 static bool left_shift, right_shift;    /* Left and right Shift keys. */
16 static bool left_alt, right_alt;        /* Left and right Alt keys. */
17 static bool left_ctrl, right_ctrl;      /* Left and right Ctl keys. */
18
19 /* Status of Caps Lock.
20    True when on, false when off. */
21 static bool caps_lock;
22
23 /* Keyboard buffer. */
24 static struct intq buffer;
25
26 /* Number of keys pressed. */
27 static int64_t key_cnt;
28
29 static intr_handler_func keyboard_interrupt;
30
31 /* Initializes the keyboard. */
32 void
33 kbd_init (void) 
34 {
35   intq_init (&buffer);
36   intr_register_ext (0x21, keyboard_interrupt, "8042 Keyboard");
37 }
38
39 /* Retrieves a key from the keyboard buffer.
40    If the buffer is empty, waits for a key to be pressed. */
41 uint8_t
42 kbd_getc (void) 
43 {
44   enum intr_level old_level;
45   uint8_t key;
46
47   old_level = intr_disable ();
48   key = intq_getc (&buffer);
49   intr_set_level (old_level);
50   
51   return key;
52 }
53
54 /* Prints keyboard statistics. */
55 void
56 kbd_print_stats (void) 
57 {
58   printf ("Keyboard: %lld keys pressed\n", key_cnt);
59 }
60 \f
61 /* Maps a set of contiguous scancodes into characters. */
62 struct keymap
63   {
64     uint8_t first_scancode;     /* First scancode. */
65     const char *chars;          /* chars[0] has scancode first_scancode,
66                                    chars[1] has scancode first_scancode + 1,
67                                    and so on to the end of the string. */
68   };
69   
70 /* Keys that produce the same characters regardless of whether
71    the Shift keys are down.  Case of letters is an exception
72    that we handle elsewhere.  */
73 static const struct keymap invariant_keymap[] = 
74   {
75     {0x01, "\033"},
76     {0x0e, "\b"},
77     {0x0f, "\tQWERTYUIOP"},
78     {0x1c, "\n"},
79     {0x1e, "ASDFGHJKL"},
80     {0x2c, "ZXCVBNM"},
81     {0x37, "*"},
82     {0x39, " "},
83     {0, NULL},
84   };
85
86 /* Characters for keys pressed without Shift, for those keys
87    where it matters. */
88 static const struct keymap unshifted_keymap[] = 
89   {
90     {0x02, "1234567890-="},
91     {0x1a, "[]"},
92     {0x27, ";'`"},
93     {0x2b, "\\"},
94     {0x33, ",./"},
95     {0, NULL},
96   };
97   
98 /* Characters for keys pressed with Shift, for those keys where
99    it matters. */
100 static const struct keymap shifted_keymap[] = 
101   {
102     {0x02, "!@#$%^&*()_+"},
103     {0x1a, "{}"},
104     {0x27, ":\"~"},
105     {0x2b, "|"},
106     {0x33, "<>?"},
107     {0, NULL},
108   };
109
110 static bool map_key (const struct keymap[], unsigned scancode, uint8_t *);
111
112 static void
113 keyboard_interrupt (struct intr_frame *args UNUSED) 
114 {
115   /* Status of shift keys. */
116   bool shift = left_shift || right_shift;
117   bool alt = left_alt || right_alt;
118   bool ctrl = left_ctrl || right_ctrl;
119
120   /* Keyboard scancode. */
121   unsigned code;
122
123   /* False if key pressed, true if key released. */
124   bool release;
125
126   /* Character that corresponds to `code'. */
127   uint8_t c;
128
129   /* Read scancode, including second byte if prefix code. */
130   code = inb (DATA_REG);
131   if (code == 0xe0)
132     code = (code << 8) | inb (DATA_REG);
133
134   /* Bit 0x80 distinguishes key press from key release
135      (even if there's a prefix). */
136   release = (code & 0x80) != 0;
137   code &= ~0x80u;
138
139   /* Interpret key. */
140   if (code == 0x3a) 
141     {
142       /* Caps Lock. */
143       if (!release)
144         caps_lock = !caps_lock;
145     }
146   else if (map_key (invariant_keymap, code, &c)
147            || (!shift && map_key (unshifted_keymap, code, &c))
148            || (shift && map_key (shifted_keymap, code, &c)))
149     {
150       /* Ordinary character. */
151       if (!release) 
152         {
153           /* Handle Ctrl, Shift.
154              Note that Ctrl overrides Shift. */
155           if (ctrl && c >= 0x40 && c < 0x60) 
156             {
157               /* A is 0x41, Ctrl+A is 0x01, etc. */
158               c -= 0x40; 
159             }
160           else if (shift == caps_lock)
161             c = tolower (c);
162
163           /* Handle Alt by setting the high bit.
164              This 0x80 is unrelated to the one used to
165              distinguish key press from key release. */
166           if (alt)
167             c += 0x80;
168
169           /* Append to keyboard buffer. */
170           if (!intq_full (&buffer)) 
171             {
172               key_cnt++;
173               intq_putc (&buffer, c); 
174             }
175         }
176     }
177   else
178     {
179       /* Maps a keycode into a shift state variable. */
180       struct shift_key 
181         {
182           unsigned scancode;
183           bool *state_var;
184         };
185
186       /* Table of shift keys. */
187       static const struct shift_key shift_keys[] = 
188         {
189           {  0x2a, &left_shift},
190           {  0x36, &right_shift},
191           {  0x38, &left_alt},
192           {0xe038, &right_alt},
193           {  0x1d, &left_ctrl},
194           {0xe01d, &right_ctrl},
195           {0,      NULL},
196         };
197   
198       const struct shift_key *key;
199
200       /* Scan the table. */
201       for (key = shift_keys; key->scancode != 0; key++) 
202         if (key->scancode == code)
203           {
204             *key->state_var = !release;
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 }