diff --git a/src/device/mouse_upc.c b/src/device/mouse_upc.c index 7a8d6143e..9f55c9214 100644 --- a/src/device/mouse_upc.c +++ b/src/device/mouse_upc.c @@ -226,6 +226,8 @@ mouse_upc_port_2_write(uint16_t port, uint8_t val, void *priv) if (dev->status & (STAT_CLEAR | STAT_RESET)) { /* TODO: Silently reset the mouse. */ + dev->status &= ~STAT_RX_FULL; + dev->status |= (STAT_DEV_IDLE | STAT_TX_IDLE); } } diff --git a/src/include/86box/machine.h b/src/include/86box/machine.h index b7deedd14..fe679775d 100644 --- a/src/include/86box/machine.h +++ b/src/include/86box/machine.h @@ -523,7 +523,11 @@ extern int machine_at_neat_ami_init(const machine_t *); extern int machine_at_3302_init(const machine_t *); extern int machine_at_px286_init(const machine_t *); +/* SCAMP */ +extern int machine_at_pc7286_init(const machine_t *); + /* SCAT */ +extern int machine_at_pc5286_init(const machine_t *); extern int machine_at_gw286ct_init(const machine_t *); extern int machine_at_gdc212m_init(const machine_t *); extern int machine_at_award286_init(const machine_t *); diff --git a/src/include/86box/sio.h b/src/include/86box/sio.h index 965aa3461..2bae49ae1 100644 --- a/src/include/86box/sio.h +++ b/src/include/86box/sio.h @@ -31,6 +31,12 @@ extern const device_t f82c710_pc5086_device; /* Commodore */ extern const device_t cbm_io_device; +/* Dataworld 90C50 (COMBAT) */ +#define DW90C50_IDE 0x00001 + +extern const device_t dw90c50_device; + +extern const device_t pc87310_device; /* SM(S)C */ #define FDC37C651 0x00051 #define FDC37C661 0x00061 diff --git a/src/machine/m_at_286.c b/src/machine/m_at_286.c index 7ae3c2a18..5448272b6 100644 --- a/src/machine/m_at_286.c +++ b/src/machine/m_at_286.c @@ -798,13 +798,38 @@ machine_at_px286_init(const machine_t *model) return ret; } +/* SCAMP */ +int +machine_at_pc7286_init(const machine_t *model) +{ + int ret; + + ret = bios_load_linear("roms/machines/pc7286/PC7286 BIOS (AM27C010@DIP32).BIN", + 0x000e0000, 131072, 0); + + if (bios_only || !ret) + return ret; + + machine_at_common_init_ex(model, 2); + + if (gfxcard[0] == VID_INTERNAL) + device_add(&gd5401_onboard_device); + + device_add_params(&dw90c50_device, (void *) DW90C50_IDE); + device_add(&vl82c113_device); /* The keyboard controller is part of the VL82c113. */ + + device_add(&vlsi_scamp_device); + + return ret; +} + /* SCAT */ static void machine_at_scat_init(const machine_t *model, int is_v4, int is_ami) { machine_at_common_init(model); - if (machines[machine].bus_flags & MACHINE_BUS_PS2) { + if ((machines[machine].bus_flags & MACHINE_BUS_PS2) && (strcmp(machine_get_internal_name(), "pc5286"))) { if (is_ami) device_add(&kbc_ps2_ami_device); else @@ -822,6 +847,30 @@ machine_at_scat_init(const machine_t *model, int is_v4, int is_ami) device_add(&scat_device); } +int +machine_at_pc5286_init(const machine_t *model) +{ + int ret; + + ret = bios_load_linear("roms/machines/pc5286/PC5286", + 0x000f0000, 65536, 0); + + if (bios_only || !ret) + return ret; + + /* Patch the checksum to avoid checksum error. */ + if (rom[0xffff] == 0x2c) + rom[0xffff] = 0x2b; + + machine_at_scat_init(model, 1, 0); + + device_add(&f82c710_device); + + device_add(&ide_isa_device); + + return ret; +} + int machine_at_gw286ct_init(const machine_t *model) { @@ -833,10 +882,10 @@ machine_at_gw286ct_init(const machine_t *model) if (bios_only || !ret) return ret; - device_add(&f82c710_device); - machine_at_scat_init(model, 1, 0); + device_add(&f82c710_device); + device_add(&ide_isa_device); return ret; diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index 78e98bb56..984b3932d 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -4191,6 +4191,92 @@ const machine_t machines[] = { .snd_device = NULL, .net_device = NULL }, + /* Has Quadtel KBC firmware. */ + { + .name = "[SCAMP] Amstrad PC7286", + .internal_name = "pc7286", + .type = MACHINE_TYPE_286, + .chipset = MACHINE_CHIPSET_VLSI_SCAMP, + .init = machine_at_pc7286_init, + .p1_handler = NULL, + .gpio_handler = NULL, + .available_flag = MACHINE_AVAILABLE, + .gpio_acpi_handler = NULL, + .cpu = { + .package = CPU_PKG_286, + .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_PS2, + .flags = MACHINE_IDE | MACHINE_VIDEO, + .ram = { + .min = 1024, + .max = 32768, + .step = 1024 + }, + .nvrmask = 127, + .jumpered_ecp_dma = 0, + .default_jumpered_ecp_dma = -1, + .kbc_device = NULL, + .kbc_p1 = 0xff, + .gpio = 0xffffffff, + .gpio_acpi = 0xffffffff, + .device = NULL, + .kbd_device = NULL, + .fdc_device = NULL, + .sio_device = NULL, + .vid_device = NULL, + .snd_device = NULL, + .net_device = NULL + }, + /* Has unknown KBC firmware. */ + { + .name = "[SCAT] Amstrad PC5286", + .internal_name = "pc5286", + .type = MACHINE_TYPE_286, + .chipset = MACHINE_CHIPSET_SCAT, + .init = machine_at_pc5286_init, + .p1_handler = NULL, + .gpio_handler = NULL, + .available_flag = MACHINE_AVAILABLE, + .gpio_acpi_handler = NULL, + .cpu = { + .package = CPU_PKG_286, + .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_AT | MACHINE_BUS_PS2, + .flags = MACHINE_IDE, + .ram = { + .min = 512, + .max = 16384, + .step = 128 + }, + .nvrmask = 127, + .jumpered_ecp_dma = 0, + .default_jumpered_ecp_dma = -1, + .kbc_device = NULL, + .kbc_p1 = 0xff, + .gpio = 0xffffffff, + .gpio_acpi = 0xffffffff, + .device = &f82c710_device, + .kbd_device = NULL, + .fdc_device = NULL, + .sio_device = NULL, + .vid_device = NULL, + .snd_device = NULL, + .net_device = NULL + }, /* Has Chips & Technologies KBC firmware. */ { .name = "[SCAT] GW-286CT GEAR", @@ -4226,7 +4312,7 @@ const machine_t machines[] = { .kbc_p1 = 0xff, .gpio = 0xffffffff, .gpio_acpi = 0xffffffff, - .device = NULL, + .device = &f82c710_device, .kbd_device = NULL, .fdc_device = NULL, .sio_device = NULL, diff --git a/src/sio/CMakeLists.txt b/src/sio/CMakeLists.txt index 3fb1eda66..b2ca643cf 100644 --- a/src/sio/CMakeLists.txt +++ b/src/sio/CMakeLists.txt @@ -17,6 +17,7 @@ add_library(sio OBJECT sio_82091aa.c + sio_90c50.c sio_acc3221.c sio_ali5123.c sio_cbm_io.c diff --git a/src/sio/sio_90c50.c b/src/sio/sio_90c50.c new file mode 100644 index 000000000..5e7f19b6f --- /dev/null +++ b/src/sio/sio_90c50.c @@ -0,0 +1,266 @@ +/* + * 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. + * + * Emulation of the Dataworld 90C50 (COMBAT) Super I/O chip. + * + * Authors: Miran Grca, + * + * Copyright 2020-2025 Miran Grca. + */ +#include +#include +#include +#include +#include +#include +#define HAVE_STDARG_H +#include <86box/86box.h> +#include <86box/io.h> +#include <86box/timer.h> +#include <86box/device.h> +#include <86box/lpt.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/sio.h> +#include <86box/plat_unused.h> + +#ifdef ENABLE_90C50_LOG +int dw90c50_do_log = ENABLE_90C50_LOG; + +static void +dw90c50_log(const char *fmt, ...) +{ + va_list ap; + + if (dw90c50_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +# define dw90c50_log(fmt, ...) +#endif + +typedef struct dw90c50_t { + uint8_t flags; + uint8_t reg; + fdc_t *fdc; + serial_t *uart[2]; + lpt_t *lpt; +} dw90c50_t; + +static void +lpt_handler(dw90c50_t *dev) +{ + int temp; + uint16_t lpt_port = LPT1_ADDR; + uint8_t lpt_irq = LPT1_IRQ; + + /* bits 0-1: + * 00 378h + * 01 3bch + * 10 278h + * 11 disabled + */ + temp = (dev->reg & 0x06) >> 1; + + lpt_port_remove(dev->lpt); + + switch (temp) { + case 0x00: + lpt_port = 0x000; + lpt_irq = 0xff; + break; + case 0x01: + lpt_port = LPT_MDA_ADDR; + break; + case 0x02: + lpt_port = LPT1_ADDR; + break; + case 0x03: + lpt_port = LPT2_ADDR; + break; + + default: + break; + } + + if (lpt_port) + lpt_port_setup(dev->lpt, lpt_port); + + lpt_port_irq(dev->lpt, lpt_irq); +} + +static void +serial_handler(dw90c50_t *dev) +{ + uint16_t base1 = 0x0000, base2 = 0x0000; + uint8_t irq1, irq2; + + serial_remove(dev->uart[0]); + serial_remove(dev->uart[1]); + + switch ((dev->reg >> 3) & 0x07) { + case 0x0001: + base1 = 0x03f8; + break; + case 0x0002: + base2 = 0x02f8; + break; + case 0x0003: + base1 = 0x03f8; + base2 = 0x02f8; + break; + case 0x0004: + base1 = 0x03e8; + base2 = 0x02e8; + break; + case 0x0006: + base2 = 0x03f8; + break; + case 0x0007: + base1 = 0x02f8; + base2 = 0x03f8; + break; + } + + irq1 = (base1 & 0x0100) ? COM1_IRQ : COM2_IRQ; + irq2 = (base2 & 0x0100) ? COM1_IRQ : COM2_IRQ; + + if (base1 != 0x0000) + serial_setup(dev->uart[0], base1, irq1); + + if (base2 != 0x0000) + serial_setup(dev->uart[0], base2, irq2); +} + +static void +dw90c50_write(UNUSED(uint16_t port), uint8_t val, void *priv) +{ + dw90c50_t *dev = (dw90c50_t *) priv; + uint8_t valxor; + + dw90c50_log("[%04X:%08X] [W] %02X = %02X (%i)\n", CS, cpu_state.pc, port, val, dev->tries); + + /* Second write to config register. */ + valxor = val ^ dev->reg; + dev->reg = val; + + dw90c50_log("SIO: Register written %02X\n", val); + + /* Reconfigure floppy disk controller. */ + if (valxor & 0x01) { + dw90c50_log("SIO: FDC disabled\n"); + fdc_remove(dev->fdc); + /* Bit 0: 1 = Enable FDC. */ + if (val & 0x01) { + dw90c50_log("SIO: FDC enabled\n"); + fdc_set_base(dev->fdc, FDC_PRIMARY_ADDR); + } + } + + /* Reconfigure parallel port. */ + if (valxor & 0x06) + lpt_handler(dev); + + /* Reconfigure serial ports. */ + if (valxor & 0x38) + serial_handler(dev); + + /* Reconfigure IDE controller. */ + if ((dev->flags & PCX73XX_IDE) && (valxor & 0x40)) { + dw90c50_log("SIO: HDC disabled\n"); + ide_pri_disable(); + /* Bit 6: 1 = Enable IDE controller. */ + if (val & 0x40) { + dw90c50_log("SIO: HDC enabled\n"); + ide_set_base(0, 0x1f0); + ide_set_side(0, 0x3f6); + ide_pri_enable(); + } + } +} + +uint8_t +dw90c50_read(UNUSED(uint16_t port), void *priv) +{ + dw90c50_t *dev = (dw90c50_t *) priv; + uint8_t ret = 0xff; + + ret = dev->reg; + + dw90c50_log("[%04X:%08X] [R] %02X = %02X\n", CS, cpu_state.pc, port, ret); + + return ret; +} + +void +dw90c50_reset(dw90c50_t *dev) +{ + fdc_reset(dev->fdc); + + dev->reg = 0x62; + dw90c50_write(0x03f3, 0x9d, dev); +} + +static void +dw90c50_close(void *priv) +{ + dw90c50_t *dev = (dw90c50_t *) priv; + + free(dev); +} + +static void * +dw90c50_init(const device_t *info) +{ + dw90c50_t *dev = (dw90c50_t *) calloc(1, sizeof(dw90c50_t)); + + /* Avoid conflicting with machines that make no use of the 90C50 Internal IDE */ + dev->flags = info->local; + + dev->fdc = device_add(&fdc_at_nsc_device); + + dev->uart[0] = device_add_inst(&ns16450_device, 1); + dev->uart[1] = device_add_inst(&ns16450_device, 2); + + dev->lpt = device_add_inst(&lpt_port_device, 1); + lpt_set_ext(dev->lpt, 1); + + if (dev->flags & DW90C50_IDE) + device_add(&ide_isa_device); + + dw90c50_reset(dev); + + io_sethandler(0x03f3, 0x0001, + dw90c50_read, NULL, NULL, dw90c50_write, NULL, NULL, dev); + + return dev; +} + +const device_t dw90c50_device = { + .name = "National Semiconductor 90C50 Super I/O", + .internal_name = "90c50", + .flags = 0, + .local = 0, + .init = dw90c50_init, + .close = dw90c50_close, + .reset = NULL, + .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 0e9cb0d9b..f7ff1409f 100644 --- a/src/sio/sio_f82c710.c +++ b/src/sio/sio_f82c710.c @@ -357,7 +357,7 @@ f82c710_init(const device_t *info) dev->uart = device_add_inst(&ns16450_device, 1); dev->lpt = device_add_inst(&lpt_port_device, 1); - dev->mouse = device_add_params(&mouse_upc_device, (void *) (uintptr_t) (is8086 ? 2 : 12)); + dev->mouse = device_add_params(&mouse_upc_device, (void *) (uintptr_t) (is286 ? 12 : 2)); dev->serial_irq = device_get_config_int("serial_irq"); dev->lpt_irq = device_get_config_int("lpt_irq");