25 |
* SUCH DAMAGE. |
* SUCH DAMAGE. |
26 |
* |
* |
27 |
* |
* |
28 |
* $Id: dev_pckbc.c,v 1.37 2005/02/22 06:26:10 debug Exp $ |
* $Id: dev_pckbc.c,v 1.45 2005/06/02 15:42:49 debug Exp $ |
29 |
* |
* |
30 |
* Standard 8042 PC keyboard controller, and a 8242WB PS2 keyboard/mouse |
* Standard 8042 PC keyboard controller (and a 8242WB PS2 keyboard/mouse |
31 |
* controller. |
* controller), including the 8048 keyboard chip. |
32 |
* |
* |
33 |
* |
* |
34 |
* TODO: Finish the rewrite for 8242. |
* TODO: Finish the rewrite for 8242. |
69 |
struct pckbc_data { |
struct pckbc_data { |
70 |
int console_handle; |
int console_handle; |
71 |
int in_use; |
int in_use; |
|
int any_command_used; |
|
72 |
|
|
73 |
int reg[DEV_PCKBC_LENGTH]; |
int reg[DEV_PCKBC_LENGTH]; |
74 |
int keyboard_irqnr; |
int keyboard_irqnr; |
75 |
int mouse_irqnr; |
int mouse_irqnr; |
76 |
int type; |
int type; |
77 |
|
int pc_style_flag; |
78 |
|
|
79 |
/* TODO: one of these for each port? */ |
/* TODO: one of these for each port? */ |
80 |
int clocksignal; |
int clocksignal; |
84 |
int keyscanning_enabled; |
int keyscanning_enabled; |
85 |
int state; |
int state; |
86 |
int cmdbyte; |
int cmdbyte; |
87 |
|
int output_byte; |
88 |
int last_scancode; |
int last_scancode; |
89 |
|
|
90 |
unsigned key_queue[2][MAX_8042_QUEUELEN]; |
unsigned key_queue[2][MAX_8042_QUEUELEN]; |
95 |
#define STATE_LDCMDBYTE 1 |
#define STATE_LDCMDBYTE 1 |
96 |
#define STATE_RDCMDBYTE 2 |
#define STATE_RDCMDBYTE 2 |
97 |
#define STATE_WAITING_FOR_TRANSLTABLE 3 |
#define STATE_WAITING_FOR_TRANSLTABLE 3 |
98 |
|
#define STATE_LDOUTPUT 4 |
99 |
|
#define STATE_RDOUTPUT 5 |
100 |
|
|
101 |
|
|
102 |
/* |
/* |
124 |
{ |
{ |
125 |
if (d->head[port] == d->tail[port]) |
if (d->head[port] == d->tail[port]) |
126 |
fatal("[ pckbc: queue empty, port %i! ]\n", port); |
fatal("[ pckbc: queue empty, port %i! ]\n", port); |
127 |
|
else |
128 |
d->tail[port] = (d->tail[port]+1) % MAX_8042_QUEUELEN; |
d->tail[port] = (d->tail[port]+1) % MAX_8042_QUEUELEN; |
129 |
return d->key_queue[port][d->tail[port]]; |
return d->key_queue[port][d->tail[port]]; |
130 |
} |
} |
131 |
|
|
138 |
*/ |
*/ |
139 |
static void ascii_to_pc_scancodes(int a, struct pckbc_data *d) |
static void ascii_to_pc_scancodes(int a, struct pckbc_data *d) |
140 |
{ |
{ |
141 |
|
int old_head; |
142 |
int p = 0; /* port */ |
int p = 0; /* port */ |
143 |
int shift = 0, ctrl = 0; |
int shift = 0, ctrl = 0; |
144 |
|
|
155 |
pckbc_add_code(d, 0x1d, p); |
pckbc_add_code(d, 0x1d, p); |
156 |
|
|
157 |
/* |
/* |
158 |
* TODO: Release for all of these? |
* Note: The ugly hack used to add release codes for all of these |
159 |
|
* keys is as follows: we remember how much of the kbd buf that |
160 |
|
* is in use here, before we add any scancode. After we've added |
161 |
|
* one or more scancodes (ie an optional shift + another key) |
162 |
|
* then we duplicate the last scancode | 0x80 _if_ the kbd buf |
163 |
|
* was altered. |
164 |
*/ |
*/ |
165 |
|
|
166 |
|
old_head = d->head[p]; |
167 |
|
|
168 |
if (a==27) pckbc_add_code(d, 0x01, p); |
if (a==27) pckbc_add_code(d, 0x01, p); |
169 |
|
|
170 |
if (a=='1') pckbc_add_code(d, 0x02, p); |
if (a=='1') pckbc_add_code(d, 0x02, p); |
270 |
|
|
271 |
if (a==' ') pckbc_add_code(d, 0x39, p); |
if (a==' ') pckbc_add_code(d, 0x39, p); |
272 |
|
|
273 |
|
/* Add release code, if a key was pressed: */ |
274 |
|
if (d->head[p] != old_head) { |
275 |
|
int code = d->key_queue[p][d->head[p]] | 0x80; |
276 |
|
pckbc_add_code(d, code, p); |
277 |
|
} |
278 |
|
|
279 |
/* Release ctrl: */ |
/* Release ctrl: */ |
280 |
if (ctrl) |
if (ctrl) |
281 |
pckbc_add_code(d, 0x1d + 0x80, p); |
pckbc_add_code(d, 0x1d + 0x80, p); |
288 |
void dev_pckbc_tick(struct cpu *cpu, void *extra) |
void dev_pckbc_tick(struct cpu *cpu, void *extra) |
289 |
{ |
{ |
290 |
struct pckbc_data *d = extra; |
struct pckbc_data *d = extra; |
291 |
int port_nr; |
int port_nr, ch, ints_enabled; |
|
int ch; |
|
292 |
|
|
293 |
if (d->in_use && d->any_command_used && |
if (d->in_use && console_charavail(d->console_handle)) { |
|
console_charavail(d->console_handle)) { |
|
294 |
ch = console_readchar(d->console_handle); |
ch = console_readchar(d->console_handle); |
295 |
if (ch >= 0) |
if (ch >= 0) |
296 |
ascii_to_pc_scancodes(ch, d); |
ascii_to_pc_scancodes(ch, d); |
297 |
} |
} |
298 |
|
|
299 |
|
ints_enabled = d->rx_int_enable; |
300 |
|
|
301 |
/* TODO: mouse movements? */ |
/* TODO: mouse movements? */ |
302 |
|
|
303 |
|
if (d->cmdbyte & KC8_KDISABLE) |
304 |
|
ints_enabled = 0; |
305 |
|
|
306 |
for (port_nr=0; port_nr<2; port_nr++) { |
for (port_nr=0; port_nr<2; port_nr++) { |
307 |
/* Cause receive interrupt, |
/* Cause receive interrupt, if there's something in the |
308 |
if there's something in the receive buffer: */ |
receive buffer: (Otherwise deassert the interrupt.) */ |
309 |
if (d->head[port_nr] != d->tail[port_nr] && d->rx_int_enable) { |
if (d->head[port_nr] != d->tail[port_nr] && ints_enabled) { |
310 |
cpu_interrupt(cpu, port_nr==0? d->keyboard_irqnr |
cpu_interrupt(cpu, port_nr==0? d->keyboard_irqnr |
311 |
: d->mouse_irqnr); |
: d->mouse_irqnr); |
312 |
} else { |
} else { |
319 |
|
|
320 |
/* |
/* |
321 |
* dev_pckbc_command(): |
* dev_pckbc_command(): |
322 |
|
* |
323 |
|
* Handle commands to the 8048 in the emulated keyboard. |
324 |
*/ |
*/ |
325 |
static void dev_pckbc_command(struct pckbc_data *d, int port_nr) |
static void dev_pckbc_command(struct pckbc_data *d, int port_nr) |
326 |
{ |
{ |
327 |
int cmd = d->reg[PC_CMD]; |
int cmd = d->reg[PC_CMD]; |
328 |
|
|
|
d->any_command_used = 1; |
|
|
|
|
329 |
if (d->type == PCKBC_8242) |
if (d->type == PCKBC_8242) |
330 |
cmd = d->reg[PS2_TXBUF]; |
cmd = d->reg[PS2_TXBUF]; |
331 |
|
|
365 |
pckbc_add_code(d, KBR_RSTDONE, port_nr); |
pckbc_add_code(d, KBR_RSTDONE, port_nr); |
366 |
break; |
break; |
367 |
default: |
default: |
368 |
fatal("[ pckbc: UNIMPLEMENTED command 0x%02x ]\n", cmd); |
fatal("[ pckbc: UNIMPLEMENTED 8048 command 0x%02x ]\n", cmd); |
369 |
} |
} |
370 |
} |
} |
371 |
|
|
393 |
#endif |
#endif |
394 |
|
|
395 |
/* For JAZZ-based machines: */ |
/* For JAZZ-based machines: */ |
396 |
if (relative_addr >= 0x60) |
if (relative_addr >= 0x60) { |
397 |
relative_addr -= 0x60; |
relative_addr -= 0x60; |
398 |
|
if (relative_addr != 0) |
399 |
/* 8242 PS2-style: */ |
relative_addr = 1; |
400 |
if (d->type == PCKBC_8242) { |
} else if (d->type == PCKBC_8242) { |
401 |
|
/* 8242 PS2-style: */ |
402 |
/* when using 8-byte alignment... */ |
/* when using 8-byte alignment... */ |
403 |
relative_addr /= sizeof(uint64_t); |
relative_addr /= sizeof(uint64_t); |
404 |
/* port_nr = 0 for keyboard, 1 for mouse */ |
/* port_nr = 0 for keyboard, 1 for mouse */ |
405 |
port_nr = (relative_addr >> 2); |
port_nr = (relative_addr >> 2); |
406 |
relative_addr &= 3; |
relative_addr &= 3; |
407 |
relative_addr += PS2; |
relative_addr += PS2; |
408 |
|
} else if (d->pc_style_flag) { |
409 |
|
/* PC-style: */ |
410 |
|
if (relative_addr != 0 && relative_addr != 4) { |
411 |
|
/* TODO (port 0x61) */ |
412 |
|
odata = 0x21; |
413 |
|
{ |
414 |
|
static int x = 0; |
415 |
|
x++; |
416 |
|
if (x&1) |
417 |
|
odata ^= 0x10; |
418 |
|
} |
419 |
|
if (writeflag == MEM_READ) |
420 |
|
memory_writemax64(cpu, data, len, odata); |
421 |
|
return 0; |
422 |
|
} |
423 |
|
if (relative_addr != 0) |
424 |
|
relative_addr = 1; |
425 |
} else { |
} else { |
426 |
/* The relative_addr is either 0 or 1, |
/* Others... Non-Jazz ARC-based machines etc. */ |
427 |
but some machines use longer registers than one byte |
if (relative_addr != 0) |
|
each, so this will make things simpler for us: */ |
|
|
if (relative_addr) |
|
428 |
relative_addr = 1; |
relative_addr = 1; |
429 |
} |
} |
430 |
|
|
436 |
|
|
437 |
case 0: /* data */ |
case 0: /* data */ |
438 |
if (writeflag==MEM_READ) { |
if (writeflag==MEM_READ) { |
439 |
if (d->state == STATE_RDCMDBYTE) { |
switch (d->state) { |
440 |
|
case STATE_RDCMDBYTE: |
441 |
odata = d->cmdbyte; |
odata = d->cmdbyte; |
442 |
d->state = STATE_NORMAL; |
d->state = STATE_NORMAL; |
443 |
} else { |
break; |
444 |
if (d->head[0] != d->tail[0]) { |
case STATE_RDOUTPUT: |
445 |
|
odata = d->output_byte; |
446 |
|
d->state = STATE_NORMAL; |
447 |
|
break; |
448 |
|
default:if (d->head[0] != d->tail[0]) { |
449 |
odata = pckbc_get_code(d, 0); |
odata = pckbc_get_code(d, 0); |
450 |
d->last_scancode = odata; |
d->last_scancode = odata; |
451 |
} else { |
} else { |
453 |
d->last_scancode |= 0x80; |
d->last_scancode |= 0x80; |
454 |
} |
} |
455 |
} |
} |
456 |
debug("[ pckbc: read from DATA: 0x%02x ]\n", odata); |
/* debug("[ pckbc: read from DATA: 0x%02x ]\n", |
457 |
|
odata); */ |
458 |
} else { |
} else { |
459 |
debug("[ pckbc: write to DATA:"); |
debug("[ pckbc: write to DATA:"); |
460 |
for (i=0; i<len; i++) |
for (i=0; i<len; i++) |
461 |
debug(" %02x", data[i]); |
debug(" %02x", data[i]); |
462 |
debug(" ]\n"); |
debug(" ]\n"); |
463 |
|
|
464 |
if (d->state == STATE_LDCMDBYTE) { |
switch (d->state) { |
465 |
|
case STATE_LDCMDBYTE: |
466 |
d->cmdbyte = idata; |
d->cmdbyte = idata; |
467 |
d->rx_int_enable = d->cmdbyte & |
d->rx_int_enable = d->cmdbyte & |
468 |
(KC8_KENABLE | KC8_MENABLE) ? 1 : 0; |
(KC8_KENABLE | KC8_MENABLE) ? 1 : 0; |
469 |
d->state = STATE_NORMAL; |
d->state = STATE_NORMAL; |
470 |
} else { |
break; |
471 |
d->reg[relative_addr] = idata; |
case STATE_LDOUTPUT: |
472 |
|
d->output_byte = idata; |
473 |
|
d->state = STATE_NORMAL; |
474 |
|
break; |
475 |
|
default:d->reg[relative_addr] = idata; |
476 |
dev_pckbc_command(d, port_nr); |
dev_pckbc_command(d, port_nr); |
477 |
} |
} |
478 |
} |
} |
485 |
|
|
486 |
/* "Data in buffer" bit */ |
/* "Data in buffer" bit */ |
487 |
if (d->head[0] != d->tail[0] || |
if (d->head[0] != d->tail[0] || |
488 |
d->state == STATE_RDCMDBYTE) |
d->state == STATE_RDCMDBYTE || |
489 |
|
d->state == STATE_RDOUTPUT) |
490 |
odata |= KBS_DIB; |
odata |= KBS_DIB; |
491 |
/* odata |= KBS_OCMD; */ |
|
492 |
|
if (d->state == STATE_RDCMDBYTE) |
493 |
|
odata |= KBS_OCMD; |
494 |
|
|
495 |
|
odata |= KBS_NOSEC; |
496 |
/* debug("[ pckbc: read from CTL status port: " |
/* debug("[ pckbc: read from CTL status port: " |
497 |
"0x%02x ]\n", (int)odata); */ |
"0x%02x ]\n", (int)odata); */ |
498 |
} else { |
} else { |
509 |
case K_LDCMDBYTE: |
case K_LDCMDBYTE: |
510 |
d->state = STATE_LDCMDBYTE; |
d->state = STATE_LDCMDBYTE; |
511 |
break; |
break; |
512 |
|
case 0xa7: |
513 |
|
d->cmdbyte |= KC8_MDISABLE; |
514 |
|
break; |
515 |
|
case 0xa8: |
516 |
|
d->cmdbyte &= ~KC8_MDISABLE; |
517 |
|
break; |
518 |
case 0xa9: /* test auxiliary port */ |
case 0xa9: /* test auxiliary port */ |
519 |
debug("[ pckbc: CONTROL 0xa9, TODO ]\n"); |
debug("[ pckbc: CONTROL 0xa9, TODO ]\n"); |
520 |
break; |
break; |
521 |
case 0xaa: /* keyboard self-test */ |
case 0xaa: /* keyboard self-test */ |
522 |
pckbc_add_code(d, 0x55, port_nr); |
pckbc_add_code(d, 0x55, port_nr); |
523 |
break; |
break; |
524 |
|
case 0xad: |
525 |
|
d->cmdbyte |= KC8_KDISABLE; |
526 |
|
break; |
527 |
|
case 0xae: |
528 |
|
d->cmdbyte &= ~KC8_KDISABLE; |
529 |
|
break; |
530 |
|
case 0xd0: |
531 |
|
d->state = STATE_RDOUTPUT; |
532 |
|
break; |
533 |
|
case 0xd1: |
534 |
|
d->state = STATE_LDOUTPUT; |
535 |
|
break; |
536 |
case 0xd4: /* write to auxiliary port */ |
case 0xd4: /* write to auxiliary port */ |
537 |
debug("[ pckbc: CONTROL 0xd4, TODO ]\n"); |
debug("[ pckbc: CONTROL 0xd4, TODO ]\n"); |
538 |
break; |
break; |
703 |
*/ |
*/ |
704 |
int dev_pckbc_init(struct machine *machine, struct memory *mem, |
int dev_pckbc_init(struct machine *machine, struct memory *mem, |
705 |
uint64_t baseaddr, int type, int keyboard_irqnr, int mouse_irqnr, |
uint64_t baseaddr, int type, int keyboard_irqnr, int mouse_irqnr, |
706 |
int in_use) |
int in_use, int pc_style_flag) |
707 |
{ |
{ |
708 |
struct pckbc_data *d; |
struct pckbc_data *d; |
709 |
int len = DEV_PCKBC_LENGTH; |
int len = DEV_PCKBC_LENGTH; |
724 |
d->keyboard_irqnr = keyboard_irqnr; |
d->keyboard_irqnr = keyboard_irqnr; |
725 |
d->mouse_irqnr = mouse_irqnr; |
d->mouse_irqnr = mouse_irqnr; |
726 |
d->in_use = in_use; |
d->in_use = in_use; |
727 |
|
d->pc_style_flag = pc_style_flag; |
728 |
d->console_handle = console_start_slave_inputonly(machine, "pckbc"); |
d->console_handle = console_start_slave_inputonly(machine, "pckbc"); |
729 |
|
d->rx_int_enable = 1; |
730 |
|
d->output_byte = 0x02; /* A20 enable on PCs */ |
731 |
|
|
732 |
memory_device_register(mem, "pckbc", baseaddr, |
memory_device_register(mem, "pckbc", baseaddr, |
733 |
len, dev_pckbc_access, d, MEM_DEFAULT, NULL); |
len, dev_pckbc_access, d, MEM_DEFAULT, NULL); |