diff --git a/src/sio/sio_f82c710.c b/src/sio/sio_f82c710.c index 9d52e399b..6b866c094 100644 --- a/src/sio/sio_f82c710.c +++ b/src/sio/sio_f82c710.c @@ -43,306 +43,318 @@ #include <86box/nvr.h> #include <86box/sio.h> + typedef struct upc_t { - uint32_t local; - int configuration_state; /* state of algorithm to enter configuration mode */ - int configuration_mode; - uint16_t cri_addr; /* cri = configuration index register, addr is even */ - uint16_t cap_addr; /* cap = configuration access port, addr is odd and is cri_addr + 1 */ - uint8_t cri; /* currently indexed register */ + uint32_t local; + int configuration_state; /* state of algorithm to enter configuration mode */ + int configuration_mode; + uint16_t cri_addr; /* cri = configuration index register, addr is even */ + uint16_t cap_addr; /* cap = configuration access port, addr is odd and is cri_addr + 1 */ + uint8_t cri; /* currently indexed register */ + uint8_t last_write; - /* these regs are not affected by reset */ - uint8_t regs[15]; /* there are 16 indexes, but there is no need to store the last one which is: R = cri_addr / 4, W = exit config mode */ - fdc_t *fdc; - nvr_t *nvr; - void *gameport; - serial_t *uart[2]; + /* these regs are not affected by reset */ + uint8_t regs[15]; /* there are 16 indexes, but there is no need to store the last one which is: R = cri_addr / 4, W = exit config mode */ + fdc_t * fdc; + nvr_t * nvr; + void * gameport; + serial_t * uart[2]; } upc_t; + static void -f82c710_update_ports(upc_t *upc) +f82c710_update_ports(upc_t *dev, int set) { - uint16_t com_addr = 0; - uint16_t lpt_addr = 0; - - serial_remove(upc->uart[0]); - serial_remove(upc->uart[1]); - lpt1_remove(); - lpt2_remove(); - fdc_remove(upc->fdc); - ide_pri_disable(); + uint16_t com_addr = 0; + uint16_t lpt_addr = 0; - if (upc->regs[0] & 4) { - com_addr = upc->regs[4] * 4; - if (com_addr == SERIAL1_ADDR) { - serial_setup(upc->uart[0], com_addr, 4); - } else if (com_addr == SERIAL2_ADDR) { - serial_setup(upc->uart[1], com_addr, 3); - } - } - + serial_remove(dev->uart[0]); + serial_remove(dev->uart[1]); + lpt1_remove(); + lpt2_remove(); + fdc_remove(dev->fdc); + ide_pri_disable(); - if (upc->regs[0] & 8) { - lpt_addr = upc->regs[6] * 4; - lpt1_init(lpt_addr); - if ((lpt_addr == 0x378) || (lpt_addr == 0x3bc)) { - lpt1_irq(7); - } else if (lpt_addr == 0x278) { - lpt1_irq(5); - } - } + if (!set) + return; - if (upc->regs[12] & 0x80) { - ide_pri_enable(); - } + if (dev->regs[0] & 4) { + com_addr = dev->regs[4] * 4; + if (com_addr == SERIAL1_ADDR) + serial_setup(dev->uart[0], com_addr, 4); + else if (com_addr == SERIAL2_ADDR) + serial_setup(dev->uart[1], com_addr, 3); + } - if (upc->regs[12] & 0x20) { - fdc_set_base(upc->fdc, 0x03f0); - } + if (dev->regs[0] & 8) { + lpt_addr = dev->regs[6] * 4; + lpt1_init(lpt_addr); + if ((lpt_addr == 0x378) || (lpt_addr == 0x3bc)) + lpt1_irq(7); + else if (lpt_addr == 0x278) + lpt1_irq(5); + } + + if (dev->regs[12] & 0x80) + ide_pri_enable(); + + if (dev->regs[12] & 0x20) + fdc_set_base(dev->fdc, 0x03f0); } + static void -f82c606_update_ports(upc_t *upc) +f82c606_update_ports(upc_t *dev, int set) { - uint8_t uart1_int = 0xff; - uint8_t uart2_int = 0xff; - uint8_t lpt1_int = 0xff; - int nvr_int = -1; + uint8_t uart1_int = 0xff; + uint8_t uart2_int = 0xff; + uint8_t lpt1_int = 0xff; + int nvr_int = -1; - serial_remove(upc->uart[0]); - serial_remove(upc->uart[1]); - lpt1_remove(); - lpt2_remove(); + serial_remove(dev->uart[0]); + serial_remove(dev->uart[1]); + lpt1_remove(); + lpt2_remove(); - nvr_at_handler(0, upc->regs[3] * 4, upc->nvr); - nvr_at_handler(0, 0x70, upc->nvr); + nvr_at_handler(0, ((uint16_t) dev->regs[3]) << 2, dev->nvr); + nvr_at_handler(0, 0x70, dev->nvr); - switch (upc->regs[8] & 0xc0) { - case 0x40: nvr_int = 4; break; - case 0x80: uart1_int = 4; break; - case 0xc0: uart2_int = 4; break; - } + gameport_remap(dev->gameport, 0); - switch (upc->regs[8] & 0x30) { - case 0x10: nvr_int = 3; break; - case 0x20: uart1_int = 3; break; - case 0x30: uart2_int = 3; break; - } + if (!set) + return; - switch (upc->regs[8] & 0x0c) { + switch (dev->regs[8] & 0xc0) { + case 0x40: nvr_int = 3; break; + case 0x80: uart1_int = 3; break; + case 0xc0: uart2_int = 3; break; + } + + switch (dev->regs[8] & 0x30) { + case 0x10: nvr_int = 4; break; + case 0x20: uart1_int = 4; break; + case 0x30: uart2_int = 4; break; + } + + switch (dev->regs[8] & 0x0c) { case 0x04: nvr_int = 5; break; case 0x08: uart1_int = 5; break; case 0x0c: lpt1_int = 5; break; - } + } - switch (upc->regs[8] & 0x03) { + switch (dev->regs[8] & 0x03) { case 0x01: nvr_int = 7; break; case 0x02: uart2_int = 7; break; case 0x03: lpt1_int = 7; break; - } + } - if (upc->regs[0] & 1) - gameport_remap(upc->gameport, upc->regs[7] * 4); - else - gameport_remap(upc->gameport, 0); + if (dev->regs[0] & 1) { + gameport_remap(dev->gameport, ((uint16_t) dev->regs[7]) << 2); + pclog("Game port at %04X\n", ((uint16_t) dev->regs[7]) << 2); + } - if (upc->regs[0] & 2) - serial_setup(upc->uart[0], upc->regs[4] * 4, uart1_int); + if (dev->regs[0] & 2) { + serial_setup(dev->uart[0], ((uint16_t) dev->regs[4]) << 2, uart1_int); + pclog("UART 1 at %04X, IRQ %i\n", ((uint16_t) dev->regs[4]) << 2, uart1_int); + } - if (upc->regs[0] & 4) - serial_setup(upc->uart[1], upc->regs[5] * 4, uart2_int); + if (dev->regs[0] & 4) { + serial_setup(dev->uart[1], ((uint16_t) dev->regs[5]) << 2, uart2_int); + pclog("UART 2 at %04X, IRQ %i\n", ((uint16_t) dev->regs[5]) << 2, uart2_int); + } - if (upc->regs[0] & 8) { - lpt1_init(upc->regs[6] * 4); - lpt1_irq(lpt1_int); - } + if (dev->regs[0] & 8) { + lpt1_init(((uint16_t) dev->regs[6]) << 2); + lpt1_irq(lpt1_int); + pclog("LPT1 at %04X, IRQ %i\n", ((uint16_t) dev->regs[6]) << 2, lpt1_int); + } - nvr_at_handler(1, upc->regs[3] * 4, upc->nvr); - nvr_irq_set(nvr_int, upc->nvr); + nvr_at_handler(1, ((uint16_t) dev->regs[3]) << 2, dev->nvr); + nvr_irq_set(nvr_int, dev->nvr); + pclog("RTC at %04X, IRQ %i\n", ((uint16_t) dev->regs[3]) << 2, nvr_int); } + static uint8_t f82c710_config_read(uint16_t port, void *priv) { - upc_t *upc = (upc_t *)priv; - uint8_t temp = 0xff; + upc_t *dev = (upc_t *) priv; + uint8_t temp = 0xff; - if (upc->configuration_mode) { - if (port == upc->cri_addr) { - temp = upc->cri; - } else if (port == upc->cap_addr) { - if (upc->cri == 0xf) - temp = upc->cri_addr / 4; - else - temp = upc->regs[upc->cri]; - } - } + if (dev->configuration_mode) { + if (port == dev->cri_addr) { + temp = dev->cri; + } else if (port == dev->cap_addr) { + if (dev->cri == 0xf) + temp = dev->cri_addr / 4; + else + temp = dev->regs[dev->cri]; + } + } - return temp; + return temp; } + static void f82c710_config_write(uint16_t port, uint8_t val, void *priv) { - upc_t *upc = (upc_t *)priv; - int configuration_state_event = 0; + upc_t *dev = (upc_t *) priv; + int configuration_state_event = 0; - switch(port) { - case 0x2fa: - if (upc->configuration_state == 0 && val == 0x55) - configuration_state_event = 1; - else if (upc->configuration_state == 4) { - uint8_t addr_verify = upc->cri_addr / 4; - addr_verify += val; - if (addr_verify == 0xff) { - upc->configuration_mode = 1; - /* TODO: is the value of cri reset here or when exiting configuration mode? */ - io_sethandler(upc->cri_addr, 0x0002, f82c710_config_read, NULL, NULL, f82c710_config_write, NULL, NULL, upc); - } else { - upc->configuration_mode = 0; - } - } - break; - case 0x3fa: - if (upc->configuration_state == 1 && val == 0xaa) - configuration_state_event = 1; - else if (upc->configuration_state == 2 && val == 0x36) - configuration_state_event = 1; - else if (upc->configuration_state == 3) { - upc->cri_addr = val * 4; - upc->cap_addr = upc->cri_addr + 1; - configuration_state_event = 1; - } - break; - default: - break; - } - - if (upc->configuration_mode) { - if (port == upc->cri_addr) { - upc->cri = val & 0xf; - } else if (port == upc->cap_addr) { - if (upc->cri == 0xf) { - upc->configuration_mode = 0; - io_removehandler(upc->cri_addr, 0x0002, f82c710_config_read, NULL, NULL, f82c710_config_write, NULL, NULL, upc); - /* TODO: any benefit in updating at each register write instead of when exiting config mode? */ - if (upc->local == 710) - f82c710_update_ports(upc); - if (upc->local == 606) - f82c606_update_ports(upc); - } else { - upc->regs[upc->cri] = val; - } - } - } + switch (port) { + case 0x2fa: + if ((dev->configuration_state == 0) && (val != 0x00) && (val != 0xff) && (dev->local == 606)) { + configuration_state_event = 1; + dev->last_write = val; + } else if ((dev->configuration_state == 0) && (val == 0x55) && (dev->local == 710)) + configuration_state_event = 1; + else if (dev->configuration_state == 4) { + if ((val | dev->last_write) == 0xff) { + dev->cri_addr = ((uint16_t) dev->last_write) << 2; + dev->cap_addr = dev->cri_addr + 1; + dev->configuration_mode = 1; + if (dev->local == 606) + f82c606_update_ports(dev, 0); + else if (dev->local == 710) + f82c710_update_ports(dev, 0); + /* TODO: is the value of cri reset here or when exiting configuration mode? */ + io_sethandler(dev->cri_addr, 0x0002, f82c710_config_read, NULL, NULL, f82c710_config_write, NULL, NULL, dev); + } else + dev->configuration_mode = 0; + } + break; + case 0x3fa: + if ((dev->configuration_state == 1) && ((val | dev->last_write) == 0xff) && (dev->local == 606)) + configuration_state_event = 1; + else if ((dev->configuration_state == 1) && (val == 0xaa) && (dev->local == 710)) + configuration_state_event = 1; + else if ((dev->configuration_state == 2) && (val == 0x36)) + configuration_state_event = 1; + else if (dev->configuration_state == 3) { + dev->last_write = val; + configuration_state_event = 1; + } + break; + default: + break; + } - /* TODO: is the state only reset when accessing 0x2fa and 0x3fa wrongly? */ - if ((port == 0x2fa || port == 0x3fa) && configuration_state_event) - upc->configuration_state++; - else - upc->configuration_state = 0; + if (dev->configuration_mode) { + if (port == dev->cri_addr) { + dev->cri = val & 0xf; + } else if (port == dev->cap_addr) { + if (dev->cri == 0xf) { + dev->configuration_mode = 0; + io_removehandler(dev->cri_addr, 0x0002, f82c710_config_read, NULL, NULL, f82c710_config_write, NULL, NULL, dev); + /* TODO: any benefit in updating at each register write instead of when exiting config mode? */ + if (dev->local == 606) + f82c606_update_ports(dev, 1); + else if (dev->local == 710) + f82c710_update_ports(dev, 1); + } else + dev->regs[dev->cri] = val; + } + } + + /* TODO: is the state only reset when accessing 0x2fa and 0x3fa wrongly? */ + if ((port == 0x2fa || port == 0x3fa) && configuration_state_event) + dev->configuration_state++; + else + dev->configuration_state = 0; } static void -f82c710_reset(upc_t *upc) +f82c710_reset(void *priv) { - serial_remove(upc->uart[0]); - serial_setup(upc->uart[0], SERIAL1_ADDR, SERIAL1_IRQ); + upc_t *dev = (upc_t *) priv; - serial_remove(upc->uart[1]); - serial_setup(upc->uart[1], SERIAL2_ADDR, SERIAL2_IRQ); - - lpt1_remove(); - lpt1_init(0x378); - lpt1_irq(7); + /* Set power-on defaults. */ + if (dev->local == 606) { + dev->regs[0] = 0x00; /* Enable */ + dev->regs[1] = 0x00; /* Configuration Register */ + dev->regs[2] = 0x00; /* Ext Baud Rate Select */ + dev->regs[3] = 0xb0; /* RTC Base */ + dev->regs[4] = 0xfe; /* UART1 Base */ + dev->regs[5] = 0xbe; /* UART2 Base */ + dev->regs[6] = 0x9e; /* Parallel Base */ + dev->regs[7] = 0x80; /* Game Base */ + dev->regs[8] = 0xec; /* Interrupt Select */ + } else if (dev->local == 710) { + dev->regs[0] = 0x0c; + dev->regs[1] = 0x00; + dev->regs[2] = 0x00; + dev->regs[3] = 0x00; + dev->regs[4] = 0xfe; + dev->regs[5] = 0x00; + dev->regs[6] = 0x9e; + dev->regs[7] = 0x00; + dev->regs[8] = 0x00; + dev->regs[9] = 0xb0; + dev->regs[10] = 0x00; + dev->regs[11] = 0x00; + dev->regs[12] = 0xa0; + dev->regs[13] = 0x00; + dev->regs[14] = 0x00; + } - if (upc->local == 710) - fdc_reset(upc->fdc); + if (dev->local == 606) + f82c606_update_ports(dev, 1); + else if (dev->local == 710) + f82c710_update_ports(dev, 1); } -static void * -f82c710_init(const device_t *info) -{ - upc_t *upc = (upc_t *) malloc(sizeof(upc_t)); - memset(upc, 0, sizeof(upc_t)); - upc->local = info->local; - - if (upc->local == 710) { - upc->regs[0] = 0x0c; - upc->regs[1] = 0x00; - upc->regs[2] = 0x00; - upc->regs[3] = 0x00; - upc->regs[4] = 0xfe; - upc->regs[5] = 0x00; - upc->regs[6] = 0x9e; - upc->regs[7] = 0x00; - upc->regs[8] = 0x00; - upc->regs[9] = 0xb0; - upc->regs[10] = 0x00; - upc->regs[11] = 0x00; - upc->regs[12] = 0xa0; - upc->regs[13] = 0x00; - upc->regs[14] = 0x00; - - upc->fdc = device_add(&fdc_at_device); - } - - if (upc->local == 606) { - /* Set power-on defaults. */ - upc->regs[0] = 0x00; /* Enable */ - upc->regs[1] = 0x00; /* Configuration Register */ - upc->regs[2] = 0x00; /* Ext Baud Rate Select */ - upc->regs[3] = 0xb0; /* RTC Base */ - upc->regs[4] = 0xfe; /* UART1 Base */ - upc->regs[5] = 0xbe; /* UART2 Base */ - upc->regs[6] = 0x9e; /* Parallel Base */ - upc->regs[7] = 0x80; /* Game Base */ - upc->regs[8] = 0xec; /* Interrupt Select */ - - upc->nvr = device_add(&at_nvr_old_device); - upc->gameport = gameport_add(&gameport_sio_device); - } - - upc->uart[0] = device_add_inst(&ns16450_device, 1); - upc->uart[1] = device_add_inst(&ns16450_device, 2); - - io_sethandler(0x02fa, 0x0001, NULL, NULL, NULL, f82c710_config_write, NULL, NULL, upc); - io_sethandler(0x03fa, 0x0001, NULL, NULL, NULL, f82c710_config_write, NULL, NULL, upc); - - f82c710_reset(upc); - - if (upc->local == 710) - f82c710_update_ports(upc); - if (upc->local == 606) - f82c606_update_ports(upc); - - return upc; -} static void f82c710_close(void *priv) { - upc_t *upc = (upc_t *)priv; + upc_t *dev = (upc_t *) priv; - free(upc); + free(dev); } + +static void * +f82c710_init(const device_t *info) +{ + upc_t *dev = (upc_t *) malloc(sizeof(upc_t)); + memset(dev, 0, sizeof(upc_t)); + dev->local = info->local; + + if (dev->local == 606) { + dev->nvr = device_add(&at_nvr_old_device); + dev->gameport = gameport_add(&gameport_sio_device); + } else if (dev->local == 710) + dev->fdc = device_add(&fdc_at_device); + + dev->uart[0] = device_add_inst(&ns16450_device, 1); + dev->uart[1] = device_add_inst(&ns16450_device, 2); + + io_sethandler(0x02fa, 0x0001, NULL, NULL, NULL, f82c710_config_write, NULL, NULL, dev); + io_sethandler(0x03fa, 0x0001, NULL, NULL, NULL, f82c710_config_write, NULL, NULL, dev); + + f82c710_reset(dev); + + return dev; +} + + const device_t f82c606_device = { - "82C606 CHIPSpak Multifunction Controller", - 0, - 606, - f82c710_init, f82c710_close, NULL, - { NULL }, NULL, NULL, - NULL + "82C606 CHIPSpak Multifunction Controller", + 0, + 606, + f82c710_init, f82c710_close, f82c710_reset, + { NULL }, NULL, NULL, + NULL }; const device_t f82c710_device = { - "F82C710 UPC Super I/O", - 0, - 710, - f82c710_init, f82c710_close, NULL, - { NULL }, NULL, NULL, - NULL + "F82C710 UPC Super I/O", + 0, + 710, + f82c710_init, f82c710_close, f82c710_reset, + { NULL }, NULL, NULL, + NULL };