warning and chance to abort
[com32] / src / keyb.c
1 /*
2 pcboot - bootable PC demo/game kernel
3 Copyright (C) 2018-2019  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 #include "kbscan.h"
25 #include "power.h"
26 #include "panic.h"
27
28 #define delay7us() \
29         do { \
30                 iodelay(); iodelay(); iodelay(); iodelay(); \
31                 iodelay(); iodelay(); iodelay(); \
32         } while(0)
33
34 static void set_ccb(unsigned char ccb);
35 static unsigned char get_ccb(void);
36
37 static void kbintr();
38
39 #define BUFSZ   64
40 #define ADVANCE(x)      ((x) = ((x) + 1) & (BUFSZ - 1))
41
42 static int buffer[BUFSZ];
43 static int buf_ridx, buf_widx;
44
45 static unsigned int num_pressed;
46 static unsigned char keystate[256];
47
48
49 void kb_init(void)
50 {
51         buf_ridx = buf_widx = 0;
52         num_pressed = 0;
53         memset(keystate, 0, sizeof keystate);
54
55         /* make sure set1 translation is enabled */
56         kb_set_translate(1);
57
58         interrupt(IRQ_TO_INTR(KB_IRQ), kbintr);
59         kb_intr_enable();
60         unmask_irq(KB_IRQ);
61 }
62
63 void kb_intr_enable(void)
64 {
65         unsigned char ccb = get_ccb();
66         ccb |= KB_CCB_KB_INTREN;
67         set_ccb(ccb);
68 }
69
70 void kb_intr_disable(void)
71 {
72         unsigned char ccb = get_ccb();
73         ccb &= ~KB_CCB_KB_INTREN;
74         set_ccb(ccb);
75 }
76
77 int kb_setmode(int mode)
78 {
79         kb_send_data(0xf0);
80         if(!kb_wait_read() || kb_read_data() != KB_ACK) {
81                 return -1;
82         }
83         kb_send_data(mode);
84         if(!kb_wait_read() || kb_read_data() != KB_ACK) {
85                 return -1;
86         }
87         return 0;
88 }
89
90 int kb_getmode(void)
91 {
92         int mode;
93
94         kb_send_data(0xf0);
95         if(!kb_wait_read() || kb_read_data() != KB_ACK) {
96                 return -1;
97         }
98         kb_send_data(0);
99         if(!kb_wait_read() || kb_read_data() != KB_ACK) {
100                 return -1;
101         }
102         mode = kb_read_data();
103
104         switch(mode) {
105         case 0x43: return 1;
106         case 0x41: return 2;
107         case 0x3f: return 3;
108         default:
109                 break;
110         }
111         return mode;
112 }
113
114 void kb_set_translate(int xlat)
115 {
116         unsigned char ccb = get_ccb();
117         if(xlat) {
118                 ccb |= KB_CCB_KB_XLAT;
119         } else {
120                 ccb &= ~KB_CCB_KB_XLAT;
121         }
122         set_ccb(ccb);
123 }
124
125 int kb_get_translate(void)
126 {
127         return get_ccb() & KB_CCB_KB_XLAT;
128 }
129
130 int kb_isdown(int key)
131 {
132         switch(key) {
133         case KB_ANY:
134                 return num_pressed;
135
136         case KB_ALT:
137                 return keystate[KB_LALT] + keystate[KB_RALT];
138
139         case KB_CTRL:
140                 return keystate[KB_LCTRL] + keystate[KB_RCTRL];
141         }
142         return keystate[key];
143 }
144
145 void kb_wait(void)
146 {
147         int key;
148         while((key = kb_getkey()) == -1) {
149                 /* put the processor to sleep while waiting for keypresses, but first
150                  * make sure interrupts are enabled, or we'll sleep forever
151                  */
152                 enable_intr();
153                 halt_cpu();
154         }
155         kb_putback(key);
156 }
157
158 int kb_getkey(void)
159 {
160         int res;
161
162         if(buf_ridx == buf_widx) {
163                 return -1;
164         }
165         res = buffer[buf_ridx];
166         ADVANCE(buf_ridx);
167         return res;
168 }
169
170 void kb_putback(int key)
171 {
172         /* go back a place */
173         if(--buf_ridx < 0) {
174                 buf_ridx += BUFSZ;
175         }
176
177         /* if the write end hasn't caught up with us, go back one place
178          * and put it there, otherwise just overwrite the oldest key which
179          * is right where we were.
180          */
181         if(buf_ridx == buf_widx) {
182                 ADVANCE(buf_ridx);
183         }
184
185         buffer[buf_ridx] = key;
186 }
187
188 int kb_wait_write(void)
189 {
190         int i;
191         for(i=0; i<32768; i++) {
192                 if(!(inp(KB_STATUS_PORT) & KB_STAT_INBUF_FULL)) {
193                         return 1;
194                 }
195                 iodelay();
196         }
197         /*printf("kb_wait_write timeout\n");*/
198         return 0;
199 }
200
201 int kb_wait_read(void)
202 {
203         int i;
204         for(i=0; i<32768; i++) {
205                 if((inp(KB_STATUS_PORT) & KB_STAT_OUTBUF_FULL)) {
206                         return 1;
207                 }
208                 iodelay();
209         }
210         /*printf("kb_wait_read timeout\n");*/
211         return 0;
212 }
213
214 void kb_send_cmd(unsigned char cmd)
215 {
216         kb_wait_write();
217         outp(KB_CMD_PORT, cmd);
218 }
219
220 void kb_send_data(unsigned char data)
221 {
222         kb_wait_write();
223         outp(KB_DATA_PORT, data);
224 }
225
226 unsigned char kb_read_data(void)
227 {
228         kb_wait_read();
229         delay7us();
230         return inp(KB_DATA_PORT);
231 }
232
233 static void set_ccb(unsigned char ccb)
234 {
235         kb_send_cmd(KB_CMD_SET_CMDBYTE);
236         kb_send_data(ccb);
237
238         if(kb_wait_read()) {
239                 kb_read_data();
240         }
241 }
242
243 static unsigned char get_ccb(void)
244 {
245         kb_send_cmd(KB_CMD_GET_CMDBYTE);
246         return kb_read_data();
247 }
248
249 static void kbintr()
250 {
251         unsigned char code;
252         int key, press;
253         static int ext = 0;
254
255         code = inp(KB_DATA_PORT);
256
257         if(code == 0xe0) {
258                 ext = 1;
259                 return;
260         }
261
262         if(code & 0x80) {
263                 press = 0;
264                 code &= 0x7f;
265
266                 if(num_pressed > 0) {
267                         num_pressed--;
268                 }
269         } else {
270                 press = 1;
271
272                 num_pressed++;
273         }
274
275         if(!ext) {
276                 key = code < 0x59 ? scantbl_set1[code] : 0;
277         } else {
278                 switch(code) {
279                 case 0x1c: key = '\r'; break;
280                 case 0x1d: key = KB_RCTRL; break;
281                 //case 0x35: key = KB_NUM_MINUS; break;
282                 //case 0x37: key = KB_SYSRQ; break;
283                 case 0x38: key = KB_RALT; break;
284                 case 0x47: key = KB_HOME; break;
285                 case 0x48: key = KB_UP; break;
286                 case 0x49: key = KB_PGUP; break;
287                 case 0x4b: key = KB_LEFT; break;
288                 case 0x4d: key = KB_RIGHT; break;
289                 case 0x4f: key = KB_END; break;
290                 case 0x50: key = KB_DOWN; break;
291                 case 0x51: key = KB_PGDN; break;
292                 case 0x52: key = KB_INSERT; break;
293                 case 0x53: key = KB_DEL; break;
294                 default:
295                         key = 0;
296                 }
297                 ext = 0;
298         }
299
300         if(press) {
301                 if(key == KB_DEL && (keystate[KB_LALT] || keystate[KB_RALT]) && (keystate[KB_LCTRL] || keystate[KB_RCTRL])) {
302                         reboot();
303                 }
304                 /* append to buffer */
305                 buffer[buf_widx] = key;
306                 ADVANCE(buf_widx);
307                 /* if the write end overtook the read end, advance the read end
308                  * too, to discard the oldest keypress from the buffer
309                  */
310                 if(buf_widx == buf_ridx) {
311                         ADVANCE(buf_ridx);
312                 }
313         }
314
315         /* and update keystate table */
316         keystate[key] = press;
317 }