summaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
author AJR <ajrhacker@users.noreply.github.com>2022-12-02 21:52:47 -0500
committer AJR <ajrhacker@users.noreply.github.com>2022-12-02 21:52:57 -0500
commitb3ec67fc4c28299a5710c62dcb9a8d86252649fb (patch)
treed2817cb9e9d5fee206fd5bcdde02e710680bed95 /src
parentb54ae938c6423bbc075708e2baabb2e4f2f9d2ed (diff)
unidasm: Add IBM 1800, IBM 1130 and (DG) Nova disassemblers
Diffstat (limited to 'src')
-rw-r--r--src/devices/cpu/ibm1800/ibm1800d.cpp258
-rw-r--r--src/devices/cpu/ibm1800/ibm1800d.h35
-rw-r--r--src/devices/cpu/nova/novadasm.cpp262
-rw-r--r--src/devices/cpu/nova/novadasm.h25
-rw-r--r--src/tools/unidasm.cpp5
5 files changed, 585 insertions, 0 deletions
diff --git a/src/devices/cpu/ibm1800/ibm1800d.cpp b/src/devices/cpu/ibm1800/ibm1800d.cpp
new file mode 100644
index 00000000000..0b151eae291
--- /dev/null
+++ b/src/devices/cpu/ibm1800/ibm1800d.cpp
@@ -0,0 +1,258 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+/***************************************************************************
+
+ IBM 1130/1800 disassembler
+
+ The 1800 has a few more instructions than the 1130 (and some later 1800
+ clones have further extensions to the instruction set). The most
+ significant difference, however, is that the index registers are part
+ of core storage on the 1130 but not memory-mapped on the 1800; some
+ 1130 programs take advantage of this fact, and IBM's diagnostics use it
+ to distinguish the two processors.
+
+ Endianness note: the architectural format for double-word operands in
+ memory requires the upper 16 bits to be located at an even address and
+ the lower 16 bits at the next higher address. However, in the 1442
+ packed binary card format, words are punched with the lower 8 bits in
+ positions 12 to 5 of odd-numbered columns and the upper 8 bits in the
+ same positions of the even-numbered columns to their right. Punched
+ card dumps will therefore likely need byte-swapping to be correctly
+ parsed by tools such as unidasm. Similar byte-swapping might also be
+ necessary for disk pack dumps.
+
+ The 1130/1800 assembler source format requires that the format and tag
+ fields be placed in specific columns to the left of the operand field.
+ This is particularly significant for shift instructions, whose count
+ is obtained either from the operand field or from an index register.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "ibm1800d.h"
+
+namespace {
+
+const char *const s_memory_ops[32] =
+{
+ "", "XIO", "", "",
+ "", "STS", "", "",
+ "BSI", "", "", "",
+ "LDX", "STX", "MDX", "",
+ "A", "AD", "S", "SD",
+ "M", "D", "CMP", "DCM",
+ "LD", "LDD", "STO", "STD",
+ "AND", "OR", "EOR", ""
+};
+
+const char *const s_shift_ops[8] =
+{
+ "SLA", "SLCA", "SLT", "SLC",
+ "SRA", "", "SRT", "RTE"
+};
+
+} // anonymous namespace
+
+ibm1800_disassembler::ibm1800_disassembler(bool type_1130)
+ : util::disasm_interface()
+ , m_1130(type_1130)
+{
+}
+
+ibm1130_disassembler::ibm1130_disassembler()
+ : ibm1800_disassembler(true)
+{
+}
+
+u32 ibm1800_disassembler::opcode_alignment() const
+{
+ return 1;
+}
+
+void ibm1800_disassembler::format_cond(std::ostream &stream, u8 cond)
+{
+ if (cond == 0)
+ {
+ stream << '0';
+ return;
+ }
+
+ for (int b = 5; b != 0; --b)
+ {
+ if (BIT(cond, b))
+ stream << "OCE+-Z"[b];
+ }
+}
+
+void ibm1800_disassembler::format_disp(std::ostream &stream, int disp)
+{
+ if (disp < 0)
+ {
+ stream << '-';
+ disp = -disp;
+ }
+ if (unsigned(disp) > 9)
+ stream << '/';
+ util::stream_format(stream, "%X", unsigned(disp));
+}
+
+offs_t ibm1800_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params)
+{
+ u16 inst = opcodes.r16(pc);
+
+ if ((inst & 0xf400) == 0x1000 && (inst & 0x08c0) != 0x0840)
+ {
+ // Shift instructions
+ if (inst == 0x1000)
+ stream << "NOP";
+ else if (inst == 0x18d0)
+ stream << "XCH";
+ else
+ {
+ util::stream_format(stream, "%-5s", s_shift_ops[bitswap<3>(inst, 11, 7, 6)]);
+ if (BIT(inst, 8, 2) != 0)
+ util::stream_format(stream, " %d", BIT(inst, 8, 2));
+ else
+ util::stream_format(stream, " %d", inst & 0x003f);
+ }
+ return 1 | SUPPORTED;
+ }
+ else if ((inst & 0xff00) == 0x2000)
+ {
+ // LDS sets or resets carry and overflow indicators
+ util::stream_format(stream, "%-8s%d", "LDS", inst & 0x0003);
+ return 1 | SUPPORTED;
+ }
+ else if ((inst & 0xff00) == 0x3000)
+ {
+ u8 disp = inst & 0x00ff;
+ if (disp != 0)
+ util::stream_format(stream, "%-8s/%02X", "WAIT", disp);
+ else
+ stream << "WAIT";
+ return 1 | SUPPORTED;
+ }
+ else if ((inst & 0xff80) == 0x4800)
+ {
+ // Single word skip instructions
+ util::stream_format(stream, "%-8s", BIT(inst, 6) ? "BOSC" : "SKP");
+ format_cond(stream, inst & 0x003f);
+ return 1 | STEP_COND | SUPPORTED;
+ }
+ else if ((inst & 0xfc00) == 0x4c00)
+ {
+ // Long branch instructions
+ bool bsc = false;
+ switch (inst & 0x007f)
+ {
+ case 0x00:
+ util::stream_format(stream, "%-5s", "B");
+ break;
+
+ case 0x01:
+ util::stream_format(stream, "%-5s", "BO");
+ break;
+
+ case 0x02:
+ util::stream_format(stream, "%-5s", "BC");
+ break;
+
+ case 0x04:
+ util::stream_format(stream, "%-5s", "BOD");
+ break;
+
+ case 0x08:
+ util::stream_format(stream, "%-5s", "BNP");
+ break;
+
+ case 0x10:
+ util::stream_format(stream, "%-5s", "BNN");
+ break;
+
+ case 0x18:
+ util::stream_format(stream, "%-5s", "BZ");
+ break;
+
+ case 0x20:
+ util::stream_format(stream, "%-5s", "BNZ");
+ break;
+
+ case 0x28:
+ util::stream_format(stream, "%-5s", "BN");
+ break;
+
+ case 0x30:
+ util::stream_format(stream, "%-5s", "BP");
+ break;
+
+ default:
+ util::stream_format(stream, "%-5s", BIT(inst, 6) ? "BOSC" : "BSC");
+ bsc = true;
+ break;
+ }
+ util::stream_format(stream, "%c%c ", BIT(inst, 7) ? 'I' : bsc || (inst & 0x003f) == 0 ? 'L' : ' ', BIT(inst, 8, 2) != 0 ? '0' + BIT(inst, 8, 2) : ' ');
+ if (BIT(inst, 8, 2) != 0)
+ format_disp(stream, s16(opcodes.r16(pc + 1)));
+ else
+ util::stream_format(stream, "/%04X", opcodes.r16(pc + 1));
+ if (bsc && (inst & 0x003f) != 0)
+ {
+ stream << ',';
+ format_cond(stream, inst & 0x003f);
+ }
+ return 2 | ((inst & 0x003f) != 0 ? STEP_COND : 0) | ((inst & 0x0380) == 0x0080 ? STEP_OUT : 0) | SUPPORTED;
+ }
+ else if ((inst & 0xff00) == 0x7400)
+ {
+ // Special long-format version of MDX which takes an 8-bit signed displacement and does not allow indirection
+ util::stream_format(stream, "%-8s/%04X,", "MDM", opcodes.r16(pc + 1));
+ format_disp(stream, s8(inst & 0x00ff));
+ return 2 | STEP_COND | SUPPORTED;
+ }
+ else
+ {
+ const char *op = s_memory_ops[BIT(inst, 11, 5)];
+ if (*op != '\0' && ((inst & 0xf000) != 0xb000 || !m_1130))
+ {
+ util::stream_format(stream, "%-5s", op);
+ offs_t flags = (inst & 0xf800) == 0x4000 ? STEP_OVER : (inst & 0xf800) == 0x7000 || (inst & 0xf000) == 0xb000 ? STEP_COND : 0;
+ if (BIT(inst, 10))
+ {
+ // Long format
+ util::stream_format(stream, "%c%c ", BIT(inst, 7) ? 'I' : 'L', BIT(inst, 8, 2) != 0 ? '0' + BIT(inst, 8, 2) : ' ');
+ u16 addr = opcodes.r16(pc + 1);
+ if (BIT(inst, 8, 2) != 0 && (inst & 0xe880) != 0x6080 && (inst & 0xf800) != 0x6800)
+ format_disp(stream, s16(addr));
+ else if (m_1130 && addr <= 0x0003 && addr != 0x0000)
+ util::stream_format(stream, "XR%d", addr);
+ else
+ util::stream_format(stream, "/%04X", addr);
+ // This extra field can be used on the 1800 for the storage protect status variation of STS (/40, /41, /C0, /C1)
+ if ((inst & 0x007f) != 0)
+ util::stream_format(stream, ",/%02X", inst & 0x007f);
+ return 2 | flags | SUPPORTED;
+ }
+ else if (BIT(inst, 8, 2) != 0)
+ {
+ // Short format with indexing
+ util::stream_format(stream, " %d ", BIT(inst, 8, 2));
+ if ((inst & 0xf800) == 0x6800)
+ util::stream_format(stream, "/%04X", (pc + 1 + s8(inst & 0x00ff)) & 0xffff);
+ else
+ format_disp(stream, s8(inst & 0x00ff));
+ return 1 | flags | SUPPORTED;
+ }
+ else
+ {
+ // Short format without indexing
+ util::stream_format(stream, " /%04X", (((inst & 0xf800) == 0x6000 ? 0 : pc + 1) + s8(inst & 0x00ff)) & 0xffff);
+ return 1 | ((inst & 0xf800) == 0x7000 ? 0 : flags) | SUPPORTED; // MDX branches unconditionally in this mode
+ }
+ }
+ else
+ {
+ util::stream_format(stream, "%-8s/%04X", "DC", inst);
+ return 1 | SUPPORTED;
+ }
+ }
+}
diff --git a/src/devices/cpu/ibm1800/ibm1800d.h b/src/devices/cpu/ibm1800/ibm1800d.h
new file mode 100644
index 00000000000..47c7a9c3312
--- /dev/null
+++ b/src/devices/cpu/ibm1800/ibm1800d.h
@@ -0,0 +1,35 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+
+#ifndef MAME_CPU_IBM1800_IBM1800D_H
+#define MAME_CPU_IBM1800_IBM1800D_H
+
+#pragma once
+
+class ibm1800_disassembler : public util::disasm_interface
+{
+public:
+ // construction/destruction
+ ibm1800_disassembler(bool type_1130 = false);
+
+protected:
+ // util::disasm_interface 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:
+ // formatting helpers
+ static void format_cond(std::ostream &stream, u8 cond);
+ static void format_disp(std::ostream &stream, int disp);
+
+ const bool m_1130;
+};
+
+class ibm1130_disassembler : public ibm1800_disassembler
+{
+public:
+ // construction/destrution
+ ibm1130_disassembler();
+};
+
+#endif // MAME_CPU_IBM1800_IBM1800D_H
diff --git a/src/devices/cpu/nova/novadasm.cpp b/src/devices/cpu/nova/novadasm.cpp
new file mode 100644
index 00000000000..408f6329fb9
--- /dev/null
+++ b/src/devices/cpu/nova/novadasm.cpp
@@ -0,0 +1,262 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+/***************************************************************************
+
+ Data General Nova disassembler
+
+***************************************************************************/
+
+#include "emu.h"
+#include "novadasm.h"
+
+nova_disassembler::nova_disassembler()
+{
+}
+
+u32 nova_disassembler::opcode_alignment() const
+{
+ return 1;
+}
+
+namespace {
+
+const char *const s_alc_ops[8] =
+{
+ "COM", "NEG", "MOV", "INC",
+ "ADC", "SUB", "ADD", "AND"
+};
+
+const char *const s_skip_codes[8] =
+{
+ "0", "SKP",
+ "SZC", "SNC",
+ "SZR", "SNR",
+ "SEZ", "SBN"
+};
+
+const char *const s_io_ops[8] =
+{
+ "NIO", "DIA", "DOA", "DIB",
+ "DOB", "DIC", "DOC", "SKP"
+};
+
+} // anonymous namespace
+
+void nova_disassembler::format_effective_address(std::ostream &stream, offs_t pc, u16 inst)
+{
+ if (BIT(inst, 10))
+ stream << '@';
+ u8 disp = inst & 0377;
+ if (BIT(inst, 9))
+ {
+ if (s8(disp) < 0)
+ {
+ stream << '-';
+ disp = -disp;
+ }
+ util::stream_format(stream, "%o,%d", disp, BIT(inst, 8, 2));
+ }
+ else
+ util::stream_format(stream, "%05o", BIT(inst, 8) ? (pc + int(s8(disp))) & 077777 : disp);
+}
+
+void nova_disassembler::format_device_code(std::ostream &stream, u8 device)
+{
+ switch (device)
+ {
+ case 001:
+ stream << "MDV";
+ break;
+
+ case 002: case 003: case 004:
+ util::stream_format(stream, "MAP%d", device - 2);
+ break;
+
+ case 006:
+ stream << "MCAT";
+ break;
+
+ case 007:
+ stream << "MCAR";
+ break;
+
+ case 010:
+ stream << "TTI";
+ break;
+
+ case 011:
+ stream << "TTO";
+ break;
+
+ case 012:
+ stream << "PTR";
+ break;
+
+ case 013:
+ stream << "PTP";
+ break;
+
+ case 014:
+ stream << "RTC";
+ break;
+
+ case 015:
+ stream << "PLT";
+ break;
+
+ case 016:
+ stream << "CDR";
+ break;
+
+ case 017:
+ stream << "LPT";
+ break;
+
+ case 020:
+ stream << "DSK";
+ break;
+
+ case 021:
+ stream << "ADCV";
+ break;
+
+ case 022:
+ stream << "MTA";
+ break;
+
+ case 023:
+ stream << "DACV";
+ break;
+
+ case 024:
+ stream << "DCM";
+ break;
+
+ case 033:
+ stream << "DKP";
+ break;
+
+ case 040:
+ stream << "SCR";
+ break;
+
+ case 041:
+ stream << "SCT";
+ break;
+
+ case 077:
+ stream << "CPU";
+ break;
+
+ default:
+ util::stream_format(stream, "%o", device);
+ break;
+ }
+}
+
+offs_t nova_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params)
+{
+ u16 inst = opcodes.r16(pc);
+ if (BIT(inst, 15))
+ {
+ // ALC (arithmetic-logical)
+ std::string alcop(s_alc_ops[BIT(inst, 8, 3)]);
+ if (BIT(inst, 4, 2) != 0)
+ alcop.push_back("ZOC"[BIT(inst, 4, 2) - 1]);
+ if (BIT(inst, 6, 2) != 0)
+ alcop.push_back("LRS"[BIT(inst, 6, 2) - 1]);
+ if (BIT(inst, 3))
+ alcop.push_back('#');
+ util::stream_format(stream, "%-8s%d,%d", alcop, BIT(inst, 13, 2), BIT(inst, 11, 2));
+ if (BIT(inst, 0, 3) != 0)
+ stream << ',' << s_skip_codes[BIT(inst, 0, 3)];
+ return 1 | (BIT(inst, 1, 2) != 0 ? STEP_COND : 0) | SUPPORTED;
+ }
+ else if (inst >= 060000)
+ {
+ // I/O
+ switch (inst & 03777)
+ {
+ case 00177:
+ stream << "INTEN";
+ return 1 | SUPPORTED;
+
+ case 00277:
+ stream << "INTDS";
+ return 1 | SUPPORTED;
+
+ case 00477:
+ util::stream_format(stream, "%-8s%d", "READS", BIT(inst, 11, 2));
+ return 1 | SUPPORTED;
+
+ case 01477:
+ util::stream_format(stream, "%-8s%d", "INTA", BIT(inst, 11, 2));
+ return 1 | SUPPORTED;
+
+ case 02077:
+ util::stream_format(stream, "%-8s%d", "MSKO", BIT(inst, 11, 2));
+ return 1 | SUPPORTED;
+
+ case 02677:
+ stream << "IORST";
+ return 1 | SUPPORTED;
+
+ case 03077:
+ stream << "HALT";
+ return 1 | SUPPORTED;
+
+ case 03101:
+ if (BIT(inst, 11, 2) == 2)
+ {
+ stream << "DIV";
+ return 1 | SUPPORTED;
+ }
+ break;
+
+ case 03301:
+ if (BIT(inst, 11, 2) == 2)
+ {
+ stream << "MUL";
+ return 1 | SUPPORTED;
+ }
+ break;
+ }
+
+ stream << s_io_ops[BIT(inst, 8, 3)];
+ if (BIT(inst, 8, 3) == 7)
+ {
+ util::stream_format(stream, "%c%-4c", BIT(inst, 7) ? 'D' : 'B', BIT(inst, 6) ? 'Z' : 'N');
+ format_device_code(stream, BIT(inst, 0, 6));
+ return 1 | STEP_COND | SUPPORTED;
+ }
+ else
+ {
+ util::stream_format(stream, "%-5c", " SCP"[BIT(inst, 6, 2)]);
+ if (BIT(inst, 8, 3) != 0)
+ util::stream_format(stream, "%d,", BIT(inst, 11, 2));
+ format_device_code(stream, BIT(inst, 0, 6));
+ return 1 | SUPPORTED;
+ }
+ }
+ else if (inst >= 020000)
+ {
+ // Memory reference with accumulator
+ util::stream_format(stream, "%-8s%d,", BIT(inst, 14) ? "STA" : "LDA", BIT(inst, 11, 2));
+ format_effective_address(stream, pc, inst);
+ return 1 | SUPPORTED;
+ }
+ else if (BIT(inst, 12))
+ {
+ // Memory reference with conditional skip
+ util::stream_format(stream, "%-8s", BIT(inst, 11) ? "DSZ" : "ISZ");
+ format_effective_address(stream, pc, inst);
+ return 1 | STEP_COND | SUPPORTED;
+ }
+ else
+ {
+ // Memory reference with jump
+ util::stream_format(stream, "%-8s", BIT(inst, 11) ? "JSR" : "JMP");
+ format_effective_address(stream, pc, inst);
+ return 1 | (BIT(inst, 11) ? STEP_OVER : 0) | SUPPORTED;
+ }
+}
diff --git a/src/devices/cpu/nova/novadasm.h b/src/devices/cpu/nova/novadasm.h
new file mode 100644
index 00000000000..e5105c5efd7
--- /dev/null
+++ b/src/devices/cpu/nova/novadasm.h
@@ -0,0 +1,25 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+
+#ifndef MAME_CPU_NOVA_NOVADASM_H
+#define MAME_CPU_NOVA_NOVADASM_H
+
+#pragma once
+
+class nova_disassembler : public util::disasm_interface
+{
+public:
+ // construction/destruction
+ nova_disassembler();
+
+protected:
+ // util::disasm_interface 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;
+
+ // formatting helpers
+ static void format_effective_address(std::ostream &stream, offs_t pc, u16 inst);
+ static void format_device_code(std::ostream &stream, u8 device);
+};
+
+#endif // MAME_CPU_NOVA_NOVADASM_H
diff --git a/src/tools/unidasm.cpp b/src/tools/unidasm.cpp
index 918c6fab73f..24123927902 100644
--- a/src/tools/unidasm.cpp
+++ b/src/tools/unidasm.cpp
@@ -74,6 +74,7 @@ using util::BIT;
#include "cpu/i8089/i8089_dasm.h"
#include "cpu/i860/i860dis.h"
#include "cpu/i960/i960dis.h"
+#include "cpu/ibm1800/ibm1800d.h"
#include "cpu/ie15/ie15dasm.h"
#include "cpu/jaguar/jagdasm.h"
#include "cpu/ks0164/ks0164d.h"
@@ -123,6 +124,7 @@ using util::BIT;
#include "cpu/nanoprocessor/nanoprocessor_dasm.h"
#include "cpu/nec/necdasm.h"
#include "cpu/nios2/nios2dasm.h"
+#include "cpu/nova/novadasm.h"
#include "cpu/ns32000/ns32000dasm.h"
#include "cpu/nuon/nuondasm.h"
#include "cpu/pace/pacedasm.h"
@@ -479,6 +481,8 @@ static const dasm_table_entry dasm_table[] =
{ "i8xc51fx", le, 0, []() -> util::disasm_interface * { return new i8xc51fx_disassembler; } },
{ "i8xc51gb", le, 0, []() -> util::disasm_interface * { return new i8xc51gb_disassembler; } },
{ "i960", le, 0, []() -> util::disasm_interface * { return new i960_disassembler; } },
+ { "ibm1130", be, -1, []() -> util::disasm_interface * { return new ibm1130_disassembler; } },
+ { "ibm1800", be, -1, []() -> util::disasm_interface * { return new ibm1800_disassembler; } },
{ "ie15", le, 0, []() -> util::disasm_interface * { return new ie15_disassembler; } },
{ "jaguardsp", be, 0, []() -> util::disasm_interface * { return new jaguar_disassembler(jaguar_disassembler::variant::DSP); } },
{ "jaguargpu", be, 0, []() -> util::disasm_interface * { return new jaguar_disassembler(jaguar_disassembler::variant::GPU); } },
@@ -539,6 +543,7 @@ static const dasm_table_entry dasm_table[] =
{ "nanoprocessor", le, 0, []() -> util::disasm_interface * { return new hp_nanoprocessor_disassembler; } },
{ "nec", le, 0, []() -> util::disasm_interface * { return new nec_disassembler(&nec_unidasm); } },
{ "nios2", le, 0, []() -> util::disasm_interface * { return new nios2_disassembler; } },
+ { "nova", be, -1, []() -> util::disasm_interface * { return new nova_disassembler; } },
{ "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); } },