diff --git a/src/pic.c b/src/pic.c index f4e1a0a60..c01053268 100644 --- a/src/pic.c +++ b/src/pic.c @@ -51,23 +51,20 @@ pic_t pic2; static pc_timer_t pic_timer; -static uint16_t smi_irq_mask = 0x0000; -static uint16_t smi_irq_status = 0x0000; - -static uint16_t enabled_latches = 0x0000; -static uint16_t latched_irqs = 0x0000; - static int shadow = 0; static int elcr_enabled = 0; static int tmr_inited = 0; +static int latched = 0; static int pic_pci = 0; +static int kbd_latch = 0; +static int mouse_latch = 0; -static void (*update_pending)(void); +static uint16_t smi_irq_mask = 0x0000; +static uint16_t smi_irq_status = 0x0000; -static void pic_update_request(pic_t *dev, int irq); -static void pic_update_irr(pic_t *dev, uint16_t num); +static uint16_t latched_irqs = 0x0000; -static void pic_cascade(int set); +static void (*update_pending)(void); #ifdef ENABLE_PIC_LOG int pic_do_log = ENABLE_PIC_LOG; @@ -226,29 +223,37 @@ find_best_interrupt(pic_t *dev) static __inline void pic_update_pending_xt(void) { - if (!(pic.flags & PIC_FREEZE)) - pic.int_pending = (find_best_interrupt(&pic) != -1); + if (find_best_interrupt(&pic) != -1) { + latched++; + if (latched == 1) + timer_on_auto(&pic_timer, 0.35); + } else if (latched == 0) + pic.int_pending = 0; } static __inline void pic_update_pending_at(void) { - if (!(pic2.flags & PIC_FREEZE)) { - pic2.int_pending = (find_best_interrupt(&pic2) != -1); + pic2.int_pending = (find_best_interrupt(&pic2) != -1); - pic_cascade(pic2.int_pending); - } + if (pic2.int_pending) + pic.irr |= (1 << pic2.icw3); + else + pic.irr &= ~(1 << pic2.icw3); - if (!(pic.flags & PIC_FREEZE)) - pic.int_pending = (find_best_interrupt(&pic) != -1); - - pic_log("pic_update_pending_at(): dev->int_pending = %i (%i)\n", pic.int_pending, !!(pic.flags & PIC_FREEZE)); + pic.int_pending = (find_best_interrupt(&pic) != -1); } static void -pic_callback(UNUSED(void *priv)) +pic_callback(void *priv) { - update_pending(); + pic_t *dev = (pic_t *) priv; + + dev->int_pending = 1; + + latched--; + if (latched > 0) + timer_on_auto(&pic_timer, 0.35); } void @@ -263,13 +268,8 @@ pic_reset(void) pic.is_master = 1; pic.interrupt = pic2.interrupt = 0x17; - pic.has_slaves = 0; - pic2.has_slaves = 0; - - if (is_at) { + if (is_at) pic.slaves[2] = &pic2; - pic.has_slaves = 1; - } if (tmr_inited) timer_on_auto(&pic_timer, 0.0); @@ -320,25 +320,14 @@ picint_is_level(int irq) } static void -pic_acknowledge(pic_t *dev, int poll) +pic_acknowledge(pic_t *dev) { int pic_int = dev->interrupt & 7; int pic_int_num = 1 << pic_int; dev->isr |= pic_int_num; - - /* Simulate the clearing of the edge pulse. */ - dev->edge_lines &= ~pic_int_num; - /* Clear the edge sense latch. */ - dev->irq_latch &= ~pic_int_num; - - if (!poll) { - dev->flags |= PIC_FREEZE; /* Freeze it so it still takes interrupts but they do not - override the one currently being processed. */ - - /* Clear the reset latch. */ - pic_update_request(dev, pic_int); - } + if (!pic_level_triggered(dev, pic_int) || (dev->lines[pic_int] == 0)) + dev->irr &= ~pic_int_num; } /* Find IRQ for non-specific EOI (either by command or automatic) by finding the highest IRQ @@ -375,7 +364,6 @@ pic_action(pic_t *dev, uint8_t irq, uint8_t eoi, uint8_t rotate) if (rotate) dev->priority = (irq + 1) & 7; - pic_update_request(dev, irq); update_pending(); } } @@ -415,10 +403,13 @@ pic_latch_read(UNUSED(uint16_t addr), UNUSED(void *priv)) { uint8_t ret = 0xff; - pic_log("pic_latch_read(%04X): %04X\n", enabled_latches, latched_irqs & 0x1002); + pic_log("pic_latch_read(%i, %i)\n", kbd_latch, mouse_latch); - if (latched_irqs & 0x1002) - picintc(latched_irqs & 0x1002); + if (kbd_latch && (latched_irqs & 0x0002)) + picintc(0x0002); + + if (mouse_latch && (latched_irqs & 0x1000)) + picintc(0x1000); /* Return FF - we just lower IRQ 1 and IRQ 12. */ return ret; @@ -447,23 +438,24 @@ pic_read(uint16_t addr, void *priv) dev->data_bus = dev->irr; #endif if (dev->ocw3 & 0x04) { + dev->interrupt &= ~0x20; /* Freeze the interrupt until the poll is over. */ if (dev->int_pending) { dev->data_bus = 0x80 | (dev->interrupt & 7); - pic_acknowledge(dev, 1); + pic_acknowledge(dev); dev->int_pending = 0; + update_pending(); } else dev->data_bus = 0x00; dev->ocw3 &= ~0x04; - dev->flags &= ~PIC_FREEZE; /* Freeze the interrupt until the poll is over. */ - pic_update_irr(dev, 0x00ff); /* Update IRR, just in case anything came while frozen. */ - update_pending(); } else if (addr & 0x0001) dev->data_bus = dev->imr; else if (dev->ocw3 & 0x02) { if (dev->ocw3 & 0x01) dev->data_bus = dev->isr; +#ifdef UNDEFINED_READ else - dev->data_bus = dev->irr; + dev->data_bus = 0x00; +#endif } /* If A0 = 0, VIA shadow is disabled, and poll mode is disabled, simply read whatever is currently on the data bus. */ @@ -502,10 +494,7 @@ pic_write(uint16_t addr, uint8_t val, void *priv) break; case STATE_NONE: dev->imr = val; - if (is286) - update_pending(); - else - timer_on_auto(&pic_timer, 1.0 * ((10000000.0 * (double) xt_cpu_multi) / (double) cpu_s->rspeed)); + update_pending(); break; default: @@ -522,13 +511,11 @@ pic_write(uint16_t addr, uint8_t val, void *priv) if (!(dev->icw1 & 1)) dev->icw4 = 0x00; dev->ocw2 = dev->ocw3 = 0x00; - dev->flags = PIC_MASTER_CLEAR; - dev->irr = 0x00; - dev->edge_lines = 0x00; - dev->irq_latch = 0x00; - for (uint8_t i = 0; i <= 7; i++) - pic_update_request(dev, i); - dev->flags &= ~PIC_MASTER_CLEAR; + dev->irr = 0x00; + for (uint8_t i = 0; i <= 7; i++) { + if (dev->lines[i] > 0) + dev->irr |= (1 << i); + } dev->imr = dev->isr = 0x00; dev->ack_bytes = dev->priority = 0x00; dev->auto_eoi_rotate = dev->special_mask_mode = 0x00; @@ -539,7 +526,7 @@ pic_write(uint16_t addr, uint8_t val, void *priv) } else if (val & 0x08) { dev->ocw3 = val; if (dev->ocw3 & 0x04) - dev->flags |= PIC_FREEZE; /* Freeze the interrupt until the poll is over. */ + dev->interrupt |= 0x20; /* Freeze the interrupt until the poll is over. */ if (dev->ocw3 & 0x40) dev->special_mask_mode = !!(dev->ocw3 & 0x20); } else { @@ -566,15 +553,12 @@ pic_set_pci(void) void pic_kbd_latch(int enable) { - uint16_t old_latches = enabled_latches; - pic_log("PIC keyboard latch now %sabled\n", enable ? "en" : "dis"); - enable = (!!enable) << 1; - enabled_latches = (enabled_latches & 0x1000) | enable; + if (!!(enable | mouse_latch) != !!(kbd_latch | mouse_latch)) + io_handler(!!(enable | mouse_latch), 0x0060, 0x0001, pic_latch_read, NULL, NULL, NULL, NULL, NULL, NULL); - if (!!(enabled_latches & 0x1002) != !!(old_latches & 0x1002)) - io_handler(!!(enabled_latches & 0x1002), 0x0060, 0x0001, pic_latch_read, NULL, NULL, NULL, NULL, NULL, NULL); + kbd_latch = !!enable; if (!enable) picintc(0x0002); @@ -583,15 +567,12 @@ pic_kbd_latch(int enable) void pic_mouse_latch(int enable) { - uint16_t old_latches = enabled_latches; - pic_log("PIC mouse latch now %sabled\n", enable ? "en" : "dis"); - enable = (!!enable) << 12; - enabled_latches = (enabled_latches & 0x0002) | enable; + if (!!(kbd_latch | enable) != !!(kbd_latch | mouse_latch)) + io_handler(!!(kbd_latch | enable), 0x0060, 0x0001, pic_latch_read, NULL, NULL, NULL, NULL, NULL, NULL); - if (!!(enabled_latches & 0x1002) != !!(old_latches & 0x1002)) - io_handler(!!(enabled_latches & 0x1002), 0x0060, 0x0001, pic_latch_read, NULL, NULL, NULL, NULL, NULL, NULL); + mouse_latch = !!enable; if (!enable) picintc(0x1000); @@ -603,11 +584,11 @@ pic_reset_hard(void) pic_reset(); /* Explicitly reset the latches. */ - enabled_latches = 0x0000; + kbd_latch = mouse_latch = 0; latched_irqs = 0x0000; /* The situation is as follows: There is a giant mess when it comes to these latches on real hardware, - to the point that there's even boards with board-level latches that get used in place of the latches + to the point that there's even boards with board-level latched that get used in place of the latches on the chipset, therefore, I'm just doing this here for the sake of simplicity. */ if (machine_has_bus(machine, MACHINE_BUS_PS2_LATCH)) { pic_kbd_latch(0x01); @@ -643,125 +624,31 @@ pic2_init(void) pic.slaves[2] = &pic2; } -void -pic_update_lines(pic_t *dev, uint16_t num, int level, int set, uint8_t *irq_state) -{ - uint8_t old_edge_lines; - uint8_t bit; - - switch (level) { - case PIC_IRQ_EDGE: - old_edge_lines = dev->edge_lines; - - dev->edge_lines &= ~num; - if (set) - dev->edge_lines |= num; - - if ((dev->isr & num) || (dev->flags & PIC_MASTER_CLEAR)) - dev->irq_latch = (dev->irq_latch & ~num) | (dev->edge_lines & num); - else if ((dev->edge_lines & num) && !(old_edge_lines & num)) - dev->irq_latch |= num; - break; - case PIC_IRQ_LEVEL: - for (uint8_t i = 0; i < 8; i++) { - bit = (1 << i); - if ((num & bit) && ((!!*irq_state) != !!set)) - dev->lines[i] += (set ? 1 : -1); - } - - if ((!!*irq_state) != !!set) - *irq_state = set; - break; - - default: - break; - } -} - -static uint8_t -pic_irq_get_request(pic_t *dev, int irq) -{ - uint8_t ret; - - ret = ((dev->edge_lines & (1 << irq)) || (dev->lines[irq] > 0)); - - return ret; -} - -static uint8_t -pic_es_latch_clear(pic_t *dev, int irq) -{ - uint8_t ret; - - ret = (dev->isr & (1 << irq)) || (dev->flags & PIC_MASTER_CLEAR); - - return ret; -} - -static uint8_t -pic_es_latch_out(pic_t *dev, int irq) -{ - uint8_t ret; - - ret = !((pic_es_latch_clear(dev, irq) && (dev->irq_latch & (1 << irq))) || !pic_irq_get_request(dev, irq)); - - return ret; -} - -static uint8_t -pic_es_latch_nor(pic_t *dev, int irq) -{ - uint8_t ret; - - ret = !(pic_es_latch_out(dev, irq) || picint_is_level(irq)); - - return ret; -} - -static uint8_t -pic_irq_request_nor(pic_t *dev, int irq) -{ - uint8_t ret; - - ret = !(pic_es_latch_nor(dev, irq) || !pic_irq_get_request(dev, irq)); - - return ret; -} - -static void -pic_update_request(pic_t *dev, int irq) -{ - dev->irr &= ~(1 << irq); - - if (!(dev->flags & PIC_FREEZE)) - dev->irr |= (pic_irq_request_nor(dev, irq) << irq); -} - -static void -pic_update_irr(pic_t *dev, uint16_t num) -{ - for (uint8_t i = 0; i < 8; i++) { - if (num & (1 << i)) - pic_update_request(dev, i); - } -} - void picint_common(uint16_t num, int level, int set, uint8_t *irq_state) { - pic_log("picint_common(%04X, %i, %i, %08X)\n", num, level, set, (uint32_t) (uintptr_t) irq_state); - - set = !!set; + int raise; + uint8_t b; + uint8_t slaves = 0; /* Make sure to ignore all slave IRQ's, and in case of AT+, translate IRQ 2 to IRQ 9. */ - if (num & pic.icw3) { - num &= ~pic.icw3; - if (pic.at) - num |= (1 << 9); + for (uint8_t i = 0; i < 8; i++) { + b = (uint8_t) (1 << i); + raise = num & b; + + if (pic.icw3 & b) { + slaves++; + + if (raise) { + num &= ~b; + if (pic.at && (i == 2)) + num |= (1 << 9); + } + } } - if (!pic.has_slaves) + if (!slaves) num &= 0x00ff; if (!num) { @@ -772,40 +659,93 @@ picint_common(uint16_t num, int level, int set, uint8_t *irq_state) if (num & 0x0100) acpi_rtc_status = !!set; - smi_irq_status &= ~num; - if (set && (smi_irq_mask & num)) { - smi_raise(); - smi_irq_status |= num; + if (set) { + if (smi_irq_mask & num) { + smi_raise(); + smi_irq_status |= num; + } + + if (num & 0xff00) { + if (level) { + for (uint8_t i = 0; i < 8; i++) { + b = (uint8_t) (1 << i); + if (((num >> 8) & b) && ((!!*irq_state) != !!set)) + pic2.lines[i]++; + } + + if ((!!*irq_state) != !!set) + *irq_state = set; + } + + /* Latch IRQ 12 if the mouse latch is enabled. */ + if ((num & 0x1000) && mouse_latch) + latched_irqs |= 0x1000; + + pic2.irr |= (num >> 8); + } + + if (num & 0x00ff) { + if (level) { + for (uint8_t i = 0; i < 8; i++) { + b = (uint8_t) (1 << i); + if ((num & b) && ((!!*irq_state) != !!set)) + pic.lines[i]++; + } + + if ((!!*irq_state) != !!set) + *irq_state = set; + } + + /* Latch IRQ 1 if the keyboard latch is enabled. */ + if (kbd_latch && (num & 0x0002)) + latched_irqs |= 0x0002; + + pic.irr |= (num & 0x00ff); + } + } else { + smi_irq_status &= ~num; + + if (num & 0xff00) { + if (level) { + for (uint8_t i = 0; i < 8; i++) { + b = (uint8_t) (1 << i); + if (((num >> 8) & b) && ((!!*irq_state) != !!set)) + pic2.lines[i]--; + } + + if ((!!*irq_state) != !!set) + *irq_state = set; + } + + /* Unlatch IRQ 12 if the mouse latch is enabled. */ + if ((num & 0x1000) && mouse_latch) + latched_irqs &= 0xefff; + + pic2.irr &= ~(num >> 8); + } + + if (num & 0x00ff) { + if (level) { + for (uint8_t i = 0; i < 8; i++) { + b = (uint8_t) (1 << i); + if ((num & b) && ((!!*irq_state) != !!set)) + pic.lines[i]--; + } + + if ((!!*irq_state) != !!set) + *irq_state = set; + } + + /* Unlatch IRQ 1 if the keyboard latch is enabled. */ + if (kbd_latch && (num & 0x0002)) + latched_irqs &= 0xfffd; + + pic.irr &= ~(num & 0x00ff); + } } - if (num & 0xff00) { - pic_update_lines(&pic2, num >> 8, level, set, irq_state); - - /* Latch IRQ 12 if the mouse latch is enabled. */ - if ((num & enabled_latches) & 0x1000) - latched_irqs = (latched_irqs & 0xefff) | (set << 12); - - pic_update_irr(&pic2, num >> 8); - } - - if (num & 0x00ff) { - pic_update_lines(&pic, num & 0x00ff, level, set, irq_state); - - /* Latch IRQ 1 if the keyboard latch is enabled. */ - if ((num & enabled_latches) & 0x0002) - latched_irqs = (latched_irqs & 0xfffd) | (set << 1); - - pic_update_irr(&pic, num & 0x00ff); - } - - update_pending(); -} - -static void -pic_cascade(int set) -{ - pic_update_lines(&pic, (1 << pic2.icw3), PIC_IRQ_EDGE, set, NULL); - pic_update_irr(&pic, (1 << pic2.icw3)); + if (!(pic.interrupt & 0x20) && !(pic2.interrupt & 0x20)) + update_pending(); } static uint8_t @@ -817,13 +757,16 @@ pic_i86_mode(pic_t *dev) static uint8_t pic_irq_ack_read(pic_t *dev, int phase) { - uint8_t intr = dev->interrupt & 0x07; - uint8_t slave = dev->flags & PIC_SLAVE_PENDING; + uint8_t intr = dev->interrupt & 0x47; + uint8_t slave = intr & 0x40; + intr &= 0x07; pic_log(" pic_irq_ack_read(%08X, %i)\n", dev, phase); if (dev != NULL) { if (phase == 0) { - pic_acknowledge(dev, 0); + dev->interrupt |= 0x20; /* Freeze it so it still takes interrupts but they do not + override the one currently being processed. */ + pic_acknowledge(dev); if (slave) dev->data_bus = pic_irq_ack_read(dev->slaves[intr], phase); else @@ -855,9 +798,6 @@ pic_irq_ack_read(pic_t *dev, int phase) return dev->data_bus; } -/* 808x: Update the requests for all interrupts since any of them - could have arrived during the freeze. */ - uint8_t pic_irq_ack(void) { @@ -871,7 +811,7 @@ pic_irq_ack(void) exit(-1); } - pic.flags |= PIC_SLAVE_PENDING; + pic.interrupt |= 0x40; /* Mark slave pending. */ } ret = pic_irq_ack_read(&pic, pic.ack_bytes); @@ -879,13 +819,8 @@ pic_irq_ack(void) if (pic.ack_bytes == 0) { /* Needed for Xi8088. */ - if (pic.flags & PIC_SLAVE_PENDING) { - pic2.flags &= ~PIC_FREEZE; - pic_update_irr(&pic2, 0x00ff); + if (pic.interrupt & 0x40) pic2.interrupt = 0x17; - } - pic.flags &= ~(PIC_SLAVE_PENDING | PIC_FREEZE); - pic_update_irr(&pic, 0x00ff); pic.interrupt = 0x17; update_pending(); } @@ -893,9 +828,6 @@ pic_irq_ack(void) return ret; } -/* 286+: Only update the request for the pending interrupt as it is - impossible that any other interrupt has arrived during the - freeze. */ int picinterrupt(void) { @@ -909,7 +841,7 @@ picinterrupt(void) exit(-1); } - pic.flags |= PIC_SLAVE_PENDING; + pic.interrupt |= 0x40; /* Mark slave pending. */ } if ((pic.interrupt == 0) && (pit_devs[1].data != NULL)) @@ -921,13 +853,8 @@ picinterrupt(void) pic.ack_bytes = (pic.ack_bytes + 1) % (pic_i86_mode(&pic) ? 2 : 3); if (pic.ack_bytes == 0) { - if (pic.flags & PIC_SLAVE_PENDING) { - pic2.flags &= ~PIC_FREEZE; - pic_update_request(&pic2, pic2.interrupt & 0x07); + if (pic.interrupt & 0x40) pic2.interrupt = 0x17; - } - pic.flags &= ~(PIC_SLAVE_PENDING | PIC_FREEZE); - pic_update_request(&pic, pic.interrupt & 0x07); pic.interrupt = 0x17; update_pending(); }