summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine
diff options
context:
space:
mode:
author Nigel Barnes <Pernod70@users.noreply.github.com>2021-11-14 13:05:28 +0000
committer Nigel Barnes <Pernod70@users.noreply.github.com>2021-11-14 19:08:39 +0000
commit8acb4d846de5738ff26da7f4a535be7b4bd03e62 (patch)
tree23b8521e10b85c605d10ab3603e420e8d7771273 /src/devices/machine
parent7481bdef865a14b78b27d4b8111e33efe0c3b167 (diff)
New devices for Archimedes machines:
- Acorn Battery Management Unit - Acorn LC ASIC (preliminary) - Universal Peripheral Controller 82C710/82C711
Diffstat (limited to 'src/devices/machine')
-rw-r--r--src/devices/machine/acorn_bmu.cpp263
-rw-r--r--src/devices/machine/acorn_bmu.h87
-rw-r--r--src/devices/machine/acorn_lc.cpp152
-rw-r--r--src/devices/machine/acorn_lc.h85
-rw-r--r--src/devices/machine/upc82c710.cpp419
-rw-r--r--src/devices/machine/upc82c710.h110
-rw-r--r--src/devices/machine/upc82c711.cpp478
-rw-r--r--src/devices/machine/upc82c711.h128
8 files changed, 1722 insertions, 0 deletions
diff --git a/src/devices/machine/acorn_bmu.cpp b/src/devices/machine/acorn_bmu.cpp
new file mode 100644
index 00000000000..3ff529525ff
--- /dev/null
+++ b/src/devices/machine/acorn_bmu.cpp
@@ -0,0 +1,263 @@
+// license:BSD-3-Clause
+// copyright-holders:Nigel Barnes
+/*********************************************************************
+
+ Acorn Battery Management Unit
+
+ The BMU is actually a 4-bit Hitachi HD404304F MCU, marked as
+ BMU 0290032.
+
+ TODO:
+ - configurable status
+ - callback when battery low
+
+*********************************************************************/
+
+#include "emu.h"
+#include "acorn_bmu.h"
+
+#define LOG_DATA (1 << 1)
+#define LOG_LINE (1 << 2)
+
+#define VERBOSE (0)
+#include "logmacro.h"
+
+
+DEFINE_DEVICE_TYPE(ACORN_BMU, acorn_bmu_device, "acorn_bmu", "Acorn Battery Management Unit")
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// acorn_bmu_device - constructor
+//-------------------------------------------------
+
+acorn_bmu_device::acorn_bmu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, ACORN_BMU, tag, owner, clock)
+ , m_slave_address(BMU_SLAVE_ADDRESS)
+ , m_scl(0)
+ , m_sdaw(0)
+ , m_sdar(1)
+ , m_state(STATE_IDLE)
+ , m_bits(0)
+ , m_shift(0)
+ , m_devsel(0)
+ , m_register(0)
+ , m_estimate(0)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void acorn_bmu_device::device_start()
+{
+ save_item(NAME(m_scl));
+ save_item(NAME(m_sdaw));
+ save_item(NAME(m_sdar));
+ save_item(NAME(m_state));
+ save_item(NAME(m_bits));
+ save_item(NAME(m_shift));
+ save_item(NAME(m_devsel));
+ save_item(NAME(m_register));
+ save_item(NAME(m_slave_address));
+ save_item(NAME(m_estimate));
+}
+
+
+//**************************************************************************
+// READ/WRITE HANDLERS
+//**************************************************************************
+
+WRITE_LINE_MEMBER(acorn_bmu_device::scl_w)
+{
+ if (m_scl != state)
+ {
+ m_scl = state;
+ LOGMASKED(LOG_LINE, "set_scl_line %d\n", m_scl);
+
+ switch (m_state)
+ {
+ case STATE_DEVSEL:
+ case STATE_REGISTER:
+ case STATE_DATAIN:
+ if (m_bits < 8)
+ {
+ if (m_scl)
+ {
+ m_shift = ((m_shift << 1) | m_sdaw) & 0xff;
+ m_bits++;
+ }
+ }
+ else
+ {
+ if (m_scl)
+ {
+ switch (m_state)
+ {
+ case STATE_DEVSEL:
+ m_devsel = m_shift;
+
+ if ((m_devsel & 0xfe) != m_slave_address)
+ {
+ LOGMASKED(LOG_DATA, "devsel %02x: not this device\n", m_devsel);
+ m_state = STATE_IDLE;
+ }
+ else if ((m_devsel & 1) == 0)
+ {
+ LOGMASKED(LOG_DATA, "devsel %02x: write\n", m_devsel);
+ m_state = STATE_REGISTER;
+ }
+ else
+ {
+ LOGMASKED(LOG_DATA, "devsel %02x: read\n", m_devsel);
+ m_state = STATE_DATAOUT;
+ }
+ break;
+
+ case STATE_REGISTER:
+ m_register = m_shift;
+
+ LOGMASKED(LOG_DATA, "register %02x\n", m_register);
+
+ m_state = STATE_DATAIN;
+ break;
+
+ case STATE_DATAIN:
+ LOGMASKED(LOG_DATA, "register[ %02x ] <- %02x\n", m_register, m_shift);
+
+ switch (m_register)
+ {
+ case BMU_CHARGE_ESTIMATE:
+ m_estimate = m_shift;
+ break;
+ }
+
+ m_register++;
+ break;
+ }
+
+ m_bits++;
+ }
+ else
+ {
+ if (m_bits == 8)
+ {
+ m_sdar = 0;
+ }
+ else
+ {
+ m_bits = 0;
+ m_sdar = 1;
+ }
+ }
+ }
+ break;
+
+ case STATE_DATAOUT:
+ if (m_bits < 8)
+ {
+ if (m_scl)
+ {
+ if (m_bits == 0)
+ {
+ switch (m_register)
+ {
+ case BMU_VERSION:
+ m_shift = 0x03;
+ break;
+
+ case BMU_STATUS:
+ m_shift = STATUS_CHARGER_PRESENT | STATUS_BATTERY_PRESENT | STATUS_CHARGE_STATE_KNOWN | STATUS_LID_OPEN;
+ break;
+
+ case BMU_CAPACITY_USED:
+ m_shift = 0x40;
+ break;
+
+ case BMU_CHARGE_ESTIMATE:
+ m_shift = m_estimate;
+ break;
+
+ case BMU_COMMAND:
+ m_shift = 0;
+ break;
+ }
+
+ LOGMASKED(LOG_DATA, "register[ %02x ] -> %02x\n", m_register, m_shift);
+ m_register++;
+ }
+
+ m_sdar = (m_shift >> 7) & 1;
+
+ m_shift = (m_shift << 1) & 0xff;
+ m_bits++;
+ }
+ }
+ else
+ {
+ if (m_scl)
+ {
+ if (m_sdaw)
+ {
+ LOGMASKED(LOG_DATA, "nack\n");
+ m_state = STATE_IDLE;
+ }
+
+ m_bits++;
+ }
+ else
+ {
+ if (m_bits == 8)
+ {
+ m_sdar = 1;
+ }
+ else
+ {
+ m_bits = 0;
+ }
+ }
+ }
+ break;
+ }
+ }
+}
+
+WRITE_LINE_MEMBER(acorn_bmu_device::sda_w)
+{
+ state &= 1;
+ if (m_sdaw != state)
+ {
+ LOGMASKED(LOG_LINE, "set sda %d\n", state);
+ m_sdaw = state;
+
+ if (m_scl)
+ {
+ if (m_sdaw)
+ {
+ LOGMASKED(LOG_DATA, "stop\n");
+ m_state = STATE_IDLE;
+ }
+ else
+ {
+ LOGMASKED(LOG_DATA, "start\n");
+ m_state = STATE_DEVSEL;
+ m_bits = 0;
+ }
+
+ m_sdar = 1;
+ }
+ }
+}
+
+READ_LINE_MEMBER(acorn_bmu_device::sda_r)
+{
+ int res = m_sdar & 1;
+
+ LOGMASKED(LOG_LINE, "read sda %d\n", res);
+
+ return res;
+}
diff --git a/src/devices/machine/acorn_bmu.h b/src/devices/machine/acorn_bmu.h
new file mode 100644
index 00000000000..56c6c32a3df
--- /dev/null
+++ b/src/devices/machine/acorn_bmu.h
@@ -0,0 +1,87 @@
+// license:BSD-3-Clause
+// copyright-holders:Nigel Barnes
+/*********************************************************************
+
+ Acorn Battery Management Unit
+
+*********************************************************************/
+
+#ifndef MAME_MACHINE_ACORN_BMU_H
+#define MAME_MACHINE_ACORN_BMU_H
+
+#pragma once
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> acorn_bmu_device
+
+class acorn_bmu_device : public device_t
+{
+public:
+ acorn_bmu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ DECLARE_WRITE_LINE_MEMBER(scl_w);
+ DECLARE_WRITE_LINE_MEMBER(sda_w);
+ DECLARE_READ_LINE_MEMBER(sda_r);
+
+protected:
+ // device-level overrides
+ virtual void device_start() override;
+
+private:
+ static constexpr uint8_t BMU_SLAVE_ADDRESS = 0xa2;
+
+ // internal state
+ int m_slave_address;
+ int m_scl;
+ int m_sdaw;
+ int m_sdar;
+ int m_state;
+ int m_bits;
+ int m_shift;
+ int m_devsel;
+ int m_register;
+
+ enum { STATE_IDLE, STATE_DEVSEL, STATE_REGISTER, STATE_DATAIN, STATE_DATAOUT };
+
+ // registers
+ enum
+ {
+ BMU_VERSION = 0x50,
+ BMU_TEMPERATURE = 0x52,
+ BMU_CURRENT = 0x54,
+ BMU_VOLTAGE = 0x56,
+ BMU_STATUS = 0x5c,
+ BMU_CHARGE_RATE = 0x5e,
+ BMU_CAPACITY_NOMINAL = 0x80,
+ BMU_CAPACITY_MEASURED = 0x82,
+ BMU_CAPACITY_USED = 0x88,
+ BMU_CAPACITY_USABLE = 0x8a,
+ BMU_CHARGE_ESTIMATE = 0x8e,
+ BMU_COMMAND = 0x90,
+ BMU_AUTOSTART = 0x9e
+ };
+
+ // flags
+ enum
+ {
+ STATUS_THRESHOLD_3 = 1 << 0, // Battery fully charged
+ STATUS_LID_OPEN = 1 << 1, // Lid open
+ STATUS_THRESHOLD_2 = 1 << 2, // Battery flat [shutdown now]
+ STATUS_THRESHOLD_1 = 1 << 3, // Battery low [warn user]
+ STATUS_CHARGING_FAULT = 1 << 4, // Fault in charging system
+ STATUS_CHARGE_STATE_KNOWN = 1 << 5, // Charge state known
+ STATUS_BATTERY_PRESENT = 1 << 6, // Battery present
+ STATUS_CHARGER_PRESENT = 1 << 7 // Charger present
+ };
+
+ uint8_t m_estimate;
+};
+
+// device type definition
+DECLARE_DEVICE_TYPE(ACORN_BMU, acorn_bmu_device)
+
+#endif // MAME_MACHINE_ACORN_BMU_H
diff --git a/src/devices/machine/acorn_lc.cpp b/src/devices/machine/acorn_lc.cpp
new file mode 100644
index 00000000000..71a545521cb
--- /dev/null
+++ b/src/devices/machine/acorn_lc.cpp
@@ -0,0 +1,152 @@
+// license:BSD-3-Clause
+// copyright-holders:Nigel Barnes
+/*********************************************************************
+
+ Acorn LC ASIC (for Acorn A4)
+
+ No public documentation exists, this is mostly based on the A4 TRM.
+
+*********************************************************************/
+
+#include "emu.h"
+#include "acorn_lc.h"
+#include "screen.h"
+
+
+DEFINE_DEVICE_TYPE(ACORN_LC, acorn_lc_device, "acorn_lc", "Acorn LC ASIC")
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// acorn_lc_device - constructor
+//-------------------------------------------------
+
+acorn_lc_device::acorn_lc_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, ACORN_LC, tag, owner, clock)
+ //, device_memory_interface(mconfig, *this)
+ //, device_palette_interface(mconfig, *this)
+ //, device_video_interface(mconfig, *this)
+ , m_vdsr(0)
+ , m_vdlr(0)
+ , m_hdsr(0)
+ , m_hdlr(0)
+ , m_licr(0)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void acorn_lc_device::device_start()
+{
+ // state saving
+ save_item(NAME(m_vdsr));
+ save_item(NAME(m_vdlr));
+ save_item(NAME(m_hdsr));
+ save_item(NAME(m_hdlr));
+ save_item(NAME(m_licr));
+}
+
+
+//**************************************************************************
+// READ/WRITE HANDLERS
+//**************************************************************************
+
+u8 acorn_lc_device::read(offs_t offset)
+{
+ u8 data = 0xff;
+
+ switch (offset & 0x0f)
+ {
+ case LC_LICR_L:
+ data = m_licr & 0x0f;
+ break;
+ case LC_LICR_M:
+ data = (m_licr >> 4) & 0x0f;
+ break;
+ case LC_LICR_H:
+ data = (m_licr >> 8) & 0x0f;
+ break;
+
+ case LC_RESET:
+ data = 0x04;
+ break;
+ }
+ logerror("lc_r: % 04x % 02x\n", offset, data);
+ return data;
+}
+
+void acorn_lc_device::write(offs_t offset, u8 data)
+{
+ logerror("lc_w: % 04x % 02x\n", offset, data);
+ switch (offset & 0x0f)
+ {
+ case LC_VDSR_L:
+ m_vdsr = (m_vdsr & 0x0ff0) | (data & 0x0f);
+ break;
+ case LC_VDSR_M:
+ m_vdsr = (m_vdsr & 0x0f0f) | ((data & 0x0f) << 4);
+ break;
+ case LC_VDSR_H:
+ m_vdsr = (m_vdsr & 0x00ff) | ((data & 0x0f) << 8);
+ break;
+
+ case LC_VDLR_L:
+ m_vdlr = (m_vdlr & 0x0ff0) | (data & 0x0f);
+ break;
+ case LC_VDLR_M:
+ m_vdlr = (m_vdlr & 0x0f0f) | ((data & 0x0f) << 4);
+ break;
+ case LC_VDLR_H:
+ m_vdlr = (m_vdlr & 0x00ff) | ((data & 0x0f) << 8);
+ break;
+
+ case LC_HDSR_L:
+ m_hdsr = (m_hdsr & 0x0ff0) | (data & 0x0f);
+ break;
+ case LC_HDSR_M:
+ m_hdsr = (m_hdsr & 0x0f0f) | ((data & 0x0f) << 4);
+ break;
+ case LC_HDSR_H:
+ m_hdsr = (m_hdsr & 0x00ff) | ((data & 0x0f) << 8);
+ break;
+
+ case LC_HDLR_L:
+ m_hdlr = (m_hdlr & 0x0ff0) | (data & 0x0f);
+ break;
+ case LC_HDLR_M:
+ m_hdlr = (m_hdlr & 0x0f0f) | ((data & 0x0f) << 4);
+ break;
+ case LC_HDLR_H:
+ m_hdlr = (m_hdlr & 0x00ff) | ((data & 0x0f) << 8);
+ break;
+
+ case LC_LICR_L:
+ m_licr = (m_licr & 0x0ff0) | (data & 0x0f);
+ break;
+ case LC_LICR_M:
+ m_licr = (m_licr & 0x0f0f) | ((data & 0x0f) << 4);
+ switch (m_licr & LICR_CLOCK_MASK)
+ {
+ case LICR_CLOCK_CRYS:
+ //m_vidc->set_unscaled_clock(24_MHz_XTAL);
+ break;
+ case LICR_CLOCK_CRYS2:
+ //m_vidc->set_unscaled_clock(24_MHz_XTAL / 2);
+ break;
+ case LICR_CLOCK_IOEB:
+ default:
+ //m_vidc->set_unscaled_cloc(ioeb_clock_select);
+ break;
+ }
+ break;
+ case LC_LICR_H:
+ m_licr = (m_licr & 0x00ff) | ((data & 0x0f) << 8);
+ break;
+ }
+ //if (offset & 0x40)
+ //pal[(offset >> 2) & 0xf] = lc_palette[data & 0xf];
+}
diff --git a/src/devices/machine/acorn_lc.h b/src/devices/machine/acorn_lc.h
new file mode 100644
index 00000000000..8367c90fdf1
--- /dev/null
+++ b/src/devices/machine/acorn_lc.h
@@ -0,0 +1,85 @@
+// license:BSD-3-Clause
+// copyright-holders:Nigel Barnes
+/*********************************************************************
+
+ Acorn LC ASIC
+
+*********************************************************************/
+
+#ifndef MAME_MACHINE_ACORN_LC_H
+#define MAME_MACHINE_ACORN_LC_H
+
+#pragma once
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> acorn_lc_device
+
+class acorn_lc_device : public device_t
+ //public device_memory_interface,
+ //public device_palette_interface,
+ //public device_video_interface
+{
+public:
+ acorn_lc_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+
+ u8 read(offs_t offset);
+ void write(offs_t offset, u8 data);
+
+protected:
+ // device-level overrides
+ virtual void device_start() override;
+
+private:
+
+ enum
+ {
+ // VDSR = 512 - lines from start of VSYNC to start of display
+ LC_VDSR_L = 0,
+ LC_VDSR_M,
+ LC_VDSR_H,
+
+ // VDLC = lines per panel - 3
+ LC_VDLR_L,
+ LC_VDLR_M,
+ LC_VDLR_H,
+
+ // HDSR = 2047 - pixels of HSYNC + back porch
+ LC_HDSR_L,
+ LC_HDSR_M,
+ LC_HDSR_H,
+
+ // HDLR = horizontal display pixels / 8
+ LC_HDLR_L,
+ LC_HDLR_M ,
+ LC_HDLR_H,
+
+ LC_LICR_L,
+ LC_LICR_M,
+ LC_LICR_H,
+
+ LC_RESET,
+ };
+
+ enum
+ {
+ LICR_CLOCK_IOEB = 0 << 5,
+ LICR_CLOCK_CRYS2 = 1 << 5,
+ LICR_CLOCK_CRYS = 2 << 5,
+ LICR_CLOCK_MASK = 3 << 5
+ };
+
+ u32 m_vdsr;
+ u32 m_vdlr;
+ u32 m_hdsr;
+ u32 m_hdlr;
+ u32 m_licr;
+};
+
+// device type definition
+DECLARE_DEVICE_TYPE(ACORN_LC, acorn_lc_device)
+
+#endif // MAME_MACHINE_ACORN_LC_H
diff --git a/src/devices/machine/upc82c710.cpp b/src/devices/machine/upc82c710.cpp
new file mode 100644
index 00000000000..b2812291a1d
--- /dev/null
+++ b/src/devices/machine/upc82c710.cpp
@@ -0,0 +1,419 @@
+// license:BSD-3-Clause
+// copyright-holders:Nigel Barnes
+/**********************************************************************
+
+ Universal Peripheral Controller 82C710 emulation
+
+**********************************************************************/
+
+#include "emu.h"
+#include "machine/upc82c710.h"
+
+#define LOG_CFG (1 << 0)
+#define LOG_FDC (1 << 1)
+#define LOG_IDE (1 << 2)
+#define LOG_LPT (1 << 3)
+#define LOG_SER (1 << 4)
+
+#define VERBOSE (0)
+
+#include "logmacro.h"
+
+DEFINE_DEVICE_TYPE(UPC82C710, upc82c710_device, "upc82c710", "Universal Peripheral Controller 82C710")
+
+
+upc82c710_device::upc82c710_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, UPC82C710, tag, owner, clock)
+ , m_ide(*this, "ide")
+ , m_fdc(*this, "fdc")
+ , m_lpt(*this, "parallel")
+ , m_serial(*this, "serial")
+ , m_fintr_callback(*this)
+ , m_fdrq_callback(*this)
+ , m_pintr_callback(*this)
+ , m_sintr_callback(*this)
+ , m_txd_callback(*this)
+ , m_dtr_callback(*this)
+ , m_rts_callback(*this)
+ , m_cfg_mode(0)
+{
+}
+
+
+void upc82c710_device::device_add_mconfig(machine_config &config)
+{
+ // ide interface
+ ATA_INTERFACE(config, m_ide);
+
+ // floppy disc controller
+ UPD765A(config, m_fdc, clock() / 3, false, false); // uPD72065B
+ m_fdc->intrq_wr_callback().set(FUNC(upc82c710_device::fdc_irq_w));
+ m_fdc->drq_wr_callback().set(FUNC(upc82c710_device::fdc_drq_w));
+
+ // parallel port
+ PC_LPT(config, m_lpt);
+ m_lpt->irq_handler().set([this](int state) { m_pintr_callback(state); });
+
+ // serial port
+ NS16450(config, m_serial, clock() / 13);
+ m_serial->out_int_callback().set([this](int state) { m_sintr_callback(state); });
+ m_serial->out_tx_callback().set([this](int state) { m_txd_callback(state); });
+ m_serial->out_dtr_callback().set([this](int state) { m_dtr_callback(state); });
+ m_serial->out_rts_callback().set([this](int state) { m_rts_callback(state); });
+}
+
+
+void upc82c710_device::device_start()
+{
+ for (int i=0; i<4; i++)
+ {
+ char name[2] = {static_cast<char>('0'+i), 0};
+ floppy_connector *conn = m_fdc->subdevice<floppy_connector>(name);
+ floppy[i] = conn ? conn->get_device() : nullptr;
+ }
+
+ irq = drq = false;
+ fdc_irq = fdc_drq = false;
+
+ m_fintr_callback.resolve_safe();
+ m_fdrq_callback.resolve_safe();
+ m_pintr_callback.resolve_safe();
+ m_sintr_callback.resolve_safe();
+ m_txd_callback.resolve_safe();
+ m_dtr_callback.resolve_safe();
+ m_rts_callback.resolve_safe();
+
+ // default addresses
+ device_address[LogicalDevice::CFG] = 0x390;
+ device_address[LogicalDevice::IDE] = 0x1f0;
+ device_address[LogicalDevice::FDC] = 0x3f0;
+ device_address[LogicalDevice::LPT] = 0x278;
+ device_address[LogicalDevice::SER] = 0x3f8;
+
+ const u8 cfg_regs_defaults[] = { 0x0e, 0x00, 0x08, 0x00, 0xfe, 0xbe, 0x9e, 0x00, 0xec, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ // Set the value first and then use write_config because some flags
+ // rely on other flags being initialized properly first
+ std::copy(std::begin(cfg_regs_defaults), std::end(cfg_regs_defaults), std::begin(m_cfg_regs));
+ for (int i = 0; i < std::size(cfg_regs_defaults); i++)
+ {
+ write_cfg(i, cfg_regs_defaults[i]);
+ }
+
+ save_item(NAME(m_cfg_regs));
+ save_item(NAME(m_cfg_indx));
+ save_item(NAME(m_cfg_mode));
+}
+
+void upc82c710_device::device_reset()
+{
+ dor_w(0x00);
+}
+
+
+u16 upc82c710_device::io_r(offs_t offset, u16 mem_mask)
+{
+ u16 data = 0xffff;
+
+ // configuration
+ if (offset == 0x391 && m_cfg_mode == 5)
+ {
+ data = m_cfg_regs[m_cfg_indx];
+ LOGMASKED(LOG_CFG, "CFG read %04x -> %02x\n", offset, data);
+ }
+
+ // ide
+ if (device_enabled[LogicalDevice::IDE] && (offset & ~7) == 0x1f0)
+ {
+ data = m_ide->cs0_r(offset & 7);
+ LOGMASKED(LOG_IDE, "IDE read %04x -> %04x\n", offset, data);
+ }
+ if (device_enabled[LogicalDevice::IDE] && offset == 0x3f6)
+ {
+ data = m_ide->cs1_r(offset & 7);
+ LOGMASKED(LOG_IDE, "IDE read %04x -> %04x\n", offset, data);
+ }
+
+ // fdc
+ if (device_enabled[LogicalDevice::FDC] && (offset & ~7) == 0x3f0 && offset != 0x3f0 && offset != 0x3f1 && offset != 0x3f6)
+ {
+ switch (offset & 7)
+ {
+ case 3:
+ data = 0x20;
+ break;
+ case 4:
+ data = m_fdc->msr_r();
+ break;
+ case 5:
+ data = m_fdc->fifo_r();
+ break;
+ case 7:
+ data = m_fdc->dir_r() | (m_ide->cs1_r(7) & 0x7f);
+ break;
+ }
+ LOGMASKED(LOG_FDC, "FDC read %04x -> %02x\n", offset, data);
+ }
+
+ // parallel
+ if (device_enabled[LogicalDevice::LPT] && (offset & ~3) == device_address[LogicalDevice::LPT])
+ {
+ data = m_lpt->read(offset & 3);
+ LOGMASKED(LOG_LPT, "LPT read %04x -> %02x\n", offset, data);
+ }
+
+ // serial
+ if (device_enabled[LogicalDevice::SER] && (offset & ~7) == device_address[LogicalDevice::SER])
+ {
+ data = m_serial->ins8250_r(offset & 7);
+ LOGMASKED(LOG_SER, "SER read %04x -> %02x\n", offset, data);
+ }
+
+ return data;
+}
+
+void upc82c710_device::io_w(offs_t offset, u16 data, u16 mem_mask)
+{
+ switch (m_cfg_mode)
+ {
+ case 0:
+ if (offset == 0x2fa && data == 0x55)
+ {
+ m_cfg_mode++;
+ LOGMASKED(LOG_CFG, "CFG step %d write %04x <- %02x\n", m_cfg_mode, offset, data);
+ }
+ break;
+ case 1:
+ if (offset == 0x3fa && data == 0xaa)
+ {
+ m_cfg_mode++;
+ LOGMASKED(LOG_CFG, "CFG step %d write %04x <- %02x\n", m_cfg_mode, offset, data);
+ }
+ break;
+ case 2:
+ if (offset == 0x3fa && data == 0x36)
+ {
+ m_cfg_mode++;
+ LOGMASKED(LOG_CFG, "CFG step %d write %04x <- %02x\n", m_cfg_mode, offset, data);
+ }
+ break;
+ case 3:
+ if (offset == 0x3fa)
+ {
+ m_cfg_mode++;
+ LOGMASKED(LOG_CFG, "CFG step %d write %04x <- %02x\n", m_cfg_mode, offset, data);
+ m_cfg_regs[0x0f] = data;
+ device_address[LogicalDevice::CFG] = data << 2;
+ LOGMASKED(LOG_CFG, "CFG address %04x\n", device_address[LogicalDevice::CFG]);
+ }
+ break;
+ case 4:
+ // enter configuration
+ if (offset == 0x2fa && data == (~m_cfg_regs[0x0f] & 0xff))
+ {
+ m_cfg_mode++;
+ LOGMASKED(LOG_CFG, "CFG step %d write %04x <- %02x\n", m_cfg_mode, offset, data);
+ LOGMASKED(LOG_CFG, "CFG -- mode on --\n");
+ }
+ break;
+ }
+
+ // configuration
+ if (offset == device_address[LogicalDevice::CFG])
+ {
+ LOGMASKED(LOG_CFG, "CFG write %04x <- %02x\n", offset, data);
+ if (m_cfg_mode == 5)
+ {
+ m_cfg_indx = data & 0x0f;
+ }
+ else
+ {
+ m_cfg_mode = 0;
+ }
+ }
+ else if (offset == device_address[LogicalDevice::CFG] + 1)
+ {
+ LOGMASKED(LOG_CFG, "CFG write %04x <- %02x\n", offset, data);
+ if (m_cfg_mode == 5)
+ {
+ write_cfg(m_cfg_indx, data);
+ }
+ else
+ {
+ m_cfg_mode = 0;
+ }
+ }
+
+ // ide
+ if (device_enabled[LogicalDevice::IDE] && (offset & ~7) == 0x1f0)
+ {
+ m_ide->cs0_w(offset & 7, data);
+ LOGMASKED(LOG_IDE, "IDE write %04x <- %04x\n", offset, data);
+ }
+ if (device_enabled[LogicalDevice::IDE] && offset == 0x3f6)
+ {
+ m_ide->cs1_w(offset & 7, data);
+ LOGMASKED(LOG_IDE, "IDE write %04x <- %04x\n", offset, data);
+ }
+
+ // fdc
+ if (device_enabled[LogicalDevice::FDC] && (offset & ~7) == 0x3f0 && offset != 0x3f0 && offset != 0x3f1 && offset != 0x3f6)
+ {
+ switch (offset & 7)
+ {
+ case 2:
+ dor_w(data);
+ break;
+ case 5:
+ m_fdc->fifo_w(data);
+ break;
+ case 7:
+ m_fdc->ccr_w(data);
+ break;
+ }
+ LOGMASKED(LOG_FDC, "FDC write %04x <- %02x\n", offset, data);
+ }
+
+ // parallel
+ if (device_enabled[LogicalDevice::LPT] && (offset & ~3) == device_address[LogicalDevice::LPT])
+ {
+ m_lpt->write(offset & 3, data);
+ LOGMASKED(LOG_LPT, "LPT write %04x <- %02x\n", offset, data);
+ }
+
+ // serial
+ if (device_enabled[LogicalDevice::SER] && (offset & ~7) == device_address[LogicalDevice::SER])
+ {
+ m_serial->ins8250_w(offset & 7, data);
+ LOGMASKED(LOG_SER, "SER write %04x <- %02x\n", offset, data);
+ }
+}
+
+
+u8 upc82c710_device::dack_r()
+{
+ return m_fdc->dma_r();
+}
+
+void upc82c710_device::dack_w(u8 data)
+{
+ m_fdc->dma_w(data);
+}
+
+void upc82c710_device::tc_w(bool state)
+{
+ m_fdc->tc_w(state);
+}
+
+
+void upc82c710_device::dor_w(uint8_t data)
+{
+ dor = data;
+
+ for (int i=0; i<4; i++)
+ if (floppy[i])
+ floppy[i]->mon_w(!(dor & (0x10 << i)));
+
+ int fid = dor & 3;
+ if (dor & (0x10 << fid))
+ m_fdc->set_floppy(floppy[fid]);
+ else
+ m_fdc->set_floppy(nullptr);
+
+ check_irq();
+ check_drq();
+ m_fdc->reset_w(!BIT(dor, 2));
+}
+
+WRITE_LINE_MEMBER(upc82c710_device::fdc_irq_w)
+{
+ fdc_irq = state;
+ check_irq();
+}
+
+WRITE_LINE_MEMBER(upc82c710_device::fdc_drq_w)
+{
+ fdc_drq = state;
+ check_drq();
+}
+
+void upc82c710_device::check_irq()
+{
+ bool pirq = irq;
+ irq = fdc_irq && (dor & 4) && (dor & 8);
+ if (irq != pirq)
+ m_fintr_callback(irq);
+}
+
+void upc82c710_device::check_drq()
+{
+ bool pdrq = drq;
+ drq = fdc_drq && (dor & 4) && (dor & 8);
+ if (drq != pdrq)
+ m_fdrq_callback(drq);
+}
+
+
+void upc82c710_device::write_cfg(int index, u8 data)
+{
+ m_cfg_regs[index] = data;
+ LOGMASKED(LOG_CFG, "CR[%02x] = %02x\n", index, data);
+
+ switch (index)
+ {
+ case 0x00: // Chip Selects and Enable
+ device_enabled[LogicalDevice::LPT]= BIT(m_cfg_regs[index], 3);
+ device_enabled[LogicalDevice::SER]= BIT(m_cfg_regs[index], 2);
+ LOGMASKED(LOG_CFG, "LPT %s\n", device_enabled[LogicalDevice::LPT] ? "enabled" : "disabled");
+ LOGMASKED(LOG_CFG, "SER %s\n", device_enabled[LogicalDevice::SER] ? "enabled" : "disabled");
+ break;
+
+ case 0x04: // Serial Port Address
+ device_address[LogicalDevice::SER] = (data & 0xfe) << 2;
+ LOGMASKED(LOG_CFG, "SER address %04x\n", device_address[LogicalDevice::SER]);
+ break;
+
+ case 0x06: // Parallel Port Address
+ device_address[LogicalDevice::LPT] = data << 2;
+ LOGMASKED(LOG_CFG, "LPT address %04x\n", device_address[LogicalDevice::LPT]);
+ break;
+
+ case 0x0c:
+ device_enabled[LogicalDevice::IDE] = BIT(m_cfg_regs[index], 7);
+ device_enabled[LogicalDevice::FDC] = BIT(m_cfg_regs[index], 5);
+ LOGMASKED(LOG_CFG, "IDE %s\n", device_enabled[LogicalDevice::IDE] ? "enabled" : "disabled");
+ LOGMASKED(LOG_CFG, "FDC %s\n", device_enabled[LogicalDevice::FDC] ? "enabled" : "disabled");
+ break;
+
+ case 0x0f:
+ m_cfg_mode = 0;
+ LOGMASKED(LOG_CFG, "CFG -- mode off --\n");
+ break;
+ }
+}
+
+
+WRITE_LINE_MEMBER(upc82c710_device::rxd_w)
+{
+ m_serial->rx_w(state);
+}
+
+WRITE_LINE_MEMBER(upc82c710_device::dcd_w)
+{
+ m_serial->dcd_w(state);
+}
+
+WRITE_LINE_MEMBER(upc82c710_device::dsr_w)
+{
+ m_serial->dsr_w(state);
+}
+
+WRITE_LINE_MEMBER(upc82c710_device::ri_w)
+{
+ m_serial->ri_w(state);
+}
+
+WRITE_LINE_MEMBER(upc82c710_device::cts_w)
+{
+ m_serial->cts_w(state);
+}
diff --git a/src/devices/machine/upc82c710.h b/src/devices/machine/upc82c710.h
new file mode 100644
index 00000000000..7fa9b44a687
--- /dev/null
+++ b/src/devices/machine/upc82c710.h
@@ -0,0 +1,110 @@
+// license:BSD-3-Clause
+// copyright-holders:Nigel Barnes
+/**********************************************************************
+
+ Universal Peripheral Controller 82C710 emulation
+
+**********************************************************************/
+
+#ifndef MAME_MACHINE_UPC82C710_H
+#define MAME_MACHINE_UPC82C710_H
+
+#pragma once
+
+#include "machine/ins8250.h"
+#include "machine/pc_lpt.h"
+#include "machine/upd765.h"
+#include "machine/idectrl.h"
+#include "imagedev/floppy.h"
+
+
+class upc82c710_device : public device_t
+{
+public:
+ // construction/destruction
+ upc82c710_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+
+ u16 io_r(offs_t offset, u16 mem_mask = ~0);
+ void io_w(offs_t offset, u16 data, u16 mem_mask = ~0);
+ u8 dack_r();
+ void dack_w(u8 data);
+ void tc_w(bool state);
+
+ u8 dack_tc0_r() { tc_w(false); return dack_r(); }
+ void dack_tc0_w(u8 data) { tc_w(false); dack_w(data); }
+ u8 dack_tc1_r() { tc_w(true); return dack_r(); }
+ void dack_tc1_w(u8 data) { tc_w(true); dack_w(data); }
+
+ auto fintr() { return m_fintr_callback.bind(); }
+ auto fdrq() { return m_fdrq_callback.bind(); }
+ auto pintr() { return m_pintr_callback.bind(); }
+ auto sintr() { return m_sintr_callback.bind(); }
+ auto txd() { return m_txd_callback.bind(); }
+ auto dtr() { return m_dtr_callback.bind(); }
+ auto rts() { return m_rts_callback.bind(); }
+
+ // chip pins for uarts
+ DECLARE_WRITE_LINE_MEMBER(rxd_w);
+ DECLARE_WRITE_LINE_MEMBER(dcd_w);
+ DECLARE_WRITE_LINE_MEMBER(dsr_w);
+ DECLARE_WRITE_LINE_MEMBER(ri_w);
+ DECLARE_WRITE_LINE_MEMBER(cts_w);
+
+protected:
+ // device-level overrides
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_add_mconfig(machine_config &config) override;
+
+private:
+ required_device<ata_interface_device> m_ide;
+ required_device<upd765_family_device> m_fdc;
+ required_device<pc_lpt_device> m_lpt;
+ required_device<ns16450_device> m_serial;
+
+ void dor_w(u8 data);
+ DECLARE_WRITE_LINE_MEMBER(fdc_irq_w);
+ DECLARE_WRITE_LINE_MEMBER(fdc_drq_w);
+
+ bool irq, drq, fdc_drq, fdc_irq;
+ u8 dor;
+
+ floppy_image_device *floppy[4];
+
+ void check_irq();
+ void check_drq();
+
+ devcb_write_line m_fintr_callback;
+ devcb_write_line m_fdrq_callback;
+ devcb_write_line m_pintr_callback;
+ devcb_write_line m_sintr_callback;
+
+ devcb_write_line m_txd_callback;
+ devcb_write_line m_dtr_callback;
+ devcb_write_line m_rts_callback;
+
+ void write_cfg(int index, u8 data);
+
+ enum LogicalDevice
+ {
+ CFG = 0,
+ FDC,
+ IDE,
+ LPT,
+ SER,
+
+ LogicalDeviceEnd
+ };
+
+ bool device_enabled[LogicalDevice::LogicalDeviceEnd];
+ u16 device_address[LogicalDevice::LogicalDeviceEnd];
+
+ int m_cfg_mode;
+ u8 m_cfg_regs[16];
+ u8 m_cfg_indx;
+};
+
+
+DECLARE_DEVICE_TYPE(UPC82C710, upc82c710_device)
+
+#endif // MAME_MACHINE_UPC82C710_H
diff --git a/src/devices/machine/upc82c711.cpp b/src/devices/machine/upc82c711.cpp
new file mode 100644
index 00000000000..34d58cd6001
--- /dev/null
+++ b/src/devices/machine/upc82c711.cpp
@@ -0,0 +1,478 @@
+// license:BSD-3-Clause
+// copyright-holders:Nigel Barnes
+/**********************************************************************
+
+ Universal Peripheral Controller 82C711 emulation
+
+**********************************************************************/
+
+#include "emu.h"
+#include "machine/upc82c711.h"
+
+#define LOG_CFG (1 << 0)
+#define LOG_FDC (1 << 1)
+#define LOG_IDE (1 << 2)
+#define LOG_LPT (1 << 3)
+#define LOG_SER (1 << 4)
+
+#define VERBOSE (0)
+
+#include "logmacro.h"
+
+DEFINE_DEVICE_TYPE(UPC82C711, upc82c711_device, "upc82c711", "Universal Peripheral Controller 82C711")
+
+
+upc82c711_device::upc82c711_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, UPC82C711, tag, owner, clock)
+ , m_ide(*this, "ide")
+ , m_fdc(*this, "fdc")
+ , m_lpt(*this, "parallel")
+ , m_serial(*this, "serial%u", 1U)
+ , m_fintr_callback(*this)
+ , m_fdrq_callback(*this)
+ , m_pintr_callback(*this)
+ , m_irq3_callback(*this)
+ , m_irq4_callback(*this)
+ , m_txd1_callback(*this)
+ , m_dtr1_callback(*this)
+ , m_rts1_callback(*this)
+ , m_txd2_callback(*this)
+ , m_dtr2_callback(*this)
+ , m_rts2_callback(*this)
+ , m_cfg_mode(0)
+{
+}
+
+
+void upc82c711_device::device_add_mconfig(machine_config &config)
+{
+ // ide interface
+ ATA_INTERFACE(config, m_ide);
+
+ // floppy disc controller
+ UPD765A(config, m_fdc, clock() / 3, false, false); // uPD72065B
+ m_fdc->intrq_wr_callback().set(FUNC(upc82c711_device::fdc_irq_w));
+ m_fdc->drq_wr_callback().set(FUNC(upc82c711_device::fdc_drq_w));
+
+ // parallel port
+ PC_LPT(config, m_lpt);
+ m_lpt->irq_handler().set([this](int state) { m_pintr_callback(state); });
+
+ // serial ports
+ NS16450(config, m_serial[0], clock() / 13);
+ m_serial[0]->out_int_callback().set([this](int state) { m_irq4_callback(state); });
+ m_serial[0]->out_tx_callback().set([this](int state) { m_txd1_callback(state); });
+ m_serial[0]->out_dtr_callback().set([this](int state) { m_dtr1_callback(state); });
+ m_serial[0]->out_rts_callback().set([this](int state) { m_rts1_callback(state); });
+
+ NS16450(config, m_serial[1], clock() / 13);
+ m_serial[1]->out_int_callback().set([this](int state) { m_irq3_callback(state); });
+ m_serial[1]->out_tx_callback().set([this](int state) { m_txd2_callback(state); });
+ m_serial[1]->out_dtr_callback().set([this](int state) { m_dtr2_callback(state); });
+ m_serial[1]->out_rts_callback().set([this](int state) { m_rts2_callback(state); });
+}
+
+
+void upc82c711_device::device_start()
+{
+ for (int i=0; i<4; i++)
+ {
+ char name[2] = {static_cast<char>('0'+i), 0};
+ floppy_connector *conn = m_fdc->subdevice<floppy_connector>(name);
+ floppy[i] = conn ? conn->get_device() : nullptr;
+ }
+
+ irq = drq = false;
+ fdc_irq = fdc_drq = false;
+
+ m_fintr_callback.resolve_safe();
+ m_fdrq_callback.resolve_safe();
+ m_pintr_callback.resolve_safe();
+ m_irq3_callback.resolve_safe();
+ m_irq4_callback.resolve_safe();
+ m_txd1_callback.resolve_safe();
+ m_dtr1_callback.resolve_safe();
+ m_rts1_callback.resolve_safe();
+ m_txd2_callback.resolve_safe();
+ m_dtr2_callback.resolve_safe();
+ m_rts2_callback.resolve_safe();
+
+ // default addresses
+ com_address[0] = 0x3f8;
+ com_address[1] = 0x2f8;
+ com_address[2] = 0x338;
+ com_address[3] = 0x238;
+
+ device_address[LogicalDevice::IDE] = 0x1f0;
+ device_address[LogicalDevice::FDC] = 0x3f0;
+ device_address[LogicalDevice::LPT] = 0x278;
+ device_address[LogicalDevice::SER1] = com_address[0];
+ device_address[LogicalDevice::SER2] = com_address[1];
+
+ const u8 cfg_regs_defaults[] = { 0x3f, 0x9f, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ // Set the value first and then use write_config because some flags
+ // rely on other flags being initialized properly first
+ std::copy(std::begin(cfg_regs_defaults), std::end(cfg_regs_defaults), std::begin(m_cfg_regs));
+ for (int i = 0; i < std::size(cfg_regs_defaults); i++)
+ {
+ write_cfg(i, cfg_regs_defaults[i]);
+ }
+
+ save_item(NAME(m_cfg_regs));
+ save_item(NAME(m_cfg_indx));
+ save_item(NAME(m_cfg_mode));
+}
+
+void upc82c711_device::device_reset()
+{
+ dor_w(0x00);
+}
+
+
+u16 upc82c711_device::io_r(offs_t offset, u16 mem_mask)
+{
+ u16 data = 0xffff;
+
+ // configuration
+ if (offset == 0x3f1 && m_cfg_mode == 2)
+ {
+ data = m_cfg_regs[m_cfg_indx];
+ LOGMASKED(LOG_CFG, "CFG read %04x -> %02x\n", offset, data);
+ }
+
+ // ide
+ if (device_enabled[LogicalDevice::IDE] && (offset & ~7) == 0x1f0)
+ {
+ data = m_ide->cs0_r(offset & 7);
+ LOGMASKED(LOG_IDE, "IDE read %04x -> %04x\n", offset, data);
+ }
+ if (device_enabled[LogicalDevice::IDE] && offset == 0x3f6)
+ {
+ data = m_ide->cs1_r(offset & 7);
+ LOGMASKED(LOG_IDE, "IDE read %04x -> %04x\n", offset, data);
+ }
+
+ // fdc
+ if (device_enabled[LogicalDevice::FDC] && (offset & ~7) == 0x3f0 && offset != 0x3f0 && offset != 0x3f1 && offset != 0x3f6)
+ {
+ switch (offset & 7)
+ {
+ case 3:
+ data = 0x20;
+ break;
+ case 4:
+ data = m_fdc->msr_r();
+ break;
+ case 5:
+ data = m_fdc->fifo_r();
+ break;
+ case 7:
+ data = m_fdc->dir_r() | (m_ide->cs1_r(7) & 0x7f);
+ break;
+ }
+ LOGMASKED(LOG_FDC, "FDC read %04x -> %02x\n", offset, data);
+ }
+
+ // parallel
+ if (device_enabled[LogicalDevice::LPT] && (offset & ~3) == device_address[LogicalDevice::LPT])
+ {
+ data = m_lpt->read(offset & 3);
+ LOGMASKED(LOG_LPT, "LPT read %04x -> %02x\n", offset, data);
+ }
+
+ // serial 1
+ if (device_enabled[LogicalDevice::SER1] && (offset & ~7) == device_address[LogicalDevice::SER1])
+ {
+ data = m_serial[0]->ins8250_r(offset & 7);
+ LOGMASKED(LOG_SER, "SER1 read %04x -> %02x\n", offset, data);
+ }
+
+ // serial 2
+ if (device_enabled[LogicalDevice::SER2] && (offset & ~7) == device_address[LogicalDevice::SER2])
+ {
+ data = m_serial[1]->ins8250_r(offset & 7);
+ LOGMASKED(LOG_SER, "SER2 read %04x -> %02x\n", offset, data);
+ }
+
+ return data;
+}
+
+void upc82c711_device::io_w(offs_t offset, u16 data, u16 mem_mask)
+{
+ // configuration
+ if (offset == 0x3f0)
+ {
+ LOGMASKED(LOG_CFG, "CFG write %04x <- %02x\n", offset, data);
+ if (m_cfg_mode == 2)
+ {
+ if (data == 0xaa) // escape configuration
+ {
+ m_cfg_mode = 0;
+ LOGMASKED(LOG_CFG, "CFG -- mode off --\n");
+ }
+ else
+ {
+ m_cfg_indx = data & 0x0f;
+ }
+ }
+ else
+ {
+ if (data == 0x55) // enter configuration
+ {
+ m_cfg_mode++;
+ if (m_cfg_mode == 2)
+ LOGMASKED(LOG_CFG, "CFG -- mode on --\n");
+ }
+ else
+ {
+ m_cfg_mode = 0;
+ }
+ }
+ }
+ else if (offset == 0x3f1)
+ {
+ LOGMASKED(LOG_CFG, "CFG write %04x <- %02x\n", offset, data);
+ if (m_cfg_mode == 2)
+ {
+ write_cfg(m_cfg_indx, data);
+ }
+ }
+
+ // ide
+ if (device_enabled[LogicalDevice::IDE] && (offset & ~7) == 0x1f0)
+ {
+ m_ide->cs0_w(offset & 7, data);
+ LOGMASKED(LOG_IDE, "IDE write %04x <- %04x\n", offset, data);
+ }
+ if (device_enabled[LogicalDevice::IDE] && offset == 0x3f6)
+ {
+ m_ide->cs1_w(offset & 7, data);
+ LOGMASKED(LOG_IDE, "IDE write %04x <- %04x\n", offset, data);
+ }
+
+ // fdc
+ if (device_enabled[LogicalDevice::FDC] && (offset & ~7) == 0x3f0 && offset != 0x3f0 && offset != 0x3f1 && offset != 0x3f6)
+ {
+ switch (offset & 7)
+ {
+ case 2:
+ dor_w(data);
+ break;
+ case 5:
+ m_fdc->fifo_w(data);
+ break;
+ case 7:
+ m_fdc->ccr_w(data);
+ break;
+ }
+ LOGMASKED(LOG_FDC, "FDC write %04x <- %02x\n", offset, data);
+ }
+
+ // parallel
+ if (device_enabled[LogicalDevice::LPT] && (offset & ~3) == device_address[LogicalDevice::LPT])
+ {
+ m_lpt->write(offset & 3, data);
+ LOGMASKED(LOG_LPT, "LPT write %04x <- %02x\n", offset, data);
+ }
+
+ // serial 1
+ if (device_enabled[LogicalDevice::SER1] && (offset & ~7) == device_address[LogicalDevice::SER1])
+ {
+ m_serial[0]->ins8250_w(offset & 7, data);
+ LOGMASKED(LOG_SER, "SER1 write %04x <- %02x\n", offset, data);
+ }
+
+ // serial 2
+ if (device_enabled[LogicalDevice::SER2] && (offset & ~7) == device_address[LogicalDevice::SER2])
+ {
+ m_serial[1]->ins8250_w(offset & 7, data);
+ LOGMASKED(LOG_SER, "SER2 write %04x <- %02x\n", offset, data);
+ }
+}
+
+
+u8 upc82c711_device::dack_r()
+{
+ return m_fdc->dma_r();
+}
+
+void upc82c711_device::dack_w(u8 data)
+{
+ m_fdc->dma_w(data);
+}
+
+void upc82c711_device::tc_w(bool state)
+{
+ m_fdc->tc_w(state);
+}
+
+
+void upc82c711_device::dor_w(uint8_t data)
+{
+ dor = data;
+
+ for (int i=0; i<4; i++)
+ if (floppy[i])
+ floppy[i]->mon_w(!(dor & (0x10 << i)));
+
+ int fid = dor & 3;
+ if (dor & (0x10 << fid))
+ m_fdc->set_floppy(floppy[fid]);
+ else
+ m_fdc->set_floppy(nullptr);
+
+ check_irq();
+ check_drq();
+ m_fdc->reset_w(!BIT(dor, 2));
+}
+
+WRITE_LINE_MEMBER(upc82c711_device::fdc_irq_w)
+{
+ fdc_irq = state;
+ check_irq();
+}
+
+WRITE_LINE_MEMBER(upc82c711_device::fdc_drq_w)
+{
+ fdc_drq = state;
+ check_drq();
+}
+
+void upc82c711_device::check_irq()
+{
+ bool pirq = irq;
+ irq = fdc_irq && (dor & 4) && (dor & 8);
+ if (irq != pirq)
+ m_fintr_callback(irq);
+}
+
+void upc82c711_device::check_drq()
+{
+ bool pdrq = drq;
+ drq = fdc_drq && (dor & 4) && (dor & 8);
+ if (drq != pdrq)
+ m_fdrq_callback(drq);
+}
+
+
+void upc82c711_device::write_cfg(int index, u8 data)
+{
+ m_cfg_regs[index] = data;
+ LOGMASKED(LOG_CFG, "CR[%02x] = %02x\n", index, data);
+
+ switch (index)
+ {
+ case 0x00:
+ device_enabled[LogicalDevice::IDE] = BIT(m_cfg_regs[index], 0);
+ device_enabled[LogicalDevice::FDC] = BIT(m_cfg_regs[index], 3) && BIT(m_cfg_regs[index], 4);
+ LOGMASKED(LOG_CFG, "IDE %s\n", device_enabled[LogicalDevice::IDE] ? "enabled" : "disabled");
+ LOGMASKED(LOG_CFG, "FDC %s\n", device_enabled[LogicalDevice::FDC] ? "enabled" : "disabled");
+ break;
+
+ case 0x01:
+ {
+ device_enabled[LogicalDevice::LPT] = BIT(m_cfg_regs[index], 2);
+
+ auto lpt_port = BIT(m_cfg_regs[index], 0, 2);
+ switch (lpt_port)
+ {
+ case 0: // Disabled
+ device_enabled[LogicalDevice::LPT] = false;
+ break;
+ case 1:
+ device_address[LogicalDevice::LPT] = 0x3bc;
+ break;
+ case 2:
+ device_address[LogicalDevice::LPT] = 0x378;
+ break;
+ case 3: // Default
+ device_address[LogicalDevice::LPT] = 0x278;
+ break;
+ }
+ LOGMASKED(LOG_CFG, "LPT %04x %s\n", device_address[LogicalDevice::LPT], device_enabled[LogicalDevice::LPT] ? "enabled" : "disabled");
+ auto com34 = BIT(m_cfg_regs[index], 5, 2);
+ switch (com34)
+ {
+ case 0: // Default
+ com_address[2] = 0x338;
+ com_address[3] = 0x238;
+ break;
+ case 1:
+ com_address[2] = 0x3e8;
+ com_address[3] = 0x2e8;
+ break;
+ case 2:
+ com_address[2] = 0x2e8;
+ com_address[3] = 0x2e0;
+ break;
+ case 3:
+ com_address[2] = 0x220;
+ com_address[3] = 0x228;
+ break;
+ }
+ break;
+ }
+ case 0x02:
+ device_enabled[LogicalDevice::SER1] = BIT(m_cfg_regs[index], 2) && BIT(m_cfg_regs[index], 3);
+ device_address[LogicalDevice::SER1] = com_address[BIT(m_cfg_regs[index], 0, 2)];
+
+ device_enabled[LogicalDevice::SER2] = BIT(m_cfg_regs[index], 6) && BIT(m_cfg_regs[index], 7);
+ device_address[LogicalDevice::SER2] = com_address[BIT(m_cfg_regs[index], 4, 2)];
+
+ LOGMASKED(LOG_CFG, "SER1 %04x %s\n", device_address[LogicalDevice::SER1], device_enabled[LogicalDevice::SER1] ? "enabled" : "disabled");
+ LOGMASKED(LOG_CFG, "SER2 %04x %s\n", device_address[LogicalDevice::SER2], device_enabled[LogicalDevice::SER2] ? "enabled" : "disabled");
+ break;
+ }
+}
+
+
+WRITE_LINE_MEMBER(upc82c711_device::rxd1_w)
+{
+ m_serial[0]->rx_w(state);
+}
+
+WRITE_LINE_MEMBER(upc82c711_device::dcd1_w)
+{
+ m_serial[0]->dcd_w(state);
+}
+
+WRITE_LINE_MEMBER(upc82c711_device::dsr1_w)
+{
+ m_serial[0]->dsr_w(state);
+}
+
+WRITE_LINE_MEMBER(upc82c711_device::ri1_w)
+{
+ m_serial[0]->ri_w(state);
+}
+
+WRITE_LINE_MEMBER(upc82c711_device::cts1_w)
+{
+ m_serial[0]->cts_w(state);
+}
+
+WRITE_LINE_MEMBER(upc82c711_device::rxd2_w)
+{
+ m_serial[1]->rx_w(state);
+}
+
+WRITE_LINE_MEMBER(upc82c711_device::dcd2_w)
+{
+ m_serial[1]->dcd_w(state);
+}
+
+WRITE_LINE_MEMBER(upc82c711_device::dsr2_w)
+{
+ m_serial[1]->dsr_w(state);
+}
+
+WRITE_LINE_MEMBER(upc82c711_device::ri2_w)
+{
+ m_serial[1]->ri_w(state);
+}
+
+WRITE_LINE_MEMBER(upc82c711_device::cts2_w)
+{
+ m_serial[1]->cts_w(state);
+}
diff --git a/src/devices/machine/upc82c711.h b/src/devices/machine/upc82c711.h
new file mode 100644
index 00000000000..dfaa243be0b
--- /dev/null
+++ b/src/devices/machine/upc82c711.h
@@ -0,0 +1,128 @@
+// license:BSD-3-Clause
+// copyright-holders:Nigel Barnes
+/**********************************************************************
+
+ Universal Peripheral Controller 82C711 emulation
+
+**********************************************************************/
+
+#ifndef MAME_MACHINE_UPC82C711_H
+#define MAME_MACHINE_UPC82C711_H
+
+#pragma once
+
+#include "machine/ins8250.h"
+#include "machine/pc_lpt.h"
+#include "machine/upd765.h"
+#include "bus/ata/ataintf.h"
+#include "imagedev/floppy.h"
+
+
+// ======================> upc82c710_device
+
+class upc82c711_device : public device_t
+{
+public:
+ // construction/destruction
+ upc82c711_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+
+ u16 io_r(offs_t offset, u16 mem_mask = ~0);
+ void io_w(offs_t offset, u16 data, u16 mem_mask = ~0);
+
+ u8 dack_r();
+ void dack_w(u8 data);
+ void tc_w(bool state);
+
+ u8 dack_tc0_r() { tc_w(false); return dack_r(); }
+ void dack_tc0_w(u8 data) { tc_w(false); dack_w(data); }
+ u8 dack_tc1_r() { tc_w(true); return dack_r(); }
+ void dack_tc1_w(u8 data) { tc_w(true); dack_w(data); }
+
+ auto fintr() { return m_fintr_callback.bind(); }
+ auto fdrq() { return m_fdrq_callback.bind(); }
+ auto pintr() { return m_pintr_callback.bind(); }
+ auto irq3() { return m_irq3_callback.bind(); }
+ auto irq4() { return m_irq4_callback.bind(); }
+ auto txd1() { return m_txd1_callback.bind(); }
+ auto dtr1() { return m_dtr1_callback.bind(); }
+ auto rts1() { return m_rts1_callback.bind(); }
+ auto txd2() { return m_txd2_callback.bind(); }
+ auto dtr2() { return m_dtr2_callback.bind(); }
+ auto rts2() { return m_rts2_callback.bind(); }
+
+ // chip pins for uarts
+ DECLARE_WRITE_LINE_MEMBER(rxd1_w);
+ DECLARE_WRITE_LINE_MEMBER(dcd1_w);
+ DECLARE_WRITE_LINE_MEMBER(dsr1_w);
+ DECLARE_WRITE_LINE_MEMBER(ri1_w);
+ DECLARE_WRITE_LINE_MEMBER(cts1_w);
+ DECLARE_WRITE_LINE_MEMBER(rxd2_w);
+ DECLARE_WRITE_LINE_MEMBER(dcd2_w);
+ DECLARE_WRITE_LINE_MEMBER(dsr2_w);
+ DECLARE_WRITE_LINE_MEMBER(ri2_w);
+ DECLARE_WRITE_LINE_MEMBER(cts2_w);
+
+protected:
+ // device-level overrides
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_add_mconfig(machine_config &config) override;
+
+private:
+ required_device<ata_interface_device> m_ide;
+ required_device<upd765_family_device> m_fdc;
+ required_device<pc_lpt_device> m_lpt;
+ required_device_array<ns16450_device, 2> m_serial;
+
+ void dor_w(u8 data);
+ DECLARE_WRITE_LINE_MEMBER(fdc_irq_w);
+ DECLARE_WRITE_LINE_MEMBER(fdc_drq_w);
+
+ bool irq, drq, fdc_drq, fdc_irq;
+ u8 dor;
+
+ floppy_image_device *floppy[4];
+
+ void check_irq();
+ void check_drq();
+
+ devcb_write_line m_fintr_callback;
+ devcb_write_line m_fdrq_callback;
+ devcb_write_line m_pintr_callback;
+ devcb_write_line m_irq3_callback; // Serial Port COM1/COM3
+ devcb_write_line m_irq4_callback; // Serial Port COM2/COM4
+
+ devcb_write_line m_txd1_callback;
+ devcb_write_line m_dtr1_callback;
+ devcb_write_line m_rts1_callback;
+ devcb_write_line m_txd2_callback;
+ devcb_write_line m_dtr2_callback;
+ devcb_write_line m_rts2_callback;
+
+ void write_cfg(int index, u8 data);
+
+ enum LogicalDevice
+ {
+ FDC = 0,
+ IDE,
+ LPT,
+ SER1,
+ SER2,
+
+ LogicalDeviceEnd
+ };
+
+ bool device_enabled[LogicalDevice::LogicalDeviceEnd];
+ u16 device_address[LogicalDevice::LogicalDeviceEnd];
+
+ u16 com_address[4];
+
+ int m_cfg_mode;
+ u8 m_cfg_regs[16];
+ u8 m_cfg_indx;
+};
+
+
+DECLARE_DEVICE_TYPE(UPC82C711, upc82c711_device)
+
+#endif // MAME_MACHINE_UPC82C711_H