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_ps2.c
mouse_serial.c
mouse_upc.c
nec_mate_unk.c
novell_cardkey.c
pci_bridge.c

View File

@@ -8,13 +8,11 @@
*
* Intel 8042 (AT keyboard controller) emulation.
*
*
*
* Authors: Miran Grca, <mgrca8@gmail.com>
* EngiNerd, <webmaster.crrc@yahoo.it>
*
* Copyright 2023 Miran Grca.
* Copyright 2023 EngiNerd.
* Copyright 2023-2025 Miran Grca.
* Copyright 2023-2025 EngiNerd.
*/
#include <stdio.h>
#include <stdint.h>
@@ -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

View File

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

View File

@@ -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
};

View File

@@ -10,7 +10,7 @@
*
* Authors: Miran Grca, <mgrca8@gmail.com>
*
* Copyright 2023 Miran Grca.
* Copyright 2023-2025 Miran Grca.
*/
#include <stdarg.h>
#include <stdatomic.h>
@@ -21,7 +21,6 @@
#include <wchar.h>
#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;
}

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 },
{ &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 },

View File

@@ -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
};

View File

@@ -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*/

View File

@@ -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 *);

View File

@@ -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

View File

@@ -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;

View File

@@ -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

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/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",

View File

@@ -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

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.
*
* 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.
* <https://archive.org/download/82C606/82C606.pdf>
* 82710 Univeral Peripheral Controller, Data Sheet,
* PRELIMINARY, August 1990.
* <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>
*
*
* Authors: Eluan Costa Miranda <eluancm@gmail.com>
* Lubomir Rintel <lkundrak@v3.sk>
*
* 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 <stdio.h>
#include <stdint.h>
@@ -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
};