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

413
src/pic.c
View File

@@ -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();
}