diff --git a/src/device/CMakeLists.txt b/src/device/CMakeLists.txt index 999b83aeb..89706706f 100644 --- a/src/device/CMakeLists.txt +++ b/src/device/CMakeLists.txt @@ -47,6 +47,7 @@ add_library(dev OBJECT mouse_microtouch_touchscreen.c mouse_ps2.c mouse_serial.c + mouse_upc.c nec_mate_unk.c novell_cardkey.c pci_bridge.c diff --git a/src/device/kbc_at.c b/src/device/kbc_at.c index d19b08f84..2252410b5 100644 --- a/src/device/kbc_at.c +++ b/src/device/kbc_at.c @@ -8,13 +8,11 @@ * * Intel 8042 (AT keyboard controller) emulation. * - * - * * Authors: Miran Grca, * EngiNerd, * - * Copyright 2023 Miran Grca. - * Copyright 2023 EngiNerd. + * Copyright 2023-2025 Miran Grca. + * Copyright 2023-2025 EngiNerd. */ #include #include @@ -29,24 +27,19 @@ #include <86box/timer.h> #include <86box/io.h> #include <86box/pic.h> -#include <86box/pit.h> #include <86box/plat_fallthrough.h> #include <86box/plat_unused.h> -#include <86box/ppi.h> #include <86box/mem.h> #include <86box/device.h> +#include <86box/dma.h> #include <86box/machine.h> #include <86box/m_at_t3100e.h> #include <86box/fdd.h> #include <86box/fdc.h> -#include <86box/sound.h> -#include <86box/snd_speaker.h> +#include <86box/pci.h> #include <86box/video.h> #include <86box/keyboard.h> -#include <86box/dma.h> -#include <86box/pci.h> - #define STAT_PARITY 0x80 #define STAT_RTIMEOUT 0x40 #define STAT_TTIMEOUT 0x20 diff --git a/src/device/kbc_at_dev.c b/src/device/kbc_at_dev.c index b20e38860..81ab0aee9 100644 --- a/src/device/kbc_at_dev.c +++ b/src/device/kbc_at_dev.c @@ -8,11 +8,9 @@ * * AT / PS/2 attached device emulation. * - * - * * Authors: Miran Grca, * - * Copyright 2023 Miran Grca. + * Copyright 2023-2025 Miran Grca. */ #include #include @@ -22,23 +20,9 @@ #define HAVE_STDARG_H #include #include <86box/86box.h> -#include "cpu.h" -#include <86box/timer.h> -#include <86box/io.h> -#include <86box/pic.h> -#include <86box/pit.h> -#include <86box/ppi.h> -#include <86box/mem.h> #include <86box/device.h> -#include <86box/machine.h> -#include <86box/m_at_t3100e.h> -#include <86box/fdd.h> -#include <86box/fdc.h> -#include <86box/sound.h> -#include <86box/snd_speaker.h> -#include <86box/video.h> -#include <86box/keyboard.h> #include <86box/plat_fallthrough.h> +#include <86box/keyboard.h> #ifdef ENABLE_KBC_AT_DEV_LOG int kbc_at_dev_do_log = ENABLE_KBC_AT_DEV_LOG; diff --git a/src/device/mouse.c b/src/device/mouse.c index 0bf87934d..8732ca4c5 100644 --- a/src/device/mouse.c +++ b/src/device/mouse.c @@ -83,24 +83,25 @@ static const device_t mouse_internal_device = { static mouse_t mouse_devices[] = { // clang-format off - { &mouse_none_device }, - { &mouse_internal_device }, - { &mouse_logibus_device }, - { &mouse_msinport_device }, + { &mouse_none_device }, + { &mouse_internal_device }, + { &mouse_logibus_device }, + { &mouse_msinport_device }, #ifdef USE_GENIBUS - { &mouse_genibus_device }, + { &mouse_genibus_device }, #endif - { &mouse_mssystems_device }, - { &mouse_mssystems_bus_device }, - { &mouse_msserial_device }, - { &mouse_ltserial_device }, - { &mouse_ps2_device }, + { &mouse_mssystems_device }, + { &mouse_mssystems_bus_device }, + { &mouse_msserial_device }, + { &mouse_ltserial_device }, + { &mouse_ps2_device }, + { &mouse_upc_standalone_device }, #ifdef USE_WACOM - { &mouse_wacom_device }, - { &mouse_wacom_artpad_device }, + { &mouse_wacom_device }, + { &mouse_wacom_artpad_device }, #endif - { &mouse_mtouch_device }, - { NULL } + { &mouse_mtouch_device }, + { NULL } // clang-format on }; diff --git a/src/device/mouse_ps2.c b/src/device/mouse_ps2.c index 2c1722f51..80d9f3876 100644 --- a/src/device/mouse_ps2.c +++ b/src/device/mouse_ps2.c @@ -10,7 +10,7 @@ * * Authors: Miran Grca, * - * Copyright 2023 Miran Grca. + * Copyright 2023-2025 Miran Grca. */ #include #include @@ -21,7 +21,6 @@ #include #define HAVE_STDARG_H #include <86box/86box.h> -#include "cpu.h" #include <86box/device.h> #include <86box/keyboard.h> #include <86box/mouse.h> @@ -353,6 +352,9 @@ mouse_ps2_init(const device_t *info) atkbc_dev_t *dev = kbc_at_dev_init(DEV_AUX); int i; + if (info->local & MOUSE_TYPE_QPORT) + device_context(&mouse_upc_standalone_device); + dev->name = info->name; dev->type = info->local; @@ -382,6 +384,9 @@ mouse_ps2_init(const device_t *info) mouse_set_poll(ps2_poll, dev); + if (info->local & MOUSE_TYPE_QPORT) + device_context_restore(); + /* Return our private data to the I/O layer. */ return dev; } diff --git a/src/device/mouse_upc.c b/src/device/mouse_upc.c new file mode 100644 index 000000000..70dd26f2f --- /dev/null +++ b/src/device/mouse_upc.c @@ -0,0 +1,445 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Implementation of the Chips & Technologies F82C710 Universal + * Peripheral Controller (UPC) PS/2 mouse port. + * + * Authors: Miran Grca, + * + * Copyright 2025 Miran Grca. + */ +#include +#include +#include +#include +#include +#define HAVE_STDARG_H +#include +#include <86box/86box.h> +#include "cpu.h" +#include "x86seg.h" +#include <86box/device.h> +#include <86box/timer.h> +#include <86box/io.h> +#include <86box/pic.h> +#include <86box/keyboard.h> +#include <86box/mouse.h> + +#define STAT_DEV_IDLE 0x01 +#define STAT_RX_FULL 0x02 +#define STAT_TX_IDLE 0x04 +#define STAT_RESET 0x08 +#define STAT_INTS_ON 0x10 +#define STAT_ERROR_FLAG 0x20 +#define STAT_CLEAR 0x40 +#define STAT_ENABLE 0x80 + +typedef struct mouse_upc_t { + uint8_t status; + uint8_t ib; + uint8_t ob; + uint8_t state; + uint8_t ctrl_queue_start; + uint8_t ctrl_queue_end; + + uint8_t handler_enable[2]; + + uint16_t mdata_addr; + uint16_t mstat_addr; + + uint16_t irq; + + uint16_t base_addr[2]; + + /* Local copies of the pointers to both ports for easier swapping (AMI '5' MegaKey). */ + kbc_at_port_t *port; + + /* Main timers. */ + pc_timer_t poll_timer; + pc_timer_t dev_poll_timer; + + struct { + uint8_t (*read)(uint16_t port, void *priv); + void (*write)(uint16_t port, uint8_t val, void *priv); + } handlers[2]; + + void *mouse_ps2; +} mouse_upc_t; + +enum { + STATE_MAIN_IBF, /* UPC checking if the input buffer is full. */ + STATE_MAIN, /* UPC checking if the auxiliary has anything to send. */ + STATE_OUT, /* UPC is sending multiple bytes. */ + STATE_SEND, /* UPC is sending command to the auxiliary device. */ + STATE_SCAN /* UPC is waiting for the auxiliary command response. */ +}; + +#define ENABLE_MOUSE_UPC_LOG 1 +#ifdef ENABLE_MOUSE_UPC_LOG +int mouse_upc_do_log = ENABLE_MOUSE_UPC_LOG; + +static void +mouse_upc_log(const char *fmt, ...) +{ + va_list ap; + + if (mouse_upc_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +# define mouse_upc_log(fmt, ...) +#endif + +static void +mouse_upc_send_to_ob(mouse_upc_t *dev, uint8_t val) +{ + dev->status = (dev->status & ~STAT_DEV_IDLE) | STAT_RX_FULL; + + if (dev->status & STAT_INTS_ON) { + picint_common(1 << dev->irq, 0, 0, NULL); + picint_common(1 << dev->irq, 0, 1, NULL); + } + + dev->ob = val; +} + +static void +set_enable_aux(mouse_upc_t *dev, uint8_t enable) +{ + dev->status &= ~STAT_ENABLE; + dev->status |= (enable ? STAT_ENABLE : 0x00); +} + +static void +mouse_upc_ibf_process(mouse_upc_t *dev) +{ + /* IBF set, process both commands and data. */ + dev->status |= STAT_TX_IDLE; + dev->state = STATE_MAIN_IBF; + + set_enable_aux(dev, 1); + + if (dev->port != NULL) { + dev->port->wantcmd = 1; + dev->port->dat = dev->ib; + dev->state = STATE_SEND; + } +} + +/* + Correct Procedure: + 1. Controller asks the device (keyboard or auxiliary device) for a byte. + 2. The device, unless it's in the reset or command states, sees if there's anything to give it, + and if yes, begins the transfer. + 3. The controller checks if there is a transfer, if yes, transfers the byte and sends it to the host, + otherwise, checks the next device, or if there is no device left to check, checks if IBF is full + and if yes, processes it. + */ +static int +mouse_upc_scan(mouse_upc_t *dev) +{ + if ((dev->port != NULL) && (dev->port->out_new != -1)) { + mouse_upc_log("UPC Mouse: %02X coming\n", dev->port->out_new & 0xff); + mouse_upc_send_to_ob(dev, dev->port->out_new); + dev->port->out_new = -1; + dev->state = STATE_MAIN_IBF; + return 1; + } + + return 0; +} + +static void +mouse_upc_poll(void *priv) +{ + mouse_upc_t *dev = (mouse_upc_t *) priv; + + timer_advance_u64(&dev->poll_timer, (100ULL * TIMER_USEC)); + + switch (dev->state) { + case STATE_MAIN_IBF: + default: + if (!(dev->status & STAT_TX_IDLE)) + mouse_upc_ibf_process(dev); + else if (!(dev->status & STAT_RX_FULL)) { + if (dev->status & STAT_ENABLE) + dev->state = STATE_MAIN; + } + break; + case STATE_MAIN: + if (!(dev->status & STAT_TX_IDLE)) + mouse_upc_ibf_process(dev); + else { + (void) mouse_upc_scan(dev); + dev->state = STATE_MAIN_IBF; + } + break; + case STATE_SEND: + if (!dev->port->wantcmd) + dev->state = STATE_SCAN; + break; + case STATE_SCAN: + (void) mouse_upc_scan(dev); + break; + } +} + +static void +mouse_upc_dev_poll(void *priv) +{ + mouse_upc_t *dev = (mouse_upc_t *) priv; + + timer_advance_u64(&dev->dev_poll_timer, (100ULL * TIMER_USEC)); + + if ((dev->port != NULL) && (dev->port->priv != NULL)) + dev->port->poll(dev->port->priv); +} + +static void +mouse_upc_port_1_write(uint16_t port, uint8_t val, void *priv) +{ + mouse_upc_t *dev = (mouse_upc_t *) priv; + + mouse_upc_log("UPC Mouse: [%04X:%08X] write(%04X) = %02X\n", CS, cpu_state.pc, port, val); + + if ((dev->status & STAT_TX_IDLE) && (dev->status & STAT_ENABLE)) { + dev->ib = val; + dev->status &= ~STAT_TX_IDLE; + } +} + +static void +mouse_upc_port_2_write(uint16_t port, uint8_t val, void *priv) +{ + mouse_upc_t *dev = (mouse_upc_t *) priv; + + mouse_upc_log("UPC Mouse: [%04X:%08X] write(%04X) = %02X\n", CS, cpu_state.pc, port, val); + + dev->status = (dev->status & 0x27) | (val & 0xd8); + + if (dev->status & (STAT_CLEAR | STAT_RESET)) { + /* TODO: Silently reset the mouse. */ + } +} + +static uint8_t +mouse_upc_port_1_read(uint16_t port, void *priv) +{ + mouse_upc_t *dev = (mouse_upc_t *) priv; + uint8_t ret = 0xff; + + cycles -= ISA_CYCLES(8); + + ret = dev->ob; + dev->ob = 0xff; + dev->status &= ~STAT_RX_FULL; + dev->status |= STAT_DEV_IDLE; + + mouse_upc_log("UPC Mouse: [%04X:%08X] read (%04X) = %02X\n", CS, cpu_state.pc, port, ret); + + return ret; +} + +static uint8_t +mouse_upc_port_2_read(uint16_t port, void *priv) +{ + mouse_upc_t *dev = (mouse_upc_t *) priv; + uint8_t ret = 0xff; + + cycles -= ISA_CYCLES(8); + + ret = dev->status; + + mouse_upc_log("UPC Mouse: [%04X:%08X] read (%04X) = %02X\n", CS, cpu_state.pc, port, ret); + + return ret; +} + +static void +mouse_upc_reset(void *priv) +{ + mouse_upc_t *dev = (mouse_upc_t *) priv; + + dev->status = STAT_DEV_IDLE | STAT_TX_IDLE; + dev->ob = 0xff; + + dev->state = STATE_MAIN_IBF; +} + +static void +mouse_upc_close(void *priv) +{ + mouse_upc_t *dev = (mouse_upc_t *) priv; + + /* Stop timers. */ + timer_disable(&dev->dev_poll_timer); + timer_disable(&dev->poll_timer); + + if (kbc_at_ports[1] != NULL) { + free(kbc_at_ports[1]); + kbc_at_ports[1] = NULL; + } + + free(dev); +} + +void +mouse_upc_port_handler(int num, int set, uint16_t port, void *priv) +{ + mouse_upc_t *dev = (mouse_upc_t *) priv; + + if (dev->handler_enable[num] && (dev->base_addr[num] != 0x0000)) + io_removehandler(dev->base_addr[num], 1, + dev->handlers[num].read, NULL, NULL, + dev->handlers[num].write, NULL, NULL, priv); + + dev->handler_enable[num] = set; + dev->base_addr[num] = port; + + if (dev->handler_enable[num] && (dev->base_addr[num] != 0x0000)) + io_sethandler(dev->base_addr[num], 1, + dev->handlers[num].read, NULL, NULL, + dev->handlers[num].write, NULL, NULL, priv); +} + +void +mouse_upc_handler(int set, uint16_t port, void *priv) +{ + mouse_upc_port_handler(0, set, port, priv); + mouse_upc_port_handler(1, set, port + 0x0001, priv); +} + +void +mouse_upc_set_irq(int num, uint16_t irq, void *priv) +{ + mouse_upc_t *dev = (mouse_upc_t *) priv; + + if (dev->irq != 0xffff) + picintc(1 << dev->irq); + + dev->irq = irq; +} + +static void * +mouse_upc_init_common(int standalone, int irq) +{ + mouse_upc_t *dev; + + dev = (mouse_upc_t *) calloc(1, sizeof(mouse_upc_t)); + + mouse_upc_reset(dev); + + dev->handlers[0].read = mouse_upc_port_1_read; + dev->handlers[0].write = mouse_upc_port_1_write; + dev->handlers[1].read = mouse_upc_port_2_read; + dev->handlers[1].write = mouse_upc_port_2_write; + + dev->irq = irq; + + if (kbc_at_ports[1] == NULL) { + kbc_at_ports[1] = (kbc_at_port_t *) calloc(1, sizeof(kbc_at_port_t)); + kbc_at_ports[1]->out_new = -1; + } + + dev->port = kbc_at_ports[1]; + + timer_add(&dev->poll_timer, mouse_upc_poll, dev, 1); + timer_add(&dev->dev_poll_timer, mouse_upc_dev_poll, dev, 1); + + if (standalone) { + mouse_upc_handler(1, 0x02d4, dev); + dev->mouse_ps2 = device_add_params(&mouse_ps2_device, (void *) (uintptr_t) MOUSE_TYPE_PS2_QPORT); + } + + return dev; +} + +static void * +mouse_upc_init(const device_t *info) +{ + void *dev = NULL; + + if (info->local) + dev = mouse_upc_init_common(1, device_get_config_int("irq")); + else + dev = mouse_upc_init_common(0, info->local); + + return dev; +} + +static const device_config_t upc_config[] = { + // clang-format off + { + .name = "irq", + .description = "IRQ", + .type = CONFIG_SELECTION, + .default_string = NULL, + .default_int = 12, + .file_filter = NULL, + .spinner = { 0 }, + .selection = { + { .description = "IRQ 12", .value = 12 }, + { .description = "IRQ 2/9", .value = 2 }, + { .description = "" } + }, + .bios = { { 0 } } + }, + { + .name = "buttons", + .description = "Buttons", + .type = CONFIG_SELECTION, + .default_string = NULL, + .default_int = 2, + .file_filter = NULL, + .spinner = { 0 }, + .selection = { + { .description = "Two", .value = 2 }, + { .description = "Three", .value = 3 }, + { .description = "Wheel", .value = 4 }, + { .description = "Five + Wheel", .value = 5 }, + { .description = "Five + 2 Wheels", .value = 6 }, + { .description = "" } + }, + .bios = { { 0 } } + }, + { + .name = "", .description = "", .type = CONFIG_END + } + // clang-format on +}; + +const device_t mouse_upc_device = { + .name = "PS/2 QuickPort Mouse (F82C710)", + .internal_name = "mouse_upc", + .flags = DEVICE_ISA, + .local = 0, + .init = mouse_upc_init, + .close = mouse_upc_close, + .reset = mouse_upc_reset, + .available = NULL, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; + +const device_t mouse_upc_standalone_device = { + .name = "PS/2 QuickPort Mouse", + .internal_name = "mouse_upc_standalone", + .flags = DEVICE_ISA, + .local = 1, + .init = mouse_upc_init, + .close = mouse_upc_close, + .reset = mouse_upc_reset, + .available = NULL, + .speed_changed = NULL, + .force_redraw = NULL, + .config = upc_config +}; diff --git a/src/disk/hdc.c b/src/disk/hdc.c index ecd609476..05691b77f 100644 --- a/src/disk/hdc.c +++ b/src/disk/hdc.c @@ -63,6 +63,7 @@ static const struct { { &xtide_device }, { &st506_xt_st11_m_device }, { &st506_xt_st11_r_device }, + { &xta_st50x_device }, { &st506_xt_victor_v86p_device }, { &st506_xt_wd1002a_27x_device }, { &st506_xt_wd1002a_wx1_device }, diff --git a/src/disk/hdc_xta.c b/src/disk/hdc_xta.c index d54f6ab0c..4f7ee2fb9 100644 --- a/src/disk/hdc_xta.c +++ b/src/disk/hdc_xta.c @@ -101,11 +101,14 @@ #include <86box/ui.h> #include <86box/hdc.h> #include <86box/hdd.h> +#include "cpu.h" #define HDC_TIME (250 * TIMER_USEC) #define WD_REV_1_BIOS_FILE "roms/hdd/xta/idexywd2.bin" #define WD_REV_2_BIOS_FILE "roms/hdd/xta/infowdbios.rom" +#define ST50X_BIOS_FILE "roms/hdd/xta/ST05XBIO.BIN" +#define PC5086_BIOS_FILE "roms/machines/pc5086/c800.bin" enum { STATE_IDLE = 0, @@ -236,6 +239,7 @@ typedef struct hdc_t { const char *name; /* controller name */ uint16_t base; /* controller base I/O address */ + uint8_t sw; /* controller switches */ int8_t irq; /* controller IRQ channel */ int8_t dma; /* controller DMA channel */ int8_t type; /* controller type ID */ @@ -269,6 +273,11 @@ typedef struct hdc_t { uint8_t sector_buf[512]; /* sector buffer */ } hdc_t; +typedef struct hdc_dual_t { + hdc_t *hdc[2]; +} hdc_dual_t; + +#define ENABLE_XTA_LOG 1 #ifdef ENABLE_XTA_LOG int xta_do_log = ENABLE_XTA_LOG; @@ -882,7 +891,7 @@ hdc_read(uint16_t port, void *priv) hdc_t *dev = (hdc_t *) priv; uint8_t ret = 0xff; - switch (port & 7) { + switch (port & 3) { case 0: /* DATA register */ dev->status &= ~STAT_IRQ; @@ -915,13 +924,15 @@ hdc_read(uint16_t port, void *priv) break; case 2: /* "read option jumpers" */ - ret = 0xff; /* all switches off */ + ret = dev->sw; break; default: break; } + pclog("[%04X:%08X] XTA: [R] %04X = %02X\n", CS, cpu_state.pc, port, ret); + return ret; } @@ -931,7 +942,9 @@ hdc_write(uint16_t port, uint8_t val, void *priv) { hdc_t *dev = (hdc_t *) priv; - switch (port & 7) { + pclog("[%04X:%08X] XTA: [W] %04X = %02X\n", CS, cpu_state.pc, port, val); + + switch (port & 3) { case 0: /* DATA register */ if (dev->state == STATE_RDATA) { if (!(dev->status & STAT_REQ)) { @@ -989,38 +1002,81 @@ hdc_write(uint16_t port, uint8_t val, void *priv) } } +void +xta_handler(void *priv, int set) +{ + hdc_t *dev = (hdc_t *) priv; + + io_handler(set, dev->base, 4, + hdc_read, NULL, NULL, hdc_write, NULL, NULL, dev); + + pclog("XTA: %sabled at %04X-%0X\n", set ? "En" : "Dis", dev->base, dev->base + 3); +} + static void * -xta_init(const device_t *info) +xta_init_common(const device_t *info, int type) { drive_t *drive; const char *bios_rev = NULL; const char *fn = NULL; hdc_t *dev; int c; - int max = XTA_NUM; + int min = 0; + int max = XTA_NUM; /* Allocate and initialize device block. */ dev = calloc(1, sizeof(hdc_t)); - dev->type = info->local; + + dev->sw = 0xff; /* all switches off */ + dev->type = type; /* Do per-controller-type setup. */ switch (dev->type) { case 0: /* WDXT-150, with BIOS */ dev->name = "WDXT-150"; + bios_rev = (char *) device_get_config_bios("bios_rev"); + fn = (char *) device_get_bios_file(info, bios_rev, 0); + max = 1; dev->base = device_get_config_hex16("base"); dev->irq = device_get_config_int("irq"); dev->rom_addr = device_get_config_hex20("bios_addr"); dev->dma = 3; - bios_rev = (char *) device_get_config_bios("bios_rev"); - fn = (char *) device_get_bios_file(info, bios_rev, 0); - max = 1; break; case 1: /* EuroPC */ - dev->name = "HD20"; - dev->base = 0x0320; - dev->irq = 5; - dev->dma = 3; + case 3: /* Amstrad PC5086 */ + switch (dev->type) { + case 1: + dev->name = "HD20"; + break; + case 3: + dev->name = "ST-50X PC5086"; + dev->rom_addr = 0xc8000; + fn = PC5086_BIOS_FILE; + max = 1; + break; + } + dev->base = 0x0320; + dev->irq = 5; + dev->dma = 3; + break; + case 2: /* Seagate ST-05X Standalone */ + case 4: /* Seagate ST-05X Standalone secondary device */ + switch (dev->type) { + case 2: + dev->name = "ST-50X PRI"; + dev->rom_addr = device_get_config_hex20("bios_addr"); + fn = ST50X_BIOS_FILE; + max = 1; + break; + case 4: + dev->name = "ST-50X SEC"; + min = 1; + break; + } + dev->base = 0x0320 + (dev->type & 4); + dev->irq = 5; + dev->dma = 3; break; default: @@ -1029,6 +1085,8 @@ xta_init(const device_t *info) xta_log("%s: initializing (I/O=%04X, IRQ=%d, DMA=%d", dev->name, dev->base, dev->irq, dev->dma); + pclog("%s: initializing (I/O=%04X, IRQ=%d, DMA=%d", + dev->name, dev->base, dev->irq, dev->dma); if (dev->rom_addr != 0x000000) xta_log(", BIOS=%06X", dev->rom_addr); @@ -1037,8 +1095,11 @@ xta_init(const device_t *info) /* Load any disks for this device class. */ c = 0; for (uint8_t i = 0; i < HDD_NUM; i++) { - if ((hdd[i].bus_type == HDD_BUS_XTA) && (hdd[i].xta_channel < max)) { - drive = &dev->drives[hdd[i].xta_channel]; + if ((hdd[i].bus_type == HDD_BUS_XTA) && (hdd[i].xta_channel >= min) && (hdd[i].xta_channel < max)) { + if (dev->type == 4) + drive = &dev->drives[0]; + else + drive = &dev->drives[hdd[i].xta_channel]; if (!hdd_image_load(i)) { drive->present = 0; @@ -1058,9 +1119,29 @@ xta_init(const device_t *info) drive->hpc = drive->cfg_hpc; drive->tracks = drive->cfg_tracks; + if ((dev->type >= 2) && (dev->type <= 4)) { + /* + Bits 1, 0: + - 1, 1 = 615/4/17 (20 MB); + - 1, 0 or 0, 0 = 980/5/17 (40 MB); + - 0, 1 = 615/6/17 (30 MB). + - 0, 0 is actually no hard disk present - switch port supposed to be readable always? + */ + if (drive->tracks == 980) + dev->sw = 0xfe; + else if (drive->hpc == 6) + dev->sw = 0xfd; + else + dev->sw = 0xff; + pclog("%s: SW = %02X\n", dev->name, dev->sw); + } + xta_log("%s: drive%d (cyl=%d,hd=%d,spt=%d), disk %d\n", dev->name, hdd[i].xta_channel, drive->tracks, drive->hpc, drive->spt, i); + pclog("%s: drive%d (cyl=%d,hd=%d,spt=%d), disk %d\n", + dev->name, hdd[i].xta_channel, drive->tracks, + drive->hpc, drive->spt, i); if (++c > max) break; @@ -1083,6 +1164,23 @@ xta_init(const device_t *info) return dev; } +static void * +xta_init(const device_t *info) +{ + return xta_init_common(info, info->local); +} + +static void * +xta_st50x_init(const device_t *info) +{ + hdc_dual_t *dev = (hdc_dual_t *) calloc(1, sizeof(hdc_dual_t)); + + dev->hdc[0] = xta_init_common(info, info->local); + dev->hdc[1] = xta_init_common(info, 4); + + return dev; +} + static void xta_close(void *priv) { @@ -1104,6 +1202,21 @@ xta_close(void *priv) free(dev); } +static void +xta_st50x_close(void *priv) +{ + hdc_dual_t *dev = (hdc_dual_t *) priv; + + xta_close(dev->hdc[1]); + xta_close(dev->hdc[0]); +} + +static int +st50x_available(void) +{ + return (rom_present(ST50X_BIOS_FILE)); +} + static const device_config_t wdxt150_config[] = { // clang-format off { @@ -1185,6 +1298,27 @@ static const device_config_t wdxt150_config[] = { // clang-format off }; +static const device_config_t st50x_config[] = { + // clang-format off + { + .name = "bios_addr", + .description = "BIOS Address", + .type = CONFIG_HEX20, + .default_string = NULL, + .default_int = 0xc8000, + .file_filter = NULL, + .spinner = { 0 }, + .selection = { + { .description = "C800H", .value = 0xc8000 }, + { .description = "CA00H", .value = 0xca000 }, + { .description = "" } + }, + .bios = { { 0 } } + }, + { .name = "", .description = "", .type = CONFIG_END } +// clang-format off +}; + const device_t xta_wdxt150_device = { .name = "WDXT-150 XTA Fixed Disk Controller", .internal_name = "xta_wdxt150", @@ -1212,3 +1346,32 @@ const device_t xta_hd20_device = { .force_redraw = NULL, .config = NULL }; + +const device_t xta_st50x_device = { + .name = "ST-50X Fixed Disk Controller", + .internal_name = "xta_st50x", + .flags = DEVICE_ISA, + .local = 2, + .init = xta_st50x_init, + .close = xta_st50x_close, + .reset = NULL, + .available = st50x_available, + .speed_changed = NULL, + .force_redraw = NULL, + .config = st50x_config +}; + + +const device_t xta_st50x_pc5086_device = { + .name = "ST-50X Fixed Disk Controller (PC5086)", + .internal_name = "xta_st50x_pc5086", + .flags = DEVICE_ISA, + .local = 3, + .init = xta_init, + .close = xta_close, + .reset = NULL, + .available = NULL, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; diff --git a/src/include/86box/hdc.h b/src/include/86box/hdc.h index ca020b4b2..33cb4b16c 100644 --- a/src/include/86box/hdc.h +++ b/src/include/86box/hdc.h @@ -102,8 +102,10 @@ extern const device_t ide_qua_pnp_device; extern const device_t mcide_device; -extern const device_t xta_wdxt150_device; /* xta_wdxt150 */ -extern const device_t xta_hd20_device; /* EuroPC internal */ +extern const device_t xta_wdxt150_device; /* xta_wdxt150 */ +extern const device_t xta_hd20_device; /* EuroPC internal */ +extern const device_t xta_st50x_device; /* ST-50X */ +extern const device_t xta_st50x_pc5086_device; /* ST-50X (PC-5086) */ extern const device_t xtide_device; /* xtide_xt */ extern const device_t xtide_at_device; /* xtide_at */ @@ -122,4 +124,6 @@ extern const device_t *hdc_get_device(int hdc); extern int hdc_get_flags(int hdc); extern int hdc_available(int hdc); +extern void xta_handler(void *priv, int set); + #endif /*EMU_HDC_H*/ diff --git a/src/include/86box/machine.h b/src/include/86box/machine.h index 93080538b..1ae79366a 100644 --- a/src/include/86box/machine.h +++ b/src/include/86box/machine.h @@ -917,6 +917,9 @@ extern int machine_xt_m19_init(const machine_t *); /* m_pcjr.c */ extern int machine_pcjr_init(const machine_t *); +/* m_pc5086.c */ +extern int machine_pc5086_init(const machine_t *); + /* m_ps1.c */ extern int machine_ps1_m2011_init(const machine_t *); extern int machine_ps1_m2121_init(const machine_t *); diff --git a/src/include/86box/mouse.h b/src/include/86box/mouse.h index 8dd3bad2d..2a4be1373 100644 --- a/src/include/86box/mouse.h +++ b/src/include/86box/mouse.h @@ -42,7 +42,9 @@ #define MOUSE_TYPE_WACOM 12 /* WACOM tablet */ #define MOUSE_TYPE_WACOMARTP 13 /* WACOM tablet (ArtPad) */ #define MOUSE_TYPE_MSYSTEMSB 14 /* Mouse Systems bus mouse */ +#define MOUSE_TYPE_PS2_QPORT 27 /* PS/2 QuickPort series Bus Mouse */ +#define MOUSE_TYPE_QPORT 0x40 /* Mouse is an on-board version of one of the above. */ #define MOUSE_TYPE_ONBOARD 0x80 /* Mouse is an on-board version of one of the above. */ @@ -73,6 +75,8 @@ extern const device_t mouse_mssystems_bus_device; extern const device_t mouse_msserial_device; extern const device_t mouse_ltserial_device; extern const device_t mouse_ps2_device; +extern const device_t mouse_upc_device; +extern const device_t mouse_upc_standalone_device; # ifdef USE_WACOM extern const device_t mouse_wacom_device; extern const device_t mouse_wacom_artpad_device; @@ -129,6 +133,11 @@ extern void mouse_init(void); extern void mouse_bus_set_irq(void *priv, int irq); +extern void mouse_upc_port_handler(int num, int set, uint16_t port, void *priv); +extern void mouse_upc_handler(int set, uint16_t port, void *priv); + +extern void mouse_upc_set_irq(int num, uint16_t irq, void *priv); + #ifdef __cplusplus } #endif diff --git a/src/include/86box/sio.h b/src/include/86box/sio.h index 72878ff46..27ad27fe3 100644 --- a/src/include/86box/sio.h +++ b/src/include/86box/sio.h @@ -26,6 +26,7 @@ extern const device_t ali5123_device; extern const device_t f82c606_device; extern const device_t f82c710_device; +extern const device_t f82c710_pc5086_device; /* SM(S)C */ extern const device_t fdc37c651_device; diff --git a/src/machine/CMakeLists.txt b/src/machine/CMakeLists.txt index f91eeb17d..63f9baf61 100644 --- a/src/machine/CMakeLists.txt +++ b/src/machine/CMakeLists.txt @@ -27,6 +27,7 @@ add_library(mch OBJECT m_xt_zenith.c m_pcjr.c m_amstrad.c + m_amstrad_pc5x86.c m_europc.c m_elt.c m_xt_olivetti.c diff --git a/src/machine/m_amstrad_pc5x86.c b/src/machine/m_amstrad_pc5x86.c new file mode 100644 index 000000000..01fef8036 --- /dev/null +++ b/src/machine/m_amstrad_pc5x86.c @@ -0,0 +1,68 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Amstrad PC5086 and PC5286 emulation. + * + * Authors: Miran Grca, + * + * Copyright 2025 Miran Grca. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307 + * USA. + */ +#include +#include +#include <86box/86box.h> +#include "cpu.h" +#include <86box/device.h> +#include <86box/mem.h> +#include <86box/rom.h> +#include <86box/timer.h> +#include <86box/chipset.h> +#include <86box/machine.h> +#include <86box/nvr.h> +#include <86box/keyboard.h> +#include <86box/sio.h> + +int +machine_pc5086_init(const machine_t *model) +{ + int ret; + + ret = bios_load_linear("roms/machines/pc5086/sys_rom.bin", + 0x000f0000, 65536, 0); + + if (bios_only || !ret) + return ret; + + machine_common_init(model); + + device_add(&ct_82c100_device); + device_add(&f82c710_pc5086_device); + + device_add(&keyboard_xt_device); + + device_add(&amstrad_megapc_nvr_device); /* NVR that is initialized to all 0x00's. */ + + return ret; +} diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index 94c03ebe6..31a79c2cc 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -30,6 +30,7 @@ #include <86box/fdd.h> #include <86box/fdc.h> #include <86box/keyboard.h> +#include <86box/sio.h> #include <86box/sound.h> #include <86box/video.h> #include <86box/plat_unused.h> @@ -2267,6 +2268,45 @@ const machine_t machines[] = { .snd_device = NULL, .net_device = NULL }, + { + .name = "[8086] Amstrad PC5086", + .internal_name = "pc5086", + .type = MACHINE_TYPE_8086, + .chipset = MACHINE_CHIPSET_PROPRIETARY, + .init = machine_pc5086_init, + .p1_handler = NULL, + .gpio_handler = NULL, + .available_flag = MACHINE_AVAILABLE, + .gpio_acpi_handler = NULL, + .cpu = { + .package = CPU_PKG_8086, + .block = CPU_BLOCK_NONE, + .min_bus = 0, + .max_bus = 0, + .min_voltage = 0, + .max_voltage = 0, + .min_multi = 0, + .max_multi = 0 + }, + .bus_flags = MACHINE_PC | MACHINE_BUS_PS2, + .flags = MACHINE_XTA, + .ram = { + .min = 512, + .max = 640, + .step = 128 + }, + .nvrmask = 63, + .kbc_device = &keyboard_xt_device, + .kbc_p1 = 0xff, + .gpio = 0xffffffff, + .gpio_acpi = 0xffffffff, + .device = &f82c710_pc5086_device, + .fdc_device = NULL, + .sio_device = NULL, + .vid_device = NULL, + .snd_device = NULL, + .net_device = NULL + }, { .name = "[8086] Amstrad PPC512/640", .internal_name = "ppc512", diff --git a/src/sio/CMakeLists.txt b/src/sio/CMakeLists.txt index 6106871bc..dd4106fbd 100644 --- a/src/sio/CMakeLists.txt +++ b/src/sio/CMakeLists.txt @@ -18,6 +18,7 @@ add_library(sio OBJECT sio_acc3221.c sio_ali5123.c + sio_f82c606.c sio_f82c710.c sio_82091aa.c sio_fdc37c6xx.c diff --git a/src/sio/sio_f82c606.c b/src/sio/sio_f82c606.c new file mode 100644 index 000000000..20c377d62 --- /dev/null +++ b/src/sio/sio_f82c606.c @@ -0,0 +1,327 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Implementation of the Chips & Technologies 82C606 CHIPSpak + * Multifunction Controller. + * + * Relevant literature: + * + * [1] Chips and Technologies, Inc., + * 82C605/82C606 CHIPSpak/CHIPSport MULTIFUNCTION CONTROLLERS, + * PRELIMINARY Data Sheet, Revision 1, May 1987. + * + * + * Authors: Eluan Costa Miranda, + * Lubomir Rintel, + * Miran Grca, + * + * Copyright 2020-2025 Eluan Costa Miranda. + * Copyright 2021-2025 Lubomir Rintel. + * Copyright 2025 Miran Grca. + */ +#include +#include +#include +#include +#include +#include <86box/86box.h> +#include <86box/io.h> +#include <86box/timer.h> +#include <86box/device.h> +#include <86box/lpt.h> +#include <86box/serial.h> +#include <86box/gameport.h> +#include <86box/hdc.h> +#include <86box/hdc_ide.h> +#include <86box/fdd.h> +#include <86box/fdc.h> +#include <86box/nvr.h> +#include <86box/sio.h> +#include <86box/machine.h> + +typedef struct upc_t { + 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 */ + nvr_t *nvr; + void *gameport; + serial_t *uart[2]; +} upc_t; + +#ifdef ENABLE_F82C606_LOG +int f82c606_do_log = ENABLE_F82C606_LOG; + +static void +f82c606_log(const char *fmt, ...) +{ + va_list ap; + + if (f82c606_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +# define f82c606_log(fmt, ...) +#endif + +static void +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; + + serial_remove(dev->uart[0]); + serial_remove(dev->uart[1]); + lpt1_remove(); + lpt2_remove(); + + nvr_at_handler(0, ((uint16_t) dev->regs[3]) << 2, dev->nvr); + nvr_at_handler(0, 0x70, dev->nvr); + + gameport_remap(dev->gameport, 0); + + if (!set) + return; + + switch (dev->regs[8] & 0xc0) { + case 0x40: + nvr_int = 3; + break; + case 0x80: + uart1_int = COM2_IRQ; + break; + case 0xc0: + uart2_int = COM2_IRQ; + break; + + default: + break; + } + + switch (dev->regs[8] & 0x30) { + case 0x10: + nvr_int = 4; + break; + case 0x20: + uart1_int = COM1_IRQ; + break; + case 0x30: + uart2_int = COM1_IRQ; + break; + + default: + break; + } + + switch (dev->regs[8] & 0x0c) { + case 0x04: + nvr_int = 5; + break; + case 0x08: + uart1_int = 5; + break; + case 0x0c: + lpt1_int = LPT2_IRQ; + break; + + default: + break; + } + + switch (dev->regs[8] & 0x03) { + case 0x01: + nvr_int = 7; + break; + case 0x02: + uart2_int = 7; + break; + case 0x03: + lpt1_int = LPT1_IRQ; + break; + + default: + break; + } + + if (dev->regs[0] & 1) { + gameport_remap(dev->gameport, ((uint16_t) dev->regs[7]) << 2); + f82c606_log("Game port at %04X\n", ((uint16_t) dev->regs[7]) << 2); + } + + if (dev->regs[0] & 2) { + serial_setup(dev->uart[0], ((uint16_t) dev->regs[4]) << 2, uart1_int); + f82c606_log("UART 1 at %04X, IRQ %i\n", ((uint16_t) dev->regs[4]) << 2, uart1_int); + } + + if (dev->regs[0] & 4) { + serial_setup(dev->uart[1], ((uint16_t) dev->regs[5]) << 2, uart2_int); + f82c606_log("UART 2 at %04X, IRQ %i\n", ((uint16_t) dev->regs[5]) << 2, uart2_int); + } + + if (dev->regs[0] & 8) { + lpt1_setup(((uint16_t) dev->regs[6]) << 2); + lpt1_irq(lpt1_int); + f82c606_log("LPT1 at %04X, IRQ %i\n", ((uint16_t) dev->regs[6]) << 2, lpt1_int); + } + + nvr_at_handler(1, ((uint16_t) dev->regs[3]) << 2, dev->nvr); + nvr_irq_set(nvr_int, dev->nvr); + f82c606_log("RTC at %04X, IRQ %i\n", ((uint16_t) dev->regs[3]) << 2, nvr_int); +} + +static uint8_t +f82c606_config_read(uint16_t port, void *priv) +{ + const upc_t *dev = (upc_t *) priv; + uint8_t temp = 0xff; + + 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; +} + +static void +f82c606_config_write(uint16_t port, uint8_t val, void *priv) +{ + upc_t *dev = (upc_t *) priv; + int configuration_state_event = 0; + + switch (port) { + case 0x2fa: + if ((dev->configuration_state == 0) && (val != 0x00) && (val != 0xff)) { + configuration_state_event = 1; + dev->last_write = val; + } 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; + f82c606_update_ports(dev, 0); + /* TODO: is the value of cri reset here or when exiting configuration mode? */ + io_sethandler(dev->cri_addr, 0x0002, + f82c606_config_read, NULL, NULL, + f82c606_config_write, NULL, NULL, dev); + } else + dev->configuration_mode = 0; + } + break; + case 0x3fa: + if ((dev->configuration_state == 1) && ((val | dev->last_write) == 0xff)) + 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; + } + + 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, + f82c606_config_read, NULL, NULL, + f82c606_config_write, NULL, NULL, dev); + /* TODO: any benefit in updating at each register write instead of when exiting config mode? */ + f82c606_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 +f82c606_reset(void *priv) +{ + upc_t *dev = (upc_t *) priv; + + /* Set power-on defaults. */ + 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 */ + + f82c606_update_ports(dev, 1); +} + +static void +f82c606_close(void *priv) +{ + upc_t *dev = (upc_t *) priv; + + free(dev); +} + +static void * +f82c606_init(const device_t *info) +{ + upc_t *dev = (upc_t *) calloc(1, sizeof(upc_t)); + + dev->nvr = device_add(&at_nvr_old_device); + dev->gameport = gameport_add(&gameport_sio_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, f82c606_config_write, NULL, NULL, dev); + io_sethandler(0x03fa, 0x0001, NULL, NULL, NULL, f82c606_config_write, NULL, NULL, dev); + + f82c606_reset(dev); + + return dev; +} + +const device_t f82c606_device = { + .name = "82C606 CHIPSpak Multifunction Controller", + .internal_name = "f82c606", + .flags = 0, + .local = 0, + .init = f82c606_init, + .close = f82c606_close, + .reset = f82c606_reset, + .available = NULL, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; diff --git a/src/sio/sio_f82c710.c b/src/sio/sio_f82c710.c index a7aa101c6..b7ba6e424 100644 --- a/src/sio/sio_f82c710.c +++ b/src/sio/sio_f82c710.c @@ -6,23 +6,23 @@ * * This file is part of the 86Box distribution. * - * Implementation of the Chips & Technologies F82C710 Universal Peripheral - * Controller (UPC) and 82C606 CHIPSpak Multifunction Controller. + * Implementation of the Chips & Technologies F82C710 Universal + * Peripheral Controller (UPC). * * Relevant literature: * * [1] Chips and Technologies, Inc., - * 82C605/82C606 CHIPSpak/CHIPSport MULTIFUNCTION CONTROLLERS, - * PRELIMINARY Data Sheet, Revision 1, May 1987. - * + * 82710 Univeral Peripheral Controller, Data Sheet, + * PRELIMINARY, August 1990. + * * + * Authors: Eluan Costa Miranda, + * Lubomir Rintel, + * Miran Grca, * - * - * Authors: Eluan Costa Miranda - * Lubomir Rintel - * - * Copyright 2020 Eluan Costa Miranda. - * Copyright 2021 Lubomir Rintel. + * Copyright 2020-2025 Eluan Costa Miranda. + * Copyright 2021-2025 Lubomir Rintel. + * Copyright 2025 Miran Grca. */ #include #include @@ -40,24 +40,37 @@ #include <86box/hdc_ide.h> #include <86box/fdd.h> #include <86box/fdc.h> -#include <86box/nvr.h> +#include <86box/machine.h> +#include <86box/mouse.h> +#include <86box/plat_fallthrough.h> #include <86box/sio.h> +#include "cpu.h" typedef struct upc_t { - uint32_t local; - int configuration_state; /* state of algorithm to enter configuration mode */ + int configuration_state; /* State of algorithm to enter the + 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 next_value; + 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; + uint16_t mouse_base; - /* 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 */ + /* 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. */ + int serial_irq; + int lpt_irq; + int xta; fdc_t *fdc; - nvr_t *nvr; void *gameport; - serial_t *uart[2]; + void *mouse; + void *hdc_xta; + serial_t *uart; } upc_t; #ifdef ENABLE_F82C710_LOG @@ -79,150 +92,96 @@ f82c710_log(const char *fmt, ...) #endif static void -f82c710_update_ports(upc_t *dev, int set) +serial_handler(upc_t *dev) { - uint16_t com_addr = 0; - uint16_t lpt_addr = 0; + uint16_t com_addr = 0x0000; - serial_remove(dev->uart[0]); - serial_remove(dev->uart[1]); - lpt1_remove(); - lpt2_remove(); - fdc_remove(dev->fdc); - ide_pri_disable(); + serial_remove(dev->uart); - if (!set) - return; + if (dev->regs[0x00] & 0x04) { + com_addr = dev->regs[0x04] << 2; - if (dev->regs[0] & 4) { - com_addr = dev->regs[4] * 4; - if (com_addr == COM1_ADDR) - serial_setup(dev->uart[0], com_addr, COM1_IRQ); - else if (com_addr == COM2_ADDR) - serial_setup(dev->uart[1], com_addr, COM2_IRQ); + serial_setup(dev->uart, com_addr, dev->serial_irq); } - - if (dev->regs[0] & 8) { - lpt_addr = dev->regs[6] * 4; - lpt1_setup(lpt_addr); - if ((lpt_addr == LPT1_ADDR) || (lpt_addr == LPT_MDA_ADDR)) - lpt1_irq(LPT1_IRQ); - else if (lpt_addr == LPT2_ADDR) - lpt1_irq(LPT2_IRQ); - } - - if (dev->regs[12] & 0x80) - ide_pri_enable(); - - if (dev->regs[12] & 0x20) - fdc_set_base(dev->fdc, FDC_PRIMARY_ADDR); } static void -f82c606_update_ports(upc_t *dev, int set) +lpt_handler(upc_t *dev) { - uint8_t uart1_int = 0xff; - uint8_t uart2_int = 0xff; - uint8_t lpt1_int = 0xff; - int nvr_int = -1; + uint16_t lpt_addr = 0x0000; - serial_remove(dev->uart[0]); - serial_remove(dev->uart[1]); lpt1_remove(); - lpt2_remove(); - nvr_at_handler(0, ((uint16_t) dev->regs[3]) << 2, dev->nvr); - nvr_at_handler(0, 0x70, dev->nvr); + if (dev->regs[0x00] & 0x08) { + lpt_addr = dev->regs[0x06] << 2; - gameport_remap(dev->gameport, 0); + lpt1_setup(lpt_addr); + lpt1_irq(dev->lpt_irq); - if (!set) - return; + lpt_set_ext(0, !!(dev->regs[0x01] & 0x40)); + } +} - switch (dev->regs[8] & 0xc0) { - case 0x40: - nvr_int = 3; - break; - case 0x80: - uart1_int = COM2_IRQ; - break; - case 0xc0: - uart2_int = COM2_IRQ; - break; - - default: - break; +static void +ide_handler(upc_t *dev) +{ + if (dev->xta) { + if (dev->hdc_xta != NULL) + xta_handler(dev->hdc_xta, 0); + pclog("IDE XT interface disabled at 320-323\n"); + } else { + ide_pri_disable(); + pclog("IDE AT interface disabled at 1F0-1F7, 3F6-3F7\n"); } - switch (dev->regs[8] & 0x30) { - case 0x10: - nvr_int = 4; - break; - case 0x20: - uart1_int = COM1_IRQ; - break; - case 0x30: - uart2_int = COM1_IRQ; - break; + if (dev->regs[0x0c] & 0x80) { + if (dev->regs[0x0c] & 0x40) { + /* TODO: See what IDE mode the Amstrad PC5086 uses. */ + if (dev->xta && (dev->hdc_xta != NULL)) + xta_handler(dev->hdc_xta, 1); + pclog("IDE XT interface enabled at 320-323\n"); + } else { + if (!dev->xta) + ide_pri_enable(); + pclog("IDE AT interface enabled at 1F0-1F7, 3F6-3F7\n"); + } + } +} - default: - break; +static void +fdc_handler(upc_t *dev) +{ + fdc_remove(dev->fdc); + + if (dev->regs[0x0c] & 0x20) + fdc_set_base(dev->fdc, FDC_PRIMARY_ADDR); + + fdc_set_power_down(dev->fdc, !!(dev->regs[0x0c] & 0x10)); +} + +static void +mouse_handler(upc_t *dev) +{ + if (dev->mouse_base != 0x0000) { + pclog("QuickPort mouse interface disabled at %04X-%04X\n", dev->mouse_base, dev->mouse_base + 1); + mouse_upc_handler(0, dev->mouse_base, dev->mouse); } - switch (dev->regs[8] & 0x0c) { - case 0x04: - nvr_int = 5; - break; - case 0x08: - uart1_int = 5; - break; - case 0x0c: - lpt1_int = LPT2_IRQ; - break; + dev->mouse_base = dev->regs[0x0d] << 2; - default: - break; + if (dev->mouse_base != 0x0000) { + pclog("QuickPort mouse interface enabled at %04X-%04X\n", dev->mouse_base, dev->mouse_base + 1); + mouse_upc_handler(1, dev->mouse_base, dev->mouse); } +} - switch (dev->regs[8] & 0x03) { - case 0x01: - nvr_int = 7; - break; - case 0x02: - uart2_int = 7; - break; - case 0x03: - lpt1_int = LPT1_IRQ; - break; - - default: - break; - } - - if (dev->regs[0] & 1) { - gameport_remap(dev->gameport, ((uint16_t) dev->regs[7]) << 2); - f82c710_log("Game port at %04X\n", ((uint16_t) dev->regs[7]) << 2); - } - - if (dev->regs[0] & 2) { - serial_setup(dev->uart[0], ((uint16_t) dev->regs[4]) << 2, uart1_int); - f82c710_log("UART 1 at %04X, IRQ %i\n", ((uint16_t) dev->regs[4]) << 2, uart1_int); - } - - if (dev->regs[0] & 4) { - serial_setup(dev->uart[1], ((uint16_t) dev->regs[5]) << 2, uart2_int); - f82c710_log("UART 2 at %04X, IRQ %i\n", ((uint16_t) dev->regs[5]) << 2, uart2_int); - } - - if (dev->regs[0] & 8) { - lpt1_setup(((uint16_t) dev->regs[6]) << 2); - lpt1_irq(lpt1_int); - f82c710_log("LPT1 at %04X, IRQ %i\n", ((uint16_t) dev->regs[6]) << 2, lpt1_int); - } - - nvr_at_handler(1, ((uint16_t) dev->regs[3]) << 2, dev->nvr); - nvr_irq_set(nvr_int, dev->nvr); - f82c710_log("RTC at %04X, IRQ %i\n", ((uint16_t) dev->regs[3]) << 2, nvr_int); +static void +f82c710_update_ports(upc_t *dev) +{ + serial_handler(dev); + lpt_handler(dev); + ide_handler(dev); + fdc_handler(dev); } static uint8_t @@ -235,9 +194,9 @@ f82c710_config_read(uint16_t port, void *priv) if (port == dev->cri_addr) { temp = dev->cri; } else if (port == dev->cap_addr) { - if (dev->cri == 0xf) - temp = dev->cri_addr / 4; - else + if (dev->cri == 0x0f) + temp = dev->cri_addr >> 2; + else if (dev->cri < 0x0f) temp = dev->regs[dev->cri]; } } @@ -248,66 +207,112 @@ f82c710_config_read(uint16_t port, void *priv) static void f82c710_config_write(uint16_t port, uint8_t val, void *priv) { - upc_t *dev = (upc_t *) priv; - int configuration_state_event = 0; + upc_t * dev = (upc_t *) priv; + uint8_t valxor = 0x00; + int configuration_state_event = 0; switch (port) { + default: + break; case 0x2fa: - if ((dev->configuration_state == 0) && (val != 0x00) && (val != 0xff) && (dev->local == 606)) { + if (dev->configuration_state == 0) { 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->next_value = 0xff - val; + } else if (dev->configuration_state == 4) { + uint8_t addr_verify = dev->cri_addr >> 2; + addr_verify += val; + if (addr_verify == 0xff) { 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); + 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)) + if ((dev->configuration_state == 1) && (val == dev->next_value)) 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; + dev->cri_addr = val << 2; + dev->cap_addr = dev->cri_addr + 1; configuration_state_event = 1; } break; - default: - break; } if (dev->configuration_mode) { - if (port == dev->cri_addr) { + 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; + else if (port == dev->cap_addr) { + valxor = (dev->regs[dev->cri] ^ val); + switch (dev->cri) { + case 0x00: + dev->regs[dev->cri] = (dev->regs[dev->cri] & 0x10) | (val & 0xef); + if (valxor & 0x08) + lpt_handler(dev); + if (valxor & 0x04) + serial_handler(dev); + break; + case 0x01: + dev->regs[dev->cri] = (dev->regs[dev->cri] & 0x07) | (val & 0xf8); + if (valxor & 0x40) + serial_handler(dev); + break; + case 0x02: + dev->regs[dev->cri] = (dev->regs[dev->cri] & 0x08) | (val & 0xf0); + break; + case 0x03: + case 0x07: case 0x08: + /* TODO: Reserved - is it actually writable? */ + fallthrough; + case 0x09: case 0x0a: + case 0x0b: + dev->regs[dev->cri] = val; + break; + case 0x04: + dev->regs[dev->cri] = (dev->regs[dev->cri] & 0x01) | (val & 0xfe); + if (valxor & 0xfe) + serial_handler(dev); + break; + case 0x06: + dev->regs[dev->cri] = val; + if (valxor) + lpt_handler(dev); + break; + case 0x0c: + pclog("[%04X:%08X] [W] 0C = %02X\n", CS, cpu_state.pc, val); + dev->regs[dev->cri] = val; + if (valxor & 0xc0) + ide_handler(dev); + if (valxor & 0x30) + fdc_handler(dev); + break; + case 0x0d: + dev->regs[dev->cri] = val; + if (valxor) + mouse_handler(dev); + break; + case 0x0e: + dev->regs[dev->cri] = (dev->regs[dev->cri] & 0x20) | (val & 0xdf); + if (valxor) + mouse_handler(dev); + break; + case 0x0f: + dev->configuration_mode = 0; + io_removehandler(dev->cri_addr, 0x0002, + f82c710_config_read, NULL, NULL, + f82c710_config_write, NULL, NULL, dev); + break; + } } } /* TODO: is the state only reset when accessing 0x2fa and 0x3fa wrongly? */ - if ((port == 0x2fa || port == 0x3fa) && configuration_state_event) + if (((port == 0x2fa) || (port == 0x3fa)) && configuration_state_event) dev->configuration_state++; else dev->configuration_state = 0; @@ -318,39 +323,27 @@ f82c710_reset(void *priv) { upc_t *dev = (upc_t *) priv; - /* 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; - } + dev->configuration_state = 0; + dev->configuration_mode = 0; - if (dev->local == 606) - f82c606_update_ports(dev, 1); - else if (dev->local == 710) - f82c710_update_ports(dev, 1); + /* Set power-on defaults. */ + dev->regs[0x00] = 0x0c; + dev->regs[0x01] = 0x00; + dev->regs[0x02] = 0x00; + dev->regs[0x03] = 0x00; + dev->regs[0x04] = 0xfe; + dev->regs[0x05] = 0x00; + dev->regs[0x06] = 0x9e; + dev->regs[0x07] = 0x00; + dev->regs[0x08] = 0x00; + dev->regs[0x09] = 0xb0; + dev->regs[0x0a] = 0x00; + dev->regs[0x0b] = 0x00; + dev->regs[0x0c] = 0xa0; + dev->regs[0x0d] = 0x00; + dev->regs[0x0e] = 0x00; + + f82c710_update_ports(dev); } static void @@ -365,16 +358,18 @@ static void * f82c710_init(const device_t *info) { upc_t *dev = (upc_t *) calloc(1, 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) + if (strstr(machine_get_internal_name(), "5086") != NULL) + dev->fdc = device_add(&fdc_at_actlow_device); + else 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); + dev->uart = device_add_inst(&ns16450_device, 1); + + dev->mouse = device_add_params(&mouse_upc_device, (void *) (uintptr_t) (is8086 ? 2 : 12)); + + dev->serial_irq = device_get_config_int("serial_irq"); + dev->lpt_irq = device_get_config_int("lpt_irq"); 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); @@ -384,30 +379,124 @@ f82c710_init(const device_t *info) return dev; } -const device_t f82c606_device = { - .name = "82C606 CHIPSpak Multifunction Controller", - .internal_name = "f82c606", - .flags = 0, - .local = 606, - .init = f82c710_init, - .close = f82c710_close, - .reset = f82c710_reset, - .available = NULL, - .speed_changed = NULL, - .force_redraw = NULL, - .config = NULL +static void * +f82c710_pc5086_init(const device_t *info) +{ + upc_t *dev = f82c710_init(info); + + int hdc_present = device_get_config_int("hdc_present"); + + if (hdc_present) + dev->hdc_xta = device_add(&xta_st50x_pc5086_device); + + dev->xta = 1; + + return dev; +} + +static const device_config_t f82c710_config[] = { + { + .name = "serial_irq", + .description = "Serial port IRQ", + .type = CONFIG_SELECTION, + .default_string = NULL, + .default_int = 4, + .file_filter = NULL, + .spinner = { 0 }, + .selection = { + { .description = "IRQ 4", .value = 4 }, + { .description = "IRQ 3", .value = 3 }, + { .description = "" } + }, + .bios = { { 0 } } + }, + { + .name = "lpt_irq", + .description = "Parallel port IRQ", + .type = CONFIG_SELECTION, + .default_string = NULL, + .default_int = 7, + .file_filter = NULL, + .spinner = { 0 }, + .selection = { + { .description = "IRQ 7", .value = 7 }, + { .description = "IRQ 5", .value = 5 }, + { .description = "" } + }, + .bios = { { 0 } } + }, + { .name = "", .description = "", .type = CONFIG_END } +}; + +static const device_config_t f82c710_pc5086_config[] = { + { + .name = "serial_irq", + .description = "Serial port IRQ", + .type = CONFIG_SELECTION, + .default_string = NULL, + .default_int = 4, + .file_filter = NULL, + .spinner = { 0 }, + .selection = { + { .description = "IRQ 4", .value = 4 }, + { .description = "IRQ 3", .value = 3 }, + { .description = "" } + }, + .bios = { { 0 } } + }, + { + .name = "lpt_irq", + .description = "Parallel port IRQ", + .type = CONFIG_SELECTION, + .default_string = NULL, + .default_int = 7, + .file_filter = NULL, + .spinner = { 0 }, + .selection = { + { .description = "IRQ 7", .value = 7 }, + { .description = "IRQ 5", .value = 5 }, + { .description = "" } + }, + .bios = { { 0 } } + }, + { + .name = "hdc_present", + .description = "Hard disk", + .type = CONFIG_BINARY, + .default_string = NULL, + .default_int = 1, + .file_filter = NULL, + .spinner = { 0 }, + .selection = { { 0 } }, + .bios = { { 0 } } + }, + { .name = "", .description = "", .type = CONFIG_END } }; const device_t f82c710_device = { .name = "F82C710 UPC Super I/O", .internal_name = "f82c710", .flags = 0, - .local = 710, + .local = 0, .init = f82c710_init, .close = f82c710_close, .reset = f82c710_reset, .available = NULL, .speed_changed = NULL, .force_redraw = NULL, - .config = NULL + .config = f82c710_config +}; + +const device_t f82c710_pc5086_device = { + .name = "F82C710 UPC Super I/O (PC5086)", + .internal_name = "f82c710_pc5086", + .flags = 0, + .local = 0, + .init = f82c710_pc5086_init, + .close = f82c710_close, + .reset = f82c710_reset, + .available = NULL, + .speed_changed = NULL, + .force_redraw = NULL, + .config = f82c710_pc5086_config };