summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Patrick Mackinlay <pmackinlay@hotmail.com>2022-07-11 14:14:45 +0700
committer Patrick Mackinlay <pmackinlay@hotmail.com>2022-07-11 14:14:45 +0700
commit1a6147aeb9fd887ffe240844bb2d4e643ac0264e (patch)
treeb33028944b7c0589f18187294987136f6cb957d6
parent27f2febd810dfcdc207b332783bbdb447148177e (diff)
opus100pm: add opus 108pm isa bus card
-rw-r--r--scripts/src/bus.lua2
-rw-r--r--src/devices/bus/isa/isa.h4
-rw-r--r--src/devices/bus/isa/isa_cards.cpp3
-rw-r--r--src/devices/bus/isa/opus100pm.cpp352
-rw-r--r--src/devices/bus/isa/opus100pm.h71
5 files changed, 432 insertions, 0 deletions
diff --git a/scripts/src/bus.lua b/scripts/src/bus.lua
index 443fe4c3922..655757c856c 100644
--- a/scripts/src/bus.lua
+++ b/scripts/src/bus.lua
@@ -1753,6 +1753,8 @@ if (BUSES["ISA"]~=null) then
MAME_DIR .. "src/devices/bus/isa/3xtwin.h",
MAME_DIR .. "src/devices/bus/isa/lrk330.cpp",
MAME_DIR .. "src/devices/bus/isa/lrk330.h",
+ MAME_DIR .. "src/devices/bus/isa/opus100pm.cpp",
+ MAME_DIR .. "src/devices/bus/isa/opus100pm.h",
}
end
diff --git a/src/devices/bus/isa/isa.h b/src/devices/bus/isa/isa.h
index ebbfefda262..939f8d88a26 100644
--- a/src/devices/bus/isa/isa.h
+++ b/src/devices/bus/isa/isa.h
@@ -158,6 +158,10 @@ public:
{
install_space(AS_ISA_MEM, start, end, rhandler, whandler);
}
+ template<typename T> void install_memory(offs_t addrstart, offs_t addrend, T &device, void (T:: *map)(class address_map &map), uint64_t unitmask = ~u64(0))
+ {
+ m_memspace->install_device(addrstart, addrend, device, map, unitmask);
+ }
void unmap_device(offs_t start, offs_t end) const { m_iospace->unmap_readwrite(start, end); }
void unmap_bank(offs_t start, offs_t end);
diff --git a/src/devices/bus/isa/isa_cards.cpp b/src/devices/bus/isa/isa_cards.cpp
index cc00f2233e0..0b01c16d501 100644
--- a/src/devices/bus/isa/isa_cards.cpp
+++ b/src/devices/bus/isa/isa_cards.cpp
@@ -85,6 +85,7 @@
#include "chessmsr.h"
#include "finalchs.h"
#include "bblue2.h"
+#include "opus100pm.h"
void pc_isa8_cards(device_slot_interface &device)
@@ -141,6 +142,7 @@ void pc_isa8_cards(device_slot_interface &device)
device.option_add("babyblue2", ISA8_BABYBLUE2);
device.option_add("acb2072", ACB2072);
device.option_add("3xtwin", ISA8_3XTWIN);
+ device.option_add("opus108pm", ISA8_OPUS108PM);
}
void pc_isa16_cards(device_slot_interface &device)
@@ -187,6 +189,7 @@ void pc_isa16_cards(device_slot_interface &device)
device.option_add("babyblue2", ISA8_BABYBLUE2);
device.option_add("acb2072", ACB2072);
device.option_add("3xtwin", ISA8_3XTWIN);
+ device.option_add("opus108pm", ISA8_OPUS108PM);
// 16-bit
device.option_add("ide", ISA16_IDE);
device.option_add("ne2000", NE2000);
diff --git a/src/devices/bus/isa/opus100pm.cpp b/src/devices/bus/isa/opus100pm.cpp
new file mode 100644
index 00000000000..86803a390a3
--- /dev/null
+++ b/src/devices/bus/isa/opus100pm.cpp
@@ -0,0 +1,352 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+/*
+ * Opus 100PM (Personal Mainframe) UNIX processor subsystem
+ *
+ * Sources:
+ * - http://bitsavers.org/pdf/opusSystems/32k/800-00237-000_Opus_100pm_User_Manual_1987.pdf
+ *
+ * TODO
+ * - 32032-based 110PM
+ */
+
+ /*
+ * WIP
+ * ---
+ * Using IBM 5160 as host avoids boot problem with 5170 and issues with
+ * shadowing in ct486.
+ *
+ * - opus ibm5160 -window -nomax -hard1 opus_c.chd -hard2 opus_d.chd -isa5 opus108pm
+ *
+ * System configuration and software installation from boot and kernel floppies
+ * appears successful. Valid Systems hard disk images appear to expect a card
+ * with different registers?
+ *
+ * Booting Opus5 from swap hangs with WAIT instruction at 0xf0087c, presume
+ * requires an interrupt from PC which never arrives. Maybe clock device isn't
+ * working properly?
+ */
+
+#include "emu.h"
+#include "opus100pm.h"
+
+#define LOG_GENERAL (1U << 0)
+#define LOG_STAT (1U << 1)
+#define LOG_REG (1U << 2)
+
+//#define VERBOSE (LOG_GENERAL|LOG_REG)
+#include "logmacro.h"
+
+DEFINE_DEVICE_TYPE(ISA8_OPUS108PM, isa8_opus108pm_device, "opus108pm", "Opus 108PM")
+
+// CPU bit definition here matches software, but differs from documentation
+enum card_stat_mask : u8
+{
+ CARD_STAT_CPU = 0x01, // CPU type (0=32016, 1=32032)
+ CARD_STAT_EIRQ = 0x04, // IRQ enabled
+ CARD_STAT_OPT = 0x08, // option switch open
+ CARD_STAT_DMA = 0x10, // DMA abort
+ CARD_STAT_PAR = 0x20, // parity error
+ CARD_STAT_IRQ = 0x40, // interrupt to host active
+ CARD_STAT_INT = 0x80, // interrupt to card active
+};
+
+// the three low bits in the host status register probably correspond to
+// unemulated CTTL, /TSO and /CWAIT signals from the TCU
+enum host_stat_mask : u8
+{
+ HOST_STAT_CTTL = 0x01, // CTTL (diagnostic only)
+ HOST_STAT_TSO = 0x02, // TSO* (diagnostic only)
+ HOST_STAT_CWT = 0x04, // CWT* (diagnostic only)
+ HOST_STAT_RUN = 0x08, // RUN* (diagnostic only)
+ HOST_STAT_DMA = 0x10, // DMA abort
+ HOST_STAT_PAR = 0x20, // parity error
+ HOST_STAT_IRQ = 0x40, // interrupt to host active
+ HOST_STAT_INT = 0x80, // interrupt to card active
+};
+
+static INPUT_PORTS_START(opus108pm)
+ PORT_START("BASE")
+ PORT_DIPNAME(0xf0000, 0xa0000, "Base Address")
+ PORT_DIPSETTING( 0x00000, "00000")
+ PORT_DIPSETTING( 0x10000, "10000")
+ PORT_DIPSETTING( 0x20000, "20000")
+ PORT_DIPSETTING( 0x30000, "30000")
+ PORT_DIPSETTING( 0x40000, "40000")
+ PORT_DIPSETTING( 0x50000, "50000")
+ PORT_DIPSETTING( 0x60000, "60000")
+ PORT_DIPSETTING( 0x70000, "70000")
+ PORT_DIPSETTING( 0x80000, "80000")
+ PORT_DIPSETTING( 0x90000, "90000")
+ PORT_DIPSETTING( 0xa0000, "A0000")
+ PORT_DIPSETTING( 0xb0000, "B0000")
+ PORT_DIPSETTING( 0xc0000, "C0000")
+ PORT_DIPSETTING( 0xd0000, "D0000")
+ PORT_DIPSETTING( 0xe0000, "E0000")
+ PORT_DIPSETTING( 0xf0000, "F0000")
+
+ PORT_START("IRQ")
+ PORT_DIPNAME(0x0f, 0x07, "IRQ")
+ PORT_DIPSETTING( 0x02, "IRQ 2")
+ PORT_DIPSETTING( 0x03, "IRQ 3")
+ PORT_DIPSETTING( 0x07, "IRQ 7")
+INPUT_PORTS_END
+
+isa8_opus108pm_device::isa8_opus108pm_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, ISA8_OPUS108PM, tag, owner, clock)
+ , device_isa8_card_interface(mconfig, *this)
+ , m_cpu(*this, "cpu")
+ , m_fpu(*this, "fpu")
+ , m_mmu(*this, "mmu")
+ , m_ram(*this, "ram")
+ , m_base(*this, "BASE")
+ , m_irq(*this, "IRQ")
+ , m_installed(false)
+{
+}
+
+void isa8_opus108pm_device::device_add_mconfig(machine_config &config)
+{
+ NS32016(config, m_cpu, 20_MHz_XTAL / 2);
+ m_cpu->set_addrmap(0, &isa8_opus108pm_device::map_cpu);
+
+ NS32081(config, m_fpu, 20_MHz_XTAL / 2);
+ m_cpu->set_fpu(m_fpu);
+
+ NS32082(config, m_mmu, 20_MHz_XTAL / 2);
+ m_cpu->set_mmu(m_mmu);
+
+ RAM(config, m_ram);
+ m_ram->set_default_size("1MiB");
+ m_ram->set_extra_options("2MiB");
+}
+
+ioport_constructor isa8_opus108pm_device::device_input_ports() const
+{
+ return INPUT_PORTS_NAME(opus108pm);
+}
+
+void isa8_opus108pm_device::device_start()
+{
+ set_isa_device();
+
+ if (!m_ram->started())
+ throw device_missing_dependencies();
+
+ save_item(NAME(m_card_stat));
+ save_item(NAME(m_host_stat));
+
+ m_cpu->space(0).install_ram(0, m_ram->mask(), m_ram->pointer());
+}
+
+void isa8_opus108pm_device::device_reset()
+{
+ if (!m_installed)
+ {
+ u32 const base = m_base->read();
+
+ m_isa->install_memory(base, base | 0xffff, *this, &isa8_opus108pm_device::map_isa);
+
+ m_installed = true;
+ }
+
+ m_card_stat = CARD_STAT_OPT;
+ m_host_stat = HOST_STAT_RUN;
+}
+
+void isa8_opus108pm_device::device_reset_after_children()
+{
+ m_cpu->suspend(SUSPEND_REASON_HALT, true);
+ m_cpu->suspend(SUSPEND_REASON_RESET, false);
+}
+
+void isa8_opus108pm_device::map_cpu(address_map &map)
+{
+ // TODO: should unmapped memory accesses cause parity errors and NMI?
+
+ // silence double-word access to byte-sized registers
+ map(0x800000, 0x800003).noprw().mirror(0x030000);
+
+ map(0x800000, 0x800000).r(FUNC(isa8_opus108pm_device::card_wait_r));
+ map(0x810000, 0x810000).r(FUNC(isa8_opus108pm_device::card_stat_r));
+ map(0x810000, 0x810000).w(FUNC(isa8_opus108pm_device::card_rste_w));
+ map(0x820000, 0x820000).w(FUNC(isa8_opus108pm_device::card_ack_w));
+ map(0x830000, 0x830000).w(FUNC(isa8_opus108pm_device::card_irq_w));
+}
+
+void isa8_opus108pm_device::map_isa(address_map &map)
+{
+ map(0x0000, 0xefff).lrw8(
+ [this](offs_t offset)
+ {
+ if (!(m_host_stat & HOST_STAT_RUN))
+ offset |= 0xff0000;
+
+ // FIXME: invalid translation probably triggers "DMA abort" status
+ // and generates an NMI; unsure what else is reported to the host
+ if (m_mmu->translate(m_cpu->space(0), 0xa, offset, false, false) != ns32000_mmu_interface::COMPLETE)
+ fatalerror("%s: host address translation failed (%s)\n", tag(), machine().describe_context());
+
+ return m_ram->read(offset);
+ }, "ram_r",
+ [this](offs_t offset, u8 data)
+ {
+ if (!(m_host_stat & HOST_STAT_RUN))
+ offset |= 0xff0000;
+
+ // FIXME: invalid translation probably triggers "DMA abort" status
+ // and generates an NMI; unsure what else is reported to the host
+ if (m_mmu->translate(m_cpu->space(0), 0xa, offset, false, true) != ns32000_mmu_interface::COMPLETE)
+ fatalerror("%s: host address translation failed (%s)\n", tag(), machine().describe_context());
+
+ m_ram->write(offset, data);
+ }, "ram_w");
+
+ // software expects these registers at 0xfffN, not 0xf00N per documentation,
+ // assume they are mirrored throughout the whole 4k range
+ map(0xf000, 0xf000).mirror(0x0ff0).r(FUNC(isa8_opus108pm_device::host_stat_r));
+ map(0xf001, 0xf001).mirror(0x0ff0).w(FUNC(isa8_opus108pm_device::host_eirq_w));
+ map(0xf002, 0xf002).mirror(0x0ff0).w(FUNC(isa8_opus108pm_device::host_ack_w));
+ map(0xf003, 0xf003).mirror(0x0ff0).w(FUNC(isa8_opus108pm_device::host_int_w));
+ map(0xf004, 0xf004).mirror(0x0ff0).w(FUNC(isa8_opus108pm_device::host_nmi_w));
+ map(0xf005, 0xf005).mirror(0x0ff0).w(FUNC(isa8_opus108pm_device::host_go_w));
+ map(0xf006, 0xf006).mirror(0x0ff0).w(FUNC(isa8_opus108pm_device::host_run_w));
+ map(0xf007, 0xf007).mirror(0x0ff0).w(FUNC(isa8_opus108pm_device::host_rst_w));
+}
+
+void isa8_opus108pm_device::update_isa_irq(int state)
+{
+ LOG("update_isa_irq %d\n", state);
+
+ switch (m_irq->read())
+ {
+ case 2: m_isa->irq2_w(state); break;
+ case 3: m_isa->irq3_w(state); break;
+ case 7: m_isa->irq7_w(state); break;
+
+ default:
+ fatalerror("%s: invalid isa irq %d\n", tag(), m_irq->read());
+ }
+}
+
+u8 isa8_opus108pm_device::card_stat_r()
+{
+ LOGMASKED(LOG_STAT, "card_stat_r 0x%02x (%s)\n", m_card_stat, machine().describe_context());
+
+ return m_card_stat;
+}
+
+void isa8_opus108pm_device::card_rste_w(u8 data)
+{
+ LOG("card_rste_w (%s)\n", machine().describe_context());
+
+ m_card_stat &= ~(CARD_STAT_PAR | CARD_STAT_DMA);
+ m_host_stat &= ~(HOST_STAT_PAR | HOST_STAT_DMA);
+}
+
+void isa8_opus108pm_device::card_ack_w(u8 data)
+{
+ if (m_card_stat & CARD_STAT_INT)
+ {
+ LOG("card_ack_w (%s)\n", machine().describe_context());
+ m_card_stat &= ~CARD_STAT_INT;
+ m_host_stat &= ~HOST_STAT_INT;
+ m_cpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
+ }
+}
+
+void isa8_opus108pm_device::card_irq_w(u8 data)
+{
+ if (!(m_card_stat & CARD_STAT_IRQ))
+ {
+ LOG("card_irq_w (%s)\n", machine().describe_context());
+ m_card_stat |= CARD_STAT_IRQ;
+ m_host_stat |= HOST_STAT_IRQ;
+
+ if (m_card_stat & CARD_STAT_EIRQ)
+ update_isa_irq(1);
+ }
+}
+
+u8 isa8_opus108pm_device::host_stat_r()
+{
+ // HACK: diagnostic test requires changes in this host status bit, this
+ // arbitrary toggle satisfies the test but is not accurate
+ m_host_stat ^= HOST_STAT_CWT;
+
+ LOGMASKED(LOG_STAT, "host_stat_r 0x%02x (%s)\n", m_host_stat, machine().describe_context());
+ return m_host_stat;
+}
+
+void isa8_opus108pm_device::host_eirq_w(u8 data)
+{
+ if (!(m_card_stat & CARD_STAT_EIRQ))
+ {
+ LOG("host_eirq_w (%s)\n", machine().describe_context());
+ m_card_stat |= CARD_STAT_EIRQ;
+
+ if (m_card_stat & CARD_STAT_IRQ)
+ update_isa_irq(1);
+ }
+}
+
+void isa8_opus108pm_device::host_ack_w(u8 data)
+{
+ if (m_host_stat & HOST_STAT_IRQ)
+ {
+ LOG("host_ack_w (%s)\n", machine().describe_context());
+ m_card_stat &= ~CARD_STAT_IRQ;
+ m_host_stat &= ~HOST_STAT_IRQ;
+
+ update_isa_irq(0);
+ }
+}
+
+void isa8_opus108pm_device::host_int_w(u8 data)
+{
+ if (!(m_host_stat & HOST_STAT_INT))
+ {
+ LOG("host_int_w (%s)\n", machine().describe_context());
+ m_card_stat |= CARD_STAT_INT;
+ m_host_stat |= HOST_STAT_INT;
+ m_cpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE);
+ }
+}
+
+void isa8_opus108pm_device::host_nmi_w(u8 data)
+{
+ LOG("host_nmi_w (%s)\n", machine().describe_context());
+ m_cpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
+ m_cpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
+}
+
+void isa8_opus108pm_device::host_go_w(u8 data)
+{
+ LOG("host_go_w (%s)\n", machine().describe_context());
+
+ m_cpu->resume(SUSPEND_REASON_RESET);
+}
+
+void isa8_opus108pm_device::host_run_w(u8 data)
+{
+ LOG("host_run_w (%s)\n", machine().describe_context());
+
+ m_host_stat &= ~HOST_STAT_RUN;
+ m_cpu->resume(SUSPEND_REASON_HALT);
+}
+
+void isa8_opus108pm_device::host_rst_w(u8 data)
+{
+ LOG("host_rst_w (%s)\n", machine().describe_context());
+
+ m_card_stat &= (CARD_STAT_OPT | CARD_STAT_CPU);
+ m_host_stat = HOST_STAT_RUN;
+
+ m_cpu->reset();
+ m_fpu->reset();
+ m_mmu->reset();
+
+ m_cpu->suspend(SUSPEND_REASON_HALT, true);
+ m_cpu->suspend(SUSPEND_REASON_RESET, false);
+}
diff --git a/src/devices/bus/isa/opus100pm.h b/src/devices/bus/isa/opus100pm.h
new file mode 100644
index 00000000000..d38d0a41884
--- /dev/null
+++ b/src/devices/bus/isa/opus100pm.h
@@ -0,0 +1,71 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+#ifndef MAME_BUS_ISA_OPUS100PM_H
+#define MAME_BUS_ISA_OPUS100PM_H
+
+#pragma once
+
+#include "cpu/ns32000/ns32000.h"
+#include "machine/ns32081.h"
+#include "machine/ns32082.h"
+#include "machine/ram.h"
+
+#include "bus/isa/isa.h"
+
+class isa8_opus108pm_device
+ : public device_t
+ , public device_isa8_card_interface
+{
+public:
+ isa8_opus108pm_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
+
+protected:
+ // device_t overrides
+ virtual void device_add_mconfig(machine_config &config) override;
+ virtual ioport_constructor device_input_ports() const override;
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_reset_after_children() override;
+
+ void map_cpu(address_map &map);
+ void map_isa(address_map &map);
+
+ // adapter register helpers
+ u8 card_wait_r() { return 0xb2; }
+ u8 card_stat_r();
+ void card_rste_w(u8 data);
+ void card_ack_w(u8 data);
+ void card_irq_w(u8 data);
+
+ // host register helpers
+ u8 host_stat_r();
+ void host_eirq_w(u8 data);
+ void host_ack_w(u8 data);
+ void host_int_w(u8 data);
+ void host_nmi_w(u8 data);
+ void host_go_w(u8 data);
+ void host_run_w(u8 data);
+ void host_rst_w(u8 data);
+
+ void update_isa_irq(int state);
+
+private:
+ required_device<ns32016_device> m_cpu;
+ required_device<ns32081_device> m_fpu;
+ required_device<ns32082_device> m_mmu;
+ required_device<ram_device> m_ram;
+
+ required_ioport m_base;
+ required_ioport m_irq;
+
+ bool m_installed;
+
+ u8 m_card_stat;
+ u8 m_host_stat;
+};
+
+// device type definition
+DECLARE_DEVICE_TYPE(ISA8_OPUS108PM, isa8_opus108pm_device)
+
+#endif // MAME_BUS_ISA_OPUS100PM_H