summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/mn1880
diff options
context:
space:
mode:
author AJR <ajrhacker@users.noreply.github.com>2020-06-04 16:15:37 -0400
committer AJR <ajrhacker@users.noreply.github.com>2020-06-04 16:21:46 -0400
commitf84e4d036b2e8a9a7d3b8f06b56e4aea49174f6e (patch)
treefa3490332e5eb0e495282ec5a3e2993edd1e6fad /src/devices/cpu/mn1880
parentf49a2f5b8ff8429c1a50fde3cee86af5d24329c9 (diff)
New machines marked as NOT_WORKING
---------------------------------- Novation BassStation Rack Analogue Synthesizer Module [DBWBP] Novation Drum Station [DBWBP] Novation Super Bass Station [DBWBP] Add disassembler and skeleton CPU device for Panasonic MN1880 architecture [AJR]
Diffstat (limited to 'src/devices/cpu/mn1880')
-rw-r--r--src/devices/cpu/mn1880/mn1880.cpp114
-rw-r--r--src/devices/cpu/mn1880/mn1880.h57
-rw-r--r--src/devices/cpu/mn1880/mn1880d.cpp568
-rw-r--r--src/devices/cpu/mn1880/mn1880d.h55
4 files changed, 794 insertions, 0 deletions
diff --git a/src/devices/cpu/mn1880/mn1880.cpp b/src/devices/cpu/mn1880/mn1880.cpp
new file mode 100644
index 00000000000..cf837a53d28
--- /dev/null
+++ b/src/devices/cpu/mn1880/mn1880.cpp
@@ -0,0 +1,114 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+/***************************************************************************
+
+ Panasonic MN1880 family
+
+ Currently this device is just a stub with no actual execution core.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "mn1880.h"
+#include "mn1880d.h"
+
+// device type definitions
+DEFINE_DEVICE_TYPE(MN1880, mn1880_device, "mn1880", "Panasonic MN1880")
+
+mn1880_device::mn1880_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : cpu_device(mconfig, MN1880, tag, owner, clock)
+ , m_program_config("program", ENDIANNESS_BIG, 8, 16, 0)
+ , m_data_config("data", ENDIANNESS_LITTLE, 8, 16, 0)
+ , m_ip(0)
+ , m_fs(0)
+ , m_xp(0)
+ , m_yp(0)
+ , m_sp(0)
+ , m_lp(0)
+ , m_icount(0)
+{
+}
+
+std::unique_ptr<util::disasm_interface> mn1880_device::create_disassembler()
+{
+ return std::make_unique<mn1880_disassembler>();
+}
+
+device_memory_interface::space_config_vector mn1880_device::memory_space_config() const
+{
+ return space_config_vector {
+ std::make_pair(AS_PROGRAM, &m_program_config),
+ std::make_pair(AS_DATA, &m_data_config)
+ };
+}
+
+void mn1880_device::device_start()
+{
+ space(AS_PROGRAM).cache(m_cache);
+ space(AS_DATA).specific(m_data);
+ set_icountptr(m_icount);
+
+ state_add(MN1880_IP, "IP", m_ip);
+ state_add(STATE_GENPC, "GENPC", m_ip).noshow();
+ state_add(STATE_GENPCBASE, "CURPC", m_ip).noshow();
+ state_add(MN1880_FS, "FS", m_fs);
+ state_add(STATE_GENFLAGS, "FLAGS", m_fs).formatstr("%10s").noshow();
+ state_add(MN1880_XP, "XP", m_xp);
+ state_add(MN1880_YP, "YP", m_yp);
+ state_add<u8>(MN1880_XPL, "XPl",
+ [this] () { return m_xp & 0x00ff; },
+ [this] (u8 data) { m_xp = (m_xp & 0xff00) | data; }
+ ).noshow();
+ state_add<u8>(MN1880_XPH, "XPh",
+ [this] () { return (m_xp & 0xff00) >> 8; },
+ [this] (u8 data) { m_xp = (m_xp & 0x00ff) | u16(data) << 8; }
+ ).noshow();
+ state_add<u8>(MN1880_YPL, "YPl",
+ [this] () { return m_yp & 0x00ff; },
+ [this] (u8 data) { m_yp = (m_yp & 0xff00) | data; }
+ ).noshow();
+ state_add<u8>(MN1880_YPH, "YPh",
+ [this] () { return (m_yp & 0xff00) >> 8; },
+ [this] (u8 data) { m_yp = (m_yp & 0x00ff) | u16(data) << 8; }
+ ).noshow();
+ state_add(MN1880_SP, "SP", m_sp);
+ state_add(MN1880_LP, "LP", m_lp);
+
+ save_item(NAME(m_ip));
+ save_item(NAME(m_fs));
+ save_item(NAME(m_xp));
+ save_item(NAME(m_yp));
+ save_item(NAME(m_sp));
+ save_item(NAME(m_lp));
+}
+
+void mn1880_device::device_reset()
+{
+ m_fs &= 0xc0; // CF & ZF might or might not be cleared as well
+ m_sp = 0x0100;
+ // TBD: is LP also initialized at reset?
+}
+
+void mn1880_device::execute_run()
+{
+ m_ip = m_cache.read_word(0);
+
+ debugger_instruction_hook(m_ip);
+
+ m_icount = 0;
+}
+
+void mn1880_device::state_string_export(const device_state_entry &entry, std::string &str) const
+{
+ switch (entry.index())
+ {
+ case STATE_GENFLAGS:
+ str = string_format("%c%c%c%c:RC=%-2d",
+ BIT(m_fs, 7) ? 'C' : '.', // Carry flag
+ BIT(m_fs, 6) ? 'Z' : '.', // Zero flag
+ BIT(m_fs, 5) ? 'D' : '.', // Direct flag
+ BIT(m_fs, 4) ? 'A' : '.', // Auto-repeat flag
+ m_fs & 0x0f);
+ break;
+ }
+}
diff --git a/src/devices/cpu/mn1880/mn1880.h b/src/devices/cpu/mn1880/mn1880.h
new file mode 100644
index 00000000000..e4ab2427f46
--- /dev/null
+++ b/src/devices/cpu/mn1880/mn1880.h
@@ -0,0 +1,57 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+
+#ifndef MAME_CPU_MN1880_MN1880_H
+#define MAME_CPU_MN1880_MN1880_H
+
+#pragma once
+
+class mn1880_device : public cpu_device
+{
+public:
+ mn1880_device(const machine_config &config, const char *tag, device_t *owner, u32 clock);
+
+ enum {
+ MN1880_IP, MN1880_FS,
+ MN1880_XP, MN1880_YP,
+ MN1880_XPL, MN1880_XPH, MN1880_YPL, MN1880_YPH,
+ MN1880_SP, MN1880_LP
+ };
+
+protected:
+ // device-level overrides
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ // device_execute_interface overrides
+ virtual void execute_run() 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;
+
+ // device_state_interface overrides
+ virtual void state_string_export(const device_state_entry &entry, std::string &str) const override;
+
+private:
+ // address spaces
+ address_space_config m_program_config;
+ address_space_config m_data_config;
+ memory_access<16, 0, 0, ENDIANNESS_BIG>::cache m_cache;
+ memory_access<16, 0, 0, ENDIANNESS_LITTLE>::specific m_data;
+
+ // internal state
+ u16 m_ip;
+ u8 m_fs;
+ u16 m_xp;
+ u16 m_yp;
+ u16 m_sp;
+ u16 m_lp;
+ s32 m_icount;
+};
+
+DECLARE_DEVICE_TYPE(MN1880, mn1880_device)
+
+#endif // MAME_CPU_MN1880_MN1880_H
diff --git a/src/devices/cpu/mn1880/mn1880d.cpp b/src/devices/cpu/mn1880/mn1880d.cpp
new file mode 100644
index 00000000000..01ab3fac038
--- /dev/null
+++ b/src/devices/cpu/mn1880/mn1880d.cpp
@@ -0,0 +1,568 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+/***************************************************************************
+
+ Panasonic MN1880 family disassembler
+
+ Due to the dearth of available documentation for the MN1880 series,
+ its instruction list has been reconstructed from MN1870 and MN1890
+ manuals, a (decidedly incomplete) list of MN1880 instructions not
+ provided in the MN1870 series, and a few educated guesses based on
+ reverse engineering (which include CALL XP actually being provided by
+ the MN1880 despite the MN1870 manual claiming it as exclusive).
+
+ The direct flag (bit 5 of the flag status register) is the key to
+ data memory addressing. When DF is cleared, the upper bytes of direct
+ addresses are zeroed; when DF is set, the upper address bytes are
+ taken from YPh for source operands and XPh for destination operands.
+
+ Note that the REP opcode causes a temporary mode switch, decoding
+ up to 15 extra sets of operands for the following instruction.
+ REPEAT_COMBINED attempts to process this correctly by treating the
+ full sequence as a single instruction, but this option has been
+ disabled by default because it results in some repeated instructions
+ being dozens of bytes long and the formatted output being difficult
+ to read anyway since MAME requires it all be on a single line.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "mn1880d.h"
+
+#define REPEAT_COMBINED 0
+
+mn1880_disassembler::mn1880_disassembler(const char *const *inst_names)
+ : util::disasm_interface()
+ , m_inst_names(inst_names)
+{
+}
+
+mn1880_disassembler::mn1880_disassembler()
+ : mn1880_disassembler(s_inst_names)
+{
+}
+
+// MOV, RDTBL, MOV1 and MOV1N may also be known as MV, MVROM, MV1 and MV1CPL
+const char *const mn1880_disassembler::s_inst_names[256] =
+{
+ "nop", "rep", "rep", "rep", "rep", "rep", "rep", "rep",
+ "rep", "rep", "rep", "rep", "rep", "rep", "rep", "rep",
+ "clr", "clr", "clr", "clr", "clr", "clr", "clr", "clr",
+ "set", "set", "set", "set", "set", "set", "set", "set",
+ "t1bnz", "t1bnz", "t1bnz", "t1bnz", "t1bnz", "t1bnz", "t1bnz", "t1bnz",
+ "t1bz", "t1bz", "t1bz", "t1bz", "t1bz", "t1bz", "t1bz", "t1bz",
+ nullptr, "movl", nullptr, "movl", "movl", "movl", "mov", "mov",
+ "movl", "movl", "movl", "movl", "asl", "asl", "asr", "asr",
+ "dec", "dec", "not", "not", "cmpm", "cmpm", "xch4", "xch4",
+ "inc", "inc", "clr", "clr", "rol", "rol", "ror", "ror",
+ "cmpm", "div", "cmpm", "movda", "mov", "mov", "mov", "mov",
+ "xch", "mul", "xch", nullptr, "movl", "movl", "movl", "movl",
+ "cmp", "cmp", "cmp", "cmp", "and", "and", "and", "and",
+ "xor", "xor", "xor", "xor", "or", "or", "or", "or",
+ "subc", "subc", "subc", "subc", "subd", "subd", "subd", "subd",
+ "addc", "addc", "addc", "addc", "addd", "addd", "addd", "addd",
+ "skip", "bc", "bz", "ble", "cmpl", "clr", "cmpl", "clr",
+ "br", "bnc", "bnz", "bgt", nullptr, "set", nullptr, "set",
+ "call", "call", "call", "call", "call", "call", "call", "call",
+ "call", "call", "call", "call", "call", "call", "call", "call",
+ "br", "br", "br", "br", "br", "br", "br", "br",
+ "br", "br", "br", "br", "br", "br", "br", "br",
+ nullptr, "pop", nullptr, "pop", "pop", "pop", "pop", "pop",
+ nullptr, "push", nullptr, "push", "push", "push", "push", "push",
+ "subcl", "div", "subcl", nullptr, "xch", "xch", "xch", "xch",
+ "addcl", "mul", "addcl", nullptr, "mov", "mov", "mov", "mov",
+ "cmp", "cmp", "cmp", "cmp", "xch", "xch", nullptr, "xch",
+ "mov", "mov", "mov", "mov", "mov", "mov", "mov", "mov",
+ "loop", "rloop", "loop", "rloop", "dec", "inc", "dec", "inc",
+ "addr", "addr", "addr", "addr", "addr", "addr", "addr", "addr",
+ "cmpbne", "cmpbne", "mov1", "wait", "ret", "reti", "br", "call",
+ "cmpbe", "cmpbe", "mov1n", "push", "br", "call", "rdtbl", "pi"
+};
+
+mn1870_disassembler::mn1870_disassembler()
+ : mn1880_disassembler(s_inst_names)
+{
+}
+
+// STOP and HALT appear to be macros each implemented as a SET followed by a T1BNZ
+// (their machine language codes are given as 19 16 21 16 FD and 18 16 20 16 FD)
+const char *const mn1870_disassembler::s_inst_names[256] =
+{
+ "nop", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ "clr", "clr", "clr", "clr", "clr", "clr", "clr", "clr",
+ "set", "set", "set", "set", "set", "set", "set", "set",
+ "t1bnz", "t1bnz", "t1bnz", "t1bnz", "t1bnz", "t1bnz", "t1bnz", "t1bnz",
+ "t1bz", "t1bz", "t1bz", "t1bz", "t1bz", "t1bz", "t1bz", "t1bz",
+ nullptr, "movl", nullptr, "movl", nullptr, nullptr, "mov", "mov",
+ "movl", "movl", "movl", "movl", nullptr, nullptr, nullptr, nullptr,
+ "dec", "dec", "not", "not", "cmpm", "cmpm", "xch4", "xch4",
+ "inc", "inc", "clr", "clr", "rol", "rol", "ror", "ror",
+ "cmpm", "div", "cmpm", nullptr, "mov", "mov", "mov", "mov",
+ "xch", "mul", "xch", nullptr, "movl", "movl", "movl", "movl",
+ "cmp", "cmp", "cmp", "cmp", "and", "and", "and", "and",
+ "xor", "xor", "xor", "xor", "or", "or", "or", "or",
+ "subc", "subc", "subc", "subc", "subd", "subd", "subd", "subd",
+ "addc", "addc", "addc", "addc", "addd", "addd", "addd", "addd",
+ "skip", "bc", "bz", "ble", "cmpl", "clr", nullptr, "clr",
+ "br", "bnc", "bnz", "bgt", nullptr, "set", nullptr, "set",
+ "call", "call", "call", "call", "call", "call", "call", "call",
+ "call", "call", "call", "call", "call", "call", "call", "call",
+ "br", "br", "br", "br", "br", "br", "br", "br",
+ "br", "br", "br", "br", "br", "br", "br", "br",
+ "clr", "pop", "t1bnz", "pop", "pop", "pop", "pop", "pop",
+ "set", "push", "t1bz", "push", "push", "push", "push", "push",
+ nullptr, "div", nullptr, "decl", nullptr, nullptr, nullptr, nullptr,
+ nullptr, "mul", nullptr, "incl", "mov", "mov", "mov", "mov",
+ nullptr, "cmp", nullptr, "cmp", nullptr, "xch", nullptr, "xch",
+ "mov", "mov", "mov", "mov", nullptr, nullptr, nullptr, nullptr,
+ "loop", "rloop", "loop", "rloop", "dec", "inc", "dec", "inc",
+ nullptr, "addr", nullptr, "addr", nullptr, "addr", nullptr, "addr",
+ "cmpbne", "cmpbne", "mov1", nullptr, "ret", "reti", "br", "call",
+ "cmpbe", "cmpbe", "mov1n", "push", "br", "call", "rdtbl", nullptr
+};
+
+u32 mn1880_disassembler::opcode_alignment() const
+{
+ return 1;
+}
+
+void mn1880_disassembler::format_direct(std::ostream &stream, u8 da) const
+{
+ util::stream_format(stream, "(x'%02X')", da);
+}
+
+void mn1880_disassembler::format_direct16(std::ostream &stream, u16 da) const
+{
+ util::stream_format(stream, "(x'%04X')", da);
+}
+
+void mn1880_disassembler::format_direct_bp(std::ostream &stream, u8 da, u8 bp) const
+{
+ util::stream_format(stream, "(x'%02X')%d", da, bp);
+}
+
+void mn1880_disassembler::format_indirect_bp(std::ostream &stream, const char *ptr, u8 bp) const
+{
+ util::stream_format(stream, "(%s)%d", ptr, bp);
+}
+
+void mn1880_disassembler::format_direct_masked(std::ostream &stream, u8 da, u8 mask) const
+{
+ util::stream_format(stream, "(x'%02X')x'%02X'", da, mask);
+}
+
+void mn1880_disassembler::format_indirect_masked(std::ostream &stream, const char *ptr, u8 mask) const
+{
+ util::stream_format(stream, "(%s)x'%02X'", ptr, mask);
+}
+
+void mn1880_disassembler::format_imm(std::ostream &stream, u8 imm) const
+{
+ util::stream_format(stream, "x'%02X'", imm);
+}
+
+void mn1880_disassembler::format_imm4(std::ostream &stream, u8 imm) const
+{
+ util::stream_format(stream, "%d", imm & 0x0f);
+}
+
+void mn1880_disassembler::format_imm16(std::ostream &stream, u16 imm) const
+{
+ util::stream_format(stream, "x'%04X'", imm);
+}
+
+void mn1880_disassembler::format_rel(std::ostream &stream, u16 base, s8 disp) const
+{
+ util::stream_format(stream, "$x'%04X'", (base + disp) & 0xffff);
+}
+
+void mn1880_disassembler::format_abs4k(std::ostream &stream, u16 label) const
+{
+ util::stream_format(stream, "x'%04X'", label);
+}
+
+void mn1880_disassembler::format_abs64k(std::ostream &stream, u16 label) const
+{
+ util::stream_format(stream, "/x'%04X'", label);
+}
+
+void mn1880_disassembler::dasm_operands(std::ostream &stream, u8 opcode, offs_t &pc, offs_t &flags, const mn1880_disassembler::data_buffer &opcodes)
+{
+ if (opcode < 0x10)
+ {
+ // REP
+ format_imm4(stream, opcode);
+ }
+ else if (opcode < 0x30)
+ {
+ // CLR, SET, T1BNZ, T1BZ direct
+ format_direct_bp(stream, opcodes.r8(pc++), opcode & 0x07);
+ if (opcode >= 0x20)
+ {
+ stream << ", ";
+ format_rel(stream, pc + 1, opcodes.r8(pc));
+ ++pc;
+ }
+ }
+ else if (opcode < 0x34)
+ {
+ // MOVL immediate to pointer register
+ if (BIT(opcode, 0))
+ {
+ util::stream_format(stream, "%s, ", BIT(opcode, 1) ? "yp" : "xp");
+ format_imm16(stream, swapendian_int16(opcodes.r16(pc)));
+ pc += 2;
+ }
+ else
+ stream << "(unknown)";
+ }
+ else if (opcode < 0x3c)
+ {
+ // MOV/MOVL direct
+ if (BIT(opcode, 0))
+ {
+ format_direct(stream, opcodes.r8(pc++));
+ stream << ", ";
+ }
+ if (opcode < 0x38)
+ util::stream_format(stream, "(%s)", BIT(opcode, 0) ? "yp" : "xp");
+ else if (BIT(opcode, 1))
+ stream << "yp";
+ else
+ stream << "xp";
+ if (!BIT(opcode, 0))
+ {
+ stream << ", ";
+ format_direct(stream, opcodes.r8(pc++));
+ }
+ }
+ else if ((opcode & 0xfe) == 0x44)
+ {
+ // CMPM immediate
+ if (BIT(opcode, 0))
+ {
+ format_direct_masked(stream, opcodes.r8(pc), opcodes.r8(pc + 1));
+ pc += 2;
+ }
+ else
+ format_indirect_masked(stream, "xp", opcodes.r8(pc++));
+ stream << ", ";
+ format_imm(stream, opcodes.r8(pc++));
+ }
+ else if (opcode < 0x50 || (opcode & 0xf7) == 0xb5 || (opcode & 0xf7) == 0xc3)
+ {
+ // Unary operations
+ if (BIT(opcode, 0))
+ format_direct(stream, opcodes.r8(pc++));
+ else
+ stream << "(xp)";
+ }
+ else if ((opcode & 0xfd) == 0x50)
+ {
+ // CMPM with two memory operands
+ if (BIT(opcode, 1))
+ {
+ u8 da2 = opcodes.r8(pc++);
+ u8 mask = opcodes.r8(pc++);
+ format_direct_masked(stream, opcodes.r8(pc++), mask);
+ stream << ", ";
+ format_direct_masked(stream, da2, mask);
+ }
+ else
+ {
+ u8 mask = opcodes.r8(pc++);
+ format_indirect_masked(stream, "xp", mask);
+ stream << ", ";
+ format_indirect_masked(stream, "yp", mask);
+ }
+ }
+ else if ((opcode & 0xf7) == 0x51 || (opcode & 0xf7) == 0xb7)
+ {
+ // MUL, DIV, POP, PUSH indirect
+ stream << "(xp)";
+ }
+ else if (opcode == 0x53)
+ {
+ // MOVDA (MN1880 only)
+ format_direct16(stream, opcodes.r16(pc + 2));
+ stream << ", ";
+ format_direct16(stream, opcodes.r16(pc));
+ pc += 4;
+ }
+ else if (opcode < 0x80 || (opcode & 0xfd) == 0x84 || (opcode & 0xf4) == 0xc0)
+ {
+ // Binary operations
+ if (BIT(opcode, 1))
+ {
+ // Direct modes
+ u8 op2 = opcodes.r8(pc++);
+ format_direct(stream, opcodes.r8(pc++));
+ stream << ", ";
+ if (BIT(opcode, 0))
+ {
+ if (opcode == 0x5f)
+ format_imm16(stream, op2 | u16(opcodes.r8(pc++)) << 8);
+ else if ((opcode & 0xf4) == 0x74)
+ format_imm4(stream, op2);
+ else
+ format_imm(stream, op2);
+ }
+ else
+ format_direct(stream, op2);
+ }
+ else
+ {
+ // Indirect modes
+ stream << "(xp), ";
+ if (opcode == 0x5d)
+ {
+ format_imm16(stream, swapendian_int16(opcodes.r16(pc)));
+ pc += 2;
+ }
+ else if (BIT(opcode, 0))
+ {
+ if ((opcode & 0xf4) == 0x74)
+ format_imm4(stream, opcodes.r8(pc++));
+ else
+ format_imm(stream, opcodes.r8(pc++));
+ }
+ else
+ stream << "(yp)";
+ }
+ }
+ else if (opcode < 0x90 || (opcode & 0xfc) == 0xe0)
+ {
+ if (BIT(opcode, 2))
+ {
+ if (BIT(opcode, 0))
+ {
+ // CLR, SET special flag
+ if (BIT(opcode, 1))
+ stream << "cf";
+ else
+ stream << "df";
+ }
+ else
+ stream << "(unknown)";
+ }
+ else
+ {
+ // Relative branches and loops
+ if ((opcode & 0xfd) == 0xe1)
+ util::stream_format(stream, "%s, ", BIT(opcode, 1) ? "yp" : "xp");
+ format_rel(stream, pc + 1, opcodes.r8(pc));
+ ++pc;
+ }
+ }
+ else if (opcode < 0xb0)
+ {
+ // CALL, BR within 4K page
+ format_abs4k(stream, ((pc + 1) & 0xf000) | u16(opcode & 0x0f) << 8 | opcodes.r8(pc));
+ ++pc;
+ if (opcode < 0xa0)
+ flags |= STEP_OVER;
+ }
+ else if ((opcode & 0xf4) == 0xb0)
+ {
+ if (BIT(opcode, 0))
+ stream << "fs";
+ else
+ {
+ // CLR, SET, T1BNZ, T1BZ indirect (MN1870 only)
+ format_indirect_bp(stream, "xp", opcodes.r8(pc++) & 0x07);
+ if (BIT(opcode, 1))
+ {
+ stream << ", ";
+ format_rel(stream, pc + 1, opcodes.r8(pc));
+ ++pc;
+ }
+ }
+ }
+ else if (opcode < 0xc0 || (opcode & 0xfc) == 0xe4 || (opcode & 0xfe) == 0xfc)
+ {
+ // PUSH, POP, DEC, INC, BR, CALL pointer
+ if (BIT(opcode, 1))
+ stream << "yp";
+ else
+ stream << "xp";
+ if (opcode == 0xfd)
+ flags |= STEP_OVER;
+ }
+ else if (opcode < 0xd0)
+ {
+ // XCH, MOV lower half of register
+ if ((opcode & 0xf5) == 0xc5)
+ {
+ util::stream_format(stream, "%sl, ", BIT(opcode, 1) ? "yp" : "xp");
+ format_direct(stream, opcodes.r8(pc++));
+ }
+ else if (BIT(opcode, 3))
+ {
+ format_direct(stream, opcodes.r8(pc++));
+ util::stream_format(stream, ", %sl", BIT(opcode, 1) ? "yp" : "xp");
+ }
+ else
+ util::stream_format(stream, "%sl, %sh", BIT(opcode, 1) ? "yp" : "xp", BIT(opcode, 1) ? "yp" : "xp");
+ }
+ else if (opcode < 0xe0)
+ {
+ if (BIT(opcode, 2))
+ {
+ // XCH, MOV between pointer registers
+ if (opcode == 0xd4)
+ stream << "xp, lp";
+ else
+ {
+ if (BIT(opcode, 0))
+ stream << "xp, ";
+ if (BIT(opcode, 1))
+ stream << "yp";
+ else
+ stream << "sp";
+ if (!BIT(opcode, 0))
+ stream << ", xp";
+ }
+ }
+ else
+ {
+ // CMP, MOV 8-bit
+ util::stream_format(stream, "%s%c, ", BIT(opcode, 1) ? "yp" : "xp", (opcode & 0xfd) == 0xd9 ? 'h' : 'l');
+ if ((opcode & 0xfd) == 0xd0)
+ format_direct(stream, opcodes.r8(pc++));
+ else
+ format_imm(stream, opcodes.r8(pc++));
+ }
+ }
+ else if (opcode < 0xf0)
+ {
+ // ADDR
+ util::stream_format(stream, "%sl, ", BIT(opcode, 1) ? "yp" : "xp");
+ if (BIT(opcode, 2))
+ {
+ util::stream_format(stream, "%sl, ", BIT(opcode, 1) ? "yp" : "xp");
+ if (BIT(opcode, 0))
+ format_imm(stream, opcodes.r8(pc++));
+ else
+ util::stream_format(stream, "%sh", BIT(opcode, 1) ? "yp" : "xp");
+ }
+ else
+ {
+ u8 op2 = opcodes.r8(pc++);
+ format_direct(stream, opcodes.r8(pc++));
+ stream << ", ";
+ if (BIT(opcode, 0))
+ format_imm(stream, op2);
+ else
+ format_direct(stream, op2);
+ }
+ }
+ else if ((opcode & 0xf6) == 0xf0)
+ {
+ // CMPBNE, CMPBE
+ u8 imm = opcodes.r8(pc++);
+ if (BIT(opcode, 0))
+ format_direct(stream, opcodes.r8(pc++));
+ else
+ stream << "(xp)";
+ stream << ", ";
+ format_imm(stream, imm);
+ stream << ", ";
+ format_rel(stream, pc + 1, opcodes.r8(pc));
+ ++pc;
+ }
+ else if ((opcode & 0xf7) == 0xf2)
+ {
+ // MOV1, MOV1N
+ u8 da2 = opcodes.r8(pc++);
+ u8 bp = opcodes.r8(pc++);
+ format_direct_bp(stream, opcodes.r8(pc++), (bp & 0x70) >> 4);
+ stream << ", ";
+ format_direct_bp(stream, da2, bp & 0x07);
+ }
+ else if (opcode == 0xf3)
+ {
+ // WAIT (MN1880 only)
+ format_imm16(stream, opcodes.r16(pc));
+ pc += 2;
+ }
+ else if ((opcode & 0xfe) == 0xf6)
+ {
+ // BR, CALL absolute
+ format_abs64k(stream, opcodes.r16(pc));
+ pc += 2;
+ if (BIT(opcode, 0))
+ flags |= STEP_OVER;
+ }
+ else if (opcode == 0xfb)
+ {
+ // PUSH immediate
+ format_imm(stream, opcodes.r8(pc++));
+ }
+ else if (opcode == 0xfe)
+ {
+ // RDTBL
+ stream << "(xp), (yp)";
+ }
+ else
+ stream << "(unknown)";
+}
+
+offs_t mn1880_disassembler::disassemble(std::ostream &stream, offs_t pc, const mn1880_disassembler::data_buffer &opcodes, const mn1880_disassembler::data_buffer &params)
+{
+ const offs_t pc0 = pc;
+ offs_t flags = SUPPORTED;
+
+ u8 opcode = opcodes.r8(pc++);
+ const char *mnemonic = m_inst_names[opcode];
+
+ u8 rc = 0;
+ if (REPEAT_COMBINED && mnemonic != nullptr && opcode > 0x00 && opcode < 0x10)
+ {
+ rc = opcode;
+ opcode = opcodes.r8(pc++);
+ mnemonic = m_inst_names[opcode];
+ }
+
+ if (mnemonic == nullptr)
+ {
+ util::stream_format(stream, "%-8s", "db");
+ format_imm(stream, opcode);
+ }
+ else if (opcode == 0x00 || opcode == 0x80)
+ {
+ // NOP, SKIP (latter actually executes in immediate mode)
+ stream << mnemonic;
+ }
+ else if (opcode == 0xff)
+ {
+ // PI (software interrupt)
+ stream << mnemonic;
+ flags |= STEP_OVER;
+ }
+ else if ((opcode & 0xfe) == 0xf4)
+ {
+ // RET, RETI
+ stream << mnemonic;
+ flags |= STEP_OUT;
+ }
+ else
+ {
+ util::stream_format(stream, "%-8s", mnemonic);
+ offs_t pc1 = pc;
+ dasm_operands(stream, opcode, pc, flags, opcodes);
+ if (REPEAT_COMBINED && rc != 0 && pc1 != pc)
+ {
+ do {
+ stream << ", ";
+ dasm_operands(stream, opcode, pc, flags, opcodes);
+ }
+ while (--rc != 0);
+ }
+ }
+
+ if (REPEAT_COMBINED && rc != 0)
+ util::stream_format(stream, ", rep %d", rc);
+
+ return (pc - pc0) | flags;
+}
diff --git a/src/devices/cpu/mn1880/mn1880d.h b/src/devices/cpu/mn1880/mn1880d.h
new file mode 100644
index 00000000000..453522aa43b
--- /dev/null
+++ b/src/devices/cpu/mn1880/mn1880d.h
@@ -0,0 +1,55 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+
+#ifndef MAME_CPU_MN1880_MN1880D_H
+#define MAME_CPU_MN1880_MN1880D_H
+
+#pragma once
+
+class mn1880_disassembler : public util::disasm_interface
+{
+public:
+ // construction/destruction
+ mn1880_disassembler();
+
+protected:
+ mn1880_disassembler(const char *const *inst_names);
+
+ // 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:
+ // instruction mnemonic table
+ static const char *const s_inst_names[256];
+
+ // internal helpers
+ void format_direct(std::ostream &stream, u8 da) const;
+ void format_direct16(std::ostream &stream, u16 da) const;
+ void format_direct_bp(std::ostream &stream, u8 da, u8 bp) const;
+ void format_indirect_bp(std::ostream &stream, const char *ptr, u8 bp) const;
+ void format_direct_masked(std::ostream &stream, u8 da, u8 mask) const;
+ void format_indirect_masked(std::ostream &stream, const char *ptr, u8 mask) const;
+ void format_imm(std::ostream &stream, u8 imm) const;
+ void format_imm4(std::ostream &stream, u8 imm) const;
+ void format_imm16(std::ostream &stream, u16 imm) const;
+ void format_rel(std::ostream &stream, u16 base, s8 disp) const;
+ void format_abs4k(std::ostream &stream, u16 label) const;
+ void format_abs64k(std::ostream &stream, u16 label) const;
+ void dasm_operands(std::ostream &stream, u8 opcode, offs_t &pc, offs_t &flags, const data_buffer &opcodes);
+
+ const char *const *m_inst_names;
+};
+
+class mn1870_disassembler : public mn1880_disassembler
+{
+public:
+ // construction/destruction
+ mn1870_disassembler();
+
+private:
+ // instruction mnemonic table
+ static const char *const s_inst_names[256];
+};
+
+#endif // MAME_CPU_PIC17_PIC17D_H