interrupts, timer, keyboard, segments, lots of kernel code
[bootcensus] / src / keyb.c
1 #include "keyb.h"
2 #include "intr.h"
3 #include "asmops.h"
4
5 #define KB_IRQ  1
6 #define KB_PORT         0x60
7
8 /* table with rough translations from set 1 scancodes to ASCII-ish */
9 static int scantbl[] = {
10         0, KB_ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b',            /* 0 - e */
11         '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',                 /* f - 1c */
12         KB_LCTRL, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`',                          /* 1d - 29 */
13         KB_LSHIFT, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', KB_RSHIFT,                   /* 2a - 36 */
14         KB_NUM_MUL, KB_LALT, ' ', KB_CAPSLK, KB_F1, KB_F2, KB_F3, KB_F4, KB_F5, KB_F6, KB_F7, KB_F8, KB_F9, KB_F10,                     /* 37 - 44 */
15         KB_NUMLK, KB_SCRLK, KB_NUM_7, KB_NUM_8, KB_NUM_9, KB_NUM_MINUS, KB_NUM_4, KB_NUM_5, KB_NUM_6, KB_NUM_PLUS,      /* 45 - 4e */
16         KB_NUM_1, KB_NUM_2, KB_NUM_3, KB_NUM_0, KB_NUM_DOT, KB_SYSRQ, 0, 0, KB_F11, KB_F12,                                             /* 4d - 58 */
17         0, 0, 0, 0, 0, 0, 0,                                                                                                                    /* 59 - 5f */
18         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                                                                 /* 60 - 6f */
19         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0                                                                  /* 70 - 7f */
20 };
21
22 static void kbintr();
23
24 #define BUFSZ   64
25 #define ADVANCE(x)      ((x) = ((x) + 1) & (BUFSZ - 1))
26
27 static int buffer[BUFSZ];
28 static int buf_ridx, buf_widx;
29
30 static unsigned int num_pressed;
31 static unsigned char keystate[256];
32
33 void kb_init(void)
34 {
35         interrupt(IRQ_TO_INTR(KB_IRQ), kbintr);
36 }
37
38 int kb_isdown(int key)
39 {
40         switch(key) {
41         case KB_ANY:
42                 return num_pressed;
43
44         case KB_ALT:
45                 return keystate[KB_LALT] + keystate[KB_RALT];
46
47         case KB_CTRL:
48                 return keystate[KB_LCTRL] + keystate[KB_RCTRL];
49         }
50         return keystate[key];
51 }
52
53 void kb_wait(void)
54 {
55         int key;
56         while((key = kb_getkey()) == -1) {
57                 /* put the processor to sleep while waiting for keypresses, but first
58                  * make sure interrupts are enabled, or we'll sleep forever
59                  */
60                 enable_intr();
61                 halt_cpu();
62         }
63         kb_putback(key);
64 }
65
66 int kb_getkey(void)
67 {
68         int res;
69
70         if(buf_ridx == buf_widx) {
71                 return -1;
72         }
73         res = buffer[buf_ridx];
74         ADVANCE(buf_ridx);
75         return res;
76 }
77
78 void kb_putback(int key)
79 {
80         /* go back a place */
81         if(--buf_ridx < 0) {
82                 buf_ridx += BUFSZ;
83         }
84
85         /* if the write end hasn't caught up with us, go back one place
86          * and put it there, otherwise just overwrite the oldest key which
87          * is right where we were.
88          */
89         if(buf_ridx == buf_widx) {
90                 ADVANCE(buf_ridx);
91         }
92
93         buffer[buf_ridx] = key;
94 }
95
96 static void kbintr()
97 {
98         unsigned char code;
99         int key, press;
100
101         code = inb(KB_PORT);
102
103         if(code >= 128) {
104                 press = 0;
105                 code -= 128;
106
107                 if(num_pressed > 0) {
108                         num_pressed--;
109                 }
110         } else {
111                 press = 1;
112
113                 num_pressed++;
114         }
115
116         key = scantbl[code];
117
118         if(press) {
119                 /* append to buffer */
120                 buffer[buf_widx] = key;
121                 ADVANCE(buf_widx);
122                 /* if the write end overtook the read end, advance the read end
123                  * too, to discard the oldest keypress from the buffer
124                  */
125                 if(buf_widx == buf_ridx) {
126                         ADVANCE(buf_ridx);
127                 }
128         }
129
130         /* and update keystate table */
131         keystate[key] = press;
132 }