// license:BSD-3-Clause // copyright-holders:AJR /*************************************************************************** Hewlett-Packard HP2100 disassembler ***************************************************************************/ #include "emu.h" #include "hp2100d.h" hp2100_disassembler::hp2100_disassembler() : util::disasm_interface() { } u32 hp2100_disassembler::opcode_alignment() const { return 1; } void hp2100_disassembler::format_address(std::ostream &stream, u16 addr) const { if ((addr & 077776) == 0) stream << char('A' + BIT(addr, 0)); else util::stream_format(stream, "%05oB", addr & 077777); // Indirect addressing? if (BIT(addr, 15)) stream << ",I"; } void hp2100_disassembler::format_sc(std::ostream &stream, u8 sc) const { util::stream_format(stream, "%o", sc); if (sc > 7) stream << 'B'; } // Memory reference group offs_t hp2100_disassembler::dasm_mrg(std::ostream &stream, u16 inst, offs_t pc) const { offs_t flags = SUPPORTED; switch (BIT(inst, 12, 3)) { case 1: if (BIT(inst, 11)) { stream << "JSB "; flags |= STEP_OVER; } else stream << "AND "; break; case 2: if (BIT(inst, 11)) { stream << "JMP "; if (BIT(inst, 15)) flags |= STEP_OUT; } else stream << "XOR "; break; case 3: if (BIT(inst, 11)) { stream << "ISZ "; flags |= STEP_COND; } else stream << "IOR "; break; case 4: util::stream_format(stream, "AD%c ", cab(inst)); break; case 5: util::stream_format(stream, "CP%c ", cab(inst)); flags |= STEP_COND; break; case 6: util::stream_format(stream, "LD%c ", cab(inst)); break; case 7: util::stream_format(stream, "ST%c ", cab(inst)); break; } // Page zero or current page format_address(stream, (BIT(inst, 10) ? (pc & 077600) : 0) | (inst & 0100177)); return 1 | flags; } const char *const hp2100_disassembler::s_shift_ops[2][8] = { { "ALS", "ARS", "RAR", "RAL", "ALR", "ERA", "ELA", "ALF" }, { "BLS", "BRS", "RBR", "RBL", "BLR", "ERB", "ELB", "BLF" } }; // Shift-rotate group offs_t hp2100_disassembler::dasm_srg(std::ostream &stream, u16 inst) const { offs_t flags = SUPPORTED; if ((inst & 001070) == 0) { if (inst == 0) stream << "NOP"; else util::stream_format(stream, "OCT %o", inst); } else { if (BIT(inst, 9)) stream << s_shift_ops[BIT(inst, 11)][BIT(inst, 6, 3)]; if (BIT(inst, 5)) { if (BIT(inst, 9)) stream << ','; stream << "CLE"; } // Skip on LSB if (BIT(inst, 3)) { if ((inst & 001040) != 0) stream << ','; util::stream_format(stream, "SL%c", cab(inst)); flags |= STEP_COND; } if (BIT(inst, 4)) { if ((inst & 001050) != 0) stream << ','; stream << s_shift_ops[BIT(inst, 11)][BIT(inst, 0, 3)]; } } return 1 | flags; } // Alter-skip group offs_t hp2100_disassembler::dasm_asg(std::ostream &stream, u16 inst) const { offs_t flags = SUPPORTED; if ((inst & 01777) == 0) util::stream_format(stream, "OCT %o", inst); else { if (BIT(inst, 8, 2) != 0) util::stream_format(stream, "C%c%c", "LMC"[BIT(inst, 8, 2) - 1], cab(inst)); // Extend skip if (BIT(inst, 5)) { if (BIT(inst, 8, 2) != 0) stream << ','; stream << "SEZ"; flags |= STEP_COND; } if (BIT(inst, 6, 2) != 0) { if ((inst & 01440) != 0) stream << ','; util::stream_format(stream, "C%cE", "LMC"[BIT(inst, 6, 2) - 1]); } // Accumulator skips if (BIT(inst, 4)) { if ((inst & 01740) != 0) stream << ','; util::stream_format(stream, "SS%c", cab(inst)); flags |= STEP_COND; } if (BIT(inst, 3)) { if ((inst & 01760) != 0) stream << ','; util::stream_format(stream, "SL%c", cab(inst)); flags |= STEP_COND; } if (BIT(inst, 2)) { if ((inst & 01770) != 0) stream << ','; util::stream_format(stream, "IN%c", cab(inst)); flags |= STEP_COND; } if (BIT(inst, 1)) { if ((inst & 01774) != 0) stream << ','; util::stream_format(stream, "SZ%c", cab(inst)); flags |= STEP_COND; } // Reverse skip sense if (BIT(inst, 0)) { if ((inst & 01776) != 0) stream << ','; stream << "RSS"; } } return 1 | flags; } // Input-output group offs_t hp2100_disassembler::dasm_iog(std::ostream &stream, u16 inst) const { u8 sc = BIT(inst, 0, 6); offs_t flags = SUPPORTED; switch (BIT(inst, 6, 3)) { case 0: stream << "HLT"; if (sc != 0) { stream << ' '; format_sc(stream, sc); } break; case 1: if (sc == 1) { // Clear or set overflow flag if (BIT(inst, 9)) stream << "CLO"; else stream << "STO"; } else { if (BIT(inst, 9)) stream << "CLF "; else stream << "STF "; format_sc(stream, sc); } break; case 2: if (sc == 1) stream << "SOC"; else { stream << "SFC "; format_sc(stream, sc); } flags |= STEP_COND; break; case 3: if (sc == 1) stream << "SOS"; else { stream << "SFS "; format_sc(stream, sc); } flags |= STEP_COND; break; case 4: util::stream_format(stream, "MI%c ", cab(inst)); format_sc(stream, sc); break; case 5: util::stream_format(stream, "LI%c ", cab(inst)); format_sc(stream, sc); break; case 6: util::stream_format(stream, "OT%c ", cab(inst)); format_sc(stream, sc); break; case 7: if (BIT(inst, 11)) stream << "CLC "; else stream << "STC "; format_sc(stream, sc); break; } // Clear flag if (BIT(inst, 9) && BIT(inst, 6, 3) != 1) stream << ",C"; return 1 | flags; } // Macro instructions (including extended arithmetic group and 12901A floating-point instructions) offs_t hp2100_disassembler::dasm_mac(std::ostream &stream, u16 inst, offs_t pc, const hp2100_disassembler::data_buffer &opcodes) const { switch (inst & 05760) { case 00020: case 01020: { u8 n = BIT(inst, 0, 4); util::stream_format(stream, "AS%c %d", BIT(inst, 9) ? 'R' : 'L', n == 0 ? 16 : n); return 1 | SUPPORTED; } case 00040: case 01040: { u8 n = BIT(inst, 0, 4); util::stream_format(stream, "LS%c %d", BIT(inst, 9) ? 'R' : 'L', n == 0 ? 16 : n); return 1 | SUPPORTED; } case 00100: case 01100: { u8 n = BIT(inst, 0, 4); util::stream_format(stream, "RR%c %d", BIT(inst, 9) ? 'R' : 'L', n == 0 ? 16 : n); return 1 | SUPPORTED; } case 00200: stream << "MPY "; format_address(stream, opcodes.r16(pc + 1)); return 2 | SUPPORTED; case 00400: stream << "DIV "; format_address(stream, opcodes.r16(pc + 1)); return 2 | SUPPORTED; case 04000: stream << "DLD "; format_address(stream, opcodes.r16(pc + 1)); return 2 | SUPPORTED; case 04400: stream << "DST "; format_address(stream, opcodes.r16(pc + 1)); return 2 | SUPPORTED; case 05000: stream << "FAD "; format_address(stream, opcodes.r16(pc + 1)); return 2 | SUPPORTED; case 05020: stream << "FSB "; format_address(stream, opcodes.r16(pc + 1)); return 2 | SUPPORTED; case 05040: stream << "FML "; format_address(stream, opcodes.r16(pc + 1)); return 2 | SUPPORTED; case 05060: stream << "FDV "; format_address(stream, opcodes.r16(pc + 1)); return 2 | SUPPORTED; case 05100: stream << "FIX"; return 1 | SUPPORTED; case 05120: stream << "FLT"; return 1 | SUPPORTED; default: util::stream_format(stream, "MAC %oB", inst & 05777); return 1 | SUPPORTED; } } offs_t hp21mx_disassembler::dasm_mac(std::ostream &stream, u16 inst, offs_t pc, const hp2100_disassembler::data_buffer &opcodes) const { if ((inst & 01740) == 01740) { // Extended instruction group switch (inst & 04027) { case 0000: case 04000: util::stream_format(stream, "S%c%c ", cab(inst), xy(inst)); format_address(stream, opcodes.r16(pc + 1)); return 2 | SUPPORTED; case 0001: case 04001: util::stream_format(stream, "C%c%c", cab(inst), xy(inst)); return 1 | SUPPORTED; case 0002: case 04002: util::stream_format(stream, "L%c%c ", cab(inst), xy(inst)); format_address(stream, opcodes.r16(pc + 1)); return 2 | SUPPORTED; case 04003: util::stream_format(stream, "ST%c ", xy(inst)); format_address(stream, opcodes.r16(pc + 1)); return 2 | SUPPORTED; case 0004: case 04004: util::stream_format(stream, "C%c%c", xy(inst), cab(inst)); return 1 | SUPPORTED; case 04005: util::stream_format(stream, "LD%c ", xy(inst)); format_address(stream, opcodes.r16(pc + 1)); return 2 | SUPPORTED; case 04006: util::stream_format(stream, "AD%c ", xy(inst)); format_address(stream, opcodes.r16(pc + 1)); return 2 | SUPPORTED; case 0007: case 04007: util::stream_format(stream, "X%c%c", cab(inst), xy(inst)); return 1 | SUPPORTED; case 04020: util::stream_format(stream, "IS%c", xy(inst)); return 1 | STEP_COND | SUPPORTED; case 04021: util::stream_format(stream, "DS%c", xy(inst)); return 1 | STEP_COND | SUPPORTED; case 04022: if (BIT(inst, 3)) { stream << "JPY "; format_address(stream, opcodes.r16(pc + 1) & 077777); } else { stream << "JLY "; format_address(stream, opcodes.r16(pc + 1)); } return 2 | SUPPORTED; case 04023: if (BIT(inst, 3)) { stream << "SBS "; format_address(stream, opcodes.r16(pc + 1)); stream << ','; format_address(stream, opcodes.r16(pc + 2)); return 3 | SUPPORTED; } else { stream << "LBT"; return 1 | SUPPORTED; } case 04024: if (BIT(inst, 3)) { stream << "CBS "; format_address(stream, opcodes.r16(pc + 1)); stream << ','; format_address(stream, opcodes.r16(pc + 2)); return 3 | STEP_COND | SUPPORTED; } else { stream << "SBT"; return 1 | SUPPORTED; } case 04025: if (BIT(inst, 3)) { stream << "TBS "; format_address(stream, opcodes.r16(pc + 1)); stream << ','; format_address(stream, opcodes.r16(pc + 2)); return 3 | STEP_COND | SUPPORTED; } else { stream << "MBT "; format_address(stream, opcodes.r16(pc + 1)); return 3 | SUPPORTED; // one extra word reserved for microcode use } case 04026: if (BIT(inst, 3)) stream << "CMW "; else stream << "CBT "; format_address(stream, opcodes.r16(pc + 1)); return 3 | STEP_COND | SUPPORTED; // one extra word reserved for microcode use case 04027: if (BIT(inst, 3)) { stream << "MVW "; format_address(stream, opcodes.r16(pc + 1)); return 3 | SUPPORTED; // one extra word reserved for microcode use } else { stream << "SBT"; return 1 | STEP_COND | SUPPORTED; } default: util::stream_format(stream, "MAC %oB", inst & 05777); return 1 | SUPPORTED; } } else if ((inst & 01740) == 01700) { // Dynamic Mapping System switch (inst & 04037) { case 04002: stream << "MBI"; return 1 | SUPPORTED; case 04003: stream << "MBF"; return 1 | SUPPORTED; case 04004: stream << "MBW"; return 1 | SUPPORTED; case 04005: stream << "MWI"; return 1 | SUPPORTED; case 04006: stream << "MWF"; return 1 | SUPPORTED; case 04007: stream << "MWW"; return 1 | SUPPORTED; case 0010: case 04010: util::stream_format(stream, "SY%c", cab(inst)); return 1 | SUPPORTED; case 0011: case 04011: util::stream_format(stream, "US%c", cab(inst)); return 1 | SUPPORTED; case 0012: case 0013: case 04012: case 04013: util::stream_format(stream, "P%c%c", 'A' + BIT(inst, 0), cab(inst)); return 1 | SUPPORTED; case 04014: stream << "SSM "; format_address(stream, opcodes.r16(pc + 1)); return 2 | SUPPORTED; case 04015: stream << "JRS "; format_address(stream, opcodes.r16(pc + 1)); stream << ','; format_address(stream, opcodes.r16(pc + 2)); return 3 | SUPPORTED; case 04020: stream << "XMM"; return 1 | SUPPORTED; case 04021: stream << "XMS"; return 1 | SUPPORTED; case 0022: case 04022: util::stream_format(stream, "XM%c", cab(inst)); return 1 | SUPPORTED; case 0024: case 04024: util::stream_format(stream, "XL%c", cab(inst)); return 1 | SUPPORTED; case 0025: case 04025: util::stream_format(stream, "XS%c", cab(inst)); return 1 | SUPPORTED; case 0026: case 04026: util::stream_format(stream, "XC%c", cab(inst)); return 1 | STEP_COND | SUPPORTED; case 0027: case 04027: util::stream_format(stream, "LF%c ", cab(inst)); format_address(stream, opcodes.r16(pc + 1)); return 2 | SUPPORTED; case 0030: case 04030: util::stream_format(stream, "RS%c", cab(inst)); return 1 | SUPPORTED; case 0031: case 04031: util::stream_format(stream, "RV%c", cab(inst)); return 1 | SUPPORTED; case 04032: case 04034: case 04036: util::stream_format(stream, "%cJP ", "DSU"[BIT(inst, 1, 2) - 1]); format_address(stream, opcodes.r16(pc + 1)); return 2 | SUPPORTED; case 04033: case 04035: case 04037: util::stream_format(stream, "%cJS ", "DSU"[BIT(inst, 1, 2) - 1]); format_address(stream, opcodes.r16(pc + 1)); return 2 | STEP_OVER | SUPPORTED; default: util::stream_format(stream, "MAC %oB", inst & 05777); return 1 | SUPPORTED; } } else return hp2100_disassembler::dasm_mac(stream, inst, pc, opcodes); } offs_t hp2100_disassembler::disassemble(std::ostream &stream, offs_t pc, const hp2100_disassembler::data_buffer &opcodes, const hp2100_disassembler::data_buffer ¶ms) { u16 inst = opcodes.r16(pc); if (BIT(inst, 12, 3) != 0) return dasm_mrg(stream, inst, pc); else if (BIT(inst, 15)) { if (BIT(inst, 10)) return dasm_iog(stream, inst); else return dasm_mac(stream, inst, pc, opcodes); } else { if (BIT(inst, 10)) return dasm_asg(stream, inst); else return dasm_srg(stream, inst); } }