diff options
| author | 2022-07-11 14:14:45 +0700 | |
|---|---|---|
| committer | 2022-07-11 14:14:45 +0700 | |
| commit | 1a6147aeb9fd887ffe240844bb2d4e643ac0264e (patch) | |
| tree | b33028944b7c0589f18187294987136f6cb957d6 | |
| parent | 27f2febd810dfcdc207b332783bbdb447148177e (diff) | |
opus100pm: add opus 108pm isa bus card
| -rw-r--r-- | scripts/src/bus.lua | 2 | ||||
| -rw-r--r-- | src/devices/bus/isa/isa.h | 4 | ||||
| -rw-r--r-- | src/devices/bus/isa/isa_cards.cpp | 3 | ||||
| -rw-r--r-- | src/devices/bus/isa/opus100pm.cpp | 352 | ||||
| -rw-r--r-- | src/devices/bus/isa/opus100pm.h | 71 |
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 |
