3e86f43df38445b3d3e0a2646cfc77c21a664621
[bootcensus] / src / keyb.c
1 /*
2 pcboot - bootable PC demo/game kernel
3 Copyright (C) 2018  John Tsiombikas <nuclear@member.fsf.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY, without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18 #include <stdio.h>
19 #include <string.h>
20 #include "keyb.h"
21 #include "intr.h"
22 #include "asmops.h"
23 #include "kbregs.h"
24
25 #define delay7us() \
26         do { \
27                 iodelay(); iodelay(); iodelay(); iodelay(); \
28                 iodelay(); iodelay(); iodelay(); \
29         } while(0)
30
31 /* table with rough translations from set 1 scancodes to ASCII-ish */
32 static int scantbl[] = {
33         0, KB_ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b',            /* 0 - e */
34         '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',                 /* f - 1c */
35         KB_LCTRL, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`',                          /* 1d - 29 */
36         KB_LSHIFT, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', KB_RSHIFT,                   /* 2a - 36 */
37         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 */
38         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 */
39         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 */
40         0, 0, 0, 0, 0, 0, 0,                                                                                                                    /* 59 - 5f */
41         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                                                                 /* 60 - 6f */
42         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0                                                                  /* 70 - 7f */
43 };
44
45 static void kbintr();
46
47 #define BUFSZ   64
48 #define ADVANCE(x)      ((x) = ((x) + 1) & (BUFSZ - 1))
49
50 static int buffer[BUFSZ];
51 static int buf_ridx, buf_widx;
52
53 static unsigned int num_pressed;
54 static unsigned char keystate[256];
55
56 void kb_init(void)
57 {
58         buf_ridx = buf_widx = 0;
59         num_pressed = 0;
60         memset(keystate, 0, sizeof keystate);
61
62         interrupt(IRQ_TO_INTR(KB_IRQ), kbintr);
63 }
64
65 int kb_isdown(int key)
66 {
67         switch(key) {
68         case KB_ANY:
69                 return num_pressed;
70
71         case KB_ALT:
72                 return keystate[KB_LALT] + keystate[KB_RALT];
73
74         case KB_CTRL:
75                 return keystate[KB_LCTRL] + keystate[KB_RCTRL];
76         }
77         return keystate[key];
78 }
79
80 void kb_wait(void)
81 {
82         int key;
83         while((key = kb_getkey()) == -1) {
84                 /* put the processor to sleep while waiting for keypresses, but first
85                  * make sure interrupts are enabled, or we'll sleep forever
86                  */
87                 enable_intr();
88                 halt_cpu();
89         }
90         kb_putback(key);
91 }
92
93 int kb_getkey(void)
94 {
95         int res;
96
97         if(buf_ridx == buf_widx) {
98                 return -1;
99         }
100         res = buffer[buf_ridx];
101         ADVANCE(buf_ridx);
102         return res;
103 }
104
105 void kb_putback(int key)
106 {
107         /* go back a place */
108         if(--buf_ridx < 0) {
109                 buf_ridx += BUFSZ;
110         }
111
112         /* if the write end hasn't caught up with us, go back one place
113          * and put it there, otherwise just overwrite the oldest key which
114          * is right where we were.
115          */
116         if(buf_ridx == buf_widx) {
117                 ADVANCE(buf_ridx);
118         }
119
120         buffer[buf_ridx] = key;
121 }
122
123 int kb_wait_write(void)
124 {
125         int i;
126         for(i=0; i<32768; i++) {
127                 if(!(inb(KB_STATUS_PORT) & KB_STAT_INBUF_FULL)) {
128                         return 1;
129                 }
130                 iodelay();
131         }
132         /*printf("kb_wait_write timeout\n");*/
133         return 0;
134 }
135
136 int kb_wait_read(void)
137 {
138         int i;
139         for(i=0; i<32768; i++) {
140                 if((inb(KB_STATUS_PORT) & KB_STAT_OUTBUF_FULL)) {
141                         return 1;
142                 }
143                 iodelay();
144         }
145         /*printf("kb_wait_read timeout\n");*/
146         return 0;
147 }
148
149 void kb_send_cmd(unsigned char cmd)
150 {
151         kb_wait_write();
152         outb(cmd, KB_CMD_PORT);
153 }
154
155 void kb_send_data(unsigned char data)
156 {
157         kb_wait_write();
158         outb(data, KB_DATA_PORT);
159 }
160
161 unsigned char kb_read_data(void)
162 {
163         kb_wait_read();
164         delay7us();
165         return inb(KB_DATA_PORT);
166 }
167
168 static void kbintr()
169 {
170         unsigned char code;
171         int key, press;
172
173         code = inb(KB_DATA_PORT);
174
175         if(code >= 128) {
176                 press = 0;
177                 code -= 128;
178
179                 if(num_pressed > 0) {
180                         num_pressed--;
181                 }
182         } else {
183                 press = 1;
184
185                 num_pressed++;
186         }
187
188         key = scantbl[code];
189
190         if(press) {
191                 /* append to buffer */
192                 buffer[buf_widx] = key;
193                 ADVANCE(buf_widx);
194                 /* if the write end overtook the read end, advance the read end
195                  * too, to discard the oldest keypress from the buffer
196                  */
197                 if(buf_widx == buf_ridx) {
198                         ADVANCE(buf_ridx);
199                 }
200         }
201
202         /* and update keystate table */
203         keystate[key] = press;
204 }