Split off the F82C606 from the F82C710, rewritten the F82C710, implemented the PS/2 QuickPort mouse interface and added it as standalone, added the Seagate XTA controller, and added the Amstrad PC5086.

This commit is contained in:
OBattler
2025-07-15 01:06:47 +02:00
parent 132b7e0e62
commit 2717f38627
18 changed files with 1430 additions and 294 deletions

View File

@@ -47,6 +47,7 @@ add_library(dev OBJECT
mouse_microtouch_touchscreen.c mouse_microtouch_touchscreen.c
mouse_ps2.c mouse_ps2.c
mouse_serial.c mouse_serial.c
mouse_upc.c
nec_mate_unk.c nec_mate_unk.c
novell_cardkey.c novell_cardkey.c
pci_bridge.c pci_bridge.c

View File

@@ -8,13 +8,11 @@
* *
* Intel 8042 (AT keyboard controller) emulation. * Intel 8042 (AT keyboard controller) emulation.
* *
*
*
* Authors: Miran Grca, <mgrca8@gmail.com> * Authors: Miran Grca, <mgrca8@gmail.com>
* EngiNerd, <webmaster.crrc@yahoo.it> * EngiNerd, <webmaster.crrc@yahoo.it>
* *
* Copyright 2023 Miran Grca. * Copyright 2023-2025 Miran Grca.
* Copyright 2023 EngiNerd. * Copyright 2023-2025 EngiNerd.
*/ */
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
@@ -29,24 +27,19 @@
#include <86box/timer.h> #include <86box/timer.h>
#include <86box/io.h> #include <86box/io.h>
#include <86box/pic.h> #include <86box/pic.h>
#include <86box/pit.h>
#include <86box/plat_fallthrough.h> #include <86box/plat_fallthrough.h>
#include <86box/plat_unused.h> #include <86box/plat_unused.h>
#include <86box/ppi.h>
#include <86box/mem.h> #include <86box/mem.h>
#include <86box/device.h> #include <86box/device.h>
#include <86box/dma.h>
#include <86box/machine.h> #include <86box/machine.h>
#include <86box/m_at_t3100e.h> #include <86box/m_at_t3100e.h>
#include <86box/fdd.h> #include <86box/fdd.h>
#include <86box/fdc.h> #include <86box/fdc.h>
#include <86box/sound.h> #include <86box/pci.h>
#include <86box/snd_speaker.h>
#include <86box/video.h> #include <86box/video.h>
#include <86box/keyboard.h> #include <86box/keyboard.h>
#include <86box/dma.h>
#include <86box/pci.h>
#define STAT_PARITY 0x80 #define STAT_PARITY 0x80
#define STAT_RTIMEOUT 0x40 #define STAT_RTIMEOUT 0x40
#define STAT_TTIMEOUT 0x20 #define STAT_TTIMEOUT 0x20

View File

@@ -8,11 +8,9 @@
* *
* AT / PS/2 attached device emulation. * AT / PS/2 attached device emulation.
* *
*
*
* Authors: Miran Grca, <mgrca8@gmail.com> * Authors: Miran Grca, <mgrca8@gmail.com>
* *
* Copyright 2023 Miran Grca. * Copyright 2023-2025 Miran Grca.
*/ */
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
@@ -22,23 +20,9 @@
#define HAVE_STDARG_H #define HAVE_STDARG_H
#include <wchar.h> #include <wchar.h>
#include <86box/86box.h> #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/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/plat_fallthrough.h>
#include <86box/keyboard.h>
#ifdef ENABLE_KBC_AT_DEV_LOG #ifdef ENABLE_KBC_AT_DEV_LOG
int kbc_at_dev_do_log = ENABLE_KBC_AT_DEV_LOG; int kbc_at_dev_do_log = ENABLE_KBC_AT_DEV_LOG;

View File

@@ -95,6 +95,7 @@ static mouse_t mouse_devices[] = {
{ &mouse_msserial_device }, { &mouse_msserial_device },
{ &mouse_ltserial_device }, { &mouse_ltserial_device },
{ &mouse_ps2_device }, { &mouse_ps2_device },
{ &mouse_upc_standalone_device },
#ifdef USE_WACOM #ifdef USE_WACOM
{ &mouse_wacom_device }, { &mouse_wacom_device },
{ &mouse_wacom_artpad_device }, { &mouse_wacom_artpad_device },

View File

@@ -10,7 +10,7 @@
* *
* Authors: Miran Grca, <mgrca8@gmail.com> * Authors: Miran Grca, <mgrca8@gmail.com>
* *
* Copyright 2023 Miran Grca. * Copyright 2023-2025 Miran Grca.
*/ */
#include <stdarg.h> #include <stdarg.h>
#include <stdatomic.h> #include <stdatomic.h>
@@ -21,7 +21,6 @@
#include <wchar.h> #include <wchar.h>
#define HAVE_STDARG_H #define HAVE_STDARG_H
#include <86box/86box.h> #include <86box/86box.h>
#include "cpu.h"
#include <86box/device.h> #include <86box/device.h>
#include <86box/keyboard.h> #include <86box/keyboard.h>
#include <86box/mouse.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); atkbc_dev_t *dev = kbc_at_dev_init(DEV_AUX);
int i; int i;
if (info->local & MOUSE_TYPE_QPORT)
device_context(&mouse_upc_standalone_device);
dev->name = info->name; dev->name = info->name;
dev->type = info->local; dev->type = info->local;
@@ -382,6 +384,9 @@ mouse_ps2_init(const device_t *info)
mouse_set_poll(ps2_poll, dev); 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 our private data to the I/O layer. */
return dev; return dev;
} }

