diff --git a/src/acpi.c b/src/acpi.c index e9549adb0..e7a0de53d 100644 --- a/src/acpi.c +++ b/src/acpi.c @@ -36,6 +36,7 @@ #include <86box/pit.h> #include <86box/apm.h> #include <86box/acpi.h> +#include <86box/dma.h> #include <86box/machine.h> #include <86box/i2c.h> #include <86box/video.h> @@ -1025,8 +1026,13 @@ acpi_reg_write_common_regs(UNUSED(int size), uint16_t addr, uint8_t val, void *p nvr_reg_write(0x000f, 0xff, dev->nvr); } - if (sus_typ & SUS_RESET_PCI) + if (sus_typ & SUS_RESET_PCI) { + /* DMA is part of the southbridge so it responds to PCI reset. */ + dma_reset(); + dma_set_at(1); + device_reset_all(DEVICE_PCI); + } if (sus_typ & SUS_RESET_CPU) cpu_alt_reset = 0; diff --git a/src/chipset/intel_piix.c b/src/chipset/intel_piix.c index ed9fd9460..b73285f70 100644 --- a/src/chipset/intel_piix.c +++ b/src/chipset/intel_piix.c @@ -155,6 +155,7 @@ piix_ide_handlers(piix_t *dev, int bus) uint16_t side; if (bus & 0x01) { + piix_log("Disabling primary IDE...\n"); ide_pri_disable(); if (dev->type == 5) { @@ -170,11 +171,14 @@ piix_ide_handlers(piix_t *dev, int bus) ide_set_side(0, side); } - if ((dev->regs[1][0x04] & 0x01) && (dev->regs[1][0x41] & 0x80)) + if ((dev->regs[1][0x04] & 0x01) && (dev->regs[1][0x41] & 0x80)) { + piix_log("Enabling primary IDE...\n"); ide_pri_enable(); + } } if (bus & 0x02) { + piix_log("Disabling secondary IDE...\n"); ide_sec_disable(); if (dev->type == 5) { @@ -190,8 +194,10 @@ piix_ide_handlers(piix_t *dev, int bus) ide_set_side(1, side); } - if ((dev->regs[1][0x04] & 0x01) && (dev->regs[1][0x43] & 0x80)) + if ((dev->regs[1][0x04] & 0x01) && (dev->regs[1][0x43] & 0x80)) { + piix_log("Enabling secondary IDE...\n"); ide_sec_enable(); + } } } @@ -465,6 +471,13 @@ piix_write(int func, int addr, uint8_t val, void *priv) uint8_t *fregs; uint16_t base; + /* Dell OptiPlex Gn+ shows that register 02:FF is aliased in 01:FF. */ + if ((dev->type == 4) && (func == 1) && (addr == 0xff)) + func = 2; + + if ((func == 1) || (addr == 0xf8) || (addr == 0xf9)) + piix_log("[W] %02X:%02X = %02X\n", func, addr, val); + /* Return on unsupported function. */ if (dev->max_func > 0) { if (func > dev->max_func) @@ -736,6 +749,8 @@ piix_write(int func, int addr, uint8_t val, void *priv) fregs[addr] = val; break; case 0xb0: + if (val & 0x10) + warning("Write %02X to B0\n", val); if (dev->type == 4) fregs[addr] = (fregs[addr] & 0x8c) | (val & 0x73); else if (dev->type == 5) @@ -745,6 +760,8 @@ piix_write(int func, int addr, uint8_t val, void *priv) alt_access = !!(val & 0x20); break; case 0xb1: + if (val & 0x18) + warning("Write %02X to B1\n", val); if (dev->type > 3) fregs[addr] = val & 0xdf; break; @@ -923,6 +940,12 @@ piix_write(int func, int addr, uint8_t val, void *priv) if (dev->type > 4) fregs[addr] = val; break; + case 0xf8: + case 0xf9: + /* Undocumented! */ + if (dev->type == 4) + fregs[addr] = val; + break; default: break; } @@ -1169,6 +1192,10 @@ piix_read(int func, int addr, void *priv) uint8_t ret = 0xff; const uint8_t *fregs; + /* Dell OptiPlex Gn+ shows that register 02:FF is aliased in 01:FF. */ + if ((dev->type == 4) && (func == 1) && (addr == 0xff)) + func = 2; + if ((dev->type == 3) && (func == 2) && (dev->max_func == 1) && (addr >= 0x40)) ret = 0x00; @@ -1199,7 +1226,7 @@ piix_reset_hard(piix_t *dev) sff_set_slot(dev->bm[1], dev->pci_slot); sff_set_irq_pin(dev->bm[1], PCI_INTA); - sff_set_irq_line(dev->bm[1], 14); + sff_set_irq_line(dev->bm[1], 15); sff_set_irq_mode(dev->bm[1], IRQ_MODE_LEGACY); } @@ -1315,6 +1342,10 @@ piix_reset_hard(piix_t *dev) fregs[0x45] = 0x55; fregs[0x46] = 0x01; } + if (dev->type == 4) { + fregs[0xf8] = 0x30; + fregs[0xf9] = 0x0f; + } if ((dev->type == 1) && (dev->rev == 2)) dev->max_func = 0; /* It starts with IDE disabled, then enables it. */ else @@ -1678,7 +1709,7 @@ const device_t piix4_device = { .name = "Intel 82371AB/EB (PIIX4/PIIX4E)", .internal_name = "piix4", .flags = DEVICE_PCI, - .local = 0x71100004, + .local = 0x71100014, .init = piix_init, .close = piix_close, .reset = piix_reset, diff --git a/src/device/kbc_at.c b/src/device/kbc_at.c index 7b6bea0c4..44e92fc6e 100644 --- a/src/device/kbc_at.c +++ b/src/device/kbc_at.c @@ -145,6 +145,11 @@ typedef struct atkbc_t { /* Internal FIFO for the purpose of commands with multi-byte output. */ uint8_t key_ctrl_queue[64]; + uint8_t handler_enable[2]; + + uint16_t base_addr[2]; + uint16_t irq[2]; + uint32_t flags; /* Main timers. */ @@ -157,8 +162,13 @@ typedef struct atkbc_t { /* Local copies of the pointers to both ports for easier swapping (AMI '5' MegaKey). */ kbc_at_port_t *ports[2]; - uint8_t (*write60_ven)(void *priv, uint8_t val); - uint8_t (*write64_ven)(void *priv, uint8_t val); + struct { + uint8_t (*read)(uint16_t port, void *priv); + void (*write)(uint16_t port, uint8_t val, void *priv); + } handlers[2]; + + uint8_t (*write_cmd_data_ven)(void *priv, uint8_t val); + uint8_t (*write_cmd_ven)(void *priv, uint8_t val); } atkbc_t; /* Keyboard controller ports. */ @@ -167,8 +177,6 @@ kbc_at_port_t *kbc_at_ports[2] = { NULL, NULL }; static uint8_t kbc_ami_revision = '8'; static uint8_t kbc_award_revision = 0x42; -static uint8_t kbc_handler_set = 0; - static void (*kbc_at_do_poll)(atkbc_t *dev); /* Non-translated to translated scan codes. */ @@ -362,12 +370,19 @@ kbc_do_irq(atkbc_t *dev) if (dev->do_irq) { /* 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. */ - picint_common(1 << 1, 0, 0, NULL); - picint_common(1 << 12, 0, 0, NULL); - if (dev->channel >= 2) - picint_common(1 << 12, 0, 1, NULL); - else - picint_common(1 << 1, 0, 1, NULL); + if (dev->irq[0] != 0xffff) + picint_common(1 << dev->irq[0], 0, 0, NULL); + + if (dev->irq[1] != 0xffff) + picint_common(1 << dev->irq[1], 0, 0, NULL); + + if (dev->channel >= 2) { + if (dev->irq[1] != 0xffff) + picint_common(1 << dev->irq[1], 0, 1, NULL); + } else { + if (dev->irq[0] != 0xffff) + picint_common(1 << dev->irq[0], 0, 1, NULL); + } dev->do_irq = 0; } @@ -404,7 +419,9 @@ kbc_send_to_ob(atkbc_t *dev, uint8_t val, uint8_t channel, uint8_t stat_hi) } else if (dev->mem[0x20] & 0x01) kbc_set_do_irq(dev, channel); } else if (dev->mem[0x20] & 0x01) - picintlevel(1 << 1, &dev->irq_state); /* AT KBC: IRQ 1 is level-triggered because it is tied to OBF. */ + /* AT KBC: IRQ 1 is level-triggered because it is tied to OBF. */ + if (dev->irq[0] != 0xffff) + picintlevel(1 << dev->irq[0], &dev->irq_state); #ifdef WRONG_CONDITION if ((dev->channel > 0) || dev->is_asic || (kbc_ven == KBC_VEN_IBM_PS1) || (kbc_ven == KBC_VEN_IBM)) @@ -784,10 +801,12 @@ write_p2(atkbc_t *dev, uint8_t val) /* PS/2: Handle IRQ's. */ if (dev->misc_flags & FLAG_PS2) { /* IRQ 12 */ - picint_common(1 << 12, 0, val & 0x20, NULL); + if (dev->irq[1] != 0xffff) + picint_common(1 << dev->irq[1], 0, val & 0x20, NULL); /* IRQ 1 */ - picint_common(1 << 1, 0, val & 0x10, NULL); + if (dev->irq[0] != 0xffff) + picint_common(1 << dev->irq[0], 0, val & 0x10, NULL); } #endif @@ -932,7 +951,7 @@ pulse_poll(void *priv) } static uint8_t -write64_generic(void *priv, uint8_t val) +write_cmd_generic(void *priv, uint8_t val) { atkbc_t *dev = (atkbc_t *) priv; uint8_t current_drive; @@ -1178,7 +1197,7 @@ write64_generic(void *priv, uint8_t val) } static uint8_t -write60_ami(void *priv, uint8_t val) +write_cmd_data_ami(void *priv, uint8_t val) { atkbc_t *dev = (atkbc_t *) priv; @@ -1248,7 +1267,7 @@ kbc_at_set_ps2(void *priv, const uint8_t ps2) } static uint8_t -write64_ami(void *priv, uint8_t val) +write_cmd_ami(void *priv, uint8_t val) { atkbc_t *dev = (atkbc_t *) priv; uint8_t kbc_ven = dev->flags & KBC_VEN_MASK; @@ -1453,11 +1472,11 @@ write64_ami(void *priv, uint8_t val) break; } - return write64_generic(dev, val); + return write_cmd_generic(dev, val); } static uint8_t -write60_phoenix(void *priv, uint8_t val) +write_cmd_data_phoenix(void *priv, uint8_t val) { atkbc_t *dev = (atkbc_t *) priv; @@ -1530,7 +1549,7 @@ write60_phoenix(void *priv, uint8_t val) } static uint8_t -write64_phoenix(void *priv, uint8_t val) +write_cmd_phoenix(void *priv, uint8_t val) { atkbc_t *dev = (atkbc_t *) priv; @@ -1676,11 +1695,11 @@ write64_phoenix(void *priv, uint8_t val) break; } - return write64_generic(dev, val); + return write_cmd_generic(dev, val); } static uint8_t -write64_siemens(void *priv, uint8_t val) +write_cmd_siemens(void *priv, uint8_t val) { atkbc_t *dev = (atkbc_t *) priv; @@ -1709,11 +1728,11 @@ write64_siemens(void *priv, uint8_t val) break; } - return write64_ami(dev, val); + return write_cmd_ami(dev, val); } static uint8_t -write60_quadtel(void *priv, UNUSED(uint8_t val)) +write_cmd_data_quadtel(void *priv, UNUSED(uint8_t val)) { const atkbc_t *dev = (atkbc_t *) priv; @@ -1730,7 +1749,7 @@ write60_quadtel(void *priv, UNUSED(uint8_t val)) } static uint8_t -write64_olivetti(void *priv, uint8_t val) +write_cmd_olivetti(void *priv, uint8_t val) { atkbc_t *dev = (atkbc_t *) priv; @@ -1751,11 +1770,11 @@ write64_olivetti(void *priv, uint8_t val) break; } - return write64_generic(dev, val); + return write_cmd_generic(dev, val); } static uint8_t -write64_quadtel(void *priv, uint8_t val) +write_cmd_quadtel(void *priv, uint8_t val) { atkbc_t *dev = (atkbc_t *) priv; @@ -1774,11 +1793,11 @@ write64_quadtel(void *priv, uint8_t val) break; } - return write64_generic(dev, val); + return write_cmd_generic(dev, val); } static uint8_t -write60_toshiba(void *priv, uint8_t val) +write_cmd_data_toshiba(void *priv, uint8_t val) { const atkbc_t *dev = (atkbc_t *) priv; @@ -1796,7 +1815,7 @@ write60_toshiba(void *priv, uint8_t val) } static uint8_t -write64_toshiba(void *priv, uint8_t val) +write_cmd_toshiba(void *priv, uint8_t val) { atkbc_t *dev = (atkbc_t *) priv; @@ -1885,7 +1904,7 @@ write64_toshiba(void *priv, uint8_t val) break; } - return write64_generic(dev, val); + return write_cmd_generic(dev, val); } static void @@ -1929,8 +1948,10 @@ kbc_at_process_cmd(void *priv) /* TODO: Proper P1 implementation, with OR and AND flags in the machine table. */ dev->p1 = dev->p1 & 0xff; write_p2(dev, 0x4b); - picintc(0x1000); - picintc(0x0002); + if (dev->irq[1] != 0xffff) + picintc(1 << dev->irq[1]); + if (dev->irq[0] != 0xffff) + picintc(1 << dev->irq[0]); } dev->status = (dev->status & 0x0f) | 0x60; @@ -1949,7 +1970,8 @@ kbc_at_process_cmd(void *priv) /* TODO: Proper P1 implementation, with OR and AND flags in the machine table. */ dev->p1 = dev->p1 & 0xff; write_p2(dev, 0xcf); - picintclevel(0x0002, &dev->irq_state); + if (dev->irq[0] != 0xffff) + picintclevel(1 << dev->irq[0], &dev->irq_state); dev->irq_state = 0; } @@ -2064,8 +2086,8 @@ kbc_at_process_cmd(void *priv) * that. Otherwise, or if that handler fails, * log a bad command. */ - if (dev->write64_ven) - bad = dev->write64_ven(dev, dev->ib); + if (dev->write_cmd_ven) + bad = dev->write_cmd_ven(dev, dev->ib); kbc_at_log(bad ? "ATkbc: bad controller command %02X\n" : "", dev->ib); } @@ -2151,8 +2173,8 @@ kbc_at_process_cmd(void *priv) * it returns an error, log a bad * controller command. */ - if (dev->write60_ven) - bad = dev->write60_ven(dev, dev->ib); + if (dev->write_cmd_data_ven) + bad = dev->write_cmd_data_ven(dev, dev->ib); if (bad) { kbc_at_log("ATkbc: bad controller command %02x data %02x\n", dev->command, dev->ib); @@ -2162,7 +2184,7 @@ kbc_at_process_cmd(void *priv) } static void -kbc_at_write(uint16_t port, uint8_t val, void *priv) +kbc_at_port_1_write(uint16_t port, uint8_t val, void *priv) { atkbc_t *dev = (atkbc_t *) priv; uint8_t kbc_ven = dev->flags & KBC_VEN_MASK; @@ -2170,83 +2192,89 @@ kbc_at_write(uint16_t port, uint8_t val, void *priv) kbc_at_log("ATkbc: [%04X:%08X] write(%04X) = %02X\n", CS, cpu_state.pc, port, val); - switch (port) { - case 0x60: - dev->status &= ~STAT_CD; - if (fast_a20 && dev->wantdata && (dev->command == 0xd1)) { - kbc_at_log("ATkbc: write P2\n"); + dev->status &= ~STAT_CD; - /* Fast A20 - ignore all other bits. */ - write_p2_fast_a20(dev, (dev->p2 & 0xfd) | (val & 0x02)); + if (fast_a20 && dev->wantdata && (dev->command == 0xd1)) { + kbc_at_log("ATkbc: write P2\n"); - dev->wantdata = 0; - dev->state = STATE_MAIN_IBF; + /* Fast A20 - ignore all other bits. */ + write_p2_fast_a20(dev, (dev->p2 & 0xfd) | (val & 0x02)); - /* - Explicitly clear IBF so that any preceding - command is not executed. - */ - dev->status &= ~STAT_IFULL; - return; - } - break; + dev->wantdata = 0; + dev->state = STATE_MAIN_IBF; - case 0x64: - dev->status |= STAT_CD; - if (fast_a20 && (val == 0xd1)) { - kbc_at_log("ATkbc: write P2\n"); - dev->wantdata = 1; - dev->state = STATE_KBC_PARAM; - dev->command = 0xd1; + /* + Explicitly clear IBF so that any preceding + command is not executed. + */ + dev->status &= ~STAT_IFULL; + return; + } - /* - Explicitly clear IBF so that any preceding - command is not executed. - */ - dev->status &= ~STAT_IFULL; - return; - } else if (fast_reset && ((val & 0xf0) == 0xf0)) { - pulse_output(dev, val & 0x0f); + dev->ib = val; + dev->status |= STAT_IFULL; +} - dev->state = STATE_MAIN_IBF; +static void +kbc_at_port_2_write(uint16_t port, uint8_t val, void *priv) +{ + atkbc_t *dev = (atkbc_t *) priv; + uint8_t kbc_ven = dev->flags & KBC_VEN_MASK; + uint8_t fast_a20 = (kbc_ven != KBC_VEN_SIEMENS); - /* - Explicitly clear IBF so that any preceding - command is not executed. - */ - dev->status &= ~STAT_IFULL; - return; - } else if (val == 0xad) { - /* Fast track it because of the Bochs BIOS. */ - kbc_at_log("ATkbc: disable keyboard\n"); - set_enable_kbd(dev, 0); + kbc_at_log("ATkbc: [%04X:%08X] write(%04X) = %02X\n", CS, cpu_state.pc, port, val); - dev->state = STATE_MAIN_IBF; + dev->status |= STAT_CD; - /* - Explicitly clear IBF so that any preceding - command is not executed. - */ - dev->status &= ~STAT_IFULL; - return; - } else if (val == 0xae) { - /* Fast track it because of the LG MultiNet. */ - kbc_at_log("ATkbc: enable keyboard\n"); - set_enable_kbd(dev, 1); + if (fast_a20 && (val == 0xd1)) { + kbc_at_log("ATkbc: write P2\n"); + dev->wantdata = 1; + dev->state = STATE_KBC_PARAM; + dev->command = 0xd1; - dev->state = STATE_MAIN_IBF; + /* + Explicitly clear IBF so that any preceding + command is not executed. + */ + dev->status &= ~STAT_IFULL; + return; + } else if (fast_reset && ((val & 0xf0) == 0xf0)) { + pulse_output(dev, val & 0x0f); - /* - Explicitly clear IBF so that any preceding - command is not executed. - */ - dev->status &= ~STAT_IFULL; - return; - } - break; + dev->state = STATE_MAIN_IBF; - default: - break; + /* + Explicitly clear IBF so that any preceding + command is not executed. + */ + dev->status &= ~STAT_IFULL; + return; + } else if (val == 0xad) { + /* Fast track it because of the Bochs BIOS. */ + kbc_at_log("ATkbc: disable keyboard\n"); + set_enable_kbd(dev, 0); + + dev->state = STATE_MAIN_IBF; + + /* + Explicitly clear IBF so that any preceding + command is not executed. + */ + dev->status &= ~STAT_IFULL; + return; + } else if (val == 0xae) { + /* Fast track it because of the LG MultiNet. */ + kbc_at_log("ATkbc: enable keyboard\n"); + set_enable_kbd(dev, 1); + + dev->state = STATE_MAIN_IBF; + + /* + Explicitly clear IBF so that any preceding + command is not executed. + */ + dev->status &= ~STAT_IFULL; + return; } dev->ib = val; @@ -2254,7 +2282,7 @@ kbc_at_write(uint16_t port, uint8_t val, void *priv) } static uint8_t -kbc_at_read(uint16_t port, void *priv) +kbc_at_port_1_read(uint16_t port, void *priv) { atkbc_t *dev = (atkbc_t *) priv; uint8_t ret = 0xff; @@ -2262,26 +2290,32 @@ kbc_at_read(uint16_t port, void *priv) if ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_1) cycles -= ISA_CYCLES(8); - switch (port) { - case 0x60: - ret = dev->ob; - dev->status &= ~STAT_OFULL; - /* TODO: IRQ is only tied to OBF on the AT KBC, on the PS/2 KBC, it is controlled by a P2 bit. - This also means that in AT mode, the IRQ is level-triggered. */ - if (!(dev->misc_flags & FLAG_PS2)) - picintclevel(1 << 1, &dev->irq_state); - if ((strstr(machine_get_internal_name(), "pb41") != NULL) && (cpu_override_dynarec == 1)) - cpu_override_dynarec = 0; - break; + ret = dev->ob; + dev->status &= ~STAT_OFULL; + /* + TODO: IRQ is only tied to OBF on the AT KBC, on the PS/2 KBC, it is controlled by a P2 bit. + This also means that in AT mode, the IRQ is level-triggered. + */ + if (!(dev->misc_flags & FLAG_PS2) && (dev->irq[0] != 0xffff)) + picintclevel(1 << dev->irq[0], &dev->irq_state); + if ((strstr(machine_get_internal_name(), "pb41") != NULL) && (cpu_override_dynarec == 1)) + cpu_override_dynarec = 0; - case 0x64: - ret = dev->status; - break; + kbc_at_log("ATkbc: [%04X:%08X] read (%04X) = %02X\n", CS, cpu_state.pc, port, ret); - default: - kbc_at_log("ATkbc: read(%04x) invalid!\n",port); - break; - } + return ret; +} + +static uint8_t +kbc_at_port_2_read(uint16_t port, void *priv) +{ + atkbc_t *dev = (atkbc_t *) priv; + uint8_t ret = 0xff; + + if ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_1) + cycles -= ISA_CYCLES(8); + + ret = dev->status; kbc_at_log("ATkbc: [%04X:%08X] read (%04X) = %02X\n", CS, cpu_state.pc, port, ret); @@ -2320,11 +2354,14 @@ kbc_at_reset(void *priv) if ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_1) { dev->misc_flags |= FLAG_PS2; kbc_at_do_poll = kbc_at_poll_ps2; - picintc(0x1000); - picintc(0x0002); + if (dev->irq[1] != 0xffff) + picintc(1 << dev->irq[1]); + if (dev->irq[0] != 0xffff) + picintc(1 << dev->irq[0]); } else { kbc_at_do_poll = kbc_at_poll_at; - picintclevel(0x0002, &dev->irq_state); + if (dev->irq[0] != 0xffff) + picintclevel(1 << dev->irq[0], &dev->irq_state); dev->irq_state = 0; } @@ -2367,21 +2404,52 @@ kbc_at_close(void *priv) } void -kbc_at_handler(int set, void *priv) +kbc_at_port_handler(int num, int set, uint16_t port, void *priv) { - if (kbc_handler_set) { - io_removehandler(0x0060, 1, kbc_at_read, NULL, NULL, kbc_at_write, NULL, NULL, priv); - io_removehandler(0x0064, 1, kbc_at_read, NULL, NULL, kbc_at_write, NULL, NULL, priv); + atkbc_t *dev = (atkbc_t *) priv; + + if (dev->handler_enable[num] && (dev->base_addr[num] != 0x0000)) { + pclog("Disabling keyboard controller port %i at %04X...\n", num, dev->base_addr[num]); + + io_removehandler(dev->base_addr[num], 1, + dev->handlers[num].read, NULL, NULL, + dev->handlers[num].write, NULL, NULL, priv); } - kbc_handler_set = set; + dev->handler_enable[num] = set; + dev->base_addr[num] = port; - if (kbc_handler_set) { - io_sethandler(0x0060, 1, kbc_at_read, NULL, NULL, kbc_at_write, NULL, NULL, priv); - io_sethandler(0x0064, 1, kbc_at_read, NULL, NULL, kbc_at_write, NULL, NULL, priv); + if (dev->handler_enable[num] && (dev->base_addr[num] != 0x0000)) { + pclog("Enabling keyboard controller port %i at %04X...\n", num, dev->base_addr[num]); + + io_sethandler(dev->base_addr[num], 1, + dev->handlers[num].read, NULL, NULL, + dev->handlers[num].write, NULL, NULL, priv); } } +void +kbc_at_handler(int set, uint16_t port, void *priv) +{ + kbc_at_port_handler(0, set, port, priv); + kbc_at_port_handler(1, set, port + 0x0004, priv); +} + +void +kbc_at_set_irq(int num, uint16_t irq, void *priv) +{ + atkbc_t *dev = (atkbc_t *) priv; + + if (dev->irq[num] != 0xffff) { + if ((num == 0) && ((dev->flags & KBC_TYPE_MASK) < KBC_TYPE_PS2_1)) + picintclevel(1 << dev->irq[num], &dev->irq_state); + else + picintc(1 << dev->irq[num]); + } + + dev->irq[num] = irq; +} + static void * kbc_at_init(const device_t *info) { @@ -2400,16 +2468,21 @@ kbc_at_init(const device_t *info) if (info->flags & DEVICE_PCI) dev->misc_flags |= FLAG_PCI; - kbc_handler_set = 0; - kbc_at_handler(1, dev); + dev->handlers[0].read = kbc_at_port_1_read; + dev->handlers[0].write = kbc_at_port_1_write; + dev->handlers[1].read = kbc_at_port_2_read; + dev->handlers[1].write = kbc_at_port_2_write; + + dev->irq[0] = 1; + dev->irq[1] = 12; timer_add(&dev->kbc_poll_timer, kbc_at_poll, dev, 1); timer_add(&dev->pulse_cb, pulse_poll, dev, 0); timer_add(&dev->kbc_dev_poll_timer, kbc_at_dev_poll, dev, 1); - dev->write60_ven = NULL; - dev->write64_ven = NULL; + dev->write_cmd_data_ven = NULL; + dev->write_cmd_ven = NULL; kbc_ami_revision = '8'; kbc_award_revision = 0x42; @@ -2418,8 +2491,8 @@ kbc_at_init(const device_t *info) case KBC_VEN_SIEMENS: kbc_ami_revision = '8'; kbc_award_revision = 0x42; - dev->write60_ven = write60_ami; - dev->write64_ven = write64_siemens; + dev->write_cmd_data_ven = write_cmd_data_ami; + dev->write_cmd_ven = write_cmd_siemens; break; case KBC_VEN_ACER: @@ -2428,24 +2501,24 @@ kbc_at_init(const device_t *info) case KBC_VEN_IBM_PS1: case KBC_VEN_IBM: case KBC_VEN_COMPAQ: - dev->write64_ven = write64_generic; + dev->write_cmd_ven = write_cmd_generic; break; case KBC_VEN_OLIVETTI: - dev->write64_ven = write64_olivetti; + dev->write_cmd_ven = write_cmd_olivetti; break; case KBC_VEN_ALI: kbc_ami_revision = 'F'; kbc_award_revision = 0x43; - dev->write60_ven = write60_ami; - dev->write64_ven = write64_ami; + dev->write_cmd_data_ven = write_cmd_data_ami; + dev->write_cmd_ven = write_cmd_ami; break; case KBC_VEN_TRIGEM_AMI: kbc_ami_revision = 'Z'; - dev->write60_ven = write60_ami; - dev->write64_ven = write64_ami; + dev->write_cmd_data_ven = write_cmd_data_ami; + dev->write_cmd_ven = write_cmd_ami; break; case KBC_VEN_AMI: @@ -2468,23 +2541,23 @@ kbc_at_init(const device_t *info) else kbc_ami_revision = 'F'; - dev->write60_ven = write60_ami; - dev->write64_ven = write64_ami; + dev->write_cmd_data_ven = write_cmd_data_ami; + dev->write_cmd_ven = write_cmd_ami; break; case KBC_VEN_PHOENIX: - dev->write60_ven = write60_phoenix; - dev->write64_ven = write64_phoenix; + dev->write_cmd_data_ven = write_cmd_data_phoenix; + dev->write_cmd_ven = write_cmd_phoenix; break; case KBC_VEN_QUADTEL: - dev->write60_ven = write60_quadtel; - dev->write64_ven = write64_quadtel; + dev->write_cmd_data_ven = write_cmd_data_quadtel; + dev->write_cmd_ven = write_cmd_quadtel; break; case KBC_VEN_TOSHIBA: - dev->write60_ven = write60_toshiba; - dev->write64_ven = write64_toshiba; + dev->write_cmd_data_ven = write_cmd_data_toshiba; + dev->write_cmd_ven = write_cmd_toshiba; break; default: @@ -2510,6 +2583,8 @@ kbc_at_init(const device_t *info) fast_reset = 0x00; + kbc_at_handler(1, 0x0060, dev); + return dev; } diff --git a/src/device/postcard.c b/src/device/postcard.c index 95a4df646..058684c0b 100644 --- a/src/device/postcard.c +++ b/src/device/postcard.c @@ -30,11 +30,13 @@ #include "cpu.h" uint8_t postcard_codes[POSTCARDS_NUM]; +char postcard_diags[5] = { 0 }; static uint16_t postcard_port; static uint8_t postcard_written[POSTCARDS_NUM]; static uint8_t postcard_ports_num = 1; static uint8_t postcard_prev_codes[POSTCARDS_NUM]; +static char postcard_prev_diags[5] = { 0 }; #define UISTR_LEN 32 static char postcard_str[UISTR_LEN]; /* UI output string */ @@ -97,6 +99,22 @@ postcard_setui(void) ps[1][0], ps[1][1], ps[1][2], ps[1][3]); break; } + } else if (strstr(machines[machine].name, " Dell ")) { + char dell_diags[10] = { 0 }; + + if (!postcard_written[1]) + snprintf(dell_diags, sizeof(dell_diags), "---- ----"); + else if (postcard_written[1] == 1) + snprintf(dell_diags, sizeof(dell_diags), "%s ----", postcard_diags); + else + snprintf(dell_diags, sizeof(dell_diags), "%s %s", postcard_diags, postcard_prev_diags); + + if (!postcard_written[0]) + snprintf(postcard_str, sizeof(postcard_str), "POST: -- -- %s", dell_diags); + else if (postcard_written[0] == 1) + snprintf(postcard_str, sizeof(postcard_str), "POST: %02X -- %s", postcard_codes[0], dell_diags); + else + snprintf(postcard_str, sizeof(postcard_str), "POST: %02X %02X %s", postcard_codes[0], postcard_prev_codes[0], dell_diags); } else { if (!postcard_written[0]) snprintf(postcard_str, sizeof(postcard_str), "POST: -- --"); @@ -122,6 +140,9 @@ postcard_reset(void) memset(postcard_codes, 0x00, POSTCARDS_NUM * sizeof(uint8_t)); memset(postcard_prev_codes, 0x00, POSTCARDS_NUM * sizeof(uint8_t)); + memset(postcard_diags, 0x00, 5 * sizeof(char)); + memset(postcard_prev_diags, 0x00, 5 * sizeof(char)); + postcard_setui(); } @@ -140,6 +161,35 @@ postcard_write(uint16_t port, uint8_t val, UNUSED(void *priv)) postcard_setui(); } +static int +postcard_cmp_diags(uint32_t val) +{ + int ret = 0; + char *pv = (char *) &val; + + for (int i = 0; i < 4; i++) + ret = ret || (pv[i] != postcard_diags[3 - i]); + + return ret; +} + +static void +postcard_writel(uint16_t port, uint32_t val, UNUSED(void *priv)) +{ + char *pv = (char *) &val; + + if (postcard_written[1] && !postcard_cmp_diags(val)) + return; + + *(uint32_t *) postcard_prev_diags = *(uint32_t *) postcard_diags; + for (int i = 0; i < 4; i++) + postcard_diags[i] = pv[3 - i]; + if (postcard_written[1] < 2) + postcard_written[1]++; + + postcard_setui(); +} + static void * postcard_init(UNUSED(const device_t *info)) { @@ -173,6 +223,10 @@ postcard_init(UNUSED(const device_t *info)) io_sethandler(postcard_port, postcard_ports_num, NULL, NULL, NULL, postcard_write, NULL, NULL, NULL); + if (strstr(machines[machine].name, " Dell ")) + io_sethandler(0x00e0, 0x0001, + NULL, NULL, NULL, NULL, NULL, postcard_writel, NULL); + return postcard_write; } diff --git a/src/dma.c b/src/dma.c index 4edeb39f8..2265947b9 100644 --- a/src/dma.c +++ b/src/dma.c @@ -856,7 +856,7 @@ dma16_read(uint16_t addr, UNUSED(void *priv)) break; } - dma_log("dma16_read(%08X) = %02X\n", port, ret); + dma_log("dma16_read(%08X) = %02X\n", addr, ret); return ret; } diff --git a/src/floppy/fdc.c b/src/floppy/fdc.c index cf5ae41bf..ebc16cbfd 100644 --- a/src/floppy/fdc.c +++ b/src/floppy/fdc.c @@ -377,6 +377,15 @@ fdc_set_power_down(fdc_t *fdc, uint8_t power_down) fdc->power_down = power_down; } +void +fdc_toggle_flag(fdc_t *fdc, int flag, int on) +{ + if (on) + fdc->flags |= flag; + else + fdc->flags &= ~flag; +} + void fdc_update_max_track(fdc_t *fdc, int max_track) { @@ -1484,7 +1493,7 @@ fdc_read(uint16_t addr, void *priv) fdc->step = 0; break; default: - ret = 0xFF; + ret = 0xff; } fdc_log("[%04X:%08X] Read FDC %04X %02X [%i:%02X]\n", CS, cpu_state.pc, addr, ret, drive, fdc->dor & (0x10 << drive)); return ret; @@ -2235,9 +2244,13 @@ fdc_set_base(fdc_t *fdc, int base) { int super_io = (fdc->flags & FDC_FLAG_SUPERIO); + if (base == 0x0000) { + fdc->base_address = base; + return; + } + if (fdc->flags & FDC_FLAG_NSC) { - io_sethandler(base + 2, 0x0001, fdc_read, NULL, NULL, fdc_write, NULL, NULL, fdc); - io_sethandler(base + 4, 0x0002, fdc_read, NULL, NULL, fdc_write, NULL, NULL, fdc); + io_sethandler(base + 2, 0x0004, fdc_read, NULL, NULL, fdc_write, NULL, NULL, fdc); io_sethandler(base + 7, 0x0001, fdc_read, NULL, NULL, fdc_write, NULL, NULL, fdc); } else { if ((fdc->flags & FDC_FLAG_AT) || (fdc->flags & FDC_FLAG_AMSTRAD)) { @@ -2266,10 +2279,12 @@ fdc_remove(fdc_t *fdc) { int super_io = (fdc->flags & FDC_FLAG_SUPERIO); + if (fdc->base_address == 0x0000) + return; + fdc_log("FDC Removed (%04X)\n", fdc->base_address); if (fdc->flags & FDC_FLAG_NSC) { - io_removehandler(fdc->base_address + 2, 0x0001, fdc_read, NULL, NULL, fdc_write, NULL, NULL, fdc); - io_removehandler(fdc->base_address + 4, 0x0002, fdc_read, NULL, NULL, fdc_write, NULL, NULL, fdc); + io_removehandler(fdc->base_address + 2, 0x0004, fdc_read, NULL, NULL, fdc_write, NULL, NULL, fdc); io_removehandler(fdc->base_address + 7, 0x0001, fdc_read, NULL, NULL, fdc_write, NULL, NULL, fdc); } else { if ((fdc->flags & FDC_FLAG_AT) || (fdc->flags & FDC_FLAG_AMSTRAD)) { diff --git a/src/include/86box/fdc.h b/src/include/86box/fdc.h index 53511daac..c98a03f67 100644 --- a/src/include/86box/fdc.h +++ b/src/include/86box/fdc.h @@ -183,6 +183,7 @@ extern uint8_t fdc_get_densel_polarity(fdc_t *fdc); extern void fdc_update_densel_force(fdc_t *fdc, int densel_force); extern void fdc_update_drvrate(fdc_t *fdc, int drive, int drvrate); extern void fdc_update_drv2en(fdc_t *fdc, int drv2en); +extern void fdc_toggle_flag(fdc_t *fdc, int flag, int on); extern void fdc_noidam(fdc_t *fdc); extern void fdc_nosector(fdc_t *fdc); diff --git a/src/include/86box/keyboard.h b/src/include/86box/keyboard.h index 9dfb1c8e4..b9bac0821 100644 --- a/src/include/86box/keyboard.h +++ b/src/include/86box/keyboard.h @@ -289,7 +289,9 @@ extern uint8_t kbc_at_read_p(void *priv, uint8_t port, uint8_t mask); extern void kbc_at_write_p(void *priv, uint8_t port, uint8_t mask, uint8_t val); extern void kbc_at_set_fast_reset(uint8_t new_fast_reset); -extern void kbc_at_handler(int set, void *priv); +extern void kbc_at_port_handler(int num, int set, uint16_t port, void *priv); +extern void kbc_at_handler(int set, uint16_t port, void *priv); +extern void kbc_at_set_irq(int num, uint16_t irq, void *priv); extern void kbc_at_dev_queue_reset(atkbc_dev_t *dev, uint8_t reset_main); extern uint8_t kbc_at_dev_queue_pos(atkbc_dev_t *dev, uint8_t main); diff --git a/src/include/86box/machine.h b/src/include/86box/machine.h index e9dc5bda9..cbf2fda15 100644 --- a/src/include/86box/machine.h +++ b/src/include/86box/machine.h @@ -752,6 +752,7 @@ extern int machine_at_gw2kte_init(const machine_t *); extern int machine_at_ma23c_init(const machine_t *); extern int machine_at_nupro592_init(const machine_t *); extern int machine_at_tx97_init(const machine_t *); +extern int machine_at_optiplex_gn_init(const machine_t *); #ifdef USE_AN430TX extern int machine_at_an430tx_init(const machine_t *); #endif /* USE_AN430TX */ diff --git a/src/include/86box/sio.h b/src/include/86box/sio.h index 06bf57f8f..358cd8c9a 100644 --- a/src/include/86box/sio.h +++ b/src/include/86box/sio.h @@ -92,13 +92,23 @@ extern const device_t pc87332_398_ide_device; extern const device_t pc87332_398_ide_sec_device; extern const device_t pc87332_398_ide_fdcon_device; +#define PCX7307_PC87307 0x00c0 +#define PCX7307_PC97307 0x00cf + +#define PC87309_PC87309 0x00e0 + +#define PCX730X_CHIP_ID 0x00ff + +#define PCX730X_AMI 0x0200 /* AMI '5' Megakey KBC firmware. */ +#define PCX730X_PHOENIX_42 0x0500 /* Phoenix Multikey/42 1.37 KBC firmware. */ +#define PCX730X_PHOENIX_42I 0x0700 /* Phoenix Multikey/42i 4.16 KBC firmware. */ +#define PCX730X_KBC 0x0f00 + +#define PCX730X_15C 0x2000 + extern const device_t pc87307_device; -extern const device_t pc87307_15c_device; -extern const device_t pc87307_both_device; -extern const device_t pc97307_device; extern const device_t pc87309_device; -extern const device_t pc87309_15c_device; /* LG Prime */ extern const device_t prime3b_device; diff --git a/src/lpt.c b/src/lpt.c index 26174d96b..072f4a34c 100644 --- a/src/lpt.c +++ b/src/lpt.c @@ -229,9 +229,9 @@ void lpt_port_setup(int i, uint16_t port) { if (lpt_ports[i].enabled) { - if (lpt_ports[i].addr != 0xffff) + if ((lpt_ports[i].addr != 0xffff) && (lpt_ports[i].addr != 0x0000)) io_removehandler(lpt_ports[i].addr, 0x0003, lpt_read, NULL, NULL, lpt_write, NULL, NULL, &lpt_ports[i]); - if (port != 0xffff) + if ((port != 0xffff) && (port != 0x0000)) io_sethandler(port, 0x0003, lpt_read, NULL, NULL, lpt_write, NULL, NULL, &lpt_ports[i]); lpt_ports[i].addr = port; } else diff --git a/src/machine/m_at_slot1.c b/src/machine/m_at_slot1.c index b2f05d33e..773826316 100644 --- a/src/machine/m_at_slot1.c +++ b/src/machine/m_at_slot1.c @@ -584,7 +584,7 @@ machine_at_s1846_init(const machine_t *model) pci_register_slot(0x01, PCI_CARD_AGPBRIDGE, 1, 2, 3, 4); device_add(&i440bx_device); device_add(&piix4e_device); - device_add(&pc87309_device); + device_add_params(&pc87309_device, (void *) (PCX730X_AMI | PC87309_PC87309)); device_add(&keyboard_ps2_ami_pci_device); device_add(&intel_flash_bxt_device); spd_register(SPD_TYPE_SDRAM, 0x7, 256); diff --git a/src/machine/m_at_slot2.c b/src/machine/m_at_slot2.c index da160c138..37c0acdfd 100644 --- a/src/machine/m_at_slot2.c +++ b/src/machine/m_at_slot2.c @@ -141,7 +141,7 @@ machine_at_fw6400gx_init(const machine_t *model) device_add(&i440gx_device); device_add(&piix4e_device); device_add(&keyboard_ps2_ami_pci_device); - device_add(&pc87309_15c_device); + device_add_params(&pc87309_device, (void *) (PCX730X_15C | PCX730X_AMI | PC87309_PC87309)); device_add(ics9xxx_get(ICS9250_08)); device_add(&sst_flash_29ee020_device); spd_register(SPD_TYPE_SDRAM, 0xF, 512); diff --git a/src/machine/m_at_socket7.c b/src/machine/m_at_socket7.c index 319856d41..3d51e1a06 100644 --- a/src/machine/m_at_socket7.c +++ b/src/machine/m_at_socket7.c @@ -969,6 +969,42 @@ machine_at_tx97_init(const machine_t *model) return ret; } +int +machine_at_optiplex_gn_init(const machine_t *model) +{ + int ret; + + ret = bios_load_linear("roms/machines/optiplex_gn/DELL.ROM", + 0x000c0000, 262144, 0); + + if (bios_only || !ret) + return ret; + + machine_at_common_init_ex(model, 2); + + pci_init(PCI_CONFIG_TYPE_1); + pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 1, 2, 3, 4); + pci_register_slot(0x0E, PCI_CARD_NORMAL, 3, 4, 2, 1); + pci_register_slot(0x0D, PCI_CARD_NORMAL, 2, 1, 3, 4); + pci_register_slot(0x10, PCI_CARD_VIDEO, 4, 0, 0, 0); /* Trio64V2/GX, temporarily Trio64V2/DX is given */ + pci_register_slot(0x11, PCI_CARD_NETWORK, 4, 0, 0, 0); /* 3C905, not yet emulated */ + pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 0, 0, 0, 4); + + if (gfxcard[0] == VID_INTERNAL) + device_add(machine_get_vid_device(machine)); + + if ((sound_card_current[0] == SOUND_INTERNAL) && machine_get_snd_device(machine)->available()) + machine_snd = device_add(machine_get_snd_device(machine)); + + device_add(&i430tx_device); + device_add(&piix4_device); + device_add_params(&pc87307_device, (void *) (PCX730X_PHOENIX_42 | PCX7307_PC87307)); + device_add(&intel_flash_bxt_device); + spd_register(SPD_TYPE_SDRAM, 0x3, 128); + + return ret; +} + #ifdef USE_AN430TX int machine_at_an430tx_init(const machine_t *model) @@ -1006,8 +1042,7 @@ machine_at_an430tx_init(const machine_t *model) pci_register_slot(0x10, PCI_CARD_NORMAL, 4, 1, 2, 3); device_add(&i430tx_device); device_add(&piix4_device); - device_add(&keyboard_ps2_ami_pci_device); - device_add(&pc87307_both_device); + device_add_params(&pc87307_device, (void *) (PCX730X_PHOENIX_42I | PCX7307_PC97307)); device_add(&intel_flash_bxt_ami_device); spd_register(SPD_TYPE_SDRAM, 0x3, 128); diff --git a/src/machine/m_at_socket8.c b/src/machine/m_at_socket8.c index 006e0f419..aabfb9b4f 100644 --- a/src/machine/m_at_socket8.c +++ b/src/machine/m_at_socket8.c @@ -250,8 +250,7 @@ machine_at_vs440fx_init(const machine_t *model) pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 0, 0, 0, 0); device_add(&i440fx_device); device_add(&piix3_device); - device_add(&keyboard_ps2_intel_ami_pci_device); - device_add(&pc87307_device); + device_add_params(&pc87307_device, (void *) (PCX730X_AMI | PCX7307_PC87307)); device_add(&intel_flash_bxt_ami_device); @@ -287,8 +286,7 @@ machine_at_gw2kvenus_init(const machine_t *model) pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 0, 0, 0, 0); device_add(&i440fx_device); device_add(&piix3_device); - device_add(&keyboard_ps2_intel_ami_pci_device); - device_add(&pc87307_device); + device_add_params(&pc87307_device, (void *) (PCX730X_AMI | PCX7307_PC87307)); device_add(&intel_flash_bxt_ami_device); @@ -324,8 +322,7 @@ machine_at_ap440fx_init(const machine_t *model) pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 0, 0, 0, 4); device_add(&i440fx_device); device_add(&piix3_device); - device_add(&keyboard_ps2_ami_pci_device); - device_add(&pc87307_device); + device_add_params(&pc87307_device, (void *) (PCX730X_AMI | PCX7307_PC87307)); device_add(&intel_flash_bxt_ami_device); if (sound_card_current[0] == SOUND_INTERNAL) diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index f4031c0b7..faada4427 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -13143,6 +13143,56 @@ const machine_t machines[] = { .snd_device = NULL, .net_device = NULL }, + /* + According to Dell specifications, it can have either National Semiconductor + PC87307 or PC87309 Super I/O. All known instances have the former, although + other similar Dells of the era have pinouts for accompanying either so this + likely also does. + + The KBC is either an AMI '5' MegaKey, Phoenix MultiKey/42 1.37, or Phoenix + MultiKey/42i 4.16. + */ + { + .name = "[i430TX] Dell OptiPlex GN+", + .internal_name = "optiplex_gn", + .type = MACHINE_TYPE_SOCKET7, + .chipset = MACHINE_CHIPSET_INTEL_430TX, + .init = machine_at_optiplex_gn_init, + .p1_handler = NULL, + .gpio_handler = NULL, + .available_flag = MACHINE_AVAILABLE, + .gpio_acpi_handler = NULL, + .cpu = { + .package = CPU_PKG_SOCKET5_7, + .block = CPU_BLOCK_NONE, + .min_bus = 50000000, + .max_bus = 66666667, + .min_voltage = 2500, + .max_voltage = 3520, + .min_multi = 1.5, + .max_multi = 3.5 + }, + .bus_flags = MACHINE_PS2_PCI, + /* Video: S3 86C785 (Trio64V2/GX), ethernet: 3C905. */ + .flags = MACHINE_IDE_DUAL | MACHINE_APM | MACHINE_ACPI | MACHINE_VIDEO | MACHINE_SOUND, + .ram = { + .min = 8192, + .max = 262144, + .step = 8192 + }, + .nvrmask = 255, + .kbc_device = NULL, + .kbc_p1 = 0xff, + .gpio = 0xffffffff, + .gpio_acpi = 0xffffffff, + .device = NULL, + .fdc_device = NULL, + .sio_device = NULL, + /* Stop-gap measure until the Trio64V2/GX is emulated, as both use the same VBIOS. */ + .vid_device = &s3_trio64v2_dx_onboard_pci_device, + .snd_device = &sb_vibra16xv_onboard_device, + .net_device = NULL + }, /* [TEST] Has AMI Megakey '5' KBC firmware on the SM(S)C FDC37C67x Super I/O chip. */ { .name = "[i430TX] Gateway E-1000", diff --git a/src/nvr_at.c b/src/nvr_at.c index 2acfa47a4..4deda98be 100644 --- a/src/nvr_at.c +++ b/src/nvr_at.c @@ -1107,9 +1107,10 @@ nvr_at_init(const device_t *info) case 1: /* standard AT */ case 5: /* AMI WinBIOS 1994 */ case 6: /* AMI BIOS 1995 */ - if ((info->local & 0x1f) == 0x11) + if ((info->local & 0x1f) == 0x11) { local->flags |= FLAG_PIIX4; - else { + local->def = 0x00; + } else { local->def = 0x00; if ((info->local & 0x1f) == 0x15) local->flags |= FLAG_AMI_1994_HACK; diff --git a/src/sio/sio_fdc37c93x.c b/src/sio/sio_fdc37c93x.c index ebc500c96..dce5b11dc 100644 --- a/src/sio/sio_fdc37c93x.c +++ b/src/sio/sio_fdc37c93x.c @@ -894,7 +894,7 @@ fdc37c93x_kbc_handler(fdc37c93x_t *dev) dev->kbc_base = local_enable ? 0x0060 : 0x0000; if (dev->kbc_base != old_base) - kbc_at_handler(local_enable, dev->kbc); + kbc_at_handler(local_enable, dev->kbc_base, dev->kbc); } static void diff --git a/src/sio/sio_pc87307.c b/src/sio/sio_pc87307.c index ae21d34af..7bc9bb441 100644 --- a/src/sio/sio_pc87307.c +++ b/src/sio/sio_pc87307.c @@ -8,11 +8,9 @@ * * Emulation of the NatSemi PC87307 Super I/O chip. * - * - * * Authors: Miran Grca, * - * Copyright 2020 Miran Grca. + * Copyright 2020-2025 Miran Grca. */ #include #include @@ -24,45 +22,89 @@ #include <86box/timer.h> #include <86box/device.h> #include <86box/lpt.h> +#include <86box/machine.h> #include <86box/mem.h> #include <86box/nvr.h> #include <86box/pci.h> #include <86box/rom.h> #include <86box/serial.h> -#include <86box/hdc.h> -#include <86box/hdc_ide.h> #include <86box/fdd.h> #include <86box/fdc.h> +#include <86box/keyboard.h> #include <86box/sio.h> #include <86box/plat_fallthrough.h> +#include "cpu.h" typedef struct pc87307_t { uint8_t id; + uint8_t baddr; uint8_t pm_idx; uint8_t regs[48]; uint8_t ld_regs[256][208]; uint8_t pcregs[16]; - uint8_t gpio[2][4]; + uint8_t gpio[2][8]; uint8_t pm[8]; + uint16_t superio_base; uint16_t gpio_base; uint16_t gpio_base2; uint16_t pm_base; int cur_reg; + void *kbc; fdc_t *fdc; serial_t *uart[2]; } pc87307_t; -static void fdc_handler(pc87307_t *dev); -static void lpt1_handler(pc87307_t *dev); -static void serial_handler(pc87307_t *dev, int uart); +enum { + LD_KBD = 0, + LD_MOUSE, + LD_RTC, + LD_FDC, + LD_LPT, + LD_UART2, + LD_UART1, + LD_GPIO, + LD_PM +} pc87307_ld_t; + +#define LD_MIN LD_KBD +#define LD_MAX LD_PM + +static void fdc_handler(pc87307_t *dev); +static void lpt1_handler(pc87307_t *dev); +static void serial_handler(pc87307_t *dev, int uart); +static void kbc_handler(pc87307_t *dev); +static void pc87307_write(uint16_t port, uint8_t val, void *priv); +static uint8_t pc87307_read(uint16_t port, void *priv); + +#ifdef ENABLE_PC87307_LOG +int pc87307_do_log = ENABLE_PC87307_LOG; + +static void +pc87307_log(const char *fmt, ...) +{ + va_list ap; + + if (pc87307_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +# define pc87307_log(fmt, ...) +#endif static void pc87307_gpio_write(uint16_t port, uint8_t val, void *priv) { pc87307_t *dev = (pc87307_t *) priv; - uint8_t bank = ((port & 0xfffc) == dev->gpio_base2); + uint8_t bank = !!(dev->regs[0x22] & 0x80); - dev->gpio[bank][port & 3] = val; + /* Bit 7 of SCNF2 = bank. */ + pc87307_log("[%04X:%08X] [W] (%04X) Bank %i = %02X\n", + CS, cpu_state.pc, port, bank, val); + + dev->gpio[bank][port & 0x0007] = val; } uint8_t @@ -70,20 +112,36 @@ pc87307_gpio_read(uint16_t port, void *priv) { const pc87307_t *dev = (pc87307_t *) priv; uint8_t pins = 0xff; - uint8_t bank = ((port & 0xfffc) == dev->gpio_base2); - uint8_t mask; - uint8_t ret = dev->gpio[bank][port & 0x0003]; + uint8_t bank = !!(dev->regs[0x22] & 0x80); + uint8_t ret = dev->gpio[bank][port & 0x0007]; switch (port & 0x0003) { case 0x0000: - mask = dev->gpio[bank][0x0001]; - ret = (ret & mask) | (pins & ~mask); + if (bank == 0) { + uint8_t mask = dev->gpio[0][1]; + pins = 0x7f; + ret = (ret & mask) | (pins & ~mask); + } + break; + case 0x0004: + if (bank == 0) { + uint8_t mask = dev->gpio[0][5]; + pins = 0xfb; + ret = (ret & mask) | (pins & ~mask); + } else + ret = 0xff; break; default: + if (bank == 1) + ret = 0xff; break; } + /* Bit 7 of SCNF2 = bank. */ + pc87307_log("[%04X:%08X] [R] (%04X) Bank %i = %02X\n", + CS, cpu_state.pc, port, bank, ret); + return ret; } @@ -123,6 +181,7 @@ pc87307_pm_write(uint16_t port, uint8_t val, void *priv) dev->pm[dev->pm_idx] = val; else { dev->pm_idx = val & 0x07; + switch (dev->pm_idx) { case 0x00: fdc_handler(dev); @@ -167,20 +226,49 @@ pc87307_pm_init(pc87307_t *dev, uint16_t addr) pc87307_pm_read, NULL, NULL, pc87307_pm_write, NULL, NULL, dev); } +static void +kbc_handler(pc87307_t *dev) +{ + uint8_t active = (dev->ld_regs[LD_KBD][0x00] & 0x01) && + (dev->pm[0x00] & 0x01); + uint8_t active_2 = dev->ld_regs[LD_MOUSE][0x00] & 0x01; + uint8_t irq = (dev->ld_regs[LD_KBD][0x40] & 0x0f); + uint8_t irq_2 = (dev->ld_regs[LD_MOUSE][0x40] & 0x0f); + uint16_t addr = (dev->ld_regs[LD_KBD][0x30] << 8) | + dev->ld_regs[LD_KBD][0x31]; + uint16_t addr_2 = (dev->ld_regs[LD_KBD][0x32] << 8) | + dev->ld_regs[LD_KBD][0x33]; + + pc87307_log("%02X, %02X, %02X, %02X, %04X, %04X\n", + active, active_2, irq, irq_2, addr, addr_2); + + if (addr <= 0xfff8) { + pc87307_log("Enabling KBC #1 on %04X...\n", addr); + kbc_at_port_handler(0, active, addr, dev->kbc); + } + + if (addr_2 <= 0xfff8) { + pc87307_log("Enabling KBC #2 on %04X...\n", addr_2); + kbc_at_port_handler(1, active, addr_2, dev->kbc); + } + + kbc_at_set_irq(0, active ? irq : 0xffff, dev->kbc); + kbc_at_set_irq(1, (active && active_2) ? irq_2 : 0xffff, dev->kbc); +} + static void fdc_handler(pc87307_t *dev) { - uint8_t irq; - uint8_t active; - uint16_t addr; - fdc_remove(dev->fdc); - active = (dev->ld_regs[0x03][0x00] & 0x01) && (dev->pm[0x00] & 0x08); - addr = ((dev->ld_regs[0x03][0x30] << 8) | dev->ld_regs[0x03][0x31]) - 0x0002; - irq = (dev->ld_regs[0x03][0x40] & 0x0f); + uint8_t active = (dev->ld_regs[LD_FDC][0x00] & 0x01) && + (dev->pm[0x00] & 0x08); + uint8_t irq = (dev->ld_regs[LD_FDC][0x40] & 0x0f); + uint16_t addr = ((dev->ld_regs[LD_FDC][0x30] << 8) | + dev->ld_regs[LD_FDC][0x31]) & 0xfff8; if (active && (addr <= 0xfff8)) { + pc87307_log("Enabling FDC on %04X, IRQ %i...\n", addr, irq); fdc_set_base(dev->fdc, addr); fdc_set_irq(dev->fdc, irq); } @@ -189,268 +277,405 @@ fdc_handler(pc87307_t *dev) static void lpt1_handler(pc87307_t *dev) { - uint8_t irq; - uint8_t active; - uint16_t addr; - - lpt1_remove(); - - active = (dev->ld_regs[0x04][0x00] & 0x01) && (dev->pm[0x00] & 0x10); - addr = (dev->ld_regs[0x04][0x30] << 8) | dev->ld_regs[0x04][0x31]; - irq = (dev->ld_regs[0x04][0x40] & 0x0f); + uint8_t active = (dev->ld_regs[LD_LPT][0x00] & 0x01) && + (dev->pm[0x00] & 0x10); + uint8_t irq = (dev->ld_regs[LD_LPT][0x40] & 0x0f); + uint16_t addr = (dev->ld_regs[LD_LPT][0x30] << 8) | + dev->ld_regs[LD_LPT][0x31]; if (active && (addr <= 0xfffc)) { + pc87307_log("Enabling LPT1 on %04X...\n", addr); lpt1_setup(addr); - lpt1_irq(irq); - } + } else + lpt1_setup(0xffff); + + lpt1_irq(irq); } static void serial_handler(pc87307_t *dev, int uart) { - uint8_t irq; - uint8_t active; - uint16_t addr; - serial_remove(dev->uart[uart]); - active = (dev->ld_regs[0x06 - uart][0x00] & 0x01) && (dev->pm[0x00] & (1 << (6 - uart))); - addr = (dev->ld_regs[0x06 - uart][0x30] << 8) | dev->ld_regs[0x06 - uart][0x31]; - irq = (dev->ld_regs[0x06 - uart][0x40] & 0x0f); + uint8_t active = (dev->ld_regs[LD_UART1 - uart][0x00] & 0x01) && + (dev->pm[0x00] & (1 << (6 - uart))); + uint8_t irq = (dev->ld_regs[LD_UART1 - uart][0x40] & 0x0f); + uint16_t addr = (dev->ld_regs[LD_UART1 - uart][0x30] << 8) | + dev->ld_regs[LD_UART1 - uart][0x31]; - if (active && (addr <= 0xfff8)) + if (active && (addr <= 0xfff8)) { + pc87307_log("Enabling COM%i on %04X...\n", uart + 1, addr); serial_setup(dev->uart[uart], addr, irq); + } else + serial_setup(dev->uart[uart], 0x0000, irq); } static void gpio_handler(pc87307_t *dev) { - uint8_t active; - uint16_t addr; - pc87307_gpio_remove(dev); - active = (dev->ld_regs[0x07][0x00] & 0x01); - addr = (dev->ld_regs[0x07][0x30] << 8) | dev->ld_regs[0x07][0x31]; + uint8_t active = (dev->ld_regs[LD_GPIO][0x00] & 0x01); + uint16_t addr = (dev->ld_regs[LD_GPIO][0x30] << 8) | + dev->ld_regs[LD_GPIO][0x31]; + uint16_t addr_2 = (dev->ld_regs[LD_GPIO][0x32] << 8) | + dev->ld_regs[LD_GPIO][0x33]; - if (active) + if (active) { + pc87307_log("Enabling GPIO #1 on %04X...\n", addr); pc87307_gpio_init(dev, 0, addr); - - addr = (dev->ld_regs[0x07][0x32] << 8) | dev->ld_regs[0x07][0x33]; - - if (active) - pc87307_gpio_init(dev, 1, addr); + pc87307_log("Enabling GPIO #2 on %04X...\n", addr_2); + pc87307_gpio_init(dev, 1, addr_2); + } } static void pm_handler(pc87307_t *dev) { - uint8_t active; - uint16_t addr; - pc87307_pm_remove(dev); - active = (dev->ld_regs[0x08][0x00] & 0x01); - addr = (dev->ld_regs[0x08][0x30] << 8) | dev->ld_regs[0x08][0x31]; + uint8_t active = (dev->ld_regs[LD_PM][0x00] & 0x01); + uint16_t addr = (dev->ld_regs[LD_PM][0x30] << 8) | + dev->ld_regs[LD_PM][0x31]; - if (active) + if (active) { + pc87307_log("Enabling power management on %04X...\n", addr); pc87307_pm_init(dev, addr); + } +} + +static void +superio_handler(pc87307_t *dev) +{ + if (dev->superio_base != 0x0000) + io_removehandler(dev->superio_base, 0x0002, + pc87307_read, NULL, NULL, + pc87307_write, NULL, NULL, dev); + + switch (dev->regs[0x22] & 0x03) { + default: + dev->superio_base = 0x0000; + break; case 0x02: + dev->superio_base = 0x015c; + break; + case 0x03: + dev->superio_base = 0x002e; + break; + } + + if (dev->superio_base != 0x0000) { + pc87307_log("Enabling Super I/O on %04X...\n", dev->superio_base); + io_sethandler(dev->superio_base, 0x0002, + pc87307_read, NULL, NULL, + pc87307_write, NULL, NULL, dev); + } } static void pc87307_write(uint16_t port, uint8_t val, void *priv) { - pc87307_t *dev = (pc87307_t *) priv; - uint8_t index; - - index = (port & 1) ? 0 : 1; + pc87307_t *dev = (pc87307_t *) priv; + uint8_t ld = dev->regs[0x07]; + uint8_t reg = dev->cur_reg - 0x30; + uint8_t index = (port & 1) ? 0 : 1; + uint8_t old = dev->regs[dev->cur_reg]; if (index) { dev->cur_reg = val; return; } else { +#ifdef ENABLE_PC87307_LOG + if (dev->cur_reg >= 0x30) + pc87307_log("[%04X:%08X] [W] (%04X) %02X:%02X = %02X\n", + CS, cpu_state.pc, port, ld, dev->cur_reg, val); + else + pc87307_log("[%04X:%08X] [W] (%04X) %02X = %02X\n", + CS, cpu_state.pc, port, dev->cur_reg, val); +#endif switch (dev->cur_reg) { case 0x00: - case 0x02: - case 0x03: - case 0x06: - case 0x07: - case 0x21: + case 0x02: case 0x03: + case 0x06: case 0x07: dev->regs[dev->cur_reg] = val; break; + case 0x21: + dev->regs[dev->cur_reg] = val; + fdc_toggle_flag(dev->fdc, FDC_FLAG_PS2_MCA, !!(val & 0x04)); + break; case 0x22: - dev->regs[dev->cur_reg] = val & 0x7f; + dev->regs[dev->cur_reg] = val; + superio_handler(dev); break; case 0x23: - dev->regs[dev->cur_reg] = val & 0x0f; + dev->regs[dev->cur_reg] = (old & 0xf0) | (val & 0x0f); break; case 0x24: dev->pcregs[dev->regs[0x23]] = val; break; default: - if (dev->cur_reg >= 0x30) { - if ((dev->regs[0x07] != 0x06) || !(dev->regs[0x21] & 0x10)) - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val; - } + if (dev->cur_reg >= 0x30) + old = dev->ld_regs[ld][reg]; break; } } switch (dev->cur_reg) { case 0x30: - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0x01; - switch (dev->regs[0x07]) { - case 0x03: + switch (ld) { + default: + break; + case LD_KBD: case LD_MOUSE: + dev->ld_regs[ld][reg] = val; + kbc_handler(dev); + break; + case LD_RTC: + dev->ld_regs[ld][reg] = val; + break; + case LD_FDC: + dev->ld_regs[ld][reg] = val; fdc_handler(dev); break; - case 0x04: + case LD_LPT: + dev->ld_regs[ld][reg] = val; lpt1_handler(dev); break; - case 0x05: + case LD_UART2: + dev->ld_regs[ld][reg] = val; serial_handler(dev, 1); break; - case 0x06: + case LD_UART1: + dev->ld_regs[ld][reg] = val; serial_handler(dev, 0); break; - case 0x07: + case LD_GPIO: + dev->ld_regs[ld][reg] = val; gpio_handler(dev); break; - case 0x08: + case LD_PM: + dev->ld_regs[ld][reg] = val; pm_handler(dev); break; - - default: - break; } break; + /* I/O Range Check. */ + case 0x31: + switch (ld) { + default: + break; + case LD_MIN ... LD_MAX: + if (ld != LD_MOUSE) + dev->ld_regs[ld][reg] = val; + break; + } + break; + /* Base Address 0 MSB. */ case 0x60: - if (dev->regs[0x07] == 0x04) { - val &= 0x03; - } - fallthrough; - case 0x62: - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val; - if ((dev->cur_reg == 0x62) && (dev->regs[0x07] != 0x07)) - break; - switch (dev->regs[0x07]) { - case 0x03: + switch (ld) { + default: + break; + case LD_KBD: + dev->ld_regs[ld][reg] = val; + kbc_handler(dev); + break; + case LD_RTC: + dev->ld_regs[ld][reg] = val; + break; + case LD_FDC: + dev->ld_regs[ld][reg] = val; fdc_handler(dev); break; - case 0x04: + case LD_LPT: + dev->ld_regs[ld][reg] = (old & 0xfc) | (val & 0x03); lpt1_handler(dev); break; - case 0x05: + case LD_UART2: + dev->ld_regs[ld][reg] = val; serial_handler(dev, 1); break; - case 0x06: + case LD_UART1: + dev->ld_regs[ld][reg] = val; serial_handler(dev, 0); break; - case 0x07: + case LD_GPIO: + dev->ld_regs[ld][reg] = val; gpio_handler(dev); break; - case 0x08: + case LD_PM: + dev->ld_regs[ld][reg] = val; pm_handler(dev); break; - - default: - break; } break; + /* Base Address 0 LSB. */ case 0x61: - switch (dev->regs[0x07]) { - case 0x00: - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0xfb; + switch (ld) { + default: break; - case 0x03: - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = (val & 0xfa) | 0x02; + case LD_KBD: + dev->ld_regs[ld][reg] = (old & 0x04) | (val & 0xfb); + kbc_handler(dev); + break; + case LD_RTC: + dev->ld_regs[ld][reg] = (old & 0x01) | (val & 0xfe); + break; + case LD_FDC: + dev->ld_regs[ld][reg] = (old & 0x07) | (val & 0xf8); fdc_handler(dev); break; - case 0x04: - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0xfc; + case LD_LPT: + dev->ld_regs[ld][reg] = (old & 0x03) | (val & 0xfc); lpt1_handler(dev); break; - case 0x05: - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0xf8; + case LD_UART2: + dev->ld_regs[ld][reg] = (old & 0x07) | (val & 0xf8); serial_handler(dev, 1); break; - case 0x06: - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0xf8; + case LD_UART1: + dev->ld_regs[ld][reg] = (old & 0x07) | (val & 0xf8); serial_handler(dev, 0); break; - case 0x07: - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0xf8; + case LD_GPIO: + dev->ld_regs[ld][reg] = (old & 0x07) | (val & 0xf8); gpio_handler(dev); break; - case 0x08: - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0xfe; + case LD_PM: + dev->ld_regs[ld][reg] = (old & 0x01) | (val & 0xfe); pm_handler(dev); break; - - default: - break; } break; + /* Base Address 1 MSB (undocumented for Logical Device 7). */ + case 0x62: + switch (ld) { + default: + break; + case LD_KBD: + dev->ld_regs[ld][reg] = val; + kbc_handler(dev); + break; + case LD_GPIO: + dev->ld_regs[ld][reg] = val; + gpio_handler(dev); + break; + } + break; + /* Base Address 1 LSB (undocumented for Logical Device 7). */ case 0x63: - if (dev->regs[0x07] == 0x00) - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = (val & 0xfb) | 0x04; - else if (dev->regs[0x07] == 0x07) { - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0xfe; - gpio_handler(dev); - } - break; - case 0x70: - case 0x74: - case 0x75: - switch (dev->regs[0x07]) { - case 0x03: - fdc_handler(dev); - break; - case 0x04: - lpt1_handler(dev); - break; - case 0x05: - serial_handler(dev, 1); - break; - case 0x06: - serial_handler(dev, 0); - break; - case 0x07: - gpio_handler(dev); - break; - case 0x08: - pm_handler(dev); - break; - + switch (ld) { default: break; + case LD_KBD: + dev->ld_regs[ld][reg] = (old & 0x04) | (val & 0xfb); + kbc_handler(dev); + break; + case LD_GPIO: + dev->ld_regs[ld][reg] = (old & 0x01) | (val & 0xfe); + gpio_handler(dev); + break; } break; - case 0xf0: - switch (dev->regs[0x07]) { - case 0x00: - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0xc1; + /* Interrupt Select. */ + case 0x70: + switch (ld) { + default: break; - case 0x03: - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0xe1; + case LD_KBD: case LD_MOUSE: + dev->ld_regs[ld][reg] = val; + kbc_handler(dev); + break; + case LD_RTC: + dev->ld_regs[ld][reg] = val; + break; + case LD_FDC: + dev->ld_regs[ld][reg] = val; + fdc_handler(dev); + break; + case LD_LPT: + dev->ld_regs[ld][reg] = val; + lpt1_handler(dev); + break; + case LD_UART2: + dev->ld_regs[ld][reg] = val; + serial_handler(dev, 1); + break; + case LD_UART1: + dev->ld_regs[ld][reg] = val; + serial_handler(dev, 0); + break; + } + break; + /* Interrupt Type. */ + case 0x71: + switch (ld) { + default: + break; + case LD_MIN ... LD_MAX: + if ((ld == LD_KBD) || (ld == LD_MOUSE)) + dev->ld_regs[ld][reg] = (old & 0xfc) | (val & 0x03); + else + dev->ld_regs[ld][reg] = (old & 0xfd) | (val & 0x02); + break; + } + break; + /* DMA Channel Select 0. */ + case 0x74: + switch (ld) { + default: + break; + case LD_FDC: + dev->ld_regs[ld][reg] = val; + fdc_handler(dev); + break; + case LD_LPT: + dev->ld_regs[ld][reg] = val; + lpt1_handler(dev); + break; + case LD_UART2: + dev->ld_regs[ld][reg] = val; + break; + } + break; + /* DMA Channel Select 1. */ + case 0x75: + switch (ld) { + default: + break; + case LD_UART2: + dev->ld_regs[ld][reg] = val; + break; + } + break; + /* Configuration Register 0. */ + case 0xf0: + switch (ld) { + default: + break; + case LD_KBD: + dev->ld_regs[ld][reg] = val; + break; + case LD_FDC: + dev->ld_regs[ld][reg] = val; fdc_update_densel_polarity(dev->fdc, (val & 0x20) ? 1 : 0); fdc_update_enh_mode(dev->fdc, (val & 0x40) ? 1 : 0); break; - case 0x04: - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0xf3; + case LD_LPT: + dev->ld_regs[ld][reg] = val; lpt1_handler(dev); break; - case 0x05: - case 0x06: - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0x87; - break; - - default: + case LD_UART2: case LD_UART1: + dev->ld_regs[ld][reg] = val; break; } break; + /* Configuration Register 1. */ case 0xf1: - if (dev->regs[0x07] == 0x03) - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0x0f; + switch (ld) { + default: + break; + case LD_FDC: + dev->ld_regs[ld][reg] = val; + break; + } break; default: @@ -458,32 +683,46 @@ pc87307_write(uint16_t port, uint8_t val, void *priv) } } -uint8_t +static uint8_t pc87307_read(uint16_t port, void *priv) { - const pc87307_t *dev = (pc87307_t *) priv; - uint8_t ret = 0xff; - uint8_t index; - - index = (port & 1) ? 0 : 1; + const pc87307_t *dev = (pc87307_t *) priv; + uint8_t ld = dev->regs[0x07]; + uint8_t reg = dev->cur_reg - 0x30; + uint8_t index = (port & 1) ? 0 : 1; + uint8_t ret = 0xff; if (index) ret = dev->cur_reg; else { if (dev->cur_reg >= 0x30) - ret = dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30]; + ret = dev->ld_regs[ld][reg]; else if (dev->cur_reg == 0x24) ret = dev->pcregs[dev->regs[0x23]]; + /* Write-only registers. */ + else if ((dev->cur_reg == 0x00) || + (dev->cur_reg == 0x02) || (dev->cur_reg == 0x03)) + ret = 0x00; else ret = dev->regs[dev->cur_reg]; +#ifdef EANBLE_PC87307_LOG + if (dev->cur_reg >= 0x30) + pc87307_log("[%04X:%08X] [R] (%04X) %02X:%02X = %02X\n", + CS, cpu_state.pc, port, ld, dev->cur_reg, ret); + else + pc87307_log("[%04X:%08X] [R] (%04X) %02X = %02X\n", + CS, cpu_state.pc, port, dev->cur_reg, ret); +#endif } return ret; } void -pc87307_reset(pc87307_t *dev) +pc87307_reset(void *priv) { + pc87307_t *dev = (pc87307_t *) priv; + memset(dev->regs, 0x00, 0x30); for (uint16_t i = 0; i < 256; i++) memset(dev->ld_regs[i], 0x00, 0xd0); @@ -493,77 +732,77 @@ pc87307_reset(pc87307_t *dev) dev->regs[0x20] = dev->id; dev->regs[0x21] = 0x04; + dev->regs[0x22] = dev->baddr; - dev->ld_regs[0x00][0x01] = 0x01; - dev->ld_regs[0x00][0x31] = 0x60; - dev->ld_regs[0x00][0x33] = 0x64; - dev->ld_regs[0x00][0x40] = 0x01; - dev->ld_regs[0x00][0x41] = 0x02; - dev->ld_regs[0x00][0x44] = 0x04; - dev->ld_regs[0x00][0x45] = 0x04; - dev->ld_regs[0x00][0xc0] = 0x40; + dev->ld_regs[LD_KBD ][0x00] = 0x01; + dev->ld_regs[LD_KBD ][0x31] = 0x60; + dev->ld_regs[LD_KBD ][0x33] = 0x64; + dev->ld_regs[LD_KBD ][0x40] = 0x01; + dev->ld_regs[LD_KBD ][0x41] = 0x02; + dev->ld_regs[LD_KBD ][0x44] = 0x04; + dev->ld_regs[LD_KBD ][0x45] = 0x04; + dev->ld_regs[LD_KBD ][0xc0] = 0x40; - dev->ld_regs[0x01][0x40] = 0x0c; - dev->ld_regs[0x01][0x41] = 0x02; - dev->ld_regs[0x01][0x44] = 0x04; - dev->ld_regs[0x01][0x45] = 0x04; + dev->ld_regs[LD_MOUSE][0x40] = 0x0c; + dev->ld_regs[LD_MOUSE][0x41] = 0x02; + dev->ld_regs[LD_MOUSE][0x44] = 0x04; + dev->ld_regs[LD_MOUSE][0x45] = 0x04; - dev->ld_regs[0x02][0x00] = 0x01; - dev->ld_regs[0x02][0x31] = 0x70; - dev->ld_regs[0x02][0x40] = 0x08; - dev->ld_regs[0x02][0x44] = 0x04; - dev->ld_regs[0x02][0x45] = 0x04; + dev->ld_regs[LD_RTC ][0x00] = 0x01; + dev->ld_regs[LD_RTC ][0x31] = 0x70; + dev->ld_regs[LD_RTC ][0x40] = 0x08; + dev->ld_regs[LD_RTC ][0x44] = 0x04; + dev->ld_regs[LD_RTC ][0x45] = 0x04; - dev->ld_regs[0x03][0x01] = 0x01; - dev->ld_regs[0x03][0x30] = 0x03; - dev->ld_regs[0x03][0x31] = 0xf2; - dev->ld_regs[0x03][0x40] = 0x06; - dev->ld_regs[0x03][0x41] = 0x03; - dev->ld_regs[0x03][0x44] = 0x02; - dev->ld_regs[0x03][0x45] = 0x04; - dev->ld_regs[0x03][0xc0] = 0x02; + dev->ld_regs[LD_FDC ][0x01] = 0x01; + dev->ld_regs[LD_FDC ][0x30] = 0x03; + dev->ld_regs[LD_FDC ][0x31] = 0xf0; + dev->ld_regs[LD_FDC ][0x32] = 0x03; + dev->ld_regs[LD_FDC ][0x33] = 0xf7; + dev->ld_regs[LD_FDC ][0x40] = 0x06; + dev->ld_regs[LD_FDC ][0x41] = 0x03; + dev->ld_regs[LD_FDC ][0x44] = 0x02; + dev->ld_regs[LD_FDC ][0x45] = 0x04; + dev->ld_regs[LD_FDC ][0xc0] = 0x02; - dev->ld_regs[0x04][0x30] = 0x02; - dev->ld_regs[0x04][0x31] = 0x78; - dev->ld_regs[0x04][0x40] = 0x07; - dev->ld_regs[0x04][0x44] = 0x04; - dev->ld_regs[0x04][0x45] = 0x04; - dev->ld_regs[0x04][0xc0] = 0xf2; + dev->ld_regs[LD_LPT ][0x30] = 0x02; + dev->ld_regs[LD_LPT ][0x31] = 0x78; + dev->ld_regs[LD_LPT ][0x40] = 0x07; + dev->ld_regs[LD_LPT ][0x44] = 0x04; + dev->ld_regs[LD_LPT ][0x45] = 0x04; + dev->ld_regs[LD_LPT ][0xc0] = 0xf2; - dev->ld_regs[0x05][0x30] = 0x02; - dev->ld_regs[0x05][0x31] = 0xf8; - dev->ld_regs[0x05][0x40] = 0x03; - dev->ld_regs[0x05][0x41] = 0x03; - dev->ld_regs[0x05][0x44] = 0x04; - dev->ld_regs[0x05][0x45] = 0x04; - dev->ld_regs[0x05][0xc0] = 0x02; + dev->ld_regs[LD_UART2][0x30] = 0x02; + dev->ld_regs[LD_UART2][0x31] = 0xf8; + dev->ld_regs[LD_UART2][0x40] = 0x03; + dev->ld_regs[LD_UART2][0x41] = 0x03; + dev->ld_regs[LD_UART2][0x44] = 0x04; + dev->ld_regs[LD_UART2][0x45] = 0x04; + dev->ld_regs[LD_UART2][0xc0] = 0x02; - dev->ld_regs[0x06][0x30] = 0x03; - dev->ld_regs[0x06][0x31] = 0xf8; - dev->ld_regs[0x06][0x40] = 0x04; - dev->ld_regs[0x06][0x41] = 0x03; - dev->ld_regs[0x06][0x44] = 0x04; - dev->ld_regs[0x06][0x45] = 0x04; - dev->ld_regs[0x06][0xc0] = 0x02; + dev->ld_regs[LD_UART1][0x30] = 0x03; + dev->ld_regs[LD_UART1][0x31] = 0xf8; + dev->ld_regs[LD_UART1][0x40] = 0x04; + dev->ld_regs[LD_UART1][0x41] = 0x03; + dev->ld_regs[LD_UART1][0x44] = 0x04; + dev->ld_regs[LD_UART1][0x45] = 0x04; + dev->ld_regs[LD_UART1][0xc0] = 0x02; - dev->ld_regs[0x07][0x44] = 0x04; - dev->ld_regs[0x07][0x45] = 0x04; + dev->ld_regs[LD_GPIO ][0x44] = 0x04; + dev->ld_regs[LD_GPIO ][0x45] = 0x04; - dev->ld_regs[0x08][0x44] = 0x04; - dev->ld_regs[0x08][0x45] = 0x04; + dev->ld_regs[LD_PM ][0x44] = 0x04; + dev->ld_regs[LD_PM ][0x45] = 0x04; -#if 0 - dev->gpio[0] = 0xff; - dev->gpio[1] = 0xfb; -#endif dev->gpio[0][0] = 0xff; dev->gpio[0][1] = 0x00; dev->gpio[0][2] = 0x00; dev->gpio[0][3] = 0xff; - dev->gpio[1][0] = 0xff; + dev->gpio[0][4] = 0xff; + dev->gpio[0][5] = 0x00; + dev->gpio[0][6] = 0x00; + dev->gpio[0][7] = 0xff; dev->gpio[1][1] = 0x00; - dev->gpio[1][2] = 0x00; - dev->gpio[1][3] = 0xff; dev->pm[0] = 0xff; dev->pm[1] = 0xff; @@ -576,10 +815,17 @@ pc87307_reset(pc87307_t *dev) 0 = 360 rpm @ 500 kbps for 3.5" 1 = Default, 300 rpm @ 500, 300, 250, 1000 kbps for 3.5" */ - lpt1_remove(); - serial_remove(dev->uart[0]); - serial_remove(dev->uart[1]); + fdc_toggle_flag(dev->fdc, FDC_FLAG_PS2_MCA, 0); fdc_reset(dev->fdc); + + kbc_handler(dev); + fdc_handler(dev); + lpt1_handler(dev); + serial_handler(dev, 0); + serial_handler(dev, 1); + gpio_handler(dev); + pm_handler(dev); + superio_handler(dev); } static void @@ -602,16 +848,26 @@ pc87307_init(const device_t *info) dev->uart[0] = device_add_inst(&ns16550_device, 1); dev->uart[1] = device_add_inst(&ns16550_device, 2); - pc87307_reset(dev); + switch (info->local & PCX730X_KBC) { + default: + case PCX730X_AMI: + dev->kbc = device_add(&keyboard_ps2_intel_ami_pci_device); + break; + /* Optiplex! */ + case PCX730X_PHOENIX_42: + dev->kbc = device_add(&keyboard_ps2_phoenix_device); + break; + case PCX730X_PHOENIX_42I: + dev->kbc = device_add(&keyboard_ps2_phoenix_pci_device); + break; + } - if (info->local & 0x100) { - io_sethandler(0x02e, 0x0002, - pc87307_read, NULL, NULL, pc87307_write, NULL, NULL, dev); - } - if (info->local & 0x200) { - io_sethandler(0x15c, 0x0002, - pc87307_read, NULL, NULL, pc87307_write, NULL, NULL, dev); - } + if (info->local & PCX730X_15C) + dev->baddr = 0x02; + else + dev->baddr = 0x03; + + pc87307_reset(dev); return dev; } @@ -623,7 +879,7 @@ const device_t pc87307_device = { .local = 0x1c0, .init = pc87307_init, .close = pc87307_close, - .reset = NULL, + .reset = pc87307_reset, .available = NULL, .speed_changed = NULL, .force_redraw = NULL, @@ -637,7 +893,7 @@ const device_t pc87307_15c_device = { .local = 0x2c0, .init = pc87307_init, .close = pc87307_close, - .reset = NULL, + .reset = pc87307_reset, .available = NULL, .speed_changed = NULL, .force_redraw = NULL, @@ -651,7 +907,7 @@ const device_t pc87307_both_device = { .local = 0x3c0, .init = pc87307_init, .close = pc87307_close, - .reset = NULL, + .reset = pc87307_reset, .available = NULL, .speed_changed = NULL, .force_redraw = NULL, @@ -665,7 +921,7 @@ const device_t pc97307_device = { .local = 0x1cf, .init = pc87307_init, .close = pc87307_close, - .reset = NULL, + .reset = pc87307_reset, .available = NULL, .speed_changed = NULL, .force_redraw = NULL, diff --git a/src/sio/sio_pc87309.c b/src/sio/sio_pc87309.c index 5e417aefb..d2d3f4ac0 100644 --- a/src/sio/sio_pc87309.c +++ b/src/sio/sio_pc87309.c @@ -8,11 +8,9 @@ * * Emulation of the NatSemi PC87309 Super I/O chip. * - * - * * Authors: Miran Grca, * - * Copyright 2020 Miran Grca. + * Copyright 2020-2025 Miran Grca. */ #include #include @@ -24,44 +22,81 @@ #include <86box/timer.h> #include <86box/device.h> #include <86box/lpt.h> +#include <86box/machine.h> #include <86box/mem.h> #include <86box/nvr.h> #include <86box/pci.h> #include <86box/rom.h> #include <86box/serial.h> -#include <86box/hdc.h> -#include <86box/hdc_ide.h> #include <86box/fdd.h> #include <86box/fdc.h> +#include <86box/keyboard.h> #include <86box/sio.h> +#include <86box/plat_fallthrough.h> +#include "cpu.h" typedef struct pc87309_t { uint8_t id; + uint8_t baddr; uint8_t pm_idx; uint8_t regs[48]; uint8_t ld_regs[256][208]; uint8_t pm[8]; - uint8_t baddr; + uint16_t superio_base; uint16_t pm_base; int cur_reg; + void *kbc; fdc_t *fdc; serial_t *uart[2]; } pc87309_t; +enum { + LD_FDC = 0, + LD_LPT, + LD_UART2, + LD_UART1, + LD_PM, + LD_KBD, + LD_MOUSE +} pc87309_ld_t; + +#define LD_MIN LD_FDC +#define LD_MAX LD_MOUSE + static void fdc_handler(pc87309_t *dev); static void lpt1_handler(pc87309_t *dev); static void serial_handler(pc87309_t *dev, int uart); - +static void kbc_handler(pc87309_t *dev); static void pc87309_write(uint16_t port, uint8_t val, void *priv); static uint8_t pc87309_read(uint16_t port, void *priv); +#ifdef ENABLE_PC87309_LOG +int pc87309_do_log = ENABLE_PC87309_LOG; + +static void +pc87309_log(const char *fmt, ...) +{ + va_list ap; + + if (pc87309_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +# define pc87309_log(fmt, ...) +#endif + static void pc87309_pm_write(uint16_t port, uint8_t val, void *priv) { pc87309_t *dev = (pc87309_t *) priv; - if (port & 1) { + if (port & 1) dev->pm[dev->pm_idx] = val; + else { + dev->pm_idx = val & 0x07; switch (dev->pm_idx) { case 0x00: @@ -74,8 +109,7 @@ pc87309_pm_write(uint16_t port, uint8_t val, void *priv) default: break; } - } else - dev->pm_idx = val & 0x07; + } } uint8_t @@ -109,43 +143,48 @@ pc87309_pm_init(pc87309_t *dev, uint16_t addr) } static void -superio_handler(pc87309_t *dev) +kbc_handler(pc87309_t *dev) { - io_removehandler(0x15c, 0x0002, - pc87309_read, NULL, NULL, pc87309_write, NULL, NULL, dev); - io_removehandler(0x02e, 0x0002, - pc87309_read, NULL, NULL, pc87309_write, NULL, NULL, dev); + uint8_t active = (dev->ld_regs[LD_KBD][0x00] & 0x01) && + (dev->pm[0x00] & 0x01); + uint8_t active_2 = dev->ld_regs[LD_MOUSE][0x00] & 0x01; + uint8_t irq = (dev->ld_regs[LD_KBD][0x40] & 0x0f); + uint8_t irq_2 = (dev->ld_regs[LD_MOUSE][0x40] & 0x0f); + uint16_t addr = (dev->ld_regs[LD_KBD][0x30] << 8) | + dev->ld_regs[LD_KBD][0x31]; + uint16_t addr_2 = (dev->ld_regs[LD_KBD][0x32] << 8) | + dev->ld_regs[LD_KBD][0x33]; - switch (dev->regs[0x21] & 0x0b) { - case 0x02: - case 0x08: - case 0x0a: - io_sethandler(0x15c, 0x0002, - pc87309_read, NULL, NULL, pc87309_write, NULL, NULL, dev); - break; - case 0x03: - case 0x09: - case 0x0b: - io_sethandler(0x02e, 0x0002, - pc87309_read, NULL, NULL, pc87309_write, NULL, NULL, dev); - break; + pc87309_log("%02X, %02X, %02X, %02X, %04X, %04X\n", + active, active_2, irq, irq_2, addr, addr_2); + + if (addr <= 0xfff8) { + pc87309_log("Enabling KBC #1 on %04X...\n", addr); + kbc_at_port_handler(0, active, addr, dev->kbc); } + + if (addr_2 <= 0xfff8) { + pc87309_log("Enabling KBC #2 on %04X...\n", addr_2); + kbc_at_port_handler(1, active, addr_2, dev->kbc); + } + + kbc_at_set_irq(0, active ? irq : 0xffff, dev->kbc); + kbc_at_set_irq(1, (active && active_2) ? irq_2 : 0xffff, dev->kbc); } static void fdc_handler(pc87309_t *dev) { - uint8_t irq; - uint8_t active; - uint16_t addr; - fdc_remove(dev->fdc); - active = (dev->ld_regs[0x00][0x00] & 0x01) && (dev->pm[0x00] & 0x08); - addr = ((dev->ld_regs[0x00][0x30] << 8) | dev->ld_regs[0x00][0x31]) - 0x0002; - irq = (dev->ld_regs[0x00][0x40] & 0x0f); + uint8_t active = (dev->ld_regs[LD_FDC][0x00] & 0x01) && + (dev->pm[0x00] & 0x08); + uint8_t irq = (dev->ld_regs[LD_FDC][0x40] & 0x0f); + uint16_t addr = ((dev->ld_regs[LD_FDC][0x30] << 8) | + dev->ld_regs[LD_FDC][0x31]) & 0xfff8; - if (active) { + if (active && (addr <= 0xfff8)) { + pc87309_log("Enabling FDC on %04X, IRQ %i...\n", addr, irq); fdc_set_base(dev->fdc, addr); fdc_set_irq(dev->fdc, irq); } @@ -154,222 +193,351 @@ fdc_handler(pc87309_t *dev) static void lpt1_handler(pc87309_t *dev) { - uint8_t irq; - uint8_t active; - uint16_t addr; + uint8_t active = (dev->ld_regs[LD_LPT][0x00] & 0x01) && + (dev->pm[0x00] & 0x10); + uint8_t irq = (dev->ld_regs[LD_LPT][0x40] & 0x0f); + uint16_t addr = (dev->ld_regs[LD_LPT][0x30] << 8) | + dev->ld_regs[LD_LPT][0x31]; - lpt1_remove(); - - active = (dev->ld_regs[0x01][0x00] & 0x01) && (dev->pm[0x00] & 0x10); - addr = (dev->ld_regs[0x01][0x30] << 8) | dev->ld_regs[0x01][0x31]; - irq = (dev->ld_regs[0x01][0x40] & 0x0f); - - if (active) { + if (active && (addr <= 0xfffc)) { + pc87309_log("Enabling LPT1 on %04X...\n", addr); lpt1_setup(addr); - lpt1_irq(irq); - } + } else + lpt1_setup(0xffff); + + lpt1_irq(irq); } static void serial_handler(pc87309_t *dev, int uart) { - uint8_t irq; - uint8_t active; - uint16_t addr; - serial_remove(dev->uart[uart]); - active = (dev->ld_regs[0x03 - uart][0x00] & 0x01) && (dev->pm[0x00] & (1 << (6 - uart))); - addr = (dev->ld_regs[0x03 - uart][0x30] << 8) | dev->ld_regs[0x03 - uart][0x31]; - irq = (dev->ld_regs[0x03 - uart][0x40] & 0x0f); + uint8_t active = (dev->ld_regs[LD_UART1 - uart][0x00] & 0x01) && + (dev->pm[0x00] & (1 << (6 - uart))); + uint8_t irq = (dev->ld_regs[LD_UART1 - uart][0x40] & 0x0f); + uint16_t addr = (dev->ld_regs[LD_UART1 - uart][0x30] << 8) | + dev->ld_regs[LD_UART1 - uart][0x31]; - if (active) + if (active && (addr <= 0xfff8)) { + pc87309_log("Enabling COM%i on %04X...\n", uart + 1, addr); serial_setup(dev->uart[uart], addr, irq); + } else + serial_setup(dev->uart[uart], 0x0000, irq); } static void pm_handler(pc87309_t *dev) { - uint8_t active; - uint16_t addr; - pc87309_pm_remove(dev); - active = (dev->ld_regs[0x04][0x00] & 0x01); - addr = (dev->ld_regs[0x04][0x30] << 8) | dev->ld_regs[0x04][0x31]; + uint8_t active = (dev->ld_regs[LD_PM][0x00] & 0x01); + uint16_t addr = (dev->ld_regs[LD_PM][0x30] << 8) | + dev->ld_regs[LD_PM][0x31]; - if (active) + if (active) { + pc87309_log("Enabling power management on %04X...\n", addr); pc87309_pm_init(dev, addr); + } +} + +static void +superio_handler(pc87309_t *dev) +{ + if (dev->superio_base != 0x0000) + io_removehandler(dev->superio_base, 0x0002, + pc87309_read, NULL, NULL, + pc87309_write, NULL, NULL, dev); + + switch (dev->regs[0x22] & 0x0b) { + default: + dev->superio_base = 0x0000; + break; + case 0x02: + case 0x08: case 0x0a: + dev->superio_base = 0x015c; + break; + case 0x03: + case 0x09: case 0x0b: + dev->superio_base = 0x002e; + break; + } + + if (dev->superio_base != 0x0000) { + pc87309_log("Enabling Super I/O on %04X...\n", dev->superio_base); + io_sethandler(dev->superio_base, 0x0002, + pc87309_read, NULL, NULL, + pc87309_write, NULL, NULL, dev); + } } static void pc87309_write(uint16_t port, uint8_t val, void *priv) { - pc87309_t *dev = (pc87309_t *) priv; - uint8_t index; - - index = (port & 1) ? 0 : 1; + pc87309_t *dev = (pc87309_t *) priv; + uint8_t ld = dev->regs[0x07]; + uint8_t reg = dev->cur_reg - 0x30; + uint8_t index = (port & 1) ? 0 : 1; + uint8_t old = dev->regs[dev->cur_reg]; if (index) { dev->cur_reg = val; return; } else { +#ifdef ENABLE_PC87309_LOG + if (dev->cur_reg >= 0x30) + pc87309_log("[%04X:%08X] [W] (%04X) %02X:%02X = %02X\n", + CS, cpu_state.pc, port, ld, dev->cur_reg, val); + else + pc87309_log("[%04X:%08X] [W] (%04X) %02X = %02X\n", + CS, cpu_state.pc, port, dev->cur_reg, val); +#endif switch (dev->cur_reg) { case 0x00: - case 0x02: - case 0x03: - case 0x06: - case 0x07: + case 0x02: case 0x03: + case 0x06: case 0x07: + dev->regs[dev->cur_reg] = val; + break; case 0x21: dev->regs[dev->cur_reg] = val; + fdc_toggle_flag(dev->fdc, FDC_FLAG_PS2_MCA, !!(val & 0x04)); superio_handler(dev); break; case 0x22: - dev->regs[dev->cur_reg] = val & 0x7f; + dev->regs[dev->cur_reg] = val; break; default: - if (dev->cur_reg >= 0x30) { - if ((dev->regs[0x07] != 0x06) || !(dev->regs[0x21] & 0x10)) - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val; - } + if (dev->cur_reg >= 0x30) + old = dev->ld_regs[ld][reg]; break; } } switch (dev->cur_reg) { case 0x30: - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0x01; - switch (dev->regs[0x07]) { - case 0x00: + switch (ld) { + default: + break; + case LD_KBD: case LD_MOUSE: + dev->ld_regs[ld][reg] = val; + kbc_handler(dev); + break; + case LD_FDC: + dev->ld_regs[ld][reg] = val; fdc_handler(dev); break; - case 0x01: + case LD_LPT: + dev->ld_regs[ld][reg] = val; lpt1_handler(dev); break; - case 0x02: + case LD_UART2: + dev->ld_regs[ld][reg] = val; serial_handler(dev, 1); break; - case 0x03: + case LD_UART1: + dev->ld_regs[ld][reg] = val; serial_handler(dev, 0); break; - case 0x04: + case LD_PM: + dev->ld_regs[ld][reg] = val; pm_handler(dev); break; - - default: - break; } break; + /* I/O Range Check. */ + case 0x31: + switch (ld) { + default: + break; + case LD_MIN ... LD_MAX: + if (ld != LD_MOUSE) + dev->ld_regs[ld][reg] = val; + break; + } + break; + /* Base Address 0 MSB. */ case 0x60: - case 0x62: - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0x07; - if (dev->cur_reg == 0x62) - break; - switch (dev->regs[0x07]) { - case 0x00: + switch (ld) { + default: + break; + case LD_KBD: + dev->ld_regs[ld][reg] = val; + kbc_handler(dev); + break; + case LD_FDC: + dev->ld_regs[ld][reg] = val; fdc_handler(dev); break; - case 0x01: + case LD_LPT: + dev->ld_regs[ld][reg] = (old & 0xfc) | (val & 0x03); lpt1_handler(dev); break; - case 0x02: + case LD_UART2: + dev->ld_regs[ld][reg] = val; serial_handler(dev, 1); break; - case 0x03: + case LD_UART1: + dev->ld_regs[ld][reg] = val; serial_handler(dev, 0); break; - case 0x04: + case LD_PM: + dev->ld_regs[ld][reg] = val; pm_handler(dev); break; - - default: - break; } break; - case 0x63: - if (dev->regs[0x07] == 0x06) - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = (val & 0xf8) | 0x04; - break; + /* Base Address 0 LSB. */ case 0x61: - switch (dev->regs[0x07]) { - case 0x00: - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = (val & 0xfa) | 0x02; + switch (ld) { + default: + break; + case LD_KBD: + dev->ld_regs[ld][reg] = (old & 0x04) | (val & 0xfb); + kbc_handler(dev); + break; + case LD_FDC: + dev->ld_regs[ld][reg] = (old & 0x07) | (val & 0xf8); fdc_handler(dev); break; - case 0x01: - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0xfc; + case LD_LPT: + dev->ld_regs[ld][reg] = (old & 0x03) | (val & 0xfc); lpt1_handler(dev); break; - case 0x02: - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0xf8; + case LD_UART2: + dev->ld_regs[ld][reg] = (old & 0x07) | (val & 0xf8); serial_handler(dev, 1); break; - case 0x03: - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0xf8; + case LD_UART1: + dev->ld_regs[ld][reg] = (old & 0x07) | (val & 0xf8); serial_handler(dev, 0); break; - case 0x04: - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0xfe; + case LD_PM: + dev->ld_regs[ld][reg] = (old & 0x01) | (val & 0xfe); pm_handler(dev); break; - case 0x06: - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0xf8; - break; - - default: - break; } break; + /* Base Address 1 MSB (undocumented for Logical Device 7). */ + case 0x62: + switch (ld) { + default: + break; + case LD_KBD: + dev->ld_regs[ld][reg] = val; + kbc_handler(dev); + break; + } + break; + /* Base Address 1 LSB (undocumented for Logical Device 7). */ + case 0x63: + switch (ld) { + default: + break; + case LD_KBD: + dev->ld_regs[ld][reg] = (old & 0x04) | (val & 0xfb); + kbc_handler(dev); + break; + } + break; + /* Interrupt Select. */ case 0x70: - case 0x74: - case 0x75: - switch (dev->regs[0x07]) { - case 0x00: + switch (ld) { + default: + break; + case LD_KBD: case LD_MOUSE: + dev->ld_regs[ld][reg] = val; + kbc_handler(dev); + break; + case LD_FDC: + dev->ld_regs[ld][reg] = val; fdc_handler(dev); break; - case 0x01: + case LD_LPT: + dev->ld_regs[ld][reg] = val; lpt1_handler(dev); break; - case 0x02: + case LD_UART2: + dev->ld_regs[ld][reg] = val; serial_handler(dev, 1); break; - case 0x03: + case LD_UART1: + dev->ld_regs[ld][reg] = val; serial_handler(dev, 0); break; - case 0x04: - pm_handler(dev); - break; - - default: - break; } break; + /* Interrupt Type. */ + case 0x71: + switch (ld) { + default: + break; + case LD_MIN ... LD_MAX: + if ((ld == LD_KBD) || (ld == LD_MOUSE)) + dev->ld_regs[ld][reg] = (old & 0xfc) | (val & 0x03); + else + dev->ld_regs[ld][reg] = (old & 0xfd) | (val & 0x02); + break; + } + break; + /* DMA Channel Select 0. */ + case 0x74: + switch (ld) { + default: + break; + case LD_FDC: + dev->ld_regs[ld][reg] = val; + fdc_handler(dev); + break; + case LD_LPT: + dev->ld_regs[ld][reg] = val; + lpt1_handler(dev); + break; + case LD_UART2: + dev->ld_regs[ld][reg] = val; + break; + } + break; + /* DMA Channel Select 1. */ + case 0x75: + switch (ld) { + default: + break; + case LD_UART2: + dev->ld_regs[ld][reg] = val; + break; + } + break; + /* Configuration Register 0. */ case 0xf0: - switch (dev->regs[0x07]) { - case 0x00: - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0xe1; + switch (ld) { + default: + break; + case LD_KBD: + dev->ld_regs[ld][reg] = val; + break; + case LD_FDC: + dev->ld_regs[ld][reg] = val; fdc_update_densel_polarity(dev->fdc, (val & 0x20) ? 1 : 0); fdc_update_enh_mode(dev->fdc, (val & 0x40) ? 1 : 0); break; - case 0x01: - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0xf3; + case LD_LPT: + dev->ld_regs[ld][reg] = val; lpt1_handler(dev); break; - case 0x02: - case 0x03: - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0x87; - break; - case 0x06: - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0xc1; - break; - - default: + case LD_UART2: case LD_UART1: + dev->ld_regs[ld][reg] = val; break; } break; + /* Configuration Register 1. */ case 0xf1: - if (dev->regs[0x07] == 0x00) - dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30] = val & 0x0f; + switch (ld) { + default: + break; + case LD_FDC: + dev->ld_regs[ld][reg] = val; + break; + } break; default: @@ -377,30 +545,44 @@ pc87309_write(uint16_t port, uint8_t val, void *priv) } } -uint8_t +static uint8_t pc87309_read(uint16_t port, void *priv) { - const pc87309_t *dev = (pc87309_t *) priv; - uint8_t ret = 0xff; - uint8_t index; - - index = (port & 1) ? 0 : 1; + const pc87309_t *dev = (pc87309_t *) priv; + uint8_t ld = dev->regs[0x07]; + uint8_t reg = dev->cur_reg - 0x30; + uint8_t index = (port & 1) ? 0 : 1; + uint8_t ret = 0xff; if (index) - ret = dev->cur_reg & 0x1f; + ret = dev->cur_reg; else { if (dev->cur_reg >= 0x30) - ret = dev->ld_regs[dev->regs[0x07]][dev->cur_reg - 0x30]; + ret = dev->ld_regs[ld][reg]; + /* Write-only registers. */ + else if ((dev->cur_reg == 0x00) || + (dev->cur_reg == 0x02) || (dev->cur_reg == 0x03)) + ret = 0x00; else ret = dev->regs[dev->cur_reg]; +#ifdef ENABLE_PC87309_LOG + if (dev->cur_reg >= 0x30) + pc87309_log("[%04X:%08X] [R] (%04X) %02X:%02X = %02X\n", + CS, cpu_state.pc, port, ld, dev->cur_reg, ret); + else + pc87309_log("[%04X:%08X] [R] (%04X) %02X = %02X\n", + CS, cpu_state.pc, port, dev->cur_reg, ret); +#endif } return ret; } void -pc87309_reset(pc87309_t *dev) +pc87309_reset(void *priv) { + pc87309_t *dev = (pc87309_t *) priv; + memset(dev->regs, 0x00, 0x30); for (uint16_t i = 0; i < 256; i++) memset(dev->ld_regs[i], 0x00, 0xd0); @@ -409,65 +591,56 @@ pc87309_reset(pc87309_t *dev) dev->regs[0x20] = dev->id; dev->regs[0x21] = 0x04 | dev->baddr; - dev->ld_regs[0x00][0x01] = 0x01; - dev->ld_regs[0x00][0x30] = 0x03; - dev->ld_regs[0x00][0x31] = 0xf2; - dev->ld_regs[0x00][0x40] = 0x06; - dev->ld_regs[0x00][0x41] = 0x03; - dev->ld_regs[0x00][0x44] = 0x02; - dev->ld_regs[0x00][0x45] = 0x04; - dev->ld_regs[0x00][0xc0] = 0x02; + dev->ld_regs[LD_KBD ][0x00] = 0x01; + dev->ld_regs[LD_KBD ][0x31] = 0x60; + dev->ld_regs[LD_KBD ][0x33] = 0x64; + dev->ld_regs[LD_KBD ][0x40] = 0x01; + dev->ld_regs[LD_KBD ][0x41] = 0x02; + dev->ld_regs[LD_KBD ][0x44] = 0x04; + dev->ld_regs[LD_KBD ][0x45] = 0x04; + dev->ld_regs[LD_KBD ][0xc0] = 0x40; - dev->ld_regs[0x01][0x30] = 0x02; - dev->ld_regs[0x01][0x31] = 0x78; - dev->ld_regs[0x01][0x40] = 0x07; - dev->ld_regs[0x01][0x44] = 0x04; - dev->ld_regs[0x01][0x45] = 0x04; - dev->ld_regs[0x01][0xc0] = 0xf2; + dev->ld_regs[LD_MOUSE][0x40] = 0x0c; + dev->ld_regs[LD_MOUSE][0x41] = 0x02; + dev->ld_regs[LD_MOUSE][0x44] = 0x04; + dev->ld_regs[LD_MOUSE][0x45] = 0x04; - dev->ld_regs[0x02][0x30] = 0x02; - dev->ld_regs[0x02][0x31] = 0xf8; - dev->ld_regs[0x02][0x40] = 0x03; - dev->ld_regs[0x02][0x41] = 0x03; - dev->ld_regs[0x02][0x44] = 0x04; - dev->ld_regs[0x02][0x45] = 0x04; - dev->ld_regs[0x02][0xc0] = 0x02; + dev->ld_regs[LD_FDC ][0x01] = 0x01; + dev->ld_regs[LD_FDC ][0x30] = 0x03; + dev->ld_regs[LD_FDC ][0x31] = 0xf0; + dev->ld_regs[LD_FDC ][0x32] = 0x03; + dev->ld_regs[LD_FDC ][0x33] = 0xf7; + dev->ld_regs[LD_FDC ][0x40] = 0x06; + dev->ld_regs[LD_FDC ][0x41] = 0x03; + dev->ld_regs[LD_FDC ][0x44] = 0x02; + dev->ld_regs[LD_FDC ][0x45] = 0x04; + dev->ld_regs[LD_FDC ][0xc0] = 0x02; - dev->ld_regs[0x03][0x30] = 0x03; - dev->ld_regs[0x03][0x31] = 0xf8; - dev->ld_regs[0x03][0x40] = 0x04; - dev->ld_regs[0x03][0x41] = 0x03; - dev->ld_regs[0x03][0x44] = 0x04; - dev->ld_regs[0x03][0x45] = 0x04; - dev->ld_regs[0x03][0xc0] = 0x02; + dev->ld_regs[LD_LPT ][0x30] = 0x02; + dev->ld_regs[LD_LPT ][0x31] = 0x78; + dev->ld_regs[LD_LPT ][0x40] = 0x07; + dev->ld_regs[LD_LPT ][0x44] = 0x04; + dev->ld_regs[LD_LPT ][0x45] = 0x04; + dev->ld_regs[LD_LPT ][0xc0] = 0xf2; - dev->ld_regs[0x04][0x44] = 0x04; - dev->ld_regs[0x04][0x45] = 0x04; + dev->ld_regs[LD_UART2][0x30] = 0x02; + dev->ld_regs[LD_UART2][0x31] = 0xf8; + dev->ld_regs[LD_UART2][0x40] = 0x03; + dev->ld_regs[LD_UART2][0x41] = 0x03; + dev->ld_regs[LD_UART2][0x44] = 0x04; + dev->ld_regs[LD_UART2][0x45] = 0x04; + dev->ld_regs[LD_UART2][0xc0] = 0x02; - dev->ld_regs[0x05][0x40] = 0x0c; - dev->ld_regs[0x05][0x41] = 0x02; - dev->ld_regs[0x05][0x44] = 0x04; - dev->ld_regs[0x05][0x45] = 0x04; + dev->ld_regs[LD_UART1][0x30] = 0x03; + dev->ld_regs[LD_UART1][0x31] = 0xf8; + dev->ld_regs[LD_UART1][0x40] = 0x04; + dev->ld_regs[LD_UART1][0x41] = 0x03; + dev->ld_regs[LD_UART1][0x44] = 0x04; + dev->ld_regs[LD_UART1][0x45] = 0x04; + dev->ld_regs[LD_UART1][0xc0] = 0x02; - dev->ld_regs[0x06][0x01] = 0x01; - dev->ld_regs[0x06][0x31] = 0x60; - dev->ld_regs[0x06][0x33] = 0x64; - dev->ld_regs[0x06][0x40] = 0x01; - dev->ld_regs[0x06][0x41] = 0x02; - dev->ld_regs[0x06][0x44] = 0x04; - dev->ld_regs[0x06][0x45] = 0x04; - dev->ld_regs[0x06][0xc0] = 0x40; - - dev->regs[0x00] = 0x0B; - dev->regs[0x01] = 0x01; - dev->regs[0x03] = 0x01; - dev->regs[0x05] = 0x0D; - dev->regs[0x08] = 0x70; - dev->regs[0x09] = 0xC0; - dev->regs[0x0b] = 0x80; - dev->regs[0x0f] = 0x1E; - dev->regs[0x12] = 0x30; - dev->regs[0x19] = 0xEF; + dev->ld_regs[LD_PM ][0x44] = 0x04; + dev->ld_regs[LD_PM ][0x45] = 0x04; dev->pm[0] = 0x79; dev->pm[4] = 0x0e; @@ -478,11 +651,15 @@ pc87309_reset(pc87309_t *dev) 0 = 360 rpm @ 500 kbps for 3.5" 1 = Default, 300 rpm @ 500, 300, 250, 1000 kbps for 3.5" */ - lpt1_remove(); - serial_remove(dev->uart[0]); - serial_remove(dev->uart[1]); + fdc_toggle_flag(dev->fdc, FDC_FLAG_PS2_MCA, 0); fdc_reset(dev->fdc); + kbc_handler(dev); + fdc_handler(dev); + lpt1_handler(dev); + serial_handler(dev, 0); + serial_handler(dev, 1); + pm_handler(dev); superio_handler(dev); } @@ -503,7 +680,27 @@ pc87309_init(const device_t *info) dev->fdc = device_add(&fdc_at_nsc_device); - dev->baddr = (info->local & 0x100) ? 8 : 9; + dev->uart[0] = device_add_inst(&ns16550_device, 1); + dev->uart[1] = device_add_inst(&ns16550_device, 2); + + switch (info->local & PCX730X_KBC) { + default: + case PCX730X_AMI: + dev->kbc = device_add(&keyboard_ps2_intel_ami_pci_device); + break; + /* Optiplex! */ + case PCX730X_PHOENIX_42: + dev->kbc = device_add(&keyboard_ps2_phoenix_device); + break; + case PCX730X_PHOENIX_42I: + dev->kbc = device_add(&keyboard_ps2_phoenix_pci_device); + break; + } + + if (info->local & PCX730X_15C) + dev->baddr = 0x0a; + else + dev->baddr = 0x0b; pc87309_reset(dev); @@ -514,24 +711,10 @@ const device_t pc87309_device = { .name = "National Semiconductor PC87309 Super I/O", .internal_name = "pc87309", .flags = 0, - .local = 0xe0, + .local = 0, .init = pc87309_init, .close = pc87309_close, - .reset = NULL, - .available = NULL, - .speed_changed = NULL, - .force_redraw = NULL, - .config = NULL -}; - -const device_t pc87309_15c_device = { - .name = "National Semiconductor PC87309 Super I/O (Port 15Ch)", - .internal_name = "pc87309_15c", - .flags = 0, - .local = 0x1e0, - .init = pc87309_init, - .close = pc87309_close, - .reset = NULL, + .reset = pc87309_reset, .available = NULL, .speed_changed = NULL, .force_redraw = NULL,