Reverted the PIC code to basically the old code with the new way of handing level-triggered IRQ's, fixes IDE hard disk and ATAPI CD-ROM problems.

This commit is contained in:
OBattler
2023-09-04 04:55:09 +02:00
parent 3039f8449f
commit 95c9c1dc95

375
src/pic.c
View File

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