unidasm: Add IBM 1800, IBM 1130 and (DG) Nova disassemblers

This commit is contained in:
AJR 2022-12-02 21:52:47 -05:00
parent b54ae938c6
commit b3ec67fc4c
6 changed files with 605 additions and 0 deletions

View File

@ -3708,3 +3708,23 @@ if opt_tool(CPUS, "NIOS2") then
table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/nios2/nios2dasm.cpp") table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/nios2/nios2dasm.cpp")
table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/nios2/nios2dasm.h") table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/nios2/nios2dasm.h")
end end
--------------------------------------------------
-- IBM 1800, disassembler only
--@src/devices/cpu/ibm1800/ibm1800.h,CPUS["IBM1800"] = true
--------------------------------------------------
if opt_tool(CPUS, "IBM1800") then
table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/ibm1800/ibm1800d.cpp")
table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/ibm1800/ibm1800d.h")
end
--------------------------------------------------
-- Data General Nova, disassembler only
--@src/devices/cpu/nova/nova.h,CPUS["NOVA"] = true
--------------------------------------------------
if opt_tool(CPUS, "NOVA") then
table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/nova/novadasm.cpp")
table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/nova/novadasm.h")
end

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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

View File

@ -74,6 +74,7 @@ using util::BIT;
#include "cpu/i8089/i8089_dasm.h" #include "cpu/i8089/i8089_dasm.h"
#include "cpu/i860/i860dis.h" #include "cpu/i860/i860dis.h"
#include "cpu/i960/i960dis.h" #include "cpu/i960/i960dis.h"
#include "cpu/ibm1800/ibm1800d.h"
#include "cpu/ie15/ie15dasm.h" #include "cpu/ie15/ie15dasm.h"
#include "cpu/jaguar/jagdasm.h" #include "cpu/jaguar/jagdasm.h"
#include "cpu/ks0164/ks0164d.h" #include "cpu/ks0164/ks0164d.h"
@ -123,6 +124,7 @@ using util::BIT;
#include "cpu/nanoprocessor/nanoprocessor_dasm.h" #include "cpu/nanoprocessor/nanoprocessor_dasm.h"
#include "cpu/nec/necdasm.h" #include "cpu/nec/necdasm.h"
#include "cpu/nios2/nios2dasm.h" #include "cpu/nios2/nios2dasm.h"
#include "cpu/nova/novadasm.h"
#include "cpu/ns32000/ns32000dasm.h" #include "cpu/ns32000/ns32000dasm.h"
#include "cpu/nuon/nuondasm.h" #include "cpu/nuon/nuondasm.h"
#include "cpu/pace/pacedasm.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; } }, { "i8xc51fx", le, 0, []() -> util::disasm_interface * { return new i8xc51fx_disassembler; } },
{ "i8xc51gb", le, 0, []() -> util::disasm_interface * { return new i8xc51gb_disassembler; } }, { "i8xc51gb", le, 0, []() -> util::disasm_interface * { return new i8xc51gb_disassembler; } },
{ "i960", le, 0, []() -> util::disasm_interface * { return new i960_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; } }, { "ie15", le, 0, []() -> util::disasm_interface * { return new ie15_disassembler; } },
{ "jaguardsp", be, 0, []() -> util::disasm_interface * { return new jaguar_disassembler(jaguar_disassembler::variant::DSP); } }, { "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); } }, { "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; } }, { "nanoprocessor", le, 0, []() -> util::disasm_interface * { return new hp_nanoprocessor_disassembler; } },
{ "nec", le, 0, []() -> util::disasm_interface * { return new nec_disassembler(&nec_unidasm); } }, { "nec", le, 0, []() -> util::disasm_interface * { return new nec_disassembler(&nec_unidasm); } },
{ "nios2", le, 0, []() -> util::disasm_interface * { return new nios2_disassembler; } }, { "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; } }, { "ns32000", le, 0, []() -> util::disasm_interface * { return new ns32000_disassembler; } },
{ "nuon", be, 0, []() -> util::disasm_interface * { return new nuon_disassembler; } }, { "nuon", be, 0, []() -> util::disasm_interface * { return new nuon_disassembler; } },
{ "nsc8105", be, 0, []() -> util::disasm_interface * { return new m680x_disassembler(8105); } }, { "nsc8105", be, 0, []() -> util::disasm_interface * { return new m680x_disassembler(8105); } },