summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author AJR <ajrhacker@users.noreply.github.com>2019-12-28 12:35:20 -0500
committer AJR <ajrhacker@users.noreply.github.com>2019-12-28 12:39:46 -0500
commit14c4dcc643d9c735abeceab7ccaf2a0cf8a7541f (patch)
treedd855e728d5c4ec425e0c36b8ffb3d3c31cc4a3c
parent468387d51fbf4314ec468a09c8d6ebb32a6adf27 (diff)
Add new disassembler for PACE architecture
New machines marked as NOT_WORKING ---------------------------------- unknown INS8900 Multibus card (980305652) [Bitsavers]
-rw-r--r--scripts/src/cpu.lua17
-rw-r--r--scripts/target/mame/mess.lua2
-rw-r--r--src/devices/cpu/pace/pace.cpp206
-rw-r--r--src/devices/cpu/pace/pace.h122
-rw-r--r--src/devices/cpu/pace/pacedasm.cpp321
-rw-r--r--src/devices/cpu/pace/pacedasm.h36
-rw-r--r--src/mame/drivers/ns5652.cpp59
-rw-r--r--src/mame/mame.lst3
-rw-r--r--src/mame/mess.flt1
-rw-r--r--src/tools/unidasm.cpp2
10 files changed, 769 insertions, 0 deletions
diff --git a/scripts/src/cpu.lua b/scripts/src/cpu.lua
index f027f0aa329..2a83be1f0ad 100644
--- a/scripts/src/cpu.lua
+++ b/scripts/src/cpu.lua
@@ -3014,3 +3014,20 @@ if (CPUS["VT50"]~=null or _OPTIONS["with-tools"]) then
table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/vt50/vt50dasm.cpp")
table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/vt50/vt50dasm.h")
end
+
+--------------------------------------------------
+-- National Semiconductor PACE/INS8900
+--@src/devices/cpu/pace/pace.h,CPUS["PACE"] = true
+--------------------------------------------------
+
+if (CPUS["PACE"]~=null) then
+ files {
+ MAME_DIR .. "src/devices/cpu/pace/pace.cpp",
+ MAME_DIR .. "src/devices/cpu/pace/pace.h",
+ }
+end
+
+if (CPUS["PACE"]~=null or _OPTIONS["with-tools"]) then
+ table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/pace/pacedasm.cpp")
+ table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/pace/pacedasm.h")
+end
diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua
index f112b19fc9d..fc5bf321b4b 100644
--- a/scripts/target/mame/mess.lua
+++ b/scripts/target/mame/mess.lua
@@ -149,6 +149,7 @@ CPUS["CR16B"] = true
CPUS["FR"] = true
CPUS["DSP56000"] = true
CPUS["VT50"] = true
+CPUS["PACE"] = true
--------------------------------------------------
-- specify available sound cores; some of these are
@@ -2787,6 +2788,7 @@ files {
createMESSProjects(_target, _subtarget, "natsemi")
files {
MAME_DIR .. "src/mame/drivers/hh_cop400.cpp",
+ MAME_DIR .. "src/mame/drivers/ns5652.cpp",
}
createMESSProjects(_target, _subtarget, "ncd")
diff --git a/src/devices/cpu/pace/pace.cpp b/src/devices/cpu/pace/pace.cpp
new file mode 100644
index 00000000000..bc0e6f5b62a
--- /dev/null
+++ b/src/devices/cpu/pace/pace.cpp
@@ -0,0 +1,206 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+/***************************************************************************
+
+ National Semiconductor PACE/INS8900
+
+ The IPC-16A PACE (Processing and Control Element) was one of the first
+ commercially available 16-bit microprocessors. It was a successor to
+ the multiple-chip IMP-16 processor and implemented a similar four-
+ accumulator architecture in a single PMOS LSI package using internal
+ microcode. PACE requires two clock inputs with non-overlapping low
+ phases and frequencies between 1.25 and 1.538 MHz.
+
+ The standard machine cycle takes 4 clock periods (and the shortest
+ instructions take 4 cycles each), though cycles can be stretched by
+ asserting the EXTEND pin. Six interrupts are available, one triggered
+ only by internal stack full/empty conditions; the nonmaskable level 0
+ interrupt is intended primarily for debugging. The on-chip 10-level
+ LIFO stack may hold both return addresses and register data. All
+ instructions are single-word.
+
+ INS8900 was a NMOS reimplementation of PACE which takes a single-phase
+ clock and allows up to 2 MHz operation. It has different power supply
+ requirements, but is functionally identical.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "pace.h"
+#include "pacedasm.h"
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// device type definition
+DEFINE_DEVICE_TYPE(INS8900, ins8900_device, "ins8900", "National Semiconductor INS8900")
+
+
+//**************************************************************************
+// DEVICE CONSTRUCTION AND INITIALIZATION
+//**************************************************************************
+
+//-------------------------------------------------
+// pace_device - constructor
+//-------------------------------------------------
+
+pace_device::pace_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock)
+ : cpu_device(mconfig, type, tag, owner, clock)
+ , m_space_config("program", ENDIANNESS_LITTLE, 16, 16, -1)
+ , m_space(nullptr)
+ , m_inst_cache(nullptr)
+ , m_jc_callback{{*this}, {*this}, {*this}}
+ , m_flag_callback{{*this}, {*this}, {*this}, {*this}}
+ , m_icount(0)
+ , m_pc(0)
+ , m_fr(0xffff)
+ , m_ac{0, 0, 0, 0}
+ , m_sp(0)
+ , m_stk{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+{
+}
+
+
+//-------------------------------------------------
+// ins8900_device - constructor
+//-------------------------------------------------
+
+ins8900_device::ins8900_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : pace_device(mconfig, INS8900, tag, owner, clock)
+{
+}
+
+
+//-------------------------------------------------
+// memory_space_config - return a vector of
+// configuration structures for memory spaces
+//-------------------------------------------------
+
+device_memory_interface::space_config_vector pace_device::memory_space_config() const
+{
+ return space_config_vector {
+ std::make_pair(AS_PROGRAM, &m_space_config)
+ };
+}
+
+
+//-------------------------------------------------
+// device_resolve_objects - resolve objects that
+// may be needed for other devices to set
+// initial conditions at start time
+//-------------------------------------------------
+
+void pace_device::device_resolve_objects()
+{
+ // resolve callbacks
+ for (auto &cb : m_jc_callback)
+ cb.resolve_safe(0);
+ for (auto &cb : m_flag_callback)
+ cb.resolve_safe();
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void pace_device::device_start()
+{
+ // get memory spaces
+ m_space = &space(AS_PROGRAM);
+ m_inst_cache = m_space->cache<1, -1, ENDIANNESS_LITTLE>();
+
+ set_icountptr(m_icount);
+
+ // debug state registration
+ state_add(PACE_PC, "PC", m_pc);
+ state_add(STATE_GENPC, "GENPC", m_pc).noshow();
+ state_add(STATE_GENPCBASE, "GENPCBASE", m_pc).noshow();
+ state_add<u16>(PACE_FR, "FR", [this]() { return m_fr; }, [this](u16 data) { fr_w(data); });
+ state_add(STATE_GENFLAGS, "GENFLAGS", m_fr).noshow();
+ for (int i = 0; i < 4; i++)
+ state_add(PACE_AC0 + i, string_format("AC%d", i).c_str(), m_ac[i]);
+ state_add<u8>(PACE_SP, "SP",
+ [this]() { return m_sp; },
+ [this](u8 data) { m_sp = data < 10 ? data : BIT(data, 0) ? 9 : 0; }).mask(0xf);
+ for (int i = 0; i < 10; i++)
+ state_add(PACE_STK0 + i, string_format("STK%d", i).c_str(), m_stk[i]);
+
+ // save states
+ save_item(NAME(m_pc));
+ save_item(NAME(m_fr));
+ save_item(NAME(m_ac));
+ save_item(NAME(m_sp));
+ save_item(NAME(m_stk));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void pace_device::device_reset()
+{
+ m_pc = 0;
+ fr_w(0);
+ m_sp = 0;
+}
+
+
+//-------------------------------------------------
+// create_disassembler - factory method for
+// disassembling program code
+//-------------------------------------------------
+
+std::unique_ptr<util::disasm_interface> pace_device::create_disassembler()
+{
+ return std::make_unique<pace_disassembler>();
+}
+
+
+//**************************************************************************
+// I/O LINES, INTERRUPTS AND FLAGS
+//**************************************************************************
+
+//-------------------------------------------------
+// fr_w - update the flag register
+//-------------------------------------------------
+
+void pace_device::fr_w(u16 data)
+{
+ for (int i = 0; i < 4; i++)
+ if (BIT(data, 11 + i) != BIT(m_fr, 11 + i))
+ m_flag_callback[i](BIT(data, 11 + i));
+
+ // Bits 0 and 15 are always logic 1
+ m_fr = data | 0x8001;
+}
+
+
+//-------------------------------------------------
+// execute_set_input -
+//-------------------------------------------------
+
+void pace_device::execute_set_input(int irqline, int state)
+{
+ // TODO
+}
+
+
+//**************************************************************************
+// PROGRAM EXECUTION
+//**************************************************************************
+
+//-------------------------------------------------
+// execute_run -
+//-------------------------------------------------
+
+void pace_device::execute_run()
+{
+ // TODO
+
+ debugger_instruction_hook(m_pc);
+ m_icount = 0;
+}
diff --git a/src/devices/cpu/pace/pace.h b/src/devices/cpu/pace/pace.h
new file mode 100644
index 00000000000..60ba9766768
--- /dev/null
+++ b/src/devices/cpu/pace/pace.h
@@ -0,0 +1,122 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+/***************************************************************************
+
+ National Semiconductor PACE/INS8900
+
+****************************************************************************
+ _____________
+ D04 1 | | 40 D05
+ D03 2 | | 39 D06
+ D02 3 | | 38 D07
+ D01 4 | | 37 D08
+ D00 5 | | 36 D09
+ IDS 6 | | 35 D10
+ ODS 7 | | 34 D11
+ NADS 8 | | 33 D12
+ NHALT 9 | | 32 D13
+ CONTIN 10 | IPC-16A | 31 D14
+ JC14 11 | INS8900 | 30 D15
+ JC15 12 | | 29 Vgg/Vdd*
+ JC13 13 | | 28 BPS
+ NIR5 14 | | 27 EXTEND
+ NIR4 15 | | 26 NINIT
+ NIR3 16 | | 25 CLK/Vcc*
+ NIR2 17 | | 24 NCLK/CLKX
+ F11 18 | | 23 Vbb*
+ F12 19 | | 22 F14
+ Vss* 20 |_____________| 21 F13
+
+ * For PACE (IPC-16A): Vss = +5V, Vbb = +8V, Vgg = -12V
+ * For INS8900: Vss = GND, Vbb = -8V, Vcc = +5V, Vdd = +12V
+
+***************************************************************************/
+
+#ifndef MAME_CPU_PACE_PACE_H
+#define MAME_CPU_PACE_PACE_H
+
+#pragma once
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> pace_device
+
+class pace_device : public cpu_device
+{
+public:
+ // register enumeration
+ enum {
+ PACE_PC,
+ PACE_FR,
+ PACE_AC0, PACE_AC1, PACE_AC2, PACE_AC3,
+ PACE_SP,
+ PACE_STK0, PACE_STK1, PACE_STK2, PACE_STK3, PACE_STK4,
+ PACE_STK5, PACE_STK6, PACE_STK7, PACE_STK8, PACE_STK9
+ };
+
+ // callback configuration
+ auto jc13_callback() { return m_jc_callback[0].bind(); }
+ auto jc14_callback() { return m_jc_callback[1].bind(); }
+ auto jc15_callback() { return m_jc_callback[2].bind(); }
+ auto f11_callback() { return m_flag_callback[0].bind(); }
+ auto f12_callback() { return m_flag_callback[1].bind(); }
+ auto f13_callback() { return m_flag_callback[2].bind(); }
+ auto f14_callback() { return m_flag_callback[3].bind(); }
+
+protected:
+ // construction/destruction
+ pace_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock);
+
+ // device-specific overrides
+ virtual void device_resolve_objects() override;
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ // device_execute_interface overrides
+ virtual void execute_run() override;
+ virtual void execute_set_input(int irqline, int state) override;
+
+ // device_disasm_interface overrides
+ virtual std::unique_ptr<util::disasm_interface> create_disassembler() override;
+
+ // device_memory_interface overrides
+ virtual space_config_vector memory_space_config() const override;
+
+private:
+ // internal helpers
+ void fr_w(u16 data);
+
+ // address space and cache
+ address_space_config m_space_config;
+ address_space *m_space;
+ memory_access_cache<1, -1, ENDIANNESS_LITTLE> *m_inst_cache;
+
+ // callback objects
+ devcb_read_line m_jc_callback[3];
+ devcb_write_line m_flag_callback[4];
+
+ // execution state
+ s32 m_icount;
+ u16 m_pc;
+ u16 m_fr;
+ u16 m_ac[4];
+ u8 m_sp;
+ u16 m_stk[10];
+};
+
+// ======================> ins8900_device
+
+class ins8900_device : public pace_device
+{
+public:
+ // device type constructor
+ ins8900_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+};
+
+// device type declarations
+DECLARE_DEVICE_TYPE(INS8900, ins8900_device)
+
+#endif // MAME_CPU_PACE_PACE_H
diff --git a/src/devices/cpu/pace/pacedasm.cpp b/src/devices/cpu/pace/pacedasm.cpp
new file mode 100644
index 00000000000..6ab93f8f75d
--- /dev/null
+++ b/src/devices/cpu/pace/pacedasm.cpp
@@ -0,0 +1,321 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+/***************************************************************************
+
+ National Semiconductor PACE (IPC-16A, INS8900) disassembler
+
+ This uses the same basic instruction set as IMP-16, but the binary
+ encoding is not compatible.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "pacedasm.h"
+
+
+//**************************************************************************
+// PACE DISASSEMBLER
+//**************************************************************************
+
+pace_disassembler::pace_disassembler()
+ : util::disasm_interface()
+{
+}
+
+u32 pace_disassembler::opcode_alignment() const
+{
+ return 1;
+}
+
+const char *const pace_disassembler::s_cc[16] = {
+ "STFL", // stack full
+ "REQ0", // AC0 equals zero
+ "PSIGN", // AC0 has positive sign
+ "BIT0", // AC0 has bit 0 set
+ "BIT1", // AC0 has bit 1 set
+ "NREQ0", // AC0 does not equal zero
+ "BIT2", // AC0 has bit 2 set
+ "CONTIN", // CONTIN input is high
+ "LINK", // LINK is set
+ "IEN", // IEN is set
+ "CARRY", // CARRY is set
+ "NSIGN", // AC0 has negative sign
+ "OV", // OV is set
+ "JC13", // JC13 is high
+ "JC14", // JC14 is high
+ "JC15" // JC15 is high
+};
+
+const char *const pace_disassembler::s_flags[16] = {
+ "F0", // does not exist in FR
+ "IE1",
+ "IE2",
+ "IE3",
+ "IE4",
+ "IE5",
+ "OVF",
+ "CRY",
+ "LINK",
+ "IEN",
+ "BYTE",
+ "F11",
+ "F12",
+ "F13",
+ "F14",
+ "F15" // does not exist in FR, but used to reset IE0 after level 0 interrupt
+};
+
+void pace_disassembler::format_addr(std::ostream &stream, u16 addr)
+{
+ if (addr >= 0x1000)
+ stream << "0";
+ util::stream_format(stream, "%04X", addr);
+}
+
+void pace_disassembler::format_disp(std::ostream &stream, u8 disp)
+{
+ if (disp >= 0x80)
+ {
+ stream << "-";
+ disp = 0x100 - disp;
+ }
+ if (disp < 0x0a)
+ util::stream_format(stream, "%d", disp);
+ else
+ util::stream_format(stream, "0%X", disp);
+}
+
+void pace_disassembler::format_ea(std::ostream &stream, u16 pc, u16 inst)
+{
+ if ((inst & 0x0300) == 0x0000)
+ {
+ // 0000–00FF or 0000–007F, FF80–FFFF depending on BPS input
+ if ((inst & 0x00ff) >= 0x10)
+ stream << "0";
+ util::stream_format(stream, "%02X", inst & 0x00ff);
+ }
+ else if ((inst & 0x0300) == 0x0100)
+ format_addr(stream, pc + 1 + s8(inst & 0x00ff));
+ else
+ {
+ format_disp(stream, inst & 0x00ff);
+ util::stream_format(stream, "(AC%d)", (inst & 0x0300) >> 8);
+ }
+}
+
+offs_t pace_disassembler::disassemble(std::ostream &stream, offs_t pc, const pace_disassembler::data_buffer &opcodes, const pace_disassembler::data_buffer &params)
+{
+ u16 inst = opcodes.r16(pc);
+
+ switch (inst & 0xfc00)
+ {
+ case 0x0000:
+ stream << "HALT";
+ return 1;
+
+ case 0x0400:
+ util::stream_format(stream, "%-8sAC%d", "CFR", (inst & 0x0300) >> 8);
+ return 1;
+
+ case 0x0800:
+ util::stream_format(stream, "%-8sAC%d", "CRF", (inst & 0x0300) >> 8);
+ return 1;
+
+ case 0x0c00:
+ stream << "PUSHF";
+ return 1;
+
+ case 0x1000:
+ stream << "PULLF";
+ return 1;
+
+ case 0x1400:
+ util::stream_format(stream, "%-8s", "JSR");
+ format_ea(stream, pc, inst);
+ return 1 | STEP_OVER;
+
+ case 0x1800:
+ util::stream_format(stream, "%-8s", "JMP");
+ format_ea(stream, pc, inst);
+ return 1;
+
+ case 0x1c00:
+ util::stream_format(stream, "%-8sAC%d", "XCHRS", (inst & 0x0300) >> 8);
+ return 1;
+
+ case 0x2000:
+ util::stream_format(stream, "%-8sAC%d,%d,%d", "ROL", (inst & 0x0300) >> 8, (inst & 0x00fe) >> 1, inst & 0x0001);
+ return 1;
+
+ case 0x2400:
+ util::stream_format(stream, "%-8sAC%d,%d,%d", "ROR", (inst & 0x0300) >> 8, (inst & 0x00fe) >> 1, inst & 0x0001);
+ return 1;
+
+ case 0x2800:
+ util::stream_format(stream, "%-8sAC%d,%d,%d", "SHL", (inst & 0x0300) >> 8, (inst & 0x00fe) >> 1, inst & 0x0001);
+ return 1;
+
+ case 0x2c00:
+ util::stream_format(stream, "%-8sAC%d,%d,%d", "SHR", (inst & 0x0300) >> 8, (inst & 0x00fe) >> 1, inst & 0x0001);
+ return 1;
+
+ case 0x3000: case 0x3400: case 0x3800: case 0x3c00:
+ util::stream_format(stream, "%-8s%s", BIT(inst, 7) ? "SFLG" : "PFLG", s_flags[(inst & 0x0f00) >> 8]);
+ return 1;
+
+ case 0x4000: case 0x4400: case 0x4800: case 0x4c00:
+ util::stream_format(stream, "%-8s%s,", "BOC", s_cc[(inst & 0x0f00) >> 8]);
+ format_addr(stream, pc + 1 + s8(inst & 0x00ff));
+ return 1;
+
+ case 0x5000:
+ util::stream_format(stream, "%-8sAC%d,", "LI", (inst & 0x0300) >> 8);
+ format_disp(stream, inst & 0x00ff);
+ return 1;
+
+ case 0x5400:
+ util::stream_format(stream, "%-8sAC%d,AC%d", "RAND", (inst & 0x00c0) >> 6, (inst & 0x0300) >> 8);
+ return 1;
+
+ case 0x5800:
+ util::stream_format(stream, "%-8sAC%d,AC%d", "RXOR", (inst & 0x00c0) >> 6, (inst & 0x0300) >> 8);
+ return 1;
+
+ case 0x5c00:
+ if ((inst & 0x0300) >> 2 == (inst & 0x00c0))
+ stream << "NOP";
+ else
+ util::stream_format(stream, "%-8sAC%d,AC%d", "RCPY", (inst & 0x00c0) >> 6, (inst & 0x0300) >> 8);
+ return 1;
+
+ case 0x6000:
+ util::stream_format(stream, "%-8sAC%d", "PUSH", (inst & 0x0300) >> 8);
+ return 1;
+
+ case 0x6400:
+ util::stream_format(stream, "%-8sAC%d", "PULL", (inst & 0x0300) >> 8);
+ return 1;
+
+ case 0x6800:
+ util::stream_format(stream, "%-8sAC%d,AC%d", "RADD", (inst & 0x00c0) >> 6, (inst & 0x0300) >> 8);
+ return 1;
+
+ case 0x6c00:
+ util::stream_format(stream, "%-8sAC%d,AC%d", "RXCH", (inst & 0x00c0) >> 6, (inst & 0x0300) >> 8);
+ return 1;
+
+ case 0x7000:
+ util::stream_format(stream, "%-8sAC%d,", "CAI", (inst & 0x0300) >> 8);
+ format_disp(stream, inst & 0x00ff);
+ return 1;
+
+ case 0x7400:
+ util::stream_format(stream, "%-8sAC%d,AC%d", "RADC", (inst & 0x00c0) >> 6, (inst & 0x0300) >> 8);
+ return 1;
+
+ case 0x7800:
+ util::stream_format(stream, "%-8sAC%d,", "AISZ", (inst & 0x0300) >> 8);
+ format_disp(stream, inst & 0x00ff);
+ return 1 | STEP_OVER;
+
+ case 0x7c00:
+ util::stream_format(stream, "%-8s", "RTI");
+ format_disp(stream, inst & 0x00ff);
+ return 1 | STEP_OUT;
+
+ case 0x8000:
+ util::stream_format(stream, "%-8s", "RTS");
+ format_disp(stream, inst & 0x00ff);
+ return 1 | STEP_OUT;
+
+ case 0x8800:
+ util::stream_format(stream, "%-8sAC0,", "DECA");
+ format_ea(stream, pc, inst);
+ return 1;
+
+ case 0x8c00:
+ util::stream_format(stream, "%-8s", "ISZ");
+ format_ea(stream, pc, inst);
+ return 1 | STEP_OVER;
+
+ case 0x9000:
+ util::stream_format(stream, "%-8sAC0,", "SUBB");
+ format_ea(stream, pc, inst);
+ return 1;
+
+ case 0x9400:
+ util::stream_format(stream, "%-8s@", "JSR");
+ format_ea(stream, pc, inst);
+ return 1 | STEP_OVER;
+
+ case 0x9800:
+ util::stream_format(stream, "%-8s@", "JMP");
+ format_ea(stream, pc, inst);
+ return 1;
+
+ case 0x9c00:
+ util::stream_format(stream, "%-8sAC0,", "SKG");
+ format_ea(stream, pc, inst);
+ return 1 | STEP_OVER;
+
+ case 0xa000:
+ util::stream_format(stream, "%-8sAC0,@", "LD");
+ format_ea(stream, pc, inst);
+ return 1;
+
+ case 0xa400:
+ util::stream_format(stream, "%-8sAC0,", "OR");
+ format_ea(stream, pc, inst);
+ return 1;
+
+ case 0xa800:
+ util::stream_format(stream, "%-8sAC0,", "AND");
+ format_ea(stream, pc, inst);
+ return 1;
+
+ case 0xac00:
+ util::stream_format(stream, "%-8s", "DSZ");
+ format_ea(stream, pc, inst);
+ return 1 | STEP_OVER;
+
+ case 0xb000:
+ util::stream_format(stream, "%-8sAC0,@", "ST");
+ format_ea(stream, pc, inst);
+ return 1;
+
+ case 0xb800:
+ util::stream_format(stream, "%-8sAC0,", "SKAZ");
+ format_ea(stream, pc, inst);
+ return 1 | STEP_OVER;
+
+ case 0xbc00:
+ util::stream_format(stream, "%-8sAC0,", "LSEX");
+ format_ea(stream, pc, inst);
+ return 1;
+
+ case 0xc000: case 0xc400: case 0xc800: case 0xcc00:
+ util::stream_format(stream, "%-8sAC%d,", "LD", (inst & 0x0c00) >> 10);
+ format_ea(stream, pc, inst);
+ return 1;
+
+ case 0xd000: case 0xd400: case 0xd800: case 0xdc00:
+ util::stream_format(stream, "%-8sAC%d,", "ST", (inst & 0x0c00) >> 10);
+ format_ea(stream, pc, inst);
+ return 1;
+
+ case 0xe000: case 0xe400: case 0xe800: case 0xec00:
+ util::stream_format(stream, "%-8sAC%d,", "ADD", (inst & 0x0c00) >> 10);
+ format_ea(stream, pc, inst);
+ return 1;
+
+ case 0xf000: case 0xf400: case 0xf800: case 0xfc00:
+ util::stream_format(stream, "%-8sAC%d,", "SKNE", (inst & 0x0c00) >> 10);
+ format_ea(stream, pc, inst);
+ return 1 | STEP_OVER;
+
+ default:
+ util::stream_format(stream, "%-8s", ".WORD");
+ format_addr(stream, inst);
+ return 1;
+ }
+}
diff --git a/src/devices/cpu/pace/pacedasm.h b/src/devices/cpu/pace/pacedasm.h
new file mode 100644
index 00000000000..19471e0950a
--- /dev/null
+++ b/src/devices/cpu/pace/pacedasm.h
@@ -0,0 +1,36 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+/***************************************************************************
+
+ National Semiconductor PACE (IPC-16, INS8900) disassembler
+
+***************************************************************************/
+
+#ifndef MAME_CPU_PACE_PACEDASM_H
+#define MAME_CPU_PACE_PACEDASM_H
+
+#pragma once
+
+class pace_disassembler : public util::disasm_interface
+{
+public:
+ // construction/destruction
+ pace_disassembler();
+
+protected:
+ // disassembler overrides
+ virtual u32 opcode_alignment() const override;
+ virtual offs_t disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params) override;
+
+private:
+ // internal helpers
+ void format_addr(std::ostream &stream, u16 addr);
+ void format_disp(std::ostream &stream, u8 disp);
+ void format_ea(std::ostream &stream, u16 pc, u16 inst);
+
+ // tables
+ static const char *const s_cc[16];
+ static const char *const s_flags[16];
+};
+
+#endif // MAME_CPU_PACE_PACEDASM_H
diff --git a/src/mame/drivers/ns5652.cpp b/src/mame/drivers/ns5652.cpp
new file mode 100644
index 00000000000..54d8f3c434c
--- /dev/null
+++ b/src/mame/drivers/ns5652.cpp
@@ -0,0 +1,59 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+/***************************************************************************
+
+ unknown National Semiconductor INS8900 Multibus card (980305652)
+
+***************************************************************************/
+
+#include "emu.h"
+#include "cpu/pace/pace.h"
+#include "machine/ins8250.h"
+
+class ns5652_state : public driver_device
+{
+public:
+ ns5652_state(const machine_config &mconfig, device_type type, const char *tag)
+ : driver_device(mconfig, type, tag)
+ , m_maincpu(*this, "maincpu")
+ {
+ }
+
+ void ns5652(machine_config &config);
+
+private:
+ void mem_map(address_map &map);
+
+ required_device<pace_device> m_maincpu;
+};
+
+
+void ns5652_state::mem_map(address_map &map)
+{
+ map(0x0000, 0x0bff).mirror(0xd000).rom().region("eproms", 0);
+ map(0xe800, 0xebff).ram(); // 4x MM2114J-3
+}
+
+
+static INPUT_PORTS_START(ns5652)
+INPUT_PORTS_END
+
+void ns5652_state::ns5652(machine_config &config)
+{
+ INS8900(config, m_maincpu, 1.8432_MHz_XTAL); // no other XTAL visible
+ m_maincpu->set_addrmap(AS_PROGRAM, &ns5652_state::mem_map);
+
+ INS8250(config, "ace", 1.8432_MHz_XTAL);
+}
+
+ROM_START(ns5652)
+ ROM_REGION16_LE(0x1800, "eproms", 0) // all MM2708Q
+ ROM_LOAD16_BYTE("5652_001b.bin", 0x0000, 0x0400, CRC(03acf738) SHA1(e512ccf64473e0b7291d8cc14f44858cac2048e6))
+ ROM_LOAD16_BYTE("5652_004b.bin", 0x0001, 0x0400, CRC(b238b1ba) SHA1(90735194cc7f111fc7c1cdde1a9aab4945b00a7e))
+ ROM_LOAD16_BYTE("5652_002b.bin", 0x0800, 0x0400, CRC(2fd33c25) SHA1(5f1bab6c149c19b8c57f9f014d7aecd5d287fae0))
+ ROM_LOAD16_BYTE("5652_005b.bin", 0x0801, 0x0400, CRC(e1d559ed) SHA1(3093d28b661275c00de8145f8424f584a4854072))
+ ROM_LOAD16_BYTE("5652_003b.bin", 0x1000, 0x0400, CRC(24abf1f8) SHA1(ef22ca58e59d8301aab9175ef7ac9dc97feae9ec))
+ ROM_LOAD16_BYTE("5652_006b.bin", 0x1001, 0x0400, CRC(db1dca74) SHA1(05149e85237a742850446c01249c83ba373e66b3))
+ROM_END
+
+COMP(19??, ns5652, 0, 0, ns5652, ns5652, ns5652_state, empty_init, "National Semiconductor", "unknown INS8900 Multibus card (980305652)", MACHINE_IS_SKELETON)
diff --git a/src/mame/mame.lst b/src/mame/mame.lst
index 0f0497f5ed4..19529ee9a4a 100644
--- a/src/mame/mame.lst
+++ b/src/mame/mame.lst
@@ -31511,6 +31511,9 @@ sfortea2 //
sforteb //
sfortec //
+@source:ns5652.cpp
+ns5652 //
+
@source:nsg6809.cpp
pitchhit //
diff --git a/src/mame/mess.flt b/src/mame/mess.flt
index d7712b3b21d..7a84d298ce3 100644
--- a/src/mame/mess.flt
+++ b/src/mame/mess.flt
@@ -588,6 +588,7 @@ novag_diablo.cpp
novag_presto.cpp
novag_savant.cpp
novag_sexpert.cpp
+ns5652.cpp
o2.cpp
ob68k1a.cpp
octane.cpp
diff --git a/src/tools/unidasm.cpp b/src/tools/unidasm.cpp
index 7f820be5cfc..fd8e932658c 100644
--- a/src/tools/unidasm.cpp
+++ b/src/tools/unidasm.cpp
@@ -108,6 +108,7 @@ using util::BIT;
#include "cpu/nec/necdasm.h"
#include "cpu/ns32000/ns32000dasm.h"
#include "cpu/nuon/nuondasm.h"
+#include "cpu/pace/pacedasm.h"
#include "cpu/patinhofeio/patinho_feio_dasm.h"
#include "cpu/pdp1/pdp1dasm.h"
#include "cpu/pdp1/tx0dasm.h"
@@ -446,6 +447,7 @@ static const dasm_table_entry dasm_table[] =
{ "ns32000", le, 0, []() -> util::disasm_interface * { return new ns32000_disassembler; } },
{ "nuon", be, 0, []() -> util::disasm_interface * { return new nuon_disassembler; } },
{ "nsc8105", be, 0, []() -> util::disasm_interface * { return new m680x_disassembler(8105); } },
+ { "pace", le, -1, []() -> util::disasm_interface * { return new pace_disassembler; } },
{ "patinho_feio", le, 0, []() -> util::disasm_interface * { return new patinho_feio_disassembler; } },
{ "pdp1", be, 0, []() -> util::disasm_interface * { return new pdp1_disassembler; } },
{ "pdp8", be, 0, []() -> util::disasm_interface * { return new pdp8_disassembler; } },