From 2fa2ef8b0e19d7c376f7779a60a20c95dc9d6ea4 Mon Sep 17 00:00:00 2001 From: AJR Date: Wed, 19 May 2021 18:15:08 -0400 Subject: pdp8: Rewrite disassembler; add alternate disassembly for HD-6120 * unidasm: Add option to use octal instead of hexadecimal for output --- src/devices/cpu/pdp8/pdp8dasm.cpp | 1008 ++++++++++++++++++++++++++++++++----- src/devices/cpu/pdp8/pdp8dasm.h | 38 +- src/tools/unidasm.cpp | 180 ++++--- 3 files changed, 1020 insertions(+), 206 deletions(-) diff --git a/src/devices/cpu/pdp8/pdp8dasm.cpp b/src/devices/cpu/pdp8/pdp8dasm.cpp index 8c2e57573b5..01bd857775a 100644 --- a/src/devices/cpu/pdp8/pdp8dasm.cpp +++ b/src/devices/cpu/pdp8/pdp8dasm.cpp @@ -1,168 +1,898 @@ // license:BSD-3-Clause -// copyright-holders:Ryan Holtz -/* - First-gen DEC PDP-8 disassembler +// copyright-holders:AJR +/*************************************************************************** - Written by Ryan Holtz -*/ + PDP-8 family disassembler + + PDP-8 peripherals were traditionally classified by their device codes, + which correspond to the second and third octal digits (bits 3:8) of + the IOT instruction word. Older PDP-8 buses had the CPU decode the + least significant three bits (9:11) into separate strobes issued one + after another, which allows some IOTs with codes 6XX1, 6XX2 and 6XX4 + to be usefully combined with each other. Though the notion of device + codes persisted in the Omnibus specification and CMOS-based systems, + the I/O transfer protocols used on the PDP-8/E and later CPUs assert + the lowest three bits in parallel with the device code, leaving + decoding of all nine bits up to the peripherals. This allowed later + options to define eight distinct functions for each of their assigned + device codes. (The peripherals are also responsible for determining, + in real time, whether their IOTs perform reads, writes and/or + conditional skips; for the PDP-8/E, PDP-8/A and IM6100 CPUs, they may + also request relative or absolute jumps.) + + Most IOTs were traditionally implemented on peripheral options outside + the CPU, which originally supported only ION and IOF by itself. (The + PDP-8/E CPU added several more which became standard.) It would have + been convenient if DEC had assigned every common device type a + distinct and unchanging set of IOTs. Unfortunately, not only do + several types of options conflict with each other (a few of these + could also have their standard device codes modified by jumpers, + particularly storage and communications options which could have + several used in one system), many Omnibus options use IOTs largely if + not entirely different from their functional predecessors built for + the older positive and negative buses. DEC reshuffled IOTs less often + after the PDP-8/E, but real time clock and parallel I/O facilities + were incompatibly reimplemented on the PDP-8/A and CMOS-8s. + + Due to this inconsistency, the "pdp8" disassembler implemented here + only specifically identifies IOTs for a few of the most commonly + used I/O devices with long and consistent implementation histories + (some of which date back all the way to the PDP-5, though the Omnibus + interfaces, being functional supersets, are the reference versions): + * TTY interface: keyboard (03) + teleprinter (04) + * High-speed paper tape reader (01) and punch (02) + * Memory extension and time-sharing modes (20–27) + * Power fail and automatic restart (10) + + The Group 3 OPR instructions belonging to the Extended Arithmetic + Element are also recognized here, though several are unique to the + KE8-E version provided for the PDP-8/E, and almost no EAE support was + provided for later PDP-8 CPUs except for transfers between AC and + MQ. (GT was reintroduced as a general-purpose flag on the HD-6120.) + PDP-8/L omits this group entirely, not even recognizing CLA. + + Group 1 OPR combinations are decoded using the standard three-letter + shorthand mnemonics. Even shorter macros for these were commonly + defined to identify those useful for loading certain constants into + AC; the resulting constant values are presented here as comments. + + Certain combinations of IAC, CMA, CML and rotate OPRs are illegal on + the PDP-5, the original PDP-8 and the PDP-8/S; of these models, the + "Classic-8", which disallows IAC with any rotation, is the only one + many programs even tried to remain compatible with. For more obvious + reasons, RAL RAR and RTL RTR were officially undefined before HD-6120 + reassigned the former as R3L, though they had some predictable and + even useful results which were highly model-dependent. + + The "hd6120" disassembler, besides adding the HD-6120 exclusive R3L + operation, specifically recognizes only its internal IOTs, which now + include the memory extension IOTs plus a large number of completely + original operations using device codes 20 through 27 (which are + strictly internal here and unavailable to external peripherals). The + different IOTs for panel mode are used when the disassembly address + points above the normal 32K words. HD-6120 based systems tended to + reimplement external IOTs using the HD-6121 Input/Output Controller, + sometimes partially emulating them with panel-mode programs, and the + resulting interfaces offered notoriously poor compatibility with + preexisting code. + +***************************************************************************/ #include "emu.h" #include "pdp8dasm.h" +pdp8_disassembler::pdp8_disassembler(bool has_r3l) + : util::disasm_interface() + , m_has_r3l(has_r3l) +{ +} + +hd6120_disassembler::hd6120_disassembler() + : pdp8_disassembler(true) +{ +} + u32 pdp8_disassembler::opcode_alignment() const { return 1; } -offs_t pdp8_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer ¶ms) +u32 hd6120_disassembler::interface_flags() const { - uint16_t op = opcodes.r16(pc); - uint8_t opcode = (op >> 011) & 07; - uint16_t current_page = pc & 07600; - uint16_t zero_addr = op & 0177; - uint16_t current_addr = current_page | zero_addr; - bool indirect = (op & 0400) ? true : false; - bool zero_page = (op & 0200) ? false : true; - - switch (opcode) + return PAGED2LEVEL; +} + +u32 hd6120_disassembler::page_address_bits() const +{ + // 5 bits for page + 7 bits for relative address + return 12; +} + +u32 hd6120_disassembler::page2_address_bits() const +{ + // 3 bits for current field + return 3; +} + +void pdp8_disassembler::dasm_memory_reference(std::ostream &stream, u16 inst, offs_t pc) +{ + // Bit 4 (DEC numbering) determines whether references are paged + const u16 addr = (BIT(inst, 7) ? (pc & 07600) : 0) | (inst & 0177); + + // Bit 3 (DEC numbering) determines indirect addressing + if (BIT(inst, 8)) { - case 0: - util::stream_format(stream, "AND %c %05o", indirect ? 'I' : ' ', zero_page ? zero_addr : current_addr); + util::stream_format(stream, " I %04o", addr); + if ((addr & 07770) == 0010) + { + stream << " /AUTO-INDEX"; + if (BIT(inst, 7)) + stream << "?"; // IM6100 does no auto-indexing in this case + } + } + else + util::stream_format(stream, " %04o", addr); +} + +offs_t pdp8_disassembler::dasm_iot(std::ostream &stream, u16 dev, offs_t pc) +{ + switch (dev) + { + case 000: + stream << "SKON"; // PDP-8/E and up + break; + + case 001: + stream << "ION"; + break; + + case 002: + stream << "IOF"; + break; + + case 003: + stream << "SRQ"; // PDP-8/E and up + break; + + case 004: + stream << "GTF"; // PDP-8/E and up (used earlier by a PDP-5/Classic-8 ADC option) + break; + + case 005: + stream << "RTF"; // PDP-8/E and up + break; + + case 006: + stream << "SGT"; // KE8-E + break; + + case 007: + stream << "CAF"; // PDP-8/E and up + break; + + case 010: + stream << "RPE"; // Paper tape reader (PC8-E) + break; + + case 011: + stream << "RSF"; // Paper tape reader + break; + + case 012: case 016: + stream << "RRB"; // Paper tape reader + if (BIT(dev, 2)) + stream << " RFC"; + break; + + case 014: + stream << "RFC"; // Paper tape reader + break; + + case 020: + stream << "PCE"; // Paper tape punch (PC8-E) + break; + + case 021: + stream << "PSF"; // Paper tape punch + break; + + case 022: + stream << "PCF"; // Paper tape punch + break; + + case 024: + stream << "PPC"; // Paper tape punch + break; + + case 026: + stream << "PLS"; // Paper tape punch + break; + + case 030: + stream << "KCF"; // TTY/keyboard (KL8-E and up) + break; + + case 031: + stream << "KSF"; // TTY/keyboard + break; + + case 032: + stream << "KCC"; // TTY/keyboard + break; + + case 034: + stream << "KRS"; // TTY/keyboard + break; + + case 035: + stream << "KIE"; // TTY/keyboard (KL8-E and up) + break; + + case 036: + stream << "KRB"; // TTY/keyboard + break; + + case 040: + stream << "TFL"; // Teleprinter (KL8-E and up) + break; + + case 041: + stream << "TSF"; // Teleprinter + break; + + case 042: + stream << "TCF"; // Teleprinter + break; + + case 044: + stream << "TPC"; // Teleprinter + break; + + case 045: + stream << "TSK"; // Teleprinter (KL8-E and up; SPI is a later name for this IOT) + break; + + case 046: + stream << "TLS"; // Teleprinter + break; + + case 0102: + stream << "SPL"; // Automatic restart + break; + + case 0201: case 0202: case 0203: + case 0211: case 0212: case 0213: + case 0221: case 0222: case 0223: + case 0231: case 0232: case 0233: + case 0241: case 0242: case 0243: + case 0251: case 0252: case 0253: + case 0261: case 0262: case 0263: + case 0271: case 0272: case 0273: // Memory extension + if (BIT(dev, 0)) + stream << "CDF "; + if (BIT(dev, 1)) + stream << "CIF "; + util::stream_format(stream, "%o", dev & 070); + break; + + case 0204: + stream << "CINT"; // Time-sharing extension + break; + + case 0214: + stream << "RDF"; // Memory extension + break; + + case 0224: + stream << "RIF"; // Memory extension + break; + + case 0234: + stream << "RIB"; // Memory extension + break; + + case 0244: + stream << "RMF"; // Memory extension + break; + + case 0254: + stream << "SINT"; // Time-sharing extension (LIF on some Intersil peripherals) + break; + + case 0264: + stream << "CUF"; // Time-sharing extension + break; + + case 0274: + stream << "SUF"; // Time-sharing extension + break; + + default: + util::stream_format(stream, "IOT %03o", dev); + break; + } + + return 1 | SUPPORTED; +} + + +offs_t hd6120_disassembler::dasm_iot(std::ostream &stream, u16 dev, offs_t pc) +{ + switch (dev) + { + case 000: + if (BIT(pc, 15)) + stream << "PRS"; + else + stream << "SKON"; + break; + + case 001: + stream << "ION"; + break; + + case 002: + stream << "IOF"; + break; + + case 003: + if (BIT(pc, 15)) + stream << "PGO"; + else + stream << "SRQ"; + break; + + case 004: + if (BIT(pc, 15)) + stream << "PEX"; + else + stream << "GTF"; + break; + + case 005: + stream << "RTF"; + break; + + case 006: + stream << "SGT"; + break; + + case 007: + stream << "CAF"; + break; + + case 0201: case 0202: case 0203: + case 0211: case 0212: case 0213: + case 0221: case 0222: case 0223: + case 0231: case 0232: case 0233: + case 0241: case 0242: case 0243: + case 0251: case 0252: case 0253: + case 0261: case 0262: case 0263: + case 0271: case 0272: case 0273: + if (BIT(dev, 0)) + stream << "CDF "; + if (BIT(dev, 1)) + stream << "CIF "; + util::stream_format(stream, "%o", dev & 070); + break; + + case 0205: case 0215: case 0245: case 0255: + util::stream_format(stream, "P%cC%d", BIT(dev, 3) ? 'A' : 'P', BIT(dev, 5) ? 2 : 1); + break; + + case 0206: case 0216: case 0226: case 0236: + if (BIT(pc, 15)) + util::stream_format(stream, "IOT %03o", dev); // NOPs if already in panel mode + else + util::stream_format(stream, "PR%d", (dev & 030) >> 3); + return 1 | SUPPORTED | STEP_OVER; + + case 0207: case 0217: case 0227: case 0237: + util::stream_format(stream, "%cSP%d", BIT(dev, 3) ? 'L' : 'R', BIT(dev, 4) ? 2 : 1); + break; + + case 0214: + stream << "RDF"; + break; + + case 0224: + stream << "RIF"; + break; + + case 0225: case 0265: + util::stream_format(stream, "RTN%d", BIT(dev, 5) ? 2 : 1); + return 1 | SUPPORTED | STEP_OUT; + + case 0235: case 0275: + util::stream_format(stream, "POP%d", BIT(dev, 5) ? 2 : 1); + break; + + case 0234: + stream << "RIB"; + break; + + case 0244: + stream << "RMF"; + break; + + case 0246: + stream << "WSR"; + break; + + case 0256: + stream << "GCF"; + break; + + case 0266: case 0276: + if (BIT(pc, 15)) + util::stream_format(stream, "%cPD", BIT(dev, 3) ? 'S' : 'C'); + else + util::stream_format(stream, "IOT %03o", dev); // Panel mode only + break; + + default: + util::stream_format(stream, "IOT %03o", dev); + break; + } + + return 1 | SUPPORTED; +} + +// Sequence 1: CLA, CLL +// Sequence 2: CMA, CML +// Sequence 3: IAC +// Sequence 4: RAL, RTL, RAR, RTR, BSW, R3L +void pdp8_disassembler::dasm_opr_group1(std::ostream &stream, u16 opr) +{ + if (opr == 000) + stream << "NOP"; + else if ((opr & 0357) == 0204) + { + if (BIT(opr, 4)) + stream << "CML "; + stream << "GLK"; // Get link (CLA RAL) + } + else + { + int count = 0; + switch (opr & 0240) + { + case 000: + break; + + case 040: + if (BIT(opr, 0)) + stream << "CIA"; // Complement and increment (CMA IAC); may not be combined with rotate operations on Classic-8 + else + stream << "CMA"; + ++count; break; - case 1: - util::stream_format(stream, "TAD %c %05o", indirect ? 'I' : ' ', zero_page ? zero_addr : current_addr); + + case 0200: + stream << "CLA"; + ++count; break; - case 2: - util::stream_format(stream, "ISZ %c %05o", indirect ? 'I' : ' ', zero_page ? zero_addr : current_addr); + + case 0240: + stream << "STA"; // Set accumulator (CLA CMA) + ++count; break; - case 3: - util::stream_format(stream, "DCA %c %05o", indirect ? 'I' : ' ', zero_page ? zero_addr : current_addr); + } + switch (opr & 0120) + { + case 000: break; - case 4: - util::stream_format(stream, "JMS %c %05o", indirect ? 'I' : ' ', zero_page ? zero_addr : current_addr); + + case 020: + if (count != 0) + stream << " "; + stream << "CML"; + ++count; break; - case 5: - util::stream_format(stream, "JMP %c %05o", indirect ? 'I' : ' ', zero_page ? zero_addr : current_addr); + + case 0100: + if (count != 0) + stream << " "; + stream << "CLL"; + ++count; break; - case 6: - util::stream_format(stream, "IOT %03o %01o", (op >> 03) & 077, op & 07); + + case 0120: + if (count != 0) + stream << " "; + stream << "STL"; // Set link (CLL CML) + ++count; break; - case 7: + } + if (BIT(opr, 0) && (opr & 0240) != 040) + { + if (count != 0) + stream << " "; + stream << "IAC"; // May not be combined with rotate operations on Classic-8 + ++count; + } + + // Rotate operations + if (m_has_r3l && (opr & 016) == 014) + { + if (count != 0) + stream << " "; + stream << "R3L"; + ++count; + } + else if ((opr & 016) == 002) { - bool group2 = ((op & 0401) == 0400); - if (!group2) + if (count != 0) + stream << " "; + stream << "BSW"; // PDP-8/E and up + ++count; + } + else + { + if (BIT(opr, 2)) { - if (!(op & 0377)) - { - util::stream_format(stream, "NOP "); - } - else - { - if (op & 0200) - { - util::stream_format(stream, "CLA "); - } - if (op & 0100) - { - util::stream_format(stream, "CLL "); - } - if (op & 040) - { - util::stream_format(stream, "CMA "); - } - if (op & 020) - { - util::stream_format(stream, "CML "); - } - if (op & 01) - { - util::stream_format(stream, "IAC "); - } - if (op & 010) - { - if (op & 02) - { - util::stream_format(stream, "RTR "); - } - else - { - util::stream_format(stream, "RAR "); - } - } - if (op & 04) - { - if (op & 02) - { - util::stream_format(stream, "RTL "); - } - else - { - util::stream_format(stream, "RAL "); - } - } - } + if (count != 0) + stream << " "; + util::stream_format(stream, "R%cL", BIT(opr, 1) ? 'T' : 'A'); + ++count; } - else + if (BIT(opr, 3)) + { + if (count != 0) + stream << " "; + util::stream_format(stream, "R%cR", BIT(opr, 1) ? 'T' : 'A'); + ++count; + } + } + + u16 nl; + switch (opr & 0314) + { + case 0200: case 0300: + nl = (opr & 041) == 040 ? 07777 : (opr & 041) == 001 ? 1 : 0; + if ((opr & 042) == 002) + nl <<= 6; + util::stream_format(stream, "%*s/NL%04o", 17 - std::min(count, 4) * 4, "", nl); + break; + + case 0214: case 0314: + if (m_has_r3l && !BIT(opr, 1)) { - if (!(op & 0377)) - { - util::stream_format(stream, "NOP "); - } - else - { - if (op & 010) - { - if (!(op & 0160)) - { - util::stream_format(stream, "SKP "); - } - else - { - if (op & 0100) - { - util::stream_format(stream, "SPA "); - } - if (op & 040) - { - util::stream_format(stream, "SNA "); - } - if (op & 020) - { - util::stream_format(stream, "SZL "); - } - } - } - else - { - if (op & 0100) - { - util::stream_format(stream, "SMA "); - } - if (op & 040) - { - util::stream_format(stream, "SZA "); - } - if (op & 020) - { - util::stream_format(stream, "SNL "); - } - } - if (op & 0200) - { - util::stream_format(stream, "CLA "); - } - if (op & 04) - { - util::stream_format(stream, "OSR "); - } - if (op & 02) - { - util::stream_format(stream, "HLT "); - } - } + nl = (opr & 041) == 040 ? 07777 : (opr & 041) == 001 ? 010 : 0; + util::stream_format(stream, "%*s/NL%04o", 17 - std::min(count, 4) * 4, "", nl); } + break; + + case 0304: + nl = ((opr & 041) == 001 ? 2 : 0) | (BIT(opr, 4) != BIT(opr, 5) ? 1 : 0); + if (BIT(opr, 1)) + nl <<= 1; + if ((opr & 041) == 040) + nl ^= 07777; + util::stream_format(stream, "%*s/NL%04o", 17 - std::min(count, 4) * 4, "", nl); + break; + + case 0310: + nl = BIT(opr, 4) != BIT(opr, 5) ? 04000 : 0; + if (BIT(opr, 1)) + { + nl >>= 1; + if ((opr & 041) == 001) + nl |= 04000; + } + if ((opr & 041) == 040) + nl ^= 07777; + util::stream_format(stream, "%*s/NL%04o", 17 - std::min(count, 4) * 4, "", nl); + break; + } + } +} + +// Sequence 1a: SKP, SPA, SNA, SZL (skip if all conditions hold) +// Sequence 1b: SMA, SZA, SZL (skip if any condition holds) +// Sequence 2: CLA +// Sequence 3: OSR, HLT +void pdp8_disassembler::dasm_opr_group2(std::ostream &stream, u16 opr) +{ + if ((opr & 0170) != 0) + { + // Skip instructions + if ((opr & 0170) == 010) + stream << "SKP"; + else + { + if (BIT(opr, 6)) + { + stream << (BIT(opr, 3) ? "SPA" : "SMA"); + if ((opr & 060) != 0) + stream << " "; + } + if (BIT(opr, 5)) + { + stream << (BIT(opr, 3) ? "SNA" : "SZA"); + if (BIT(opr, 4)) + stream << " "; + } + if (BIT(opr, 4)) + stream << (BIT(opr, 3) ? "SZL" : "SNL"); + } + if ((opr & 0206) != 0) + stream << " "; + } + if (BIT(opr, 2)) + { + if (BIT(opr, 7)) + stream << "LSR"; // Load switch register (CLA OSR) + else + stream << "OSR"; + if (BIT(opr, 1)) + stream << " HLT"; + } + else if (BIT(opr, 1)) + { + if (BIT(opr, 7)) + stream << "CLA "; + stream << "HLT"; + } + else + { + if (BIT(opr, 7)) + stream << "CLA"; + else if (opr == 07400) + stream << "NOP"; + if ((opr & 0176) == 0) + stream << "!200"; + } +} + +// Sequence 1: CLA +// Sequence 2: MQL, MQA (also SCA on older EAEs) +// Sequence 3: MUY, DIV, NMI, SHL, ASR, LSR, etc. +offs_t pdp8_disassembler::dasm_opr_group3(std::ostream &stream, u16 opr, offs_t pc, const pdp8_disassembler::data_buffer &opcodes) +{ + switch (opr & 0320) + { + case 000: + if (opr == 0401) + stream << "NOP"; + break; + + case 020: + if (opr == 0431) + { + stream << "SWAB"; // KE8-E Mode A (change to Mode B) + return 1 | SUPPORTED; + } + else + stream << "MQL"; // Extended Arithmetic Element or internal for PDP-8/E and up + break; + + case 0100: + stream << "MQA"; // Extended Arithmetic Element or internal for PDP-8/E and up + break; + + case 0120: case 0320: + if (BIT(opr, 7)) + stream << "CLA "; + if (opr == 0573) + { + stream << "DPIC"; // KE8-E Mode B + return 1 | SUPPORTED; + } + else if (opr == 0575) + { + stream << "DCM"; // KE8-E Mode B + return 1 | SUPPORTED; + } + else + stream << "SWP"; // PDP-8/E and up + break; + + case 0200: + stream << "CLA"; // NOP on PDP-8/L + break; + + case 0220: + if (opr == 0663) + { + stream << "DLD"; // KE8-E Mode B (CAM DAD) + return 1 | SUPPORTED; + } + else + stream << "CAM"; // Extended Arithmetic Element or internal for PDP-8/E and up (CLA MQA) + break; + + case 0300: + stream << "ACL"; // Extended Arithmetic Element or internal for PDP-8/E and up (CLA MQL) + break; + } + switch (opr & 056) + { + case 000: + if ((opr & 0120) == 0) + stream << "!201"; + break; + + case 002: + if ((opr & 0320) != 0) + stream << " "; + stream << "ACS"; // KE8-E Mode B + break; + + case 004: + if ((opr & 0320) != 0) + stream << " "; + util::stream_format(stream, "MUY; %04o", opcodes.r16(pc + 1) & 07777); // Extended Arithmetic Element + return 2 | SUPPORTED; + + case 006: + if ((opr & 0320) != 0) + stream << " "; + util::stream_format(stream, "DVI; %04o", opcodes.r16(pc + 1) & 07777); // Extended Arithmetic Element + return 2 | SUPPORTED; + + case 010: + if ((opr & 0320) != 0) + stream << " "; + stream << "NMI"; // Extended Arithmetic Element + break; + + case 012: + if ((opr & 0320) != 0) + stream << " "; + util::stream_format(stream, "SHL; %04o", opcodes.r16(pc + 1) & 07777); // Extended Arithmetic Element + return 2 | SUPPORTED; + + case 014: case 016: + if ((opr & 0320) != 0) + stream << " "; + util::stream_format(stream, "%cSR; %04o", BIT(opr, 1) ? 'L' : 'A', opcodes.r16(pc + 1) & 07777); // Extended Arithmetic Element + return 2 | SUPPORTED; + + case 040: + if ((opr & 0320) != 0) + stream << " "; + stream << "SCA"; // Extended Arithmetic Element + break; + + case 042: + if ((opr & 0320) != 0) + stream << " "; + stream << "DAD"; // KE8-E Mode B + break; + + case 044: + if ((opr & 0320) != 0) + stream << " "; + stream << "DST"; // KE8-E Mode B + break; + + case 046: + if ((opr & 0320) != 0) + stream << " "; + stream << "SWBA"; // KE8-E Mode B (change to mode A) + break; + + case 050: + if ((opr & 0320) != 0) + stream << " "; + stream << "DPSZ"; // KE8-E Mode B + break; + + case 056: + if ((opr & 0320) != 0) + stream << " "; + stream << "SAM"; // KE8-E Mode B + break; + + default: + if ((opr & 0320) != 0) + util::stream_format(stream, "!%02o", opr & 056); + else + util::stream_format(stream, "OPR %03o", opr & 0777); + break; + } + return 1 | SUPPORTED; +} + +offs_t hd6120_disassembler::dasm_opr_group3(std::ostream &stream, u16 opr, offs_t pc, const hd6120_disassembler::data_buffer &opcodes) +{ + switch (opr & 0320) + { + case 000: + stream << "NOP"; + break; + + case 020: + stream << "MQL"; + break; + + case 0100: + stream << "MQA"; + break; + + case 0120: case 0320: + if (BIT(opr, 7)) + stream << "CLA "; + stream << "SWP"; + break; + + case 0200: + stream << "CLA"; + break; + + case 0220: + stream << "CAM"; + break; + + case 0300: + stream << "ACL"; + break; + } + if ((opr & 0120) == 0) + util::stream_format(stream, "!%03o", opr & 0457); + else if ((opr & 056) != 0) + util::stream_format(stream, "!%02o", opr & 056); // Inhibit interrupts for next instruction + return 1 | SUPPORTED; +} + +offs_t pdp8_disassembler::disassemble(std::ostream &stream, offs_t pc, const pdp8_disassembler::data_buffer &opcodes, const pdp8_disassembler::data_buffer ¶ms) +{ + const u16 inst = opcodes.r16(pc); + + // Bits 0:2 (DEC numbering) specify instruction code + switch (BIT(inst, 9, 3)) + { + case 0: + stream << "AND"; + dasm_memory_reference(stream, inst, pc); + break; + + case 1: + stream << "TAD"; + dasm_memory_reference(stream, inst, pc); + break; + + case 2: + stream << "ISZ"; + dasm_memory_reference(stream, inst, pc); + break; + + case 3: + stream << "DCA"; + dasm_memory_reference(stream, inst, pc); + break; + + case 4: + stream << "JMS"; + dasm_memory_reference(stream, inst, pc); + return 1 | SUPPORTED | STEP_OVER; + + case 5: + stream << "JMP"; + dasm_memory_reference(stream, inst, pc); + if (BIT(inst, 8)) + return 1 | SUPPORTED | STEP_OUT; + break; + + case 6: + return dasm_iot(stream, inst & 0777, pc); + + case 7: + if (!BIT(inst, 8)) + dasm_opr_group1(stream, inst & 0777); + else if (!BIT(inst, 0)) + { + dasm_opr_group2(stream, inst & 0777); + + // HACK: HLT is a software trap on HD-6120 + if (m_has_r3l && BIT(inst, 1)) + return 1 | SUPPORTED | STEP_OVER; } + else + return dasm_opr_group3(stream, inst & 0777, pc, opcodes); + break; } - return 1 | SUPPORTED | (opcode == 4 ? STEP_OVER : 0); + return 1 | SUPPORTED; } diff --git a/src/devices/cpu/pdp8/pdp8dasm.h b/src/devices/cpu/pdp8/pdp8dasm.h index df6c92e9ddb..64821c40fc6 100644 --- a/src/devices/cpu/pdp8/pdp8dasm.h +++ b/src/devices/cpu/pdp8/pdp8dasm.h @@ -1,10 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Ryan Holtz -/* - First-gen DEC PDP-8 disassembler - - Written by Ryan Holtz -*/ +// copyright-holders:AJR #ifndef MAME_CPU_PDP8_PDP8DASM_H #define MAME_CPU_PDP8_PDP8DASM_H @@ -14,11 +9,36 @@ class pdp8_disassembler : public util::disasm_interface { public: - pdp8_disassembler() = default; - virtual ~pdp8_disassembler() = default; + pdp8_disassembler(bool has_r3l = 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 ¶ms) override; + + void dasm_memory_reference(std::ostream &stream, u16 inst, offs_t pc); + virtual offs_t dasm_iot(std::ostream &stream, u16 dev, offs_t pc); + void dasm_opr_group1(std::ostream &stream, u16 inst); + void dasm_opr_group2(std::ostream &stream, u16 inst); + virtual offs_t dasm_opr_group3(std::ostream &stream, u16 inst, offs_t pc, const data_buffer &opcodes); + +private: + const bool m_has_r3l; +}; + +class hd6120_disassembler : public pdp8_disassembler +{ +public: + hd6120_disassembler(); + +protected: + // util::disasm_interface overrides + virtual u32 interface_flags() const override; + virtual u32 page_address_bits() const override; + virtual u32 page2_address_bits() const override; + + virtual offs_t dasm_iot(std::ostream &stream, u16 dev, offs_t pc) override; + virtual offs_t dasm_opr_group3(std::ostream &stream, u16 inst, offs_t pc, const data_buffer &opcodes) override; }; -#endif +#endif // MAME_CPU_PDP8_PDP8DASM_H diff --git a/src/tools/unidasm.cpp b/src/tools/unidasm.cpp index 4a2aa8c9e54..dbe17d86ca2 100644 --- a/src/tools/unidasm.cpp +++ b/src/tools/unidasm.cpp @@ -357,6 +357,7 @@ struct options const dasm_table_entry *dasm; uint32_t skip; uint32_t count; + bool octal; }; static const dasm_table_entry dasm_table[] = @@ -426,6 +427,7 @@ static const dasm_table_entry dasm_table[] = { "h16", be, 0, []() -> util::disasm_interface * { return new h16_disassembler; } }, { "hc11", be, 0, []() -> util::disasm_interface * { return new hc11_disassembler; } }, { "hcd62121", le, 0, []() -> util::disasm_interface * { return new hcd62121_disassembler; } }, + { "hd6120", be, -1, []() -> util::disasm_interface * { return new hd6120_disassembler; } }, { "hd61700", le, -1, []() -> util::disasm_interface * { return new hd61700_disassembler; } }, { "hd6301", be, 0, []() -> util::disasm_interface * { return new m680x_disassembler(6301); } }, { "hd6309", be, 0, []() -> util::disasm_interface * { return new hd6309_disassembler; } }, @@ -1110,6 +1112,8 @@ static int parse_options(int argc, char *argv[], options *opts) opts->upper = true; else if(tolower((uint8_t)curarg[1]) == 'x') opts->xchbytes = true; + else if(tolower((uint8_t)curarg[1]) == 'o') + opts->octal = true; else goto usage; @@ -1183,7 +1187,7 @@ static int parse_options(int argc, char *argv[], options *opts) usage: printf("Usage: %s -arch [-basepc ] \n", argv[0]); printf(" [-mode ] [-norawbytes] [-xchbytes] [-flipped] [-upper] [-lower]\n"); - printf(" [-skip ] [-count ]\n"); + printf(" [-skip ] [-count ] [-octal]\n"); printf("\n"); printf("Supported architectures:"); const int colwidth = 1 + std::strlen(std::max_element(std::begin(dasm_table), std::end(dasm_table), [](const dasm_table_entry &a, const dasm_table_entry &b) { return std::strlen(a.name) < std::strlen(b.name); })->name); @@ -1350,7 +1354,7 @@ int main(int argc, char *argv[]) // pc to string conversion std::function pc_to_string; int aw = 32 - count_leading_zeros(pc_mask); - bool is_octal = false; // Parameter? Per-cpu config? + bool is_octal = opts.octal; // Parameter? Per-cpu config? if((flags & util::disasm_interface::PAGED2LEVEL) == util::disasm_interface::PAGED2LEVEL) { int bits1 = disasm->page_address_bits(); int bits2 = disasm->page2_address_bits(); @@ -1471,62 +1475,122 @@ int main(int argc, char *argv[]) max_len >>= granularity_shift; std::function dump_raw_bytes; - switch(granularity) { - case 1: - dump_raw_bytes = [step = disasm->opcode_alignment(), next_pc, base_buffer](offs_t pc, offs_t size) -> std::string { - std::string result = ""; - for(offs_t i=0; i != size; i++) { - if(i) - result += ' '; - result += util::string_format("%02x", base_buffer.r8(pc)); - pc = next_pc(pc, step); - } - return result; - }; - max_len = (max_len * 3) - 1; - break; - - case 2: - dump_raw_bytes = [step = disasm->opcode_alignment(), next_pc, base_buffer](offs_t pc, offs_t size) -> std::string { - std::string result = ""; - for(offs_t i=0; i != size; i++) { - if(i) - result += ' '; - result += util::string_format("%04x", base_buffer.r16(pc)); - pc = next_pc(pc, step); - } - return result; - }; - max_len = (max_len * 5) - 1; - break; - - case 4: - dump_raw_bytes = [step = disasm->opcode_alignment(), next_pc, base_buffer](offs_t pc, offs_t size) -> std::string { - std::string result = ""; - for(offs_t i=0; i != size; i++) { - if(i) - result += ' '; - result += util::string_format("%08x", base_buffer.r32(pc)); - pc = next_pc(pc, step); - } - return result; - }; - max_len = (max_len * 9) - 1; - break; - - case 8: - dump_raw_bytes = [step = disasm->opcode_alignment(), next_pc, base_buffer](offs_t pc, offs_t size) -> std::string { - std::string result = ""; - for(offs_t i=0; i != size; i++) { - if(i) - result += ' '; - result += util::string_format("%016x", base_buffer.r64(pc)); - pc = next_pc(pc, step); - } - return result; - }; - max_len = (max_len * 17) - 1; - break; + if(is_octal) { + switch(granularity) { + case 1: + dump_raw_bytes = [step = disasm->opcode_alignment(), next_pc, base_buffer](offs_t pc, offs_t size) -> std::string { + std::string result = ""; + for(offs_t i=0; i != size; i++) { + if(i) + result += ' '; + result += util::string_format("%03o", base_buffer.r8(pc)); + pc = next_pc(pc, step); + } + return result; + }; + max_len = (max_len * 4) - 1; + break; + + case 2: + dump_raw_bytes = [step = disasm->opcode_alignment(), next_pc, base_buffer](offs_t pc, offs_t size) -> std::string { + std::string result = ""; + for(offs_t i=0; i != size; i++) { + if(i) + result += ' '; + result += util::string_format("%06o", base_buffer.r16(pc)); + pc = next_pc(pc, step); + } + return result; + }; + max_len = (max_len * 7) - 1; + break; + + case 4: + dump_raw_bytes = [step = disasm->opcode_alignment(), next_pc, base_buffer](offs_t pc, offs_t size) -> std::string { + std::string result = ""; + for(offs_t i=0; i != size; i++) { + if(i) + result += ' '; + result += util::string_format("%011o", base_buffer.r32(pc)); + pc = next_pc(pc, step); + } + return result; + }; + max_len = (max_len * 12) - 1; + break; + + case 8: + dump_raw_bytes = [step = disasm->opcode_alignment(), next_pc, base_buffer](offs_t pc, offs_t size) -> std::string { + std::string result = ""; + for(offs_t i=0; i != size; i++) { + if(i) + result += ' '; + result += util::string_format("%022o", base_buffer.r64(pc)); + pc = next_pc(pc, step); + } + return result; + }; + max_len = (max_len * 23) - 1; + break; + } + } else { + switch(granularity) { + case 1: + dump_raw_bytes = [step = disasm->opcode_alignment(), next_pc, base_buffer](offs_t pc, offs_t size) -> std::string { + std::string result = ""; + for(offs_t i=0; i != size; i++) { + if(i) + result += ' '; + result += util::string_format("%02x", base_buffer.r8(pc)); + pc = next_pc(pc, step); + } + return result; + }; + max_len = (max_len * 3) - 1; + break; + + case 2: + dump_raw_bytes = [step = disasm->opcode_alignment(), next_pc, base_buffer](offs_t pc, offs_t size) -> std::string { + std::string result = ""; + for(offs_t i=0; i != size; i++) { + if(i) + result += ' '; + result += util::string_format("%04x", base_buffer.r16(pc)); + pc = next_pc(pc, step); + } + return result; + }; + max_len = (max_len * 5) - 1; + break; + + case 4: + dump_raw_bytes = [step = disasm->opcode_alignment(), next_pc, base_buffer](offs_t pc, offs_t size) -> std::string { + std::string result = ""; + for(offs_t i=0; i != size; i++) { + if(i) + result += ' '; + result += util::string_format("%08x", base_buffer.r32(pc)); + pc = next_pc(pc, step); + } + return result; + }; + max_len = (max_len * 9) - 1; + break; + + case 8: + dump_raw_bytes = [step = disasm->opcode_alignment(), next_pc, base_buffer](offs_t pc, offs_t size) -> std::string { + std::string result = ""; + for(offs_t i=0; i != size; i++) { + if(i) + result += ' '; + result += util::string_format("%016x", base_buffer.r64(pc)); + pc = next_pc(pc, step); + } + return result; + }; + max_len = (max_len * 17) - 1; + break; + } } if(opts.flipped) { -- cgit v1.2.3