// license:BSD-3-Clause // copyright-holders:hap /* Seiko Epson E0C6200 disassembler */ #include "emu.h" #include "e0c6200d.h" const char *const e0c6200_disassembler::em_name[] = { "JP", "RETD", "CALL", "CALZ", "LD", "LBPX", "ADC", "CP", "ADD", "SUB", "SBC", "AND", "OR", "XOR", "RLC", "FAN", "PSET", "LDPX", "LDPY", "SET", "RST", "INC", "DEC", "RRC", "ACPX", "ACPY", "SCPX", "SCPY", "PUSH", "POP", "RETS", "RET", "JPBA", "HALT", "SLP", "NOP5", "NOP7", "NOT", "SCF", "SZF", "SDF", "EI", "DI", "RDF", "RZF", "RCF", "?" }; const u32 e0c6200_disassembler::em_flags[] = { 0, STEP_OUT, STEP_OVER, STEP_OVER, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, STEP_OUT, STEP_OUT, 0, STEP_OVER, STEP_OVER, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // 0-digit is number of bits per opcode parameter, 0 bits is literal, // 0x10-digit is for shift-right, 0x100-digit is special flag for r/q param const u16 e0c6200_disassembler::ep_bits[] = { 8, 8, 4, 0x102, 0x122, 0x142, 0x102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 4, 0, 0, 0 }; // redirect for r/q param const u8 e0c6200_disassembler::ep_redirect_r[4] = { ep_A, ep_B, ep_MX, ep_MY }; // literal opcode parameter const char *const e0c6200_disassembler::ep_name[] = { " ", " ", " ", " ", " ", " ", " ", "C", "NC", "Z", "NZ", "A", "B", "X", "Y", "MX", "MY", "XP", "XH", "XL", "YP", "YH", "YL", " ", "F", " ", "SP", "SPH", "SPL" }; std::string e0c6200_disassembler::decode_param(u16 opcode, int param) { int bits = ep_bits[param] & 0xf; int shift = ep_bits[param] >> 4 & 0xf; u16 opmask = opcode >> shift & ((1 << bits) - 1); // redirect r/q to A/B/MX/MY if (ep_bits[param] & 0x100) param = ep_redirect_r[opmask]; // literal param if (ep_bits[param] == 0) return ep_name[param]; // print value like how it's documented in the manual std::string val = (bits > 4 || opmask > 9) ? util::string_format("%02XH", opmask) : util::string_format("%d", opmask); if (param == ep_MN) return 'M' + val; else return val; } offs_t e0c6200_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer ¶ms) { u16 op = opcodes.r16(pc) & 0xfff; int m; int p1 = -1; int p2 = -1; // determine id for mnemonic and param(s) switch (op & 0xf00) { // JP s case 0x000: m = em_JP; p1 = ep_S; break; // RETD e case 0x100: m = em_RETD; p1 = ep_E; break; // JP C,s case 0x200: m = em_JP; p1 = ep_cC; p2 = ep_S; break; // JP NC,s case 0x300: m = em_JP; p1 = ep_cNC; p2 = ep_S; break; // CALL s case 0x400: m = em_CALL; p1 = ep_S; break; // CALZ s case 0x500: m = em_CALZ; p1 = ep_S; break; // JP Z,s case 0x600: m = em_JP; p1 = ep_cZ; p2 = ep_S; break; // JP NZ,s case 0x700: m = em_JP; p1 = ep_cNZ; p2 = ep_S; break; // LD Y,e case 0x800: m = em_LD; p1 = ep_Y; p2 = ep_E; break; // LBPX MX,e case 0x900: m = em_LBPX; p1 = ep_MX; p2 = ep_E; break; // LD X,e case 0xb00: m = em_LD; p1 = ep_X; p2 = ep_E; break; default: switch (op) { // RLC r case 0xaf0: case 0xaf5: case 0xafa: case 0xaff: m = em_RLC; p1 = ep_R0; break; // NOT r case 0xd0f: case 0xd1f: case 0xd2f: case 0xd3f: m = em_NOT; p1 = ep_R4; break; // LD XP,r case 0xe80: case 0xe81: case 0xe82: case 0xe83: m = em_LD; p1 = ep_XP; p2 = ep_R0; break; // LD XH,r case 0xe84: case 0xe85: case 0xe86: case 0xe87: m = em_LD; p1 = ep_XH; p2 = ep_R0; break; // LD XL,r case 0xe88: case 0xe89: case 0xe8a: case 0xe8b: m = em_LD; p1 = ep_XL; p2 = ep_R0; break; // RRC r case 0xe8c: case 0xe8d: case 0xe8e: case 0xe8f: m = em_RRC; p1 = ep_R0; break; // LD YP,r case 0xe90: case 0xe91: case 0xe92: case 0xe93: m = em_LD; p1 = ep_YP; p2 = ep_R0; break; // LD YH,r case 0xe94: case 0xe95: case 0xe96: case 0xe97: m = em_LD; p1 = ep_YH; p2 = ep_R0; break; // LD YL,r case 0xe98: case 0xe99: case 0xe9a: case 0xe9b: m = em_LD; p1 = ep_YL; p2 = ep_R0; break; // LD r,XP case 0xea0: case 0xea1: case 0xea2: case 0xea3: m = em_LD; p1 = ep_R0; p2 = ep_XP; break; // LD r,XH case 0xea4: case 0xea5: case 0xea6: case 0xea7: m = em_LD; p1 = ep_R0; p2 = ep_XH; break; // LD r,XL case 0xea8: case 0xea9: case 0xeaa: case 0xeab: m = em_LD; p1 = ep_R0; p2 = ep_XL; break; // LD r,YP case 0xeb0: case 0xeb1: case 0xeb2: case 0xeb3: m = em_LD; p1 = ep_R0; p2 = ep_YP; break; // LD r,YH case 0xeb4: case 0xeb5: case 0xeb6: case 0xeb7: m = em_LD; p1 = ep_R0; p2 = ep_YH; break; // LD r,YL case 0xeb8: case 0xeb9: case 0xeba: case 0xebb: m = em_LD; p1 = ep_R0; p2 = ep_YL; break; // INC X case 0xee0: m = em_INC; p1 = ep_X; break; // INC Y case 0xef0: m = em_INC; p1 = ep_Y; break; // ACPX MX,r case 0xf28: case 0xf29: case 0xf2a: case 0xf2b: m = em_ACPX; p1 = ep_MX; p2 = ep_R0; break; // ACPY MY,r case 0xf2c: case 0xf2d: case 0xf2e: case 0xf2f: m = em_ACPY; p1 = ep_MY; p2 = ep_R0; break; // SCPX MX,r case 0xf38: case 0xf39: case 0xf3a: case 0xf3b: m = em_SCPX; p1 = ep_MX; p2 = ep_R0; break; // SCPY MY,r case 0xf3c: case 0xf3d: case 0xf3e: case 0xf3f: m = em_SCPY; p1 = ep_MY; p2 = ep_R0; break; // SCF case 0xf41: m = em_SCF; break; // SZF case 0xf42: m = em_SZF; break; // SDF case 0xf44: m = em_SDF; break; // EI case 0xf48: m = em_EI; break; // DI case 0xf57: m = em_DI; break; // RDF case 0xf5b: m = em_RDF; break; // RZF case 0xf5d: m = em_RZF; break; // RCF case 0xf5e: m = em_RCF; break; // PUSH r case 0xfc0: case 0xfc1: case 0xfc2: case 0xfc3: m = em_PUSH; p1 = ep_R0; break; // PUSH XP case 0xfc4: m = em_PUSH; p1 = ep_XP; break; // PUSH XH case 0xfc5: m = em_PUSH; p1 = ep_XH; break; // PUSH XL case 0xfc6: m = em_PUSH; p1 = ep_XL; break; // PUSH YP case 0xfc7: m = em_PUSH; p1 = ep_YP; break; // PUSH YH case 0xfc8: m = em_PUSH; p1 = ep_YH; break; // PUSH YL case 0xfc9: m = em_PUSH; p1 = ep_YL; break; // PUSH F case 0xfca: m = em_PUSH; p1 = ep_F; break; // DEC SP case 0xfcb: m = em_DEC; p1 = ep_SP; break; // POP r case 0xfd0: case 0xfd1: case 0xfd2: case 0xfd3: m = em_POP; p1 = ep_R0; break; // POP XP case 0xfd4: m = em_POP; p1 = ep_XP; break; // POP XH case 0xfd5: m = em_POP; p1 = ep_XH; break; // POP XL case 0xfd6: m = em_POP; p1 = ep_XL; break; // POP YP case 0xfd7: m = em_POP; p1 = ep_YP; break; // POP YH case 0xfd8: m = em_POP; p1 = ep_YH; break; // POP YL case 0xfd9: m = em_POP; p1 = ep_YL; break; // POP F case 0xfda: m = em_POP; p1 = ep_F; break; // INC SP case 0xfdb: m = em_INC; p1 = ep_SP; break; // RETS case 0xfde: m = em_RETS; break; // RET case 0xfdf: m = em_RET; break; // LD SPH,r case 0xfe0: case 0xfe1: case 0xfe2: case 0xfe3: m = em_LD; p1 = ep_SPH; p2 = ep_R0; break; // LD r,SPH case 0xfe4: case 0xfe5: case 0xfe6: case 0xfe7: m = em_LD; p1 = ep_R0; p2 = ep_SPH; break; // JPBA case 0xfe8: m = em_JPBA; break; // LD SPL,r case 0xff0: case 0xff1: case 0xff2: case 0xff3: m = em_LD; p1 = ep_SPL; p2 = ep_R0; break; // LD r,SPL case 0xff4: case 0xff5: case 0xff6: case 0xff7: m = em_LD; p1 = ep_R0; p2 = ep_SPL; break; // HALT case 0xff8: m = em_HALT; break; // SLP case 0xff9: m = em_SLP; break; // NOP5 case 0xffb: m = em_NOP5; break; // NOP7 case 0xfff: m = em_NOP7; break; default: switch (op & 0xff0) { // ADC XH,i case 0xa00: m = em_ADC; p1 = ep_XH; p2 = ep_I; break; // ADC XL,i case 0xa10: m = em_ADC; p1 = ep_XL; p2 = ep_I; break; // ADC YH,i case 0xa20: m = em_ADC; p1 = ep_YH; p2 = ep_I; break; // ADC YL,i case 0xa30: m = em_ADC; p1 = ep_YL; p2 = ep_I; break; // CP XH,i case 0xa40: m = em_CP; p1 = ep_XH; p2 = ep_I; break; // CP XL,i case 0xa50: m = em_CP; p1 = ep_XL; p2 = ep_I; break; // CP YH,i case 0xa60: m = em_CP; p1 = ep_YH; p2 = ep_I; break; // CP YL,i case 0xa70: m = em_CP; p1 = ep_YL; p2 = ep_I; break; // ADD r,q case 0xa80: m = em_ADD; p1 = ep_R2; p2 = ep_Q; break; // ADC r,q case 0xa90: m = em_ADC; p1 = ep_R2; p2 = ep_Q; break; // SUB r,q case 0xaa0: m = em_SUB; p1 = ep_R2; p2 = ep_Q; break; // SBC r,q case 0xab0: m = em_SBC; p1 = ep_R2; p2 = ep_Q; break; // AND r,q case 0xac0: m = em_AND; p1 = ep_R2; p2 = ep_Q; break; // OR r,q case 0xad0: m = em_OR; p1 = ep_R2; p2 = ep_Q; break; // XOR r,q case 0xae0: m = em_XOR; p1 = ep_R2; p2 = ep_Q; break; // ADD r,i case 0xc00: case 0xc10: case 0xc20: case 0xc30: m = em_ADD; p1 = ep_R4; p2 = ep_I; break; // ADC r,i case 0xc40: case 0xc50: case 0xc60: case 0xc70: m = em_ADC; p1 = ep_R4; p2 = ep_I; break; // AND r,i case 0xc80: case 0xc90: case 0xca0: case 0xcb0: m = em_AND; p1 = ep_R4; p2 = ep_I; break; // OR r,i case 0xcc0: case 0xcd0: case 0xce0: case 0xcf0: m = em_OR; p1 = ep_R4; p2 = ep_I; break; // XOR r,i case 0xd00: case 0xd10: case 0xd20: case 0xd30: m = em_XOR; p1 = ep_R4; p2 = ep_I; break; // SBC r,i case 0xd40: case 0xd50: case 0xd60: case 0xd70: m = em_SBC; p1 = ep_R4; p2 = ep_I; break; // FAN r,i case 0xd80: case 0xd90: case 0xda0: case 0xdb0: m = em_FAN; p1 = ep_R4; p2 = ep_I; break; // CP r,i case 0xdc0: case 0xdd0: case 0xde0: case 0xdf0: m = em_CP; p1 = ep_R4; p2 = ep_I; break; // LD r,i case 0xe00: case 0xe10: case 0xe20: case 0xe30: m = em_LD; p1 = ep_R4; p2 = ep_I; break; // PSET p case 0xe40: case 0xe50: m = em_PSET; p1 = ep_P; break; // LDPX MX,i case 0xe60: m = em_LDPX; p1 = ep_MX; p2 = ep_I; break; // LDPY MY,i case 0xe70: m = em_LDPY; p1 = ep_MY; p2 = ep_I; break; // LD r,q case 0xec0: m = em_LD; p1 = ep_R2; p2 = ep_Q; break; // LDPX r,q case 0xee0: m = em_LDPX; p1 = ep_R2; p2 = ep_Q; break; // LDPY r,q case 0xef0: m = em_LDPY; p1 = ep_R2; p2 = ep_Q; break; // CP r,q case 0xf00: m = em_CP; p1 = ep_R2; p2 = ep_Q; break; // FAN r,q case 0xf10: m = em_FAN; p1 = ep_R2; p2 = ep_Q; break; // SET F,i case 0xf40: m = em_SET; p1 = ep_F; p2 = ep_I; break; // RST F,i case 0xf50: m = em_RST; p1 = ep_F; p2 = ep_I; break; // INC Mn case 0xf60: m = em_INC; p1 = ep_MN; break; // DEC Mn case 0xf70: m = em_DEC; p1 = ep_MN; break; // LD Mn,A case 0xf80: m = em_LD; p1 = ep_MN; p2 = ep_A; break; // LD Mn,B case 0xf90: m = em_LD; p1 = ep_MN; p2 = ep_B; break; // LD A,Mn case 0xfa0: m = em_LD; p1 = ep_A; p2 = ep_MN; break; // LD B,Mn case 0xfb0: m = em_LD; p1 = ep_B; p2 = ep_MN; break; // illegal opcode default: m = em_ILL; break; } // 0xff0 break; } // 0xfff break; } // 0xf00 (big switch) // fetch mnemonic util::stream_format(stream, "%-6s", em_name[m]); // fetch param(s) if (p1 != -1) { util::stream_format(stream, "%s", decode_param(op, p1)); if (p2 != -1) { util::stream_format(stream, ",%s", decode_param(op, p2)); } } return 1 | em_flags[m] | SUPPORTED; } u32 e0c6200_disassembler::opcode_alignment() const { return 1; } u32 e0c6200_disassembler::interface_flags() const { return PAGED2LEVEL; } u32 e0c6200_disassembler::page_address_bits() const { return 8; } u32 e0c6200_disassembler::page2_address_bits() const { return 4; }