diff --git a/src/device/kbc_at.c b/src/device/kbc_at.c index c630960ae..74c5d83d1 100644 --- a/src/device/kbc_at.c +++ b/src/device/kbc_at.c @@ -99,7 +99,7 @@ enum { STATE_RESET = 0, /* KBC reset state, only accepts command AA. */ STATE_KBC_DELAY_OUT, /* KBC is sending one single byte. */ - STATE_KBC_AMI_OUT, /* KBC waiting for OBF - needed for AMIKey commands that require clearing of the output byte. */ + STATE_IRQC_MAIN_IBF, /* KBC checking if the input buffer is full, clear IRQ's first. */ STATE_MAIN_IBF, /* KBC checking if the input buffer is full. */ STATE_MAIN_KBD, /* KBC checking if the keyboard has anything to send. */ STATE_MAIN_AUX, /* KBC checking if the auxiliary has anything to send. */ @@ -113,7 +113,8 @@ enum { STATE_IRQ, /* KBC is raising the IRQ. */ STATE_KBC_IRQ, STATE_AUX_IRQ, - STATE_KBC_DELAY_IRQ + STATE_KBC_DELAY_IRQ, + STATE_IRQC_WAIT, }; typedef struct atkbc_t { @@ -371,25 +372,29 @@ kbc_send_to_ob(atkbc_t *dev, uint8_t val, uint8_t channel, uint8_t stat_hi) kbc_at_log("ATkbc: Sending %02X to the output buffer on channel %i...\n", temp, channel); dev->status = (dev->status & ~0xf0) | STAT_OFULL | stat_hi; + dev->state = STATE_MAIN_IBF; + /* WARNING: On PS/2, all IRQ's are level-triggered, but the IBM PS/2 KBC firmware is explicitly written to pulse its P2 IRQ bits, so they should be kept as as edge-triggered here. */ if (dev->misc_flags & FLAG_PS2) { if (channel >= 2) { dev->status |= STAT_MFULL; if ((kbc_ven == KBC_VEN_IBM_PS1) || (kbc_ven == KBC_VEN_IBM)) { - if (dev->mem[0x20] & 0x02) + if (dev->mem[0x20] & 0x02) { picint_common(1 << 12, 0, 1, NULL); - picint_common(1 << 1, 0, 0, NULL); - dev->state = STATE_MAIN_IBF; - } else + dev->state = STATE_IRQC_WAIT; + } + // picint_common(1 << 1, 0, 0, NULL); + } else if (dev->mem[0x20] & 0x02) dev->state = STATE_AUX_IRQ; } else if (channel == 1) { if ((kbc_ven == KBC_VEN_IBM_PS1) || (kbc_ven == KBC_VEN_IBM)) { - if (dev->mem[0x20] & 0x01) + if (dev->mem[0x20] & 0x01) { picint_common(1 << 1, 0, 1, NULL); - picint_common(1 << 12, 0, 0, NULL); - dev->state = STATE_MAIN_IBF; - } else + dev->state = STATE_IRQC_WAIT; + } + // picint_common(1 << 12, 0, 0, NULL); + } else if (dev->mem[0x20] & 0x01) dev->state = STATE_IRQ; } } else if (dev->mem[0x20] & 0x01) @@ -499,10 +504,6 @@ kbc_at_poll_at(atkbc_t *dev) kbc_at_process_cmd(dev); } break; - case STATE_KBC_AMI_OUT: - if (dev->status & STAT_OFULL) - break; - fallthrough; case STATE_MAIN_IBF: default: at_main_ibf: @@ -530,9 +531,6 @@ at_main_ibf: /* Keyboard controller command want to output a single byte. */ kbc_at_log("ATkbc: %02X coming from channel %i with high status %02X\n", dev->val, dev->channel, dev->stat_hi); kbc_send_to_ob(dev, dev->val, dev->channel, dev->stat_hi); -#if 0 - dev->state = (dev->pending == 2) ? STATE_KBC_AMI_OUT : STATE_MAIN_IBF; -#endif dev->state = STATE_MAIN_IBF; dev->pending = 0; goto at_main_ibf; @@ -611,6 +609,8 @@ kbc_scan_aux_ps2(atkbc_t *dev) static void kbc_at_poll_ps2(atkbc_t *dev) { + static uint8_t phase = 0; + switch (dev->state) { case STATE_RESET: if (dev->status & STAT_IFULL) { @@ -619,14 +619,19 @@ kbc_at_poll_ps2(atkbc_t *dev) kbc_at_process_cmd(dev); } break; - case STATE_KBC_AMI_OUT: - if (dev->status & STAT_OFULL) - break; + case STATE_IRQC_WAIT: + kbc_at_log("ATkbc: IRQ clear wait...\n"); + phase = (phase + 1) & 1; + if (phase) + dev->state = STATE_IRQC_MAIN_IBF; + break; + case STATE_IRQC_MAIN_IBF: + kbc_at_log("ATkbc: Clearing IRQ 1 and 12 (Main/IBF)...\n"); + picint_common(1 << 1, 0, 0, NULL); + picint_common(1 << 12, 0, 0, NULL); fallthrough; case STATE_MAIN_IBF: default: - picint_common(1 << 1, 0, 0, NULL); - picint_common(1 << 12, 0, 0, NULL); ps2_main_ibf: if (dev->status & STAT_IFULL) kbc_ibf_process(dev); @@ -671,34 +676,41 @@ ps2_main_ibf: /* Keyboard controller command want to output a single byte. */ kbc_at_log("ATkbc: %02X coming from channel %i with high status %02X\n", dev->val, dev->channel, dev->stat_hi); kbc_send_to_ob(dev, dev->val, dev->channel, dev->stat_hi); -#if 0 - dev->state = (dev->pending == 2) ? STATE_KBC_AMI_OUT : STATE_MAIN_IBF; -#endif dev->pending = 0; dev->state = STATE_KBC_DELAY_IRQ; break; case STATE_IRQ: - kbc_at_log("ATkbc: Raising IRQ 1 (keyboard)...\n"); - if (dev->mem[0x20] & 0x01) + if (dev->mem[0x20] & 0x01) { + kbc_at_log("ATkbc: Raising IRQ 1 (keyboard)...\n"); picint_common(1 << 1, 0, 1, NULL); - picint_common(1 << 12, 0, 0, NULL); - dev->state = STATE_MAIN_IBF; + dev->state = STATE_IRQC_WAIT; + } else { + kbc_at_log("ATkbc: Noping IRQ 1 (keyboard)...\n"); + dev->state = STATE_MAIN_IBF; + } break; case STATE_AUX_IRQ: kbc_at_log("ATkbc: Raising IRQ 12 (mouse)...\n"); - if (dev->mem[0x20] & 0x02) + if (dev->mem[0x20] & 0x02) { picint_common(1 << 12, 0, 1, NULL); - picint_common(1 << 1, 0, 0, NULL); - dev->state = STATE_MAIN_IBF; + dev->state = STATE_IRQC_WAIT; + } else + dev->state = STATE_MAIN_IBF; break; case STATE_KBC_DELAY_IRQ: kbc_at_log("ATkbc: Raising IRQ 1 (KBC delay)...\n"); - if (dev->mem[0x20] & 0x01) + if (dev->mem[0x20] & 0x01) { picint_common(1 << 1, 0, 1, NULL); - picint_common(1 << 12, 0, 0, NULL); - dev->state = STATE_MAIN_IBF; - goto ps2_main_ibf; + dev->state = STATE_IRQC_WAIT; + break; + } else { + dev->state = STATE_MAIN_IBF; + goto ps2_main_ibf; + } case STATE_KBC_OUT: + kbc_at_log("ATkbc: Clearing IRQ 1 and 12 (KBC out)...\n"); + picint_common(1 << 1, 0, 0, NULL); + picint_common(1 << 12, 0, 0, NULL); /* Keyboard controller command want to output multiple bytes. */ if (dev->status & STAT_IFULL) { /* Data from host aborts dumping. */ @@ -716,10 +728,11 @@ ps2_main_ibf: kbc_at_log("ATkbc: Raising IRQ 1 (KBC)...\n"); if (dev->mem[0x20] & 0x01) picint_common(1 << 1, 0, 1, NULL); - picint_common(1 << 12, 0, 0, NULL); dev->key_ctrl_queue_start = (dev->key_ctrl_queue_start + 1) & 0x3f; if (dev->key_ctrl_queue_start == dev->key_ctrl_queue_end) - dev->state = STATE_MAIN_IBF; + dev->state = STATE_IRQC_WAIT; + else + dev->state = STATE_KBC_OUT; break; case STATE_KBC_PARAM: /* Keyboard controller command wants data, wait for said data. */