445
src/device/mouse_upc.c Normal file
View File

@@ -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, <mgrca8@gmail.com>
*
* Copyright 2025 Miran Grca.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#define HAVE_STDARG_H
#include <wchar.h>
#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
};

View File

@@ -63,6 +63,7 @@ static const struct {
{ &xtide_device }, { &xtide_device },
{ &st506_xt_st11_m_device }, { &st506_xt_st11_m_device },
{ &st506_xt_st11_r_device }, { &st506_xt_st11_r_device },
{ &xta_st50x_device },
{ &st506_xt_victor_v86p_device }, { &st506_xt_victor_v86p_device },
{ &st506_xt_wd1002a_27x_device }, { &st506_xt_wd1002a_27x_device },
{ &st506_xt_wd1002a_wx1_device }, { &st506_xt_wd1002a_wx1_device },

View File

@@ -101,11 +101,14 @@
#include <86box/ui.h> #include <86box/ui.h>
#include <86box/hdc.h> #include <86box/hdc.h>
#include <86box/hdd.h> #include <86box/hdd.h>
#include "cpu.h"
#define HDC_TIME (250 * TIMER_USEC) #define HDC_TIME (250 * TIMER_USEC)
#define WD_REV_1_BIOS_FILE "roms/hdd/xta/idexywd2.bin" #define WD_REV_1_BIOS_FILE "roms/hdd/xta/idexywd2.bin"
#define WD_REV_2_BIOS_FILE "roms/hdd/xta/infowdbios.rom" #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 { enum {
STATE_IDLE = 0, STATE_IDLE = 0,
@@ -236,6 +239,7 @@ typedef struct hdc_t {
const char *name; /* controller name */ const char *name; /* controller name */
uint16_t base; /* controller base I/O address */ uint16_t base; /* controller base I/O address */
uint8_t sw; /* controller switches */
int8_t irq; /* controller IRQ channel */ int8_t irq; /* controller IRQ channel */
int8_t dma; /* controller DMA channel */ int8_t dma; /* controller DMA channel */
int8_t type; /* controller type ID */ int8_t type; /* controller type ID */
@@ -269,6 +273,11 @@ typedef struct hdc_t {
uint8_t sector_buf[512]; /* sector buffer */ uint8_t sector_buf[512]; /* sector buffer */
} hdc_t; } hdc_t;
typedef struct hdc_dual_t {
hdc_t *hdc[2];
} hdc_dual_t;
#define ENABLE_XTA_LOG 1
#ifdef ENABLE_XTA_LOG #ifdef ENABLE_XTA_LOG
int xta_do_log = 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; hdc_t *dev = (hdc_t *) priv;
uint8_t ret = 0xff; uint8_t ret = 0xff;
switch (port & 7) { switch (port & 3) {
case 0: /* DATA register */ case 0: /* DATA register */
dev->status &= ~STAT_IRQ; dev->status &= ~STAT_IRQ;
@@ -915,13 +924,15 @@ hdc_read(uint16_t port, void *priv)
break; break;
case 2: /* "read option jumpers" */ case 2: /* "read option jumpers" */
ret = 0xff; /* all switches off */ ret = dev->sw;
break; break;
default: default:
break; break;
} }
pclog("[%04X:%08X] XTA: [R] %04X = %02X\n", CS, cpu_state.pc, port, ret);
return ret; return ret;
} }
@@ -931,7 +942,9 @@ hdc_write(uint16_t port, uint8_t val, void *priv)
{ {
hdc_t *dev = (hdc_t *) 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 */ case 0: /* DATA register */
if (dev->state == STATE_RDATA) { if (dev->state == STATE_RDATA) {
if (!(dev->status & STAT_REQ)) { if (!(dev->status & STAT_REQ)) {
@@ -989,39 +1002,82 @@ 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 * static void *
xta_init(const device_t *info) xta_init_common(const device_t *info, int type)
{ {
drive_t *drive; drive_t *drive;
const char *bios_rev = NULL; const char *bios_rev = NULL;
const char *fn = NULL; const char *fn = NULL;
hdc_t *dev; hdc_t *dev;
int c; int c;
int min = 0;
int max = XTA_NUM; int max = XTA_NUM;
/* Allocate and initialize device block. */ /* Allocate and initialize device block. */
dev = calloc(1, sizeof(hdc_t)); dev = calloc(1, sizeof(hdc_t));
dev->type = info->local;
dev->sw = 0xff; /* all switches off */
dev->type = type;
/* Do per-controller-type setup. */ /* Do per-controller-type setup. */
switch (dev->type) { switch (dev->type) {
case 0: /* WDXT-150, with BIOS */ case 0: /* WDXT-150, with BIOS */
dev->name = "WDXT-150"; 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->base = device_get_config_hex16("base");
dev->irq = device_get_config_int("irq"); dev->irq = device_get_config_int("irq");
dev->rom_addr = device_get_config_hex20("bios_addr"); dev->rom_addr = device_get_config_hex20("bios_addr");
dev->dma = 3; 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; break;
case 1: /* EuroPC */ case 1: /* EuroPC */
case 3: /* Amstrad PC5086 */
switch (dev->type) {
case 1:
dev->name = "HD20"; 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->base = 0x0320;
dev->irq = 5; dev->irq = 5;
dev->dma = 3; dev->dma = 3;
break; 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: default:
break; break;
@@ -1029,6 +1085,8 @@ xta_init(const device_t *info)
xta_log("%s: initializing (I/O=%04X, IRQ=%d, DMA=%d", xta_log("%s: initializing (I/O=%04X, IRQ=%d, DMA=%d",
dev->name, dev->base, dev->irq, dev->dma); 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) if (dev->rom_addr != 0x000000)
xta_log(", BIOS=%06X", dev->rom_addr); xta_log(", BIOS=%06X", dev->rom_addr);
@@ -1037,7 +1095,10 @@ xta_init(const device_t *info)
/* Load any disks for this device class. */ /* Load any disks for this device class. */
c = 0; c = 0;
for (uint8_t i = 0; i < HDD_NUM; i++) { for (uint8_t i = 0; i < HDD_NUM; i++) {
if ((hdd[i].bus_type == HDD_BUS_XTA) && (hdd[i].xta_channel < max)) { 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]; drive = &dev->drives[hdd[i].xta_channel];
if (!hdd_image_load(i)) { if (!hdd_image_load(i)) {
@@ -1058,9 +1119,29 @@ xta_init(const device_t *info)
drive->hpc = drive->cfg_hpc; drive->hpc = drive->cfg_hpc;
drive->tracks = drive->cfg_tracks; 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", xta_log("%s: drive%d (cyl=%d,hd=%d,spt=%d), disk %d\n",
dev->name, hdd[i].xta_channel, drive->tracks, dev->name, hdd[i].xta_channel, drive->tracks,
drive->hpc, drive->spt, i); 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) if (++c > max)
break; break;
@@ -1083,6 +1164,23 @@ xta_init(const device_t *info)
return dev; 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 static void
xta_close(void *priv) xta_close(void *priv)
{ {
@@ -1104,6 +1202,21 @@ xta_close(void *priv)
free(dev); 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[] = { static const device_config_t wdxt150_config[] = {
// clang-format off // clang-format off
{ {
@@ -1185,6 +1298,27 @@ static const device_config_t wdxt150_config[] = {
// clang-format off // 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 = { const device_t xta_wdxt150_device = {
.name = "WDXT-150 XTA Fixed Disk Controller", .name = "WDXT-150 XTA Fixed Disk Controller",
.internal_name = "xta_wdxt150", .internal_name = "xta_wdxt150",
@@ -1212,3 +1346,32 @@ const device_t xta_hd20_device = {
.force_redraw = NULL, .force_redraw = NULL,
.config = 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
};

View File

@@ -104,6 +104,8 @@ extern const device_t mcide_device;
extern const device_t xta_wdxt150_device; /* xta_wdxt150 */ extern const device_t xta_wdxt150_device; /* xta_wdxt150 */
extern const device_t xta_hd20_device; /* EuroPC internal */ 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_device; /* xtide_xt */
extern const device_t xtide_at_device; /* xtide_at */ 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_get_flags(int hdc);
extern int hdc_available(int hdc); extern int hdc_available(int hdc);
extern void xta_handler(void *priv, int set);
#endif /*EMU_HDC_H*/ #endif /*EMU_HDC_H*/

View File

@@ -917,6 +917,9 @@ extern int machine_xt_m19_init(const machine_t *);
/* m_pcjr.c */ /* m_pcjr.c */
extern int machine_pcjr_init(const machine_t *); extern int machine_pcjr_init(const machine_t *);
/* m_pc5086.c */
extern int machine_pc5086_init(const machine_t *);
/* m_ps1.c */ /* m_ps1.c */
extern int machine_ps1_m2011_init(const machine_t *); extern int machine_ps1_m2011_init(const machine_t *);
extern int machine_ps1_m2121_init(const machine_t *); extern int machine_ps1_m2121_init(const machine_t *);

View File

@@ -42,7 +42,9 @@
#define MOUSE_TYPE_WACOM 12 /* WACOM tablet */ #define MOUSE_TYPE_WACOM 12 /* WACOM tablet */
#define MOUSE_TYPE_WACOMARTP 13 /* WACOM tablet (ArtPad) */ #define MOUSE_TYPE_WACOMARTP 13 /* WACOM tablet (ArtPad) */
#define MOUSE_TYPE_MSYSTEMSB 14 /* Mouse Systems bus mouse */ #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. */ #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_msserial_device;
extern const device_t mouse_ltserial_device; extern const device_t mouse_ltserial_device;
extern const device_t mouse_ps2_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 # ifdef USE_WACOM
extern const device_t mouse_wacom_device; extern const device_t mouse_wacom_device;
extern const device_t mouse_wacom_artpad_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_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 #ifdef __cplusplus
} }
#endif #endif

View File

@@ -26,6 +26,7 @@ extern const device_t ali5123_device;
extern const device_t f82c606_device; extern const device_t f82c606_device;
extern const device_t f82c710_device; extern const device_t f82c710_device;
extern const device_t f82c710_pc5086_device;
/* SM(S)C */ /* SM(S)C */
extern const device_t fdc37c651_device; extern const device_t fdc37c651_device;

View File

@@ -27,6 +27,7 @@ add_library(mch OBJECT
m_xt_zenith.c m_xt_zenith.c
m_pcjr.c m_pcjr.c
m_amstrad.c m_amstrad.c
m_amstrad_pc5x86.c
m_europc.c m_europc.c
m_elt.c m_elt.c
m_xt_olivetti.c m_xt_olivetti.c

View File

@@ -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, <mgrca8@gmail.com>
*
* 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 <stdio.h>
#include <stdint.h>
#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;
}

View File

@@ -30,6 +30,7 @@
#include <86box/fdd.h> #include <86box/fdd.h>
#include <86box/fdc.h> #include <86box/fdc.h>
#include <86box/keyboard.h> #include <86box/keyboard.h>
#include <86box/sio.h>
#include <86box/sound.h> #include <86box/sound.h>
#include <86box/video.h> #include <86box/video.h>
#include <86box/plat_unused.h> #include <86box/plat_unused.h>
@@ -2267,6 +2268,45 @@ const machine_t machines[] = {
.snd_device = NULL, .snd_device = NULL,
.net_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", .name = "[8086] Amstrad PPC512/640",
.internal_name = "ppc512", .internal_name = "ppc512",

View File

@@ -18,6 +18,7 @@
add_library(sio OBJECT add_library(sio OBJECT
sio_acc3221.c sio_acc3221.c
sio_ali5123.c sio_ali5123.c
sio_f82c606.c
sio_f82c710.c sio_f82c710.c
sio_82091aa.c sio_82091aa.c
sio_fdc37c6xx.c sio_fdc37c6xx.c

327
src/sio/sio_f82c606.c Normal file
View File

@@ -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.
* <https://archive.org/download/82C606/82C606.pdf>
*
* Authors: Eluan Costa Miranda, <eluancm@gmail.com>
* Lubomir Rintel, <lkundrak@v3.sk>
* Miran Grca, <mgrca8@gmail.com>
*
* Copyright 2020-2025 Eluan Costa Miranda.
* Copyright 2021-2025 Lubomir Rintel.
* Copyright 2025 Miran Grca.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#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
};

View File

@@ -6,23 +6,23 @@
* *
* This file is part of the 86Box distribution. * This file is part of the 86Box distribution.
* *
* Implementation of the Chips & Technologies F82C710 Universal Peripheral * Implementation of the Chips & Technologies F82C710 Universal
* Controller (UPC) and 82C606 CHIPSpak Multifunction Controller. * Peripheral Controller (UPC).
* *
* Relevant literature: * Relevant literature:
* *
* [1] Chips and Technologies, Inc., * [1] Chips and Technologies, Inc.,
* 82C605/82C606 CHIPSpak/CHIPSport MULTIFUNCTION CONTROLLERS, * 82710 Univeral Peripheral Controller, Data Sheet,
* PRELIMINARY Data Sheet, Revision 1, May 1987. * PRELIMINARY, August 1990.
* <https://archive.org/download/82C606/82C606.pdf> * <http://66.113.161.23/~mR_Slug/pub/datasheets/chipsets/CandT/82C710.pdf>
* *
* Authors: Eluan Costa Miranda, <eluancm@gmail.com>
* Lubomir Rintel, <lkundrak@v3.sk>
* Miran Grca, <mgrca8@gmail.com>
* *
* * Copyright 2020-2025 Eluan Costa Miranda.
* Authors: Eluan Costa Miranda <eluancm@gmail.com> * Copyright 2021-2025 Lubomir Rintel.
* Lubomir Rintel <lkundrak@v3.sk> * Copyright 2025 Miran Grca.
*
* Copyright 2020 Eluan Costa Miranda.
* Copyright 2021 Lubomir Rintel.
*/ */
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
@@ -40,24 +40,37 @@
#include <86box/hdc_ide.h> #include <86box/hdc_ide.h>
#include <86box/fdd.h> #include <86box/fdd.h>
#include <86box/fdc.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 <86box/sio.h>
#include "cpu.h"
typedef struct upc_t { typedef struct upc_t {
uint32_t local; int configuration_state; /* State of algorithm to enter the
int configuration_state; /* state of algorithm to enter configuration mode */ configuration mode. */
int configuration_mode; int configuration_mode;
uint16_t cri_addr; /* cri = configuration index register, addr is even */ uint8_t next_value;
uint16_t cap_addr; /* cap = configuration access port, addr is odd and is cri_addr + 1 */ uint16_t cri_addr; /* CRI = Configuration Index Register,
uint8_t cri; /* currently indexed 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; uint8_t last_write;
uint16_t mouse_base;
/* these regs are not affected by reset */ /* 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 */ 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; fdc_t *fdc;
nvr_t *nvr;
void *gameport; void *gameport;
serial_t *uart[2]; void *mouse;
void *hdc_xta;
serial_t *uart;
} upc_t; } upc_t;
#ifdef ENABLE_F82C710_LOG #ifdef ENABLE_F82C710_LOG
@@ -79,150 +92,96 @@ f82c710_log(const char *fmt, ...)
#endif #endif
static void static void
f82c710_update_ports(upc_t *dev, int set) serial_handler(upc_t *dev)
{ {
uint16_t com_addr = 0; uint16_t com_addr = 0x0000;
uint16_t lpt_addr = 0;
serial_remove(dev->uart[0]); serial_remove(dev->uart);
serial_remove(dev->uart[1]);
lpt1_remove();
lpt2_remove();
fdc_remove(dev->fdc);
ide_pri_disable();
if (!set) if (dev->regs[0x00] & 0x04) {
return; com_addr = dev->regs[0x04] << 2;
if (dev->regs[0] & 4) { serial_setup(dev->uart, com_addr, dev->serial_irq);
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);
} }
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 static void
f82c606_update_ports(upc_t *dev, int set) lpt_handler(upc_t *dev)
{ {
uint8_t uart1_int = 0xff; uint16_t lpt_addr = 0x0000;
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(); lpt1_remove();
lpt2_remove();
nvr_at_handler(0, ((uint16_t) dev->regs[3]) << 2, dev->nvr); if (dev->regs[0x00] & 0x08) {
nvr_at_handler(0, 0x70, dev->nvr); lpt_addr = dev->regs[0x06] << 2;
gameport_remap(dev->gameport, 0); lpt1_setup(lpt_addr);
lpt1_irq(dev->lpt_irq);
if (!set) lpt_set_ext(0, !!(dev->regs[0x01] & 0x40));
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) { static void
case 0x10: ide_handler(upc_t *dev)
nvr_int = 4; {
break; if (dev->xta) {
case 0x20: if (dev->hdc_xta != NULL)
uart1_int = COM1_IRQ; xta_handler(dev->hdc_xta, 0);
break; pclog("IDE XT interface disabled at 320-323\n");
case 0x30: } else {
uart2_int = COM1_IRQ; ide_pri_disable();
break; pclog("IDE AT interface disabled at 1F0-1F7, 3F6-3F7\n");
default:
break;
} }
switch (dev->regs[8] & 0x0c) { if (dev->regs[0x0c] & 0x80) {
case 0x04: if (dev->regs[0x0c] & 0x40) {
nvr_int = 5; /* TODO: See what IDE mode the Amstrad PC5086 uses. */
break; if (dev->xta && (dev->hdc_xta != NULL))
case 0x08: xta_handler(dev->hdc_xta, 1);
uart1_int = 5; pclog("IDE XT interface enabled at 320-323\n");
break; } else {
case 0x0c: if (!dev->xta)
lpt1_int = LPT2_IRQ; ide_pri_enable();
break; pclog("IDE AT interface enabled at 1F0-1F7, 3F6-3F7\n");
}
default: }
break;
} }
switch (dev->regs[8] & 0x03) { static void
case 0x01: fdc_handler(upc_t *dev)
nvr_int = 7; {
break; fdc_remove(dev->fdc);
case 0x02:
uart2_int = 7;
break;
case 0x03:
lpt1_int = LPT1_IRQ;
break;
default: if (dev->regs[0x0c] & 0x20)
break; fdc_set_base(dev->fdc, FDC_PRIMARY_ADDR);
fdc_set_power_down(dev->fdc, !!(dev->regs[0x0c] & 0x10));
} }
if (dev->regs[0] & 1) { static void
gameport_remap(dev->gameport, ((uint16_t) dev->regs[7]) << 2); mouse_handler(upc_t *dev)
f82c710_log("Game port at %04X\n", ((uint16_t) dev->regs[7]) << 2); {
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);
} }
if (dev->regs[0] & 2) { dev->mouse_base = dev->regs[0x0d] << 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->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);
}
} }
if (dev->regs[0] & 4) { static void
serial_setup(dev->uart[1], ((uint16_t) dev->regs[5]) << 2, uart2_int); f82c710_update_ports(upc_t *dev)
f82c710_log("UART 2 at %04X, IRQ %i\n", ((uint16_t) dev->regs[5]) << 2, uart2_int); {
} serial_handler(dev);
lpt_handler(dev);
if (dev->regs[0] & 8) { ide_handler(dev);
lpt1_setup(((uint16_t) dev->regs[6]) << 2); fdc_handler(dev);
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 uint8_t static uint8_t
@@ -235,9 +194,9 @@ f82c710_config_read(uint16_t port, void *priv)
if (port == dev->cri_addr) { if (port == dev->cri_addr) {
temp = dev->cri; temp = dev->cri;
} else if (port == dev->cap_addr) { } else if (port == dev->cap_addr) {
if (dev->cri == 0xf) if (dev->cri == 0x0f)
temp = dev->cri_addr / 4; temp = dev->cri_addr >> 2;
else else if (dev->cri < 0x0f)
temp = dev->regs[dev->cri]; temp = dev->regs[dev->cri];
} }
} }
@@ -249,65 +208,111 @@ static void
f82c710_config_write(uint16_t port, uint8_t val, void *priv) f82c710_config_write(uint16_t port, uint8_t val, void *priv)
{ {
upc_t * dev = (upc_t *) priv; upc_t * dev = (upc_t *) priv;
uint8_t valxor = 0x00;
int configuration_state_event = 0; int configuration_state_event = 0;
switch (port) { switch (port) {
default:
break;
case 0x2fa: case 0x2fa:
if ((dev->configuration_state == 0) && (val != 0x00) && (val != 0xff) && (dev->local == 606)) { if (dev->configuration_state == 0) {
configuration_state_event = 1; configuration_state_event = 1;
dev->last_write = val; dev->next_value = 0xff - val;
} else if ((dev->configuration_state == 0) && (val == 0x55) && (dev->local == 710)) } else if (dev->configuration_state == 4) {
configuration_state_event = 1; uint8_t addr_verify = dev->cri_addr >> 2;
else if (dev->configuration_state == 4) { addr_verify += val;
if ((val | dev->last_write) == 0xff) { if (addr_verify == 0xff) {
dev->cri_addr = ((uint16_t) dev->last_write) << 2;
dev->cap_addr = dev->cri_addr + 1;
dev->configuration_mode = 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? */ /* 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 } else
dev->configuration_mode = 0; dev->configuration_mode = 0;
} }
break; break;
case 0x3fa: case 0x3fa:
if ((dev->configuration_state == 1) && ((val | dev->last_write) == 0xff) && (dev->local == 606)) if ((dev->configuration_state == 1) && (val == dev->next_value))
configuration_state_event = 1;
else if ((dev->configuration_state == 1) && (val == 0xaa) && (dev->local == 710))
configuration_state_event = 1; configuration_state_event = 1;
else if ((dev->configuration_state == 2) && (val == 0x36)) else if ((dev->configuration_state == 2) && (val == 0x36))
configuration_state_event = 1; configuration_state_event = 1;
else if (dev->configuration_state == 3) { 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; configuration_state_event = 1;
} }
break; break;
default:
break;
} }
if (dev->configuration_mode) { if (dev->configuration_mode) {
if (port == dev->cri_addr) { if (port == dev->cri_addr)
dev->cri = val & 0xf; dev->cri = val & 0xf;
} else if (port == dev->cap_addr) { else if (port == dev->cap_addr) {
if (dev->cri == 0xf) { valxor = (dev->regs[dev->cri] ^ val);
dev->configuration_mode = 0; switch (dev->cri) {
io_removehandler(dev->cri_addr, 0x0002, f82c710_config_read, NULL, NULL, f82c710_config_write, NULL, NULL, dev); case 0x00:
/* TODO: any benefit in updating at each register write instead of when exiting config mode? */ dev->regs[dev->cri] = (dev->regs[dev->cri] & 0x10) | (val & 0xef);
if (dev->local == 606) if (valxor & 0x08)
f82c606_update_ports(dev, 1); lpt_handler(dev);
else if (dev->local == 710) if (valxor & 0x04)
f82c710_update_ports(dev, 1); serial_handler(dev);
} else 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; 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? */ /* 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++; dev->configuration_state++;
else else
dev->configuration_state = 0; dev->configuration_state = 0;
@@ -318,39 +323,27 @@ f82c710_reset(void *priv)
{ {
upc_t *dev = (upc_t *) priv; upc_t *dev = (upc_t *) priv;
/* Set power-on defaults. */ dev->configuration_state = 0;
if (dev->local == 606) { dev->configuration_mode = 0;
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 (dev->local == 606) /* Set power-on defaults. */
f82c606_update_ports(dev, 1); dev->regs[0x00] = 0x0c;
else if (dev->local == 710) dev->regs[0x01] = 0x00;
f82c710_update_ports(dev, 1); 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 static void
@@ -365,16 +358,18 @@ static void *
f82c710_init(const device_t *info) f82c710_init(const device_t *info)
{ {
upc_t *dev = (upc_t *) calloc(1, sizeof(upc_t)); upc_t *dev = (upc_t *) calloc(1, sizeof(upc_t));
dev->local = info->local;
if (dev->local == 606) { if (strstr(machine_get_internal_name(), "5086") != NULL)
dev->nvr = device_add(&at_nvr_old_device); dev->fdc = device_add(&fdc_at_actlow_device);
dev->gameport = gameport_add(&gameport_sio_device); else
} else if (dev->local == 710)
dev->fdc = device_add(&fdc_at_device); dev->fdc = device_add(&fdc_at_device);
dev->uart[0] = device_add_inst(&ns16450_device, 1); dev->uart = device_add_inst(&ns16450_device, 1);
dev->uart[1] = device_add_inst(&ns16450_device, 2);
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(0x02fa, 0x0001, NULL, NULL, NULL, f82c710_config_write, NULL, NULL, dev);
io_sethandler(0x03fa, 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; return dev;
} }
const device_t f82c606_device = { static void *
.name = "82C606 CHIPSpak Multifunction Controller", f82c710_pc5086_init(const device_t *info)
.internal_name = "f82c606", {
.flags = 0, upc_t *dev = f82c710_init(info);
.local = 606,
.init = f82c710_init, int hdc_present = device_get_config_int("hdc_present");
.close = f82c710_close,
.reset = f82c710_reset, if (hdc_present)
.available = NULL, dev->hdc_xta = device_add(&xta_st50x_pc5086_device);
.speed_changed = NULL,
.force_redraw = NULL, dev->xta = 1;
.config = NULL
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 = { const device_t f82c710_device = {
.name = "F82C710 UPC Super I/O", .name = "F82C710 UPC Super I/O",
.internal_name = "f82c710", .internal_name = "f82c710",
.flags = 0, .flags = 0,
.local = 710, .local = 0,
.init = f82c710_init, .init = f82c710_init,
.close = f82c710_close, .close = f82c710_close,
.reset = f82c710_reset, .reset = f82c710_reset,
.available = NULL, .available = NULL,
.speed_changed = NULL, .speed_changed = NULL,
.force_redraw = 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
}; };