/* Capstone Disassembly Engine */ /* MOS65XX Backend by Sebastian Macke 2018 */ #include "capstone/mos65xx.h" #include "MOS65XXDisassembler.h" #include "MOS65XXDisassemblerInternals.h" typedef struct OpInfo { mos65xx_insn ins; mos65xx_address_mode am; int operand_bytes; } OpInfo; static const struct OpInfo OpInfoTable[]= { #include "m6502.inc" #include "m65c02.inc" #include "mw65c02.inc" #include "m65816.inc" }; static const char* const RegNames[] = { "invalid", "A", "X", "Y", "P", "SP", "DP", "B", "K" }; #ifndef CAPSTONE_DIET static const char* const GroupNames[] = { NULL, "jump", "call", "ret", "int", "iret", "branch_relative" }; typedef struct InstructionInfo { const char* name; mos65xx_group_type group_type; mos65xx_reg write, read; bool modifies_status; } InstructionInfo; static const struct InstructionInfo InstructionInfoTable[]= { #include "instruction_info.inc" }; #endif #ifndef CAPSTONE_DIET static void fillDetails(MCInst *MI, struct OpInfo opinfo, int cpu_type) { int i; cs_detail *detail = MI->flat_insn->detail; InstructionInfo insinfo = InstructionInfoTable[opinfo.ins]; detail->mos65xx.am = opinfo.am; detail->mos65xx.modifies_flags = insinfo.modifies_status; detail->groups_count = 0; detail->regs_read_count = 0; detail->regs_write_count = 0; detail->mos65xx.op_count = 0; if (insinfo.group_type != MOS65XX_GRP_INVALID) { detail->groups[detail->groups_count] = insinfo.group_type; detail->groups_count++; } if (opinfo.am == MOS65XX_AM_REL || opinfo.am == MOS65XX_AM_ZP_REL) { detail->groups[detail->groups_count] = MOS65XX_GRP_BRANCH_RELATIVE; detail->groups_count++; } if (insinfo.read != MOS65XX_REG_INVALID) { detail->regs_read[detail->regs_read_count++] = insinfo.read; } else switch(opinfo.am) { case MOS65XX_AM_ACC: detail->regs_read[detail->regs_read_count++] = MOS65XX_REG_ACC; break; case MOS65XX_AM_ZP_Y: case MOS65XX_AM_ZP_IND_Y: case MOS65XX_AM_ABS_Y: case MOS65XX_AM_ZP_IND_LONG_Y: detail->regs_read[detail->regs_read_count++] = MOS65XX_REG_Y; break; case MOS65XX_AM_ZP_X: case MOS65XX_AM_ZP_X_IND: case MOS65XX_AM_ABS_X: case MOS65XX_AM_ABS_X_IND: case MOS65XX_AM_ABS_LONG_X: detail->regs_read[detail->regs_read_count++] = MOS65XX_REG_X; break; case MOS65XX_AM_SR: detail->regs_read[detail->regs_read_count++] = MOS65XX_REG_SP; break; case MOS65XX_AM_SR_IND_Y: detail->regs_read[detail->regs_read_count++] = MOS65XX_REG_SP; detail->regs_read[detail->regs_read_count++] = MOS65XX_REG_Y; break; default: break; } if (insinfo.write != MOS65XX_REG_INVALID) { detail->regs_write[detail->regs_write_count++] = insinfo.write; } else if (opinfo.am == MOS65XX_AM_ACC) { detail->regs_write[detail->regs_write_count++] = MOS65XX_REG_ACC; } switch(opinfo.ins) { case MOS65XX_INS_ADC: case MOS65XX_INS_SBC: case MOS65XX_INS_ROL: case MOS65XX_INS_ROR: /* these read carry flag (and decimal for ADC/SBC) */ detail->regs_read[detail->regs_read_count++] = MOS65XX_REG_P; break; /* stack operations */ case MOS65XX_INS_JSL: case MOS65XX_INS_JSR: case MOS65XX_INS_PEA: case MOS65XX_INS_PEI: case MOS65XX_INS_PER: case MOS65XX_INS_PHA: case MOS65XX_INS_PHB: case MOS65XX_INS_PHD: case MOS65XX_INS_PHK: case MOS65XX_INS_PHP: case MOS65XX_INS_PHX: case MOS65XX_INS_PHY: case MOS65XX_INS_PLA: case MOS65XX_INS_PLB: case MOS65XX_INS_PLD: case MOS65XX_INS_PLP: case MOS65XX_INS_PLX: case MOS65XX_INS_PLY: case MOS65XX_INS_RTI: case MOS65XX_INS_RTL: case MOS65XX_INS_RTS: detail->regs_read[detail->regs_read_count++] = MOS65XX_REG_SP; detail->regs_write[detail->regs_write_count++] = MOS65XX_REG_SP; break; default: break; } if (cpu_type == MOS65XX_CPU_TYPE_65816) { switch (opinfo.am) { case MOS65XX_AM_ZP: case MOS65XX_AM_ZP_X: case MOS65XX_AM_ZP_Y: case MOS65XX_AM_ZP_IND: case MOS65XX_AM_ZP_X_IND: case MOS65XX_AM_ZP_IND_Y: case MOS65XX_AM_ZP_IND_LONG: case MOS65XX_AM_ZP_IND_LONG_Y: detail->regs_read[detail->regs_read_count++] = MOS65XX_REG_DP; break; case MOS65XX_AM_BLOCK: detail->regs_read[detail->regs_read_count++] = MOS65XX_REG_ACC; detail->regs_read[detail->regs_read_count++] = MOS65XX_REG_X; detail->regs_read[detail->regs_read_count++] = MOS65XX_REG_Y; detail->regs_write[detail->regs_write_count++] = MOS65XX_REG_ACC; detail->regs_write[detail->regs_write_count++] = MOS65XX_REG_X; detail->regs_write[detail->regs_write_count++] = MOS65XX_REG_Y; detail->regs_write[detail->regs_write_count++] = MOS65XX_REG_B; break; default: break; } switch (opinfo.am) { case MOS65XX_AM_ZP_IND: case MOS65XX_AM_ZP_X_IND: case MOS65XX_AM_ZP_IND_Y: case MOS65XX_AM_ABS: case MOS65XX_AM_ABS_X: case MOS65XX_AM_ABS_Y: case MOS65XX_AM_ABS_X_IND: /* these depend on the databank to generate a 24-bit address */ /* exceptions: PEA, PEI, and JMP (abs) */ if (opinfo.ins == MOS65XX_INS_PEI || opinfo.ins == MOS65XX_INS_PEA) break; detail->regs_read[detail->regs_read_count++] = MOS65XX_REG_B; break; default: break; } } if (insinfo.modifies_status) { detail->regs_write[detail->regs_write_count++] = MOS65XX_REG_P; } switch(opinfo.am) { case MOS65XX_AM_IMP: break; case MOS65XX_AM_IMM: detail->mos65xx.operands[detail->mos65xx.op_count].type = MOS65XX_OP_IMM; detail->mos65xx.operands[detail->mos65xx.op_count].mem = MI->Operands[0].ImmVal; detail->mos65xx.op_count++; break; case MOS65XX_AM_ACC: detail->mos65xx.operands[detail->mos65xx.op_count].type = MOS65XX_OP_REG; detail->mos65xx.operands[detail->mos65xx.op_count].reg = MOS65XX_REG_ACC; detail->mos65xx.op_count++; break; default: for (i = 0; i < MI->size; ++i) { detail->mos65xx.operands[detail->mos65xx.op_count].type = MOS65XX_OP_MEM; detail->mos65xx.operands[detail->mos65xx.op_count].mem = MI->Operands[i].ImmVal; detail->mos65xx.op_count++; } break; } } #endif void MOS65XX_printInst(MCInst *MI, struct SStream *O, void *PrinterInfo) { #ifndef CAPSTONE_DIET unsigned int value; unsigned opcode = MCInst_getOpcode(MI); mos65xx_info *info = (mos65xx_info *)PrinterInfo; OpInfo opinfo = OpInfoTable[opcode]; const char *prefix = info->hex_prefix ? info->hex_prefix : "0x"; SStream_concat0(O, InstructionInfoTable[opinfo.ins].name); switch (opinfo.ins) { /* special case - bit included as part of the instruction name */ case MOS65XX_INS_BBR: case MOS65XX_INS_BBS: case MOS65XX_INS_RMB: case MOS65XX_INS_SMB: SStream_concat(O, "%d", (opcode >> 4) & 0x07); break; default: break; } value = MI->Operands[0].ImmVal; switch (opinfo.am) { default: break; case MOS65XX_AM_IMP: break; case MOS65XX_AM_ACC: SStream_concat(O, " a"); break; case MOS65XX_AM_IMM: if (MI->imm_size == 1) SStream_concat(O, " #%s%02x", prefix, value); else SStream_concat(O, " #%s%04x", prefix, value); break; case MOS65XX_AM_ZP: SStream_concat(O, " %s%02x", prefix, value); break; case MOS65XX_AM_ABS: SStream_concat(O, " %s%04x", prefix, value); break; case MOS65XX_AM_ABS_LONG_X: SStream_concat(O, " %s%06x, x", prefix, value); break; case MOS65XX_AM_INT: SStream_concat(O, " %s%02x", prefix, value); break; case MOS65XX_AM_ABS_X: SStream_concat(O, " %s%04x, x", prefix, value); break; case MOS65XX_AM_ABS_Y: SStream_concat(O, " %s%04x, y", prefix, value); break; case MOS65XX_AM_ABS_LONG: SStream_concat(O, " %s%06x", prefix, value); break; case MOS65XX_AM_ZP_X: SStream_concat(O, " %s%02x, x", prefix, value); break; case MOS65XX_AM_ZP_Y: SStream_concat(O, " %s%02x, y", prefix, value); break; case MOS65XX_AM_REL: if (MI->op1_size == 1) value = 2 + (signed char)value; else value = 3 + (signed short)value; SStream_concat(O, " %s%04x", prefix, (MI->address + value) & 0xffff); break; case MOS65XX_AM_ABS_IND: SStream_concat(O, " (%s%04x)", prefix, value); break; case MOS65XX_AM_ABS_X_IND: SStream_concat(O, " (%s%04x, x)", prefix, value); break; case MOS65XX_AM_ABS_IND_LONG: SStream_concat(O, " [%s%04x]", prefix, value); break; case MOS65XX_AM_ZP_IND: SStream_concat(O, " (%s%02x)", prefix, value); break; case MOS65XX_AM_ZP_X_IND: SStream_concat(O, " (%s%02x, x)", prefix, value); break; case MOS65XX_AM_ZP_IND_Y: SStream_concat(O, " (%s%02x), y", prefix, value); break; case MOS65XX_AM_ZP_IND_LONG: SStream_concat(O, " [%s%02x]", prefix, value); break; case MOS65XX_AM_ZP_IND_LONG_Y: SStream_concat(O, " [%s%02x], y", prefix, value); break; case MOS65XX_AM_SR: SStream_concat(O, " %s%02x, s", prefix, value); break; case MOS65XX_AM_SR_IND_Y: SStream_concat(O, " (%s%02x, s), y", prefix, value); break; case MOS65XX_AM_BLOCK: SStream_concat(O, " %s%02x, %s%02x", prefix, MI->Operands[0].ImmVal, prefix, MI->Operands[1].ImmVal); break; case MOS65XX_AM_ZP_REL: value = 3 + (signed char)MI->Operands[1].ImmVal; /* BBR0, zp, rel and BBS0, zp, rel */ SStream_concat(O, " %s%02x, %s%04x", prefix, MI->Operands[0].ImmVal, prefix, (MI->address + value) & 0xffff); break; } #endif } bool MOS65XX_getInstruction(csh ud, const uint8_t *code, size_t code_len, MCInst *MI, uint16_t *size, uint64_t address, void *inst_info) { int i; unsigned char opcode; unsigned char len; unsigned cpu_offset = 0; int cpu_type = MOS65XX_CPU_TYPE_6502; cs_struct* handle = MI->csh; mos65xx_info *info = (mos65xx_info *)handle->printer_info; OpInfo opinfo; if (code_len == 0) { *size = 1; return false; } cpu_type = info->cpu_type; cpu_offset = cpu_type * 256; opcode = code[0]; opinfo = OpInfoTable[cpu_offset + opcode]; if (opinfo.ins == MOS65XX_INS_INVALID) { *size = 1; return false; } len = opinfo.operand_bytes + 1; if (cpu_type == MOS65XX_CPU_TYPE_65816 && opinfo.am == MOS65XX_AM_IMM) { switch(opinfo.ins) { case MOS65XX_INS_CPX: case MOS65XX_INS_CPY: case MOS65XX_INS_LDX: case MOS65XX_INS_LDY: if (info->long_x) ++len; break; case MOS65XX_INS_ADC: case MOS65XX_INS_AND: case MOS65XX_INS_BIT: case MOS65XX_INS_CMP: case MOS65XX_INS_EOR: case MOS65XX_INS_LDA: case MOS65XX_INS_ORA: case MOS65XX_INS_SBC: if (info->long_m) ++len; break; default: break; } } if (code_len < len) { *size = 1; return false; } MI->address = address; MCInst_setOpcode(MI, cpu_offset + opcode); MCInst_setOpcodePub(MI, opinfo.ins); *size = len; /* needed to differentiate relative vs relative long */ MI->op1_size = len - 1; if (opinfo.ins == MOS65XX_INS_NOP) { for (i = 1; i < len; ++i) MCOperand_CreateImm0(MI, code[i]); } switch (opinfo.am) { case MOS65XX_AM_ZP_REL: MCOperand_CreateImm0(MI, code[1]); MCOperand_CreateImm0(MI, code[2]); break; case MOS65XX_AM_BLOCK: MCOperand_CreateImm0(MI, code[2]); MCOperand_CreateImm0(MI, code[1]); break; case MOS65XX_AM_IMP: case MOS65XX_AM_ACC: break; case MOS65XX_AM_IMM: MI->has_imm = 1; MI->imm_size = len - 1; /* 65816 immediate is either 1 or 2 bytes */ /* drop through */ default: if (len == 2) MCOperand_CreateImm0(MI, code[1]); else if (len == 3) MCOperand_CreateImm0(MI, (code[2]<<8) | code[1]); else if (len == 4) MCOperand_CreateImm0(MI, (code[3]<<16) | (code[2]<<8) | code[1]); break; } #ifndef CAPSTONE_DIET if (MI->flat_insn->detail) { fillDetails(MI, opinfo, cpu_type); } #endif return true; } const char *MOS65XX_insn_name(csh handle, unsigned int id) { #ifdef CAPSTONE_DIET return NULL; #else if (id >= ARR_SIZE(InstructionInfoTable)) { return NULL; } return InstructionInfoTable[id].name; #endif } const char* MOS65XX_reg_name(csh handle, unsigned int reg) { #ifdef CAPSTONE_DIET return NULL; #else if (reg >= ARR_SIZE(RegNames)) { return NULL; } return RegNames[(int)reg]; #endif } void MOS65XX_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id) { /* id is cpu_offset + opcode */ if (id < ARR_SIZE(OpInfoTable)) { insn->id = OpInfoTable[id].ins; } } const char *MOS65XX_group_name(csh handle, unsigned int id) { #ifdef CAPSTONE_DIET return NULL; #else if (id >= ARR_SIZE(GroupNames)) { return NULL; } return GroupNames[(int)id]; #endif }