diff --git a/src/include/86box/pic.h b/src/include/86box/pic.h index e40751f76..50ed0914e 100644 --- a/src/include/86box/pic.h +++ b/src/include/86box/pic.h @@ -25,6 +25,7 @@ typedef struct pic { ocw3, int_pending, is_master, elcr, state, ack_bytes, priority, special_mask_mode, auto_eoi_rotate, interrupt, lines, data_bus; + uint32_t at; struct pic *slaves[8]; } pic_t; diff --git a/src/pic.c b/src/pic.c index 847310d03..6d6f74dd5 100644 --- a/src/pic.c +++ b/src/pic.c @@ -55,6 +55,10 @@ static int shadow = 0, elcr_enabled = 0, tmr_inited = 0, latched = 0; +static void (*update_pending)(void); + + + #ifdef ENABLE_PIC_LOG int pic_do_log = ENABLE_PIC_LOG; @@ -160,8 +164,18 @@ static __inline int find_best_interrupt(pic_t *dev) { uint8_t b, s; + uint8_t intr; int i, j; - int is_at, ret = -1; + int ret = -1; + +#ifdef READ_LATCH + if (dev->interrupt != 0x17) { + /* We have an IRQ already latched, do not update status until it is unlatched in order to + avoid IRQ loss. */ + ret = dev->interrupt; + return ret; + } +#endif for (i = 0; i < 8; i++) { j = (i + dev->priority) & 7; @@ -178,43 +192,46 @@ find_best_interrupt(pic_t *dev) break; } - dev->interrupt = (ret == -1) ? 7 : ret; + intr = dev->interrupt = (ret == -1) ? 0x17 : ret; - is_at = IS_AT(machine); - if (is_at && (ret != -1) && (cpu_fast_off_flags & (1 << dev->interrupt))) - cpu_fast_off_count = cpu_fast_off_val + 1; + if (dev->at && (ret != 1)) { + if (dev == &pic2) + intr += 8; + + if (cpu_fast_off_flags & (1 << intr)) + cpu_fast_off_count = cpu_fast_off_val + 1; + } return ret; } static __inline void -pic_update_pending(void) +pic_update_pending_xt(void) { - int is_at = IS_AT(machine); - - if (is_at) { - pic2.int_pending = (find_best_interrupt(&pic2) != -1); - - if (pic2.int_pending) - pic.irr |= (1 << pic2.icw3); - else - pic.irr &= ~(1 << pic2.icw3); - - pic.int_pending = (find_best_interrupt(&pic) != -1); - return; - } - if (find_best_interrupt(&pic) != -1) { latched++; if (latched == 1) - timer_on_auto(&pic_timer, is_at ? 0.3 : 0.35); - /* 300 ms on AT+, 350 ns on PC/PCjr/XT */ + timer_on_auto(&pic_timer, 0.35); } else if (latched == 0) pic.int_pending = 0; } +static __inline void +pic_update_pending_at(void) +{ + pic2.int_pending = (find_best_interrupt(&pic2) != -1); + + if (pic2.int_pending) + pic.irr |= (1 << pic2.icw3); + else + pic.irr &= ~(1 << pic2.icw3); + + pic.int_pending = (find_best_interrupt(&pic) != -1); +} + + static void pic_callback(void *priv) { @@ -237,6 +254,7 @@ pic_reset() memset(&pic2, 0, sizeof(pic_t)); pic.is_master = 1; + pic.interrupt = pic2.interrupt = 0x17; if (is_at) pic.slaves[2] = &pic2; @@ -246,6 +264,9 @@ pic_reset() memset(&pic_timer, 0x00, sizeof(pc_timer_t)); timer_add(&pic_timer, pic_callback, &pic, 0); tmr_inited = 1; + + update_pending = is_at ? pic_update_pending_at : pic_update_pending_xt; + pic.at = pic2.at = is_at; } @@ -276,7 +297,7 @@ picint_is_level(int irq) static void pic_acknowledge(pic_t *dev) { - int pic_int = dev->interrupt; + int pic_int = dev->interrupt & 7; int pic_int_num = 1 << pic_int; dev->isr |= pic_int_num; @@ -319,7 +340,7 @@ pic_action(pic_t *dev, uint8_t irq, uint8_t eoi, uint8_t rotate) if (rotate) dev->priority = (irq + 1) & 7; - pic_update_pending(); + update_pending(); } } @@ -374,12 +395,16 @@ pic_read(uint16_t addr, void *priv) } } else { /* Standard 8259 PIC read */ +#ifndef UNDEFINED_READ + /* Put the IRR on to the data bus by default until the real PIC is probed. */ + dev->data_bus = dev->irr; +#endif if (dev->ocw3 & 0x04) { if (dev->int_pending) { dev->data_bus = 0x80 | (dev->interrupt & 7); pic_acknowledge(dev); dev->int_pending = 0; - pic_update_pending(); + update_pending(); } else dev->data_bus = 0x00; dev->ocw3 &= ~0x04; @@ -388,8 +413,10 @@ pic_read(uint16_t addr, void *priv) else if (dev->ocw3 & 0x02) { if (dev->ocw3 & 0x01) dev->data_bus = dev->isr; +#ifdef UNDEFINED_READ else dev->data_bus = dev->irr; +#endif } /* If A0 = 0, VIA shadow is disabled, and poll mode is disabled, simply read whatever is currently on the data bus. */ @@ -429,7 +456,7 @@ pic_write(uint16_t addr, uint8_t val, void *priv) break; case STATE_NONE: dev->imr = val; - pic_update_pending(); + update_pending(); break; } } else { @@ -447,9 +474,10 @@ pic_write(uint16_t addr, uint8_t val, void *priv) dev->imr = dev->isr = 0x00; dev->ack_bytes = dev->priority = 0x00; dev->auto_eoi_rotate = dev->special_mask_mode = 0x00; - dev->interrupt = dev->int_pending = 0x00; + dev->interrupt = 0x17; + dev->int_pending = 0x00; dev->state = STATE_ICW2; - pic_update_pending(); + update_pending(); } else if (val & 0x08) { dev->ocw3 = val; if (dev->ocw3 & 0x40) @@ -494,11 +522,8 @@ void picint_common(uint16_t num, int level, int set) { int i, raise; - int is_at; uint8_t b, slaves = 0; - is_at = IS_AT(machine); - /* Make sure to ignore all slave IRQ's, and in case of AT+, translate IRQ 2 to IRQ 9. */ for (i = 0; i < 8; i++) { @@ -510,7 +535,7 @@ picint_common(uint16_t num, int level, int set) if (raise) { num &= ~b; - if (is_at && (i == 2)) + if (pic.at && (i == 2)) num |= (1 << 9); } } @@ -553,7 +578,7 @@ picint_common(uint16_t num, int level, int set) } } - pic_update_pending(); + update_pending(); } @@ -588,33 +613,34 @@ pic_i86_mode(pic_t *dev) static uint8_t pic_irq_ack_read(pic_t *dev, int phase) { + uint8_t intr = dev->interrupt & 7; pic_log(" pic_irq_ack_read(%08X, %i)\n", dev, phase); if (dev != NULL) { if (phase == 0) { pic_acknowledge(dev); - if (pic_slave_on(dev, dev->interrupt)) - dev->data_bus = pic_irq_ack_read(dev->slaves[dev->interrupt], phase); + if (pic_slave_on(dev, intr)) + dev->data_bus = pic_irq_ack_read(dev->slaves[intr], phase); else dev->data_bus = pic_i86_mode(dev) ? 0xff : 0xcd; } else if (pic_i86_mode(dev)) { dev->int_pending = 0; - if (pic_slave_on(dev, dev->interrupt)) - dev->data_bus = pic_irq_ack_read(dev->slaves[dev->interrupt], phase); + if (pic_slave_on(dev, intr)) + dev->data_bus = pic_irq_ack_read(dev->slaves[intr], phase); else - dev->data_bus = dev->interrupt + (dev->icw2 & 0xf8); + dev->data_bus = intr + (dev->icw2 & 0xf8); pic_auto_non_specific_eoi(dev); } else if (phase == 1) { - if (pic_slave_on(dev, dev->interrupt)) - dev->data_bus = pic_irq_ack_read(dev->slaves[dev->interrupt], phase); + if (pic_slave_on(dev, intr)) + dev->data_bus = pic_irq_ack_read(dev->slaves[intr], phase); else if (dev->icw1 & 0x04) - dev->data_bus = (dev->interrupt << 2) + (dev->icw1 & 0xe0); + dev->data_bus = (intr << 2) + (dev->icw1 & 0xe0); else - dev->data_bus = (dev->interrupt << 3) + (dev->icw1 & 0xc0); + dev->data_bus = (intr << 3) + (dev->icw1 & 0xc0); } else if (phase == 2) { dev->int_pending = 0; - if (pic_slave_on(dev, dev->interrupt)) - dev->data_bus = pic_irq_ack_read(dev->slaves[dev->interrupt], phase); + if (pic_slave_on(dev, intr)) + dev->data_bus = pic_irq_ack_read(dev->slaves[intr], phase); else dev->data_bus = dev->icw2; pic_auto_non_specific_eoi(dev); @@ -633,8 +659,10 @@ pic_irq_ack(void) ret = pic_irq_ack_read(&pic, pic.ack_bytes); pic.ack_bytes = (pic.ack_bytes + 1) % (pic_i86_mode(&pic) ? 2 : 3); - if (pic.ack_bytes == 0) - pic_update_pending(); + if (pic.ack_bytes == 0) { + pic.interrupt = 0x17; + update_pending(); + } return ret; } @@ -661,8 +689,10 @@ picinterrupt() ret = pic_irq_ack_read(&pic, pic.ack_bytes); pic.ack_bytes = (pic.ack_bytes + 1) % (pic_i86_mode(&pic) ? 2 : 3); - if (pic.ack_bytes == 0) - pic_update_pending(); + if (pic.ack_bytes == 0) { + pic.interrupt = pic2.interrupt = 0x17; + update_pending(); + } } }