PCem
view src/keyboard_at.c @ 144:26a55112027e
Tweaked AT keyboard handler, fixes stuck/missing keys in Doom.
| author | TomW |
|---|---|
| date | Sat Aug 16 21:19:02 2014 +0100 |
| parents | 2b9cea0be424 |
| children | 21f926f1fabf |
line source
1 #include "ibm.h"
2 #include "io.h"
3 #include "mem.h"
4 #include "pic.h"
5 #include "pit.h"
6 #include "sound.h"
7 #include "sound_speaker.h"
8 #include "timer.h"
10 #include "keyboard.h"
11 #include "keyboard_at.h"
13 #define STAT_PARITY 0x80
14 #define STAT_RTIMEOUT 0x40
15 #define STAT_TTIMEOUT 0x20
16 #define STAT_MFULL 0x20
17 #define STAT_LOCK 0x10
18 #define STAT_CD 0x08
19 #define STAT_SYSFLAG 0x04
20 #define STAT_IFULL 0x02
21 #define STAT_OFULL 0x01
23 struct
24 {
25 int initialised;
26 int want60;
27 int wantirq, wantirq12;
28 uint8_t command;
29 uint8_t status;
30 uint8_t mem[0x20];
31 uint8_t out;
32 int out_new;
34 uint8_t input_port;
35 uint8_t output_port;
37 uint8_t key_command;
38 int key_wantdata;
39 } keyboard_at;
41 static uint8_t key_ctrl_queue[16];
42 static int key_ctrl_queue_start = 0, key_ctrl_queue_end = 0;
44 static uint8_t key_queue[16];
45 static int key_queue_start = 0, key_queue_end = 0;
47 static uint8_t mouse_queue[16];
48 int mouse_queue_start = 0, mouse_queue_end = 0;
50 void keyboard_at_poll()
51 {
52 keybsenddelay += (1000 * TIMER_USEC);
53 if (keyboard_at.wantirq)
54 {
55 keyboard_at.wantirq = 0;
56 picint(2);
57 if (keyboard_at.out_new != -1)
58 keyboard_at.out = keyboard_at.out_new;
59 keyboard_at.out_new = -1;
60 keyboard_at.status |= STAT_OFULL;
61 keyboard_at.status &= ~STAT_IFULL;
62 // pclog("keyboard_at : take IRQ\n");
63 }
64 else if (keyboard_at.wantirq12)
65 {
66 keyboard_at.wantirq12 = 0;
67 picint(0x1000);
68 // pclog("keyboard_at : take IRQ 12\n");
69 }
70 if (!(keyboard_at.status & STAT_OFULL) && !(keyboard_at.mem[0] & 0x10) &&
71 mouse_queue_start != mouse_queue_end)
72 {
73 // pclog("Reading %02X from the mouse queue at %i\n", keyboard_at.out, key_queue_start);
74 keyboard_at.out = mouse_queue[mouse_queue_start];
75 mouse_queue_start = (mouse_queue_start + 1) & 0xf;
76 keyboard_at.status |= STAT_OFULL | STAT_MFULL;
77 keyboard_at.status &= ~STAT_IFULL;
78 if (keyboard_at.mem[0] & 0x02)
79 keyboard_at.wantirq12 = 1;
80 }
81 else if (keyboard_at.out_new == -1 && !(keyboard_at.mem[0] & 0x10) &&
82 key_queue_start != key_queue_end)
83 {
84 keyboard_at.out_new = key_queue[key_queue_start];
85 // pclog("Reading %02X from the key queue at %i\n", keyboard_at.out, key_queue_start);
86 key_queue_start = (key_queue_start + 1) & 0xf;
87 if (keyboard_at.mem[0] & 0x01)
88 keyboard_at.wantirq = 1;
89 }
90 else if (keyboard_at.out_new == -1 && !(keyboard_at.status & STAT_OFULL) &&
91 key_ctrl_queue_start != key_ctrl_queue_end)
92 {
93 keyboard_at.out_new = key_ctrl_queue[key_ctrl_queue_start];
94 // pclog("Reading %02X from the key ctrl_queue at %i\n", keyboard_at.out, key_ctrl_queue_start);
95 key_ctrl_queue_start = (key_ctrl_queue_start + 1) & 0xf;
96 if (keyboard_at.mem[0] & 0x01)
97 keyboard_at.wantirq = 1;
98 }
99 }
101 void keyboard_at_adddata(uint8_t val)
102 {
103 // if (keyboard_at.status & STAT_OFULL)
104 // {
105 key_ctrl_queue[key_ctrl_queue_end] = val;
106 key_ctrl_queue_end = (key_ctrl_queue_end + 1) & 0xf;
107 // pclog("keyboard_at : %02X added to queue\n", val);
108 /* return;
109 }
110 keyboard_at.out = val;
111 keyboard_at.status |= STAT_OFULL;
112 keyboard_at.status &= ~STAT_IFULL;
113 if (keyboard_at.mem[0] & 0x01)
114 keyboard_at.wantirq = 1;
115 pclog("keyboard_at : output %02X (IRQ %i)\n", val, keyboard_at.wantirq);*/
116 }
118 void keyboard_at_adddata_keyboard(uint8_t val)
119 {
120 /* if (val == 0x1c)
121 {
122 key_1c++;
123 if (key_1c == 4)
124 output = 3;
125 }*/
126 key_queue[key_queue_end] = val;
127 key_queue_end = (key_queue_end + 1) & 0xf;
128 // pclog("keyboard_at : %02X added to key queue\n", val);
129 return;
130 }
132 void keyboard_at_adddata_mouse(uint8_t val)
133 {
134 mouse_queue[mouse_queue_end] = val;
135 mouse_queue_end = (mouse_queue_end + 1) & 0xf;
136 // pclog("keyboard_at : %02X added to mouse queue\n", val);
137 return;
138 }
140 void keyboard_at_write(uint16_t port, uint8_t val, void *priv)
141 {
142 // pclog("keyboard_at : write %04X %02X %i %02X\n", port, val, keyboard_at.key_wantdata, ram[8]);
143 /* if (ram[8] == 0xc3)
144 {
145 output = 3;
146 }*/
147 switch (port)
148 {
149 case 0x60:
150 if (keyboard_at.want60)
151 {
152 /*Write to controller*/
153 keyboard_at.want60 = 0;
154 switch (keyboard_at.command)
155 {
156 case 0x60: case 0x61: case 0x62: case 0x63:
157 case 0x64: case 0x65: case 0x66: case 0x67:
158 case 0x68: case 0x69: case 0x6a: case 0x6b:
159 case 0x6c: case 0x6d: case 0x6e: case 0x6f:
160 case 0x70: case 0x71: case 0x72: case 0x73:
161 case 0x74: case 0x75: case 0x76: case 0x77:
162 case 0x78: case 0x79: case 0x7a: case 0x7b:
163 case 0x7c: case 0x7d: case 0x7e: case 0x7f:
164 keyboard_at.mem[keyboard_at.command & 0x1f] = val;
165 if (keyboard_at.command == 0x60)
166 {
167 if ((val & 1) && (keyboard_at.status & STAT_OFULL))
168 keyboard_at.wantirq = 1;
169 if (!(val & 1) && keyboard_at.wantirq)
170 keyboard_at.wantirq = 0;
171 }
172 break;
174 case 0xcb: /*AMI - set keyboard mode*/
175 break;
177 case 0xcf: /*??? - sent by MegaPC BIOS*/
178 break;
180 case 0xd1: /*Write output port*/
181 // pclog("Write output port - %02X %02X %04X:%04X\n", keyboard_at.output_port, val, CS, pc);
182 if ((keyboard_at.output_port ^ val) & 0x02) /*A20 enable change*/
183 {
184 mem_a20_key = val & 0x02;
185 mem_a20_recalc();
186 // pclog("Rammask change to %08X %02X\n", rammask, val & 0x02);
187 flushmmucache();
188 }
189 keyboard_at.output_port = val;
190 break;
192 case 0xd3: /*Write to mouse output buffer*/
193 keyboard_at_adddata_mouse(val);
194 break;
196 case 0xd4: /*Write to mouse*/
197 if (mouse_write)
198 mouse_write(val);
199 break;
201 default:
202 pclog("Bad AT keyboard controller 0060 write %02X command %02X\n", val, keyboard_at.command);
203 // dumpregs();
204 // exit(-1);
205 }
206 }
207 else
208 {
209 /*Write to keyboard*/
210 keyboard_at.mem[0] &= ~0x10;
211 if (keyboard_at.key_wantdata)
212 {
213 keyboard_at.key_wantdata = 0;
214 switch (keyboard_at.key_command)
215 {
216 case 0xed: /*Set/reset LEDs*/
217 keyboard_at_adddata_keyboard(0xfa);
218 break;
220 case 0xf3: /*Set typematic rate/delay*/
221 keyboard_at_adddata_keyboard(0xfa);
222 break;
224 default:
225 pclog("Bad AT keyboard 0060 write %02X command %02X\n", val, keyboard_at.key_command);
226 // dumpregs();
227 // exit(-1);
228 }
229 }
230 else
231 {
232 keyboard_at.key_command = val;
233 switch (val)
234 {
235 case 0x05: /*??? - sent by NT 4.0*/
236 keyboard_at_adddata_keyboard(0xfe);
237 break;
239 case 0xed: /*Set/reset LEDs*/
240 keyboard_at.key_wantdata = 1;
241 keyboard_at_adddata_keyboard(0xfa);
242 break;
244 case 0xf2: /*Read ID*/
245 keyboard_at_adddata_keyboard(0xfa);
246 keyboard_at_adddata_keyboard(0xab);
247 keyboard_at_adddata_keyboard(0x41);
248 break;
250 case 0xf3: /*Set typematic rate/delay*/
251 keyboard_at.key_wantdata = 1;
252 keyboard_at_adddata_keyboard(0xfa);
253 break;
255 case 0xf4: /*Enable keyboard*/
256 keyboard_scan = 1;
257 break;
259 case 0xff: /*Reset*/
260 key_queue_start = key_queue_end = 0; /*Clear key queue*/
261 keyboard_at_adddata_keyboard(0xfa);
262 keyboard_at_adddata_keyboard(0xaa);
263 break;
265 default:
266 pclog("Bad AT keyboard command %02X\n", val);
267 keyboard_at_adddata_keyboard(0xfe);
268 // dumpregs();
269 // exit(-1);
270 }
271 }
272 }
273 break;
275 case 0x61:
276 ppi.pb = val;
277 speaker_gated = val & 1;
278 speaker_enable = val & 2;
279 if (speaker_enable)
280 was_speaker_enable = 1;
281 pit_set_gate(2, val & 1);
282 break;
284 case 0x64:
285 keyboard_at.want60 = 0;
286 keyboard_at.command = val;
287 /*New controller command*/
288 switch (val)
289 {
290 case 0x20: case 0x21: case 0x22: case 0x23:
291 case 0x24: case 0x25: case 0x26: case 0x27:
292 case 0x28: case 0x29: case 0x2a: case 0x2b:
293 case 0x2c: case 0x2d: case 0x2e: case 0x2f:
294 case 0x30: case 0x31: case 0x32: case 0x33:
295 case 0x34: case 0x35: case 0x36: case 0x37:
296 case 0x38: case 0x39: case 0x3a: case 0x3b:
297 case 0x3c: case 0x3d: case 0x3e: case 0x3f:
298 keyboard_at_adddata(keyboard_at.mem[val & 0x1f]);
299 break;
301 case 0x60: case 0x61: case 0x62: case 0x63:
302 case 0x64: case 0x65: case 0x66: case 0x67:
303 case 0x68: case 0x69: case 0x6a: case 0x6b:
304 case 0x6c: case 0x6d: case 0x6e: case 0x6f:
305 case 0x70: case 0x71: case 0x72: case 0x73:
306 case 0x74: case 0x75: case 0x76: case 0x77:
307 case 0x78: case 0x79: case 0x7a: case 0x7b:
308 case 0x7c: case 0x7d: case 0x7e: case 0x7f:
309 keyboard_at.want60 = 1;
310 break;
312 case 0xa1: /*AMI - get controlled version*/
313 break;
315 case 0xa7: /*Disable mouse port*/
316 break;
318 case 0xa9: /*Test mouse port*/
319 keyboard_at_adddata(0x00); /*no error*/
320 break;
322 case 0xaa: /*Self-test*/
323 if (!keyboard_at.initialised)
324 {
325 keyboard_at.initialised = 1;
326 key_ctrl_queue_start = key_ctrl_queue_end = 0;
327 keyboard_at.status &= ~STAT_OFULL;
328 }
329 keyboard_at.status |= STAT_SYSFLAG;
330 keyboard_at.mem[0] |= 0x04;
331 keyboard_at_adddata(0x55);
332 break;
334 case 0xab: /*Interface test*/
335 keyboard_at_adddata(0x00); /*no error*/
336 break;
338 case 0xad: /*Disable keyboard*/
339 keyboard_at.mem[0] |= 0x10;
340 break;
342 case 0xae: /*Enable keyboard*/
343 keyboard_at.mem[0] &= ~0x10;
344 break;
346 case 0xc0: /*Read input port*/
347 keyboard_at_adddata(keyboard_at.input_port);
348 keyboard_at.input_port = ((keyboard_at.input_port + 1) & 3) | (keyboard_at.input_port & 0xfc);
349 break;
351 case 0xc9: /*AMI - block P22 and P23 ??? */
352 break;
354 case 0xca: /*AMI - read keyboard mode*/
355 keyboard_at_adddata(0x00); /*ISA mode*/
356 break;
358 case 0xcb: /*AMI - set keyboard mode*/
359 keyboard_at.want60 = 1;
360 break;
362 case 0xcf: /*??? - sent by MegaPC BIOS*/
363 keyboard_at.want60 = 1;
364 break;
366 case 0xd0: /*Read output port*/
367 keyboard_at_adddata(keyboard_at.output_port);
368 break;
370 case 0xd1: /*Write output port*/
371 keyboard_at.want60 = 1;
372 break;
374 case 0xd3: /*Write mouse output buffer*/
375 keyboard_at.want60 = 1;
376 break;
378 case 0xd4: /*Write to mouse*/
379 keyboard_at.want60 = 1;
380 break;
382 case 0xe0: /*Read test inputs*/
383 keyboard_at_adddata(0x00);
384 break;
386 case 0xef: /*??? - sent by AMI486*/
387 break;
389 case 0xfe: /*Pulse output port - pin 0 selected - x86 reset*/
390 softresetx86(); /*Pulse reset!*/
391 break;
393 case 0xff: /*Pulse output port - but no pins selected - sent by MegaPC BIOS*/
394 break;
396 default:
397 pclog("Bad AT keyboard controller command %02X\n", val);
398 // dumpregs();
399 // exit(-1);
400 }
401 }
402 }
404 uint8_t keyboard_at_read(uint16_t port, void *priv)
405 {
406 uint8_t temp = 0xff;
407 cycles -= 4;
408 // if (port != 0x61) pclog("keyboard_at : read %04X ", port);
409 switch (port)
410 {
411 case 0x60:
412 temp = keyboard_at.out;
413 keyboard_at.status &= ~(STAT_OFULL | STAT_MFULL);
414 break;
416 case 0x61:
417 if (ppispeakon) return (ppi.pb&~0xC0)|0x20;
418 return ppi.pb&~0xC0;
419 break;
421 case 0x64:
422 temp = keyboard_at.status;
423 keyboard_at.status &= ~(STAT_RTIMEOUT | STAT_TTIMEOUT);
424 break;
425 }
426 // if (port != 0x61) pclog("%02X %08X\n", temp, rammask);
427 return temp;
428 }
430 void keyboard_at_reset()
431 {
432 keyboard_at.initialised = 0;
433 keyboard_at.status = STAT_LOCK | STAT_CD;
434 keyboard_at.mem[0] = 0x11;
435 keyboard_at.wantirq = 0;
436 keyboard_at.output_port = 0;
437 keyboard_at.input_port = 0xb0;
438 keyboard_at.out_new = -1;
440 keyboard_at.key_wantdata = 0;
442 keyboard_scan = 1;
443 }
445 void keyboard_at_init()
446 {
447 //return;
448 io_sethandler(0x0060, 0x0005, keyboard_at_read, NULL, NULL, keyboard_at_write, NULL, NULL, NULL);
449 keyboard_at_reset();
450 keyboard_send = keyboard_at_adddata_keyboard;
451 keyboard_poll = keyboard_at_poll;
452 mouse_write = NULL;
454 timer_add(keyboard_at_poll, &keybsenddelay, TIMER_ALWAYS_ENABLED, NULL);
455 }
