/* Capstone Disassembly Engine */ /* M680X Backend by Wolfgang Schwotzer 2017 */ /* ======================================================================== */ /* ================================ INCLUDES ============================== */ /* ======================================================================== */ #include #include #include #include "../../cs_priv.h" #include "../../utils.h" #include "../../MCInst.h" #include "../../MCInstrDesc.h" #include "../../MCRegisterInfo.h" #include "M680XInstPrinter.h" #include "M680XDisassembler.h" #include "M680XDisassemblerInternals.h" #ifdef CAPSTONE_HAS_M680X #ifndef DECL_SPEC #ifdef _MSC_VER #define DECL_SPEC __cdecl #else #define DECL_SPEC #endif // _MSC_VER #endif // DECL_SPEC /* ======================================================================== */ /* ============================ GENERAL DEFINES =========================== */ /* ======================================================================== */ /* ======================================================================== */ /* =============================== PROTOTYPES ============================= */ /* ======================================================================== */ typedef enum insn_hdlr_id { illgl_hid, rel8_hid, rel16_hid, imm8_hid, imm16_hid, imm32_hid, dir_hid, ext_hid, idxX_hid, idxY_hid, idx09_hid, inh_hid, rr09_hid, rbits_hid, bitmv_hid, tfm_hid, opidx_hid, opidxdr_hid, idxX0_hid, idxX16_hid, imm8rel_hid, idxS_hid, idxS16_hid, idxXp_hid, idxX0p_hid, idx12_hid, idx12s_hid, rr12_hid, loop_hid, index_hid, imm8i12x_hid, imm16i12x_hid, exti12x_hid, HANDLER_ID_ENDING, } insn_hdlr_id; // Access modes for the first 4 operands. If there are more than // four operands they use the same access mode as the 4th operand. // // u: unchanged // r: (r)read access // w: (w)write access // m: (m)odify access (= read + write) // typedef enum e_access_mode { uuuu, rrrr, wwww, rwww, rrrm, rmmm, wrrr, mrrr, mwww, mmmm, mwrr, mmrr, wmmm, rruu, muuu, ACCESS_MODE_ENDING, } e_access_mode; // Access type values are compatible with enum cs_ac_type: typedef enum e_access { UNCHANGED = CS_AC_INVALID, READ = CS_AC_READ, WRITE = CS_AC_WRITE, MODIFY = (CS_AC_READ | CS_AC_WRITE), } e_access; /* Properties of one instruction in PAGE1 (without prefix) */ typedef struct inst_page1 { m680x_insn insn : 9; insn_hdlr_id handler_id1 : 6; /* first instruction handler id */ insn_hdlr_id handler_id2 : 6; /* second instruction handler id */ } inst_page1; /* Properties of one instruction in any other PAGE X */ typedef struct inst_pageX { unsigned opcode : 8; m680x_insn insn : 9; insn_hdlr_id handler_id1 : 6; /* first instruction handler id */ insn_hdlr_id handler_id2 : 6; /* second instruction handler id */ } inst_pageX; typedef struct insn_props { unsigned group : 4; e_access_mode access_mode : 5; m680x_reg reg0 : 5; m680x_reg reg1 : 5; bool cc_modified : 1; bool update_reg_access : 1; } insn_props; #include "m6800.inc" #include "m6801.inc" #include "hd6301.inc" #include "m6811.inc" #include "cpu12.inc" #include "m6805.inc" #include "m6808.inc" #include "hcs08.inc" #include "m6809.inc" #include "hd6309.inc" #include "insn_props.inc" ////////////////////////////////////////////////////////////////////////////// // M680X instuctions have 1 up to 8 bytes (CPU12: MOVW IDX2,IDX2). // A reader is needed to read a byte or word from a given memory address. // See also X86 reader(...) static bool read_byte(const m680x_info *info, uint8_t *byte, uint16_t address) { if (address - info->offset >= info->size) // out of code buffer range return false; *byte = info->code[address - info->offset]; return true; } static bool read_byte_sign_extended(const m680x_info *info, int16_t *word, uint16_t address) { if (address - info->offset >= info->size) // out of code buffer range return false; *word = (int16_t) info->code[address - info->offset]; if (*word & 0x80) *word |= 0xFF00; return true; } static bool read_word(const m680x_info *info, uint16_t *word, uint16_t address) { if (address + 1 - info->offset >= info->size) // out of code buffer range return false; *word = (uint16_t)info->code[address - info->offset] << 8; *word |= (uint16_t)info->code[address + 1 - info->offset]; return true; } static bool read_sdword(const m680x_info *info, int32_t *sdword, uint16_t address) { if (address + 3 - info->offset >= info->size) // out of code buffer range return false; *sdword = (uint32_t)info->code[address - info->offset] << 24; *sdword |= (uint32_t)info->code[address + 1 - info->offset] << 16; *sdword |= (uint32_t)info->code[address + 2 - info->offset] << 8; *sdword |= (uint32_t)info->code[address + 3 - info->offset]; return true; } // For PAGE2 and PAGE3 opcodes when using an an array of inst_page1 most // entries have M680X_INS_ILLGL. To avoid wasting memory an inst_pageX is // used which contains the opcode. Using a binary search for the right opcode // is much faster (= O(log n) ) in comparison to a linear search ( = O(n) ). static int binary_search(const inst_pageX *const inst_pageX_table, int table_size, uint8_t opcode) { int first = 0; int last = table_size - 1; int middle = (first + last) / 2; while (first <= last) { if (inst_pageX_table[middle].opcode < opcode) { first = middle + 1; } else if (inst_pageX_table[middle].opcode == opcode) { return middle; /* item found */ } else last = middle - 1; middle = (first + last) / 2; } if (first > last) return -1; /* item not found */ return -2; } void M680X_get_insn_id(cs_struct *handle, cs_insn *insn, unsigned int id) { const m680x_info *const info = (const m680x_info *)handle->printer_info; const cpu_tables *cpu = info->cpu; uint8_t insn_prefix = (id >> 8) & 0xff; int index; int i; insn->id = M680X_INS_ILLGL; for (i = 0; i < ARR_SIZE(cpu->pageX_prefix); ++i) { if (cpu->pageX_table_size[i] == 0 || (cpu->inst_pageX_table[i] == NULL)) break; if (cpu->pageX_prefix[i] == insn_prefix) { index = binary_search(cpu->inst_pageX_table[i], cpu->pageX_table_size[i], id & 0xff); insn->id = (index >= 0) ? cpu->inst_pageX_table[i][index].insn : M680X_INS_ILLGL; return; } } if (insn_prefix != 0) return; insn->id = cpu->inst_page1_table[id].insn; if (insn->id != M680X_INS_ILLGL) return; // Check if opcode byte is present in an overlay table for (i = 0; i < ARR_SIZE(cpu->overlay_table_size); ++i) { if (cpu->overlay_table_size[i] == 0 || (cpu->inst_overlay_table[i] == NULL)) break; if ((index = binary_search(cpu->inst_overlay_table[i], cpu->overlay_table_size[i], id & 0xff)) >= 0) { insn->id = cpu->inst_overlay_table[i][index].insn; return; } } } static void add_insn_group(cs_detail *detail, m680x_group_type group) { if (detail != NULL && (group != M680X_GRP_INVALID) && (group != M680X_GRP_ENDING)) detail->groups[detail->groups_count++] = (uint8_t)group; } static bool exists_reg_list(uint16_t *regs, uint8_t count, m680x_reg reg) { uint8_t i; for (i = 0; i < count; ++i) { if (regs[i] == (uint16_t)reg) return true; } return false; } static void add_reg_to_rw_list(MCInst *MI, m680x_reg reg, e_access access) { cs_detail *detail = MI->flat_insn->detail; if (detail == NULL || (reg == M680X_REG_INVALID)) return; switch (access) { case MODIFY: if (!exists_reg_list(detail->regs_read, detail->regs_read_count, reg)) detail->regs_read[detail->regs_read_count++] = (uint16_t)reg; // intentionally fall through case WRITE: if (!exists_reg_list(detail->regs_write, detail->regs_write_count, reg)) detail->regs_write[detail->regs_write_count++] = (uint16_t)reg; break; case READ: if (!exists_reg_list(detail->regs_read, detail->regs_read_count, reg)) detail->regs_read[detail->regs_read_count++] = (uint16_t)reg; break; case UNCHANGED: default: break; } } static void update_am_reg_list(MCInst *MI, m680x_info *info, cs_m680x_op *op, e_access access) { if (MI->flat_insn->detail == NULL) return; switch (op->type) { case M680X_OP_REGISTER: add_reg_to_rw_list(MI, op->reg, access); break; case M680X_OP_INDEXED: add_reg_to_rw_list(MI, op->idx.base_reg, READ); if (op->idx.base_reg == M680X_REG_X && info->cpu->reg_byte_size[M680X_REG_H]) add_reg_to_rw_list(MI, M680X_REG_H, READ); if (op->idx.offset_reg != M680X_REG_INVALID) add_reg_to_rw_list(MI, op->idx.offset_reg, READ); if (op->idx.inc_dec) { add_reg_to_rw_list(MI, op->idx.base_reg, WRITE); if (op->idx.base_reg == M680X_REG_X && info->cpu->reg_byte_size[M680X_REG_H]) add_reg_to_rw_list(MI, M680X_REG_H, WRITE); } break; default: break; } } static const e_access g_access_mode_to_access[4][15] = { { UNCHANGED, READ, WRITE, READ, READ, READ, WRITE, MODIFY, MODIFY, MODIFY, MODIFY, MODIFY, WRITE, READ, MODIFY, }, { UNCHANGED, READ, WRITE, WRITE, READ, MODIFY, READ, READ, WRITE, MODIFY, WRITE, MODIFY, MODIFY, READ, UNCHANGED, }, { UNCHANGED, READ, WRITE, WRITE, READ, MODIFY, READ, READ, WRITE, MODIFY, READ, READ, MODIFY, UNCHANGED, UNCHANGED, }, { UNCHANGED, READ, WRITE, WRITE, MODIFY, MODIFY, READ, READ, WRITE, MODIFY, READ, READ, MODIFY, UNCHANGED, UNCHANGED, }, }; static e_access get_access(int operator_index, e_access_mode access_mode) { int idx = (operator_index > 3) ? 3 : operator_index; return g_access_mode_to_access[idx][access_mode]; } static void build_regs_read_write_counts(MCInst *MI, m680x_info *info, e_access_mode access_mode) { cs_m680x *m680x = &info->m680x; int i; if (MI->flat_insn->detail == NULL || (!m680x->op_count)) return; for (i = 0; i < m680x->op_count; ++i) { e_access access = get_access(i, access_mode); update_am_reg_list(MI, info, &m680x->operands[i], access); } } static void add_operators_access(MCInst *MI, m680x_info *info, e_access_mode access_mode) { cs_m680x *m680x = &info->m680x; int offset = 0; int i; if (MI->flat_insn->detail == NULL || (!m680x->op_count) || (access_mode == uuuu)) return; for (i = 0; i < m680x->op_count; ++i) { e_access access; // Ugly fix: MULD has a register operand, an immediate operand // AND an implicitly changed register W if (info->insn == M680X_INS_MULD && (i == 1)) offset = 1; access = get_access(i + offset, access_mode); m680x->operands[i].access = access; } } typedef struct insn_to_changed_regs { m680x_insn insn; e_access_mode access_mode; m680x_reg regs[10]; } insn_to_changed_regs; static void set_changed_regs_read_write_counts(MCInst *MI, m680x_info *info) { //TABLE #define EOL M680X_REG_INVALID static const insn_to_changed_regs changed_regs[] = { { M680X_INS_BSR, mmmm, { M680X_REG_S, EOL } }, { M680X_INS_CALL, mmmm, { M680X_REG_S, EOL } }, { M680X_INS_CWAI, mrrr, { M680X_REG_S, M680X_REG_PC, M680X_REG_U, M680X_REG_Y, M680X_REG_X, M680X_REG_DP, M680X_REG_D, M680X_REG_CC, EOL }, }, { M680X_INS_DAA, mrrr, { M680X_REG_A, EOL } }, { M680X_INS_DIV, mmrr, { M680X_REG_A, M680X_REG_H, M680X_REG_X, EOL } }, { M680X_INS_EDIV, mmrr, { M680X_REG_D, M680X_REG_Y, M680X_REG_X, EOL } }, { M680X_INS_EDIVS, mmrr, { M680X_REG_D, M680X_REG_Y, M680X_REG_X, EOL } }, { M680X_INS_EMACS, mrrr, { M680X_REG_X, M680X_REG_Y, EOL } }, { M680X_INS_EMAXM, rrrr, { M680X_REG_D, EOL } }, { M680X_INS_EMINM, rrrr, { M680X_REG_D, EOL } }, { M680X_INS_EMUL, mmrr, { M680X_REG_D, M680X_REG_Y, EOL } }, { M680X_INS_EMULS, mmrr, { M680X_REG_D, M680X_REG_Y, EOL } }, { M680X_INS_ETBL, wmmm, { M680X_REG_A, M680X_REG_B, EOL } }, { M680X_INS_FDIV, mmmm, { M680X_REG_D, M680X_REG_X, EOL } }, { M680X_INS_IDIV, mmmm, { M680X_REG_D, M680X_REG_X, EOL } }, { M680X_INS_IDIVS, mmmm, { M680X_REG_D, M680X_REG_X, EOL } }, { M680X_INS_JSR, mmmm, { M680X_REG_S, EOL } }, { M680X_INS_LBSR, mmmm, { M680X_REG_S, EOL } }, { M680X_INS_MAXM, rrrr, { M680X_REG_A, EOL } }, { M680X_INS_MINM, rrrr, { M680X_REG_A, EOL } }, { M680X_INS_MEM, mmrr, { M680X_REG_X, M680X_REG_Y, M680X_REG_A, EOL } }, { M680X_INS_MUL, mmmm, { M680X_REG_A, M680X_REG_B, EOL } }, { M680X_INS_MULD, mwrr, { M680X_REG_D, M680X_REG_W, EOL } }, { M680X_INS_PSHA, rmmm, { M680X_REG_A, M680X_REG_S, EOL } }, { M680X_INS_PSHB, rmmm, { M680X_REG_B, M680X_REG_S, EOL } }, { M680X_INS_PSHC, rmmm, { M680X_REG_CC, M680X_REG_S, EOL } }, { M680X_INS_PSHD, rmmm, { M680X_REG_D, M680X_REG_S, EOL } }, { M680X_INS_PSHH, rmmm, { M680X_REG_H, M680X_REG_S, EOL } }, { M680X_INS_PSHX, rmmm, { M680X_REG_X, M680X_REG_S, EOL } }, { M680X_INS_PSHY, rmmm, { M680X_REG_Y, M680X_REG_S, EOL } }, { M680X_INS_PULA, wmmm, { M680X_REG_A, M680X_REG_S, EOL } }, { M680X_INS_PULB, wmmm, { M680X_REG_B, M680X_REG_S, EOL } }, { M680X_INS_PULC, wmmm, { M680X_REG_CC, M680X_REG_S, EOL } }, { M680X_INS_PULD, wmmm, { M680X_REG_D, M680X_REG_S, EOL } }, { M680X_INS_PULH, wmmm, { M680X_REG_H, M680X_REG_S, EOL } }, { M680X_INS_PULX, wmmm, { M680X_REG_X, M680X_REG_S, EOL } }, { M680X_INS_PULY, wmmm, { M680X_REG_Y, M680X_REG_S, EOL } }, { M680X_INS_REV, mmrr, { M680X_REG_A, M680X_REG_X, M680X_REG_Y, EOL } }, { M680X_INS_REVW, mmmm, { M680X_REG_A, M680X_REG_X, M680X_REG_Y, EOL } }, { M680X_INS_RTC, mwww, { M680X_REG_S, M680X_REG_PC, EOL } }, { M680X_INS_RTI, mwww, { M680X_REG_S, M680X_REG_CC, M680X_REG_B, M680X_REG_A, M680X_REG_DP, M680X_REG_X, M680X_REG_Y, M680X_REG_U, M680X_REG_PC, EOL }, }, { M680X_INS_RTS, mwww, { M680X_REG_S, M680X_REG_PC, EOL } }, { M680X_INS_SEX, wrrr, { M680X_REG_A, M680X_REG_B, EOL } }, { M680X_INS_SEXW, rwww, { M680X_REG_W, M680X_REG_D, EOL } }, { M680X_INS_SWI, mmrr, { M680X_REG_S, M680X_REG_PC, M680X_REG_U, M680X_REG_Y, M680X_REG_X, M680X_REG_DP, M680X_REG_A, M680X_REG_B, M680X_REG_CC, EOL } }, { M680X_INS_SWI2, mmrr, { M680X_REG_S, M680X_REG_PC, M680X_REG_U, M680X_REG_Y, M680X_REG_X, M680X_REG_DP, M680X_REG_A, M680X_REG_B, M680X_REG_CC, EOL }, }, { M680X_INS_SWI3, mmrr, { M680X_REG_S, M680X_REG_PC, M680X_REG_U, M680X_REG_Y, M680X_REG_X, M680X_REG_DP, M680X_REG_A, M680X_REG_B, M680X_REG_CC, EOL }, }, { M680X_INS_TBL, wrrr, { M680X_REG_A, M680X_REG_B, EOL } }, { M680X_INS_WAI, mrrr, { M680X_REG_S, M680X_REG_PC, M680X_REG_X, M680X_REG_A, M680X_REG_B, M680X_REG_CC, EOL } }, { M680X_INS_WAV, rmmm, { M680X_REG_A, M680X_REG_B, M680X_REG_X, M680X_REG_Y, EOL } }, { M680X_INS_WAVR, rmmm, { M680X_REG_A, M680X_REG_B, M680X_REG_X, M680X_REG_Y, EOL } }, }; int i, j; if (MI->flat_insn->detail == NULL) return; for (i = 0; i < ARR_SIZE(changed_regs); ++i) { if (info->insn == changed_regs[i].insn) { e_access_mode access_mode = changed_regs[i].access_mode; for (j = 0; changed_regs[i].regs[j] != EOL; ++j) { e_access access; m680x_reg reg = changed_regs[i].regs[j]; if (!info->cpu->reg_byte_size[reg]) { if (info->insn != M680X_INS_MUL) continue; // Hack for M68HC05: MUL uses reg. A,X reg = M680X_REG_X; } access = get_access(j, access_mode); add_reg_to_rw_list(MI, reg, access); } } } #undef EOL } typedef struct insn_desc { uint32_t opcode; m680x_insn insn; insn_hdlr_id hid[2]; uint16_t insn_size; } insn_desc; static bool is_indexed09_post_byte_valid(const m680x_info *info, uint16_t *address, uint8_t post_byte, insn_desc *insn_description) { uint8_t ir = 0; bool retval; switch (post_byte & 0x9F) { case 0x87: case 0x8A: case 0x8E: case 0x8F: case 0x90: case 0x92: case 0x97: case 0x9A: case 0x9E: return false; // illegal indexed post bytes case 0x88: // n8,R case 0x8C: // n8,PCR case 0x98: // [n8,R] case 0x9C: // [n8,PCR] insn_description->insn_size++; return read_byte(info, &ir, (*address)++); case 0x89: // n16,R case 0x8D: // n16,PCR case 0x99: // [n16,R] case 0x9D: // [n16,PCR] insn_description->insn_size += 2; retval = read_byte(info, &ir, *address + 1); *address += 2; return retval; case 0x9F: // [n] insn_description->insn_size += 2; retval = (post_byte & 0x60) == 0 && read_byte(info, &ir, *address + 1); *address += 2; return retval; } return true; // Any other indexed post byte is valid and // no additional bytes have to be read. } static bool is_indexed12_post_byte_valid(const m680x_info *info, uint16_t *address, uint8_t post_byte, insn_desc *insn_description, bool is_subset) { uint8_t ir; bool result; if (!(post_byte & 0x20)) // n5,R return true; switch (post_byte & 0xe7) { case 0xe0: case 0xe1: // n9,R if (is_subset) return false; insn_description->insn_size++; return read_byte(info, &ir, (*address)++); case 0xe2: // n16,R case 0xe3: // [n16,R] if (is_subset) return false; insn_description->insn_size += 2; result = read_byte(info, &ir, *address + 1); *address += 2; return result; case 0xe4: // A,R case 0xe5: // B,R case 0xe6: // D,R case 0xe7: // [D,R] default: // n,-r n,+r n,r- n,r+ break; } return true; } // Check for M6809/HD6309 TFR/EXG instruction for valid register static bool is_tfr09_reg_valid(const m680x_info *info, uint8_t reg_nibble) { if (info->cpu->tfr_reg_valid != NULL) return info->cpu->tfr_reg_valid[reg_nibble]; return true; // e.g. for the M6309 all registers are valid } // Check for CPU12 TFR/EXG instruction for valid register static bool is_exg_tfr12_post_byte_valid(const m680x_info *info, uint8_t post_byte) { return !(post_byte & 0x08); } static bool is_tfm_reg_valid(const m680x_info *info, uint8_t reg_nibble) { // HD6809 TFM instruction: Only register X,Y,U,S,D is allowed return reg_nibble <= 4; } static bool is_loop_post_byte_valid(const m680x_info *info, uint8_t post_byte) { // According to documentation bit 3 is don't care and not checked here. if (post_byte >= 0xc0) return false; return ((post_byte & 0x07) != 2 && ((post_byte & 0x07) != 3)); } static bool is_sufficient_code_size(const m680x_info *info, uint16_t address, insn_desc *insn_description) { int i; bool retval; for (i = 0; i < 2; i++) { uint8_t ir = 0; bool is_subset = false; switch (insn_description->hid[i]) { case imm32_hid: insn_description->insn_size += 4; retval = read_byte(info, &ir, address + 3); address += 4; break; case ext_hid: case imm16_hid: case rel16_hid: case imm8rel_hid: case opidxdr_hid: case idxX16_hid: case idxS16_hid: insn_description->insn_size += 2; retval = read_byte(info, &ir, address + 1); address += 2; break; case rel8_hid: case dir_hid: case rbits_hid: case imm8_hid: case idxX_hid: case idxXp_hid: case idxY_hid: case idxS_hid: case index_hid: insn_description->insn_size += 1; retval = read_byte(info, &ir, address++); break; case illgl_hid: case inh_hid: case idxX0_hid: case idxX0p_hid: case opidx_hid: retval = true; break; case idx09_hid: insn_description->insn_size += 1; if (!read_byte(info, &ir, address++)) retval = false; else retval = is_indexed09_post_byte_valid(info, &address, ir, insn_description); break; case idx12s_hid: is_subset = true; // intentionally fall through case idx12_hid: insn_description->insn_size += 1; if (!read_byte(info, &ir, address++)) retval = false; else retval = is_indexed12_post_byte_valid(info, &address, ir, insn_description, is_subset); break; case exti12x_hid: case imm16i12x_hid: insn_description->insn_size += 1; if (!read_byte(info, &ir, address++)) retval = false; else if (!is_indexed12_post_byte_valid(info, &address, ir, insn_description, false)) retval = false; else { insn_description->insn_size += 2; retval = read_byte(info, &ir, address + 1); address += 2; } break; case imm8i12x_hid: insn_description->insn_size += 1; if (!read_byte(info, &ir, address++)) retval = false; else if (!is_indexed12_post_byte_valid(info, &address, ir, insn_description, false)) retval = false; else { insn_description->insn_size += 1; retval = read_byte(info, &ir, address++); } break; case tfm_hid: insn_description->insn_size += 1; if (!read_byte(info, &ir, address++)) retval = false; else retval = is_tfm_reg_valid(info, (ir >> 4) & 0x0F) && is_tfm_reg_valid(info, ir & 0x0F); break; case rr09_hid: insn_description->insn_size += 1; if (!read_byte(info, &ir, address++)) retval = false; else retval = is_tfr09_reg_valid(info, (ir >> 4) & 0x0F) && is_tfr09_reg_valid(info, ir & 0x0F); break; case rr12_hid: insn_description->insn_size += 1; if (!read_byte(info, &ir, address++)) retval = false; else retval = is_exg_tfr12_post_byte_valid(info, ir); break; case bitmv_hid: insn_description->insn_size += 2; if (!read_byte(info, &ir, address++)) retval = false; else if ((ir & 0xc0) == 0xc0) retval = false; // Invalid register specified else retval = read_byte(info, &ir, address++); break; case loop_hid: insn_description->insn_size += 2; if (!read_byte(info, &ir, address++)) retval = false; else if (!is_loop_post_byte_valid(info, ir)) retval = false; else retval = read_byte(info, &ir, address++); break; default: fprintf(stderr, "Internal error: Unexpected instruction " "handler id %d\n", insn_description->hid[i]); retval = false; break; } if (!retval) return false; } return retval; } // Check for a valid M680X instruction AND for enough bytes in the code buffer // Return an instruction description in insn_desc. static bool decode_insn(const m680x_info *info, uint16_t address, insn_desc *insn_description) { const inst_pageX *inst_table = NULL; const cpu_tables *cpu = info->cpu; int table_size = 0; uint16_t base_address = address; uint8_t ir; // instruction register int i; int index; if (!read_byte(info, &ir, address++)) return false; insn_description->insn = M680X_INS_ILLGL; insn_description->opcode = ir; // Check if a page prefix byte is present for (i = 0; i < ARR_SIZE(cpu->pageX_table_size); ++i) { if (cpu->pageX_table_size[i] == 0 || (cpu->inst_pageX_table[i] == NULL)) break; if ((cpu->pageX_prefix[i] == ir)) { // Get pageX instruction and handler id. // Abort for illegal instr. inst_table = cpu->inst_pageX_table[i]; table_size = cpu->pageX_table_size[i]; if (!read_byte(info, &ir, address++)) return false; insn_description->opcode = (insn_description->opcode << 8) | ir; if ((index = binary_search(inst_table, table_size, ir)) < 0) return false; insn_description->hid[0] = inst_table[index].handler_id1; insn_description->hid[1] = inst_table[index].handler_id2; insn_description->insn = inst_table[index].insn; break; } } if (insn_description->insn == M680X_INS_ILLGL) { // Get page1 insn description insn_description->insn = cpu->inst_page1_table[ir].insn; insn_description->hid[0] = cpu->inst_page1_table[ir].handler_id1; insn_description->hid[1] = cpu->inst_page1_table[ir].handler_id2; } if (insn_description->insn == M680X_INS_ILLGL) { // Check if opcode byte is present in an overlay table for (i = 0; i < ARR_SIZE(cpu->overlay_table_size); ++i) { if (cpu->overlay_table_size[i] == 0 || (cpu->inst_overlay_table[i] == NULL)) break; inst_table = cpu->inst_overlay_table[i]; table_size = cpu->overlay_table_size[i]; if ((index = binary_search(inst_table, table_size, ir)) >= 0) { insn_description->hid[0] = inst_table[index].handler_id1; insn_description->hid[1] = inst_table[index].handler_id2; insn_description->insn = inst_table[index].insn; break; } } } insn_description->insn_size = address - base_address; return (insn_description->insn != M680X_INS_ILLGL) && (insn_description->insn != M680X_INS_INVLD) && is_sufficient_code_size(info, address, insn_description); } static void illegal_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { cs_m680x_op *op0 = &info->m680x.operands[info->m680x.op_count++]; uint8_t temp8 = 0; info->insn = M680X_INS_ILLGL; read_byte(info, &temp8, (*address)++); op0->imm = (int32_t)temp8 & 0xff; op0->type = M680X_OP_IMMEDIATE; op0->size = 1; } static void inherent_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { // There is nothing to do here :-) } static void add_reg_operand(m680x_info *info, m680x_reg reg) { cs_m680x *m680x = &info->m680x; cs_m680x_op *op = &m680x->operands[m680x->op_count++]; op->type = M680X_OP_REGISTER; op->reg = reg; op->size = info->cpu->reg_byte_size[reg]; } static void set_operand_size(m680x_info *info, cs_m680x_op *op, uint8_t default_size) { cs_m680x *m680x = &info->m680x; if (info->insn == M680X_INS_JMP || info->insn == M680X_INS_JSR) op->size = 0; else if (info->insn == M680X_INS_DIVD || ((info->insn == M680X_INS_AIS || info->insn == M680X_INS_AIX) && op->type != M680X_OP_REGISTER)) op->size = 1; else if (info->insn == M680X_INS_DIVQ || info->insn == M680X_INS_MOVW) op->size = 2; else if (info->insn == M680X_INS_EMACS) op->size = 4; else if ((m680x->op_count > 0) && (m680x->operands[0].type == M680X_OP_REGISTER)) op->size = m680x->operands[0].size; else op->size = default_size; } static const m680x_reg reg_s_reg_ids[] = { M680X_REG_CC, M680X_REG_A, M680X_REG_B, M680X_REG_DP, M680X_REG_X, M680X_REG_Y, M680X_REG_U, M680X_REG_PC, }; static const m680x_reg reg_u_reg_ids[] = { M680X_REG_CC, M680X_REG_A, M680X_REG_B, M680X_REG_DP, M680X_REG_X, M680X_REG_Y, M680X_REG_S, M680X_REG_PC, }; static void reg_bits_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { cs_m680x_op *op0 = &info->m680x.operands[0]; uint8_t reg_bits = 0; uint16_t bit_index; const m680x_reg *reg_to_reg_ids; read_byte(info, ®_bits, (*address)++); switch (op0->reg) { case M680X_REG_U: reg_to_reg_ids = ®_u_reg_ids[0]; break; case M680X_REG_S: reg_to_reg_ids = ®_s_reg_ids[0]; break; default: fprintf(stderr, "Internal error: Unexpected operand0 register " "%d\n", op0->reg); abort(); } if ((info->insn == M680X_INS_PULU || (info->insn == M680X_INS_PULS)) && ((reg_bits & 0x80) != 0)) // PULS xxx,PC or PULU xxx,PC which is like return from // subroutine (RTS) add_insn_group(MI->flat_insn->detail, M680X_GRP_RET); for (bit_index = 0; bit_index < 8; ++bit_index) { if (reg_bits & (1 << bit_index)) add_reg_operand(info, reg_to_reg_ids[bit_index]); } } static const m680x_reg g_tfr_exg_reg_ids[] = { /* 16-bit registers */ M680X_REG_D, M680X_REG_X, M680X_REG_Y, M680X_REG_U, M680X_REG_S, M680X_REG_PC, M680X_REG_W, M680X_REG_V, /* 8-bit registers */ M680X_REG_A, M680X_REG_B, M680X_REG_CC, M680X_REG_DP, M680X_REG_0, M680X_REG_0, M680X_REG_E, M680X_REG_F, }; static void reg_reg09_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { uint8_t regs = 0; read_byte(info, ®s, (*address)++); add_reg_operand(info, g_tfr_exg_reg_ids[regs >> 4]); add_reg_operand(info, g_tfr_exg_reg_ids[regs & 0x0f]); if ((regs & 0x0f) == 0x05) { // EXG xxx,PC or TFR xxx,PC which is like a JMP add_insn_group(MI->flat_insn->detail, M680X_GRP_JUMP); } } static void reg_reg12_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { static const m680x_reg g_tfr_exg12_reg0_ids[] = { M680X_REG_A, M680X_REG_B, M680X_REG_CC, M680X_REG_TMP3, M680X_REG_D, M680X_REG_X, M680X_REG_Y, M680X_REG_S, }; static const m680x_reg g_tfr_exg12_reg1_ids[] = { M680X_REG_A, M680X_REG_B, M680X_REG_CC, M680X_REG_TMP2, M680X_REG_D, M680X_REG_X, M680X_REG_Y, M680X_REG_S, }; uint8_t regs = 0; read_byte(info, ®s, (*address)++); // The opcode of this instruction depends on // the msb of its post byte. if (regs & 0x80) info->insn = M680X_INS_EXG; else info->insn = M680X_INS_TFR; add_reg_operand(info, g_tfr_exg12_reg0_ids[(regs >> 4) & 0x07]); add_reg_operand(info, g_tfr_exg12_reg1_ids[regs & 0x07]); } static void add_rel_operand(m680x_info *info, int16_t offset, uint16_t address) { cs_m680x *m680x = &info->m680x; cs_m680x_op *op = &m680x->operands[m680x->op_count++]; op->type = M680X_OP_RELATIVE; op->size = 0; op->rel.offset = offset; op->rel.address = address; } static void relative8_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { int16_t offset = 0; read_byte_sign_extended(info, &offset, (*address)++); add_rel_operand(info, offset, *address + offset); add_insn_group(MI->flat_insn->detail, M680X_GRP_BRAREL); if ((info->insn != M680X_INS_BRA) && (info->insn != M680X_INS_BSR) && (info->insn != M680X_INS_BRN)) add_reg_to_rw_list(MI, M680X_REG_CC, READ); } static void relative16_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { uint16_t offset = 0; read_word(info, &offset, *address); *address += 2; add_rel_operand(info, (int16_t)offset, *address + offset); add_insn_group(MI->flat_insn->detail, M680X_GRP_BRAREL); if ((info->insn != M680X_INS_LBRA) && (info->insn != M680X_INS_LBSR) && (info->insn != M680X_INS_LBRN)) add_reg_to_rw_list(MI, M680X_REG_CC, READ); } static const m680x_reg g_rr5_to_reg_ids[] = { M680X_REG_X, M680X_REG_Y, M680X_REG_U, M680X_REG_S, }; static void add_indexed_operand(m680x_info *info, m680x_reg base_reg, bool post_inc_dec, uint8_t inc_dec, uint8_t offset_bits, uint16_t offset, bool no_comma) { cs_m680x *m680x = &info->m680x; cs_m680x_op *op = &m680x->operands[m680x->op_count++]; op->type = M680X_OP_INDEXED; set_operand_size(info, op, 1); op->idx.base_reg = base_reg; op->idx.offset_reg = M680X_REG_INVALID; op->idx.inc_dec = inc_dec; if (inc_dec && post_inc_dec) op->idx.flags |= M680X_IDX_POST_INC_DEC; if (offset_bits != M680X_OFFSET_NONE) { op->idx.offset = offset; op->idx.offset_addr = 0; } op->idx.offset_bits = offset_bits; op->idx.flags |= (no_comma ? M680X_IDX_NO_COMMA : 0); } // M6800/1/2/3 indexed mode handler static void indexedX_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { uint8_t offset = 0; read_byte(info, &offset, (*address)++); add_indexed_operand(info, M680X_REG_X, false, 0, M680X_OFFSET_BITS_8, (uint16_t)offset, false); } static void indexedY_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { uint8_t offset = 0; read_byte(info, &offset, (*address)++); add_indexed_operand(info, M680X_REG_Y, false, 0, M680X_OFFSET_BITS_8, (uint16_t)offset, false); } // M6809/M6309 indexed mode handler static void indexed09_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { cs_m680x *m680x = &info->m680x; cs_m680x_op *op = &m680x->operands[m680x->op_count++]; uint8_t post_byte = 0; uint16_t offset = 0; int16_t soffset = 0; read_byte(info, &post_byte, (*address)++); op->type = M680X_OP_INDEXED; set_operand_size(info, op, 1); op->idx.base_reg = g_rr5_to_reg_ids[(post_byte >> 5) & 0x03]; op->idx.offset_reg = M680X_REG_INVALID; if (!(post_byte & 0x80)) { // n5,R if ((post_byte & 0x10) == 0x10) op->idx.offset = post_byte | 0xfff0; else op->idx.offset = post_byte & 0x0f; op->idx.offset_addr = op->idx.offset + *address; op->idx.offset_bits = M680X_OFFSET_BITS_5; } else { if ((post_byte & 0x10) == 0x10) op->idx.flags |= M680X_IDX_INDIRECT; // indexed addressing switch (post_byte & 0x1f) { case 0x00: // ,R+ op->idx.inc_dec = 1; op->idx.flags |= M680X_IDX_POST_INC_DEC; break; case 0x11: // [,R++] case 0x01: // ,R++ op->idx.inc_dec = 2; op->idx.flags |= M680X_IDX_POST_INC_DEC; break; case 0x02: // ,-R op->idx.inc_dec = -1; break; case 0x13: // [,--R] case 0x03: // ,--R op->idx.inc_dec = -2; break; case 0x14: // [,R] case 0x04: // ,R break; case 0x15: // [B,R] case 0x05: // B,R op->idx.offset_reg = M680X_REG_B; break; case 0x16: // [A,R] case 0x06: // A,R op->idx.offset_reg = M680X_REG_A; break; case 0x1c: // [n8,PCR] case 0x0c: // n8,PCR op->idx.base_reg = M680X_REG_PC; read_byte_sign_extended(info, &soffset, (*address)++); op->idx.offset_addr = offset + *address; op->idx.offset = soffset; op->idx.offset_bits = M680X_OFFSET_BITS_8; break; case 0x18: // [n8,R] case 0x08: // n8,R read_byte_sign_extended(info, &soffset, (*address)++); op->idx.offset = soffset; op->idx.offset_bits = M680X_OFFSET_BITS_8; break; case 0x1d: // [n16,PCR] case 0x0d: // n16,PCR op->idx.base_reg = M680X_REG_PC; read_word(info, &offset, *address); *address += 2; op->idx.offset_addr = offset + *address; op->idx.offset = (int16_t)offset; op->idx.offset_bits = M680X_OFFSET_BITS_16; break; case 0x19: // [n16,R] case 0x09: // n16,R read_word(info, &offset, *address); *address += 2; op->idx.offset = (int16_t)offset; op->idx.offset_bits = M680X_OFFSET_BITS_16; break; case 0x1b: // [D,R] case 0x0b: // D,R op->idx.offset_reg = M680X_REG_D; break; case 0x1f: // [n16] op->type = M680X_OP_EXTENDED; op->ext.indirect = true; read_word(info, &op->ext.address, *address); *address += 2; break; default: op->idx.base_reg = M680X_REG_INVALID; break; } } if (((info->insn == M680X_INS_LEAU) || (info->insn == M680X_INS_LEAS) || (info->insn == M680X_INS_LEAX) || (info->insn == M680X_INS_LEAY)) && (m680x->operands[0].reg == M680X_REG_X || (m680x->operands[0].reg == M680X_REG_Y))) // Only LEAX and LEAY modify CC register add_reg_to_rw_list(MI, M680X_REG_CC, MODIFY); } m680x_reg g_idx12_to_reg_ids[4] = { M680X_REG_X, M680X_REG_Y, M680X_REG_S, M680X_REG_PC, }; m680x_reg g_or12_to_reg_ids[3] = { M680X_REG_A, M680X_REG_B, M680X_REG_D }; // CPU12 indexed mode handler static void indexed12_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { cs_m680x *m680x = &info->m680x; cs_m680x_op *op = &m680x->operands[m680x->op_count++]; uint8_t post_byte = 0; uint8_t offset8 = 0; read_byte(info, &post_byte, (*address)++); op->type = M680X_OP_INDEXED; set_operand_size(info, op, 1); op->idx.offset_reg = M680X_REG_INVALID; if (!(post_byte & 0x20)) { // n5,R n5 is a 5-bit signed offset op->idx.base_reg = g_idx12_to_reg_ids[(post_byte >> 6) & 0x03]; if ((post_byte & 0x10) == 0x10) op->idx.offset = post_byte | 0xfff0; else op->idx.offset = post_byte & 0x0f; op->idx.offset_addr = op->idx.offset + *address; op->idx.offset_bits = M680X_OFFSET_BITS_5; } else { if ((post_byte & 0xe0) == 0xe0) op->idx.base_reg = g_idx12_to_reg_ids[(post_byte >> 3) & 0x03]; switch (post_byte & 0xe7) { case 0xe0: case 0xe1: // n9,R read_byte(info, &offset8, (*address)++); op->idx.offset = offset8; if (post_byte & 0x01) // sign extension op->idx.offset |= 0xff00; op->idx.offset_bits = M680X_OFFSET_BITS_9; if (op->idx.base_reg == M680X_REG_PC) op->idx.offset_addr = op->idx.offset + *address; break; case 0xe3: // [n16,R] op->idx.flags |= M680X_IDX_INDIRECT; // intentionally fall through case 0xe2: // n16,R read_word(info, (uint16_t *)&op->idx.offset, *address); (*address) += 2; op->idx.offset_bits = M680X_OFFSET_BITS_16; if (op->idx.base_reg == M680X_REG_PC) op->idx.offset_addr = op->idx.offset + *address; break; case 0xe4: // A,R case 0xe5: // B,R case 0xe6: // D,R op->idx.offset_reg = g_or12_to_reg_ids[post_byte & 0x03]; break; case 0xe7: // [D,R] op->idx.offset_reg = M680X_REG_D; op->idx.flags |= M680X_IDX_INDIRECT; break; default: // n,-r n,+r n,r- n,r+ // PC is not allowed in this mode op->idx.base_reg = g_idx12_to_reg_ids[(post_byte >> 6) & 0x03]; op->idx.inc_dec = post_byte & 0x0f; if (op->idx.inc_dec & 0x08) // evtl. sign extend value op->idx.inc_dec |= 0xf0; if (op->idx.inc_dec >= 0) op->idx.inc_dec++; if (post_byte & 0x10) op->idx.flags |= M680X_IDX_POST_INC_DEC; break; } } } static void index_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { cs_m680x *m680x = &info->m680x; cs_m680x_op *op = &m680x->operands[m680x->op_count++]; op->type = M680X_OP_CONSTANT; read_byte(info, &op->const_val, (*address)++); }; static void direct_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { cs_m680x *m680x = &info->m680x; cs_m680x_op *op = &m680x->operands[m680x->op_count++]; op->type = M680X_OP_DIRECT; set_operand_size(info, op, 1); read_byte(info, &op->direct_addr, (*address)++); }; static void extended_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { cs_m680x *m680x = &info->m680x; cs_m680x_op *op = &m680x->operands[m680x->op_count++]; op->type = M680X_OP_EXTENDED; set_operand_size(info, op, 1); read_word(info, &op->ext.address, *address); *address += 2; } static void immediate_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { cs_m680x *m680x = &info->m680x; cs_m680x_op *op = &m680x->operands[m680x->op_count++]; uint16_t word = 0; int16_t sword = 0; op->type = M680X_OP_IMMEDIATE; set_operand_size(info, op, 1); switch (op->size) { case 1: read_byte_sign_extended(info, &sword, *address); op->imm = sword; break; case 2: read_word(info, &word, *address); op->imm = (int16_t)word; break; case 4: read_sdword(info, &op->imm, *address); break; default: op->imm = 0; fprintf(stderr, "Internal error: Unexpected immediate byte " "size %d.\n", op->size); } *address += op->size; } // handler for bit move instructions, e.g: BAND A,5,1,$40 Used by HD6309 static void bit_move_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { static const m680x_reg m680x_reg[] = { M680X_REG_CC, M680X_REG_A, M680X_REG_B, M680X_REG_INVALID, }; uint8_t post_byte = 0; cs_m680x *m680x = &info->m680x; cs_m680x_op *op; read_byte(info, &post_byte, *address); (*address)++; // operand[0] = register add_reg_operand(info, m680x_reg[post_byte >> 6]); // operand[1] = bit index in source operand op = &m680x->operands[m680x->op_count++]; op->type = M680X_OP_CONSTANT; op->const_val = (post_byte >> 3) & 0x07; // operand[2] = bit index in destination operand op = &m680x->operands[m680x->op_count++]; op->type = M680X_OP_CONSTANT; op->const_val = post_byte & 0x07; direct_hdlr(MI, info, address); } // handler for TFM instruction, e.g: TFM X+,Y+ Used by HD6309 static void tfm_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { static const uint8_t inc_dec_r0[] = { 1, -1, 1, 0, }; static const uint8_t inc_dec_r1[] = { 1, -1, 0, 1, }; uint8_t regs = 0; uint8_t index = (MI->Opcode & 0xff) - 0x38; read_byte(info, ®s, *address); add_indexed_operand(info, g_tfr_exg_reg_ids[regs >> 4], true, inc_dec_r0[index], M680X_OFFSET_NONE, 0, true); add_indexed_operand(info, g_tfr_exg_reg_ids[regs & 0x0f], true, inc_dec_r1[index], M680X_OFFSET_NONE, 0, true); add_reg_to_rw_list(MI, M680X_REG_W, READ | WRITE); } static void opidx_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { cs_m680x *m680x = &info->m680x; cs_m680x_op *op = &m680x->operands[m680x->op_count++]; // bit index is coded in Opcode op->type = M680X_OP_CONSTANT; op->const_val = (MI->Opcode & 0x0e) >> 1; } // handler for bit test and branch instruction. Used by M6805. // The bit index is part of the opcode. // Example: BRSET 3,<$40,LOOP static void opidx_dir_rel_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { cs_m680x *m680x = &info->m680x; cs_m680x_op *op = &m680x->operands[m680x->op_count++]; // bit index is coded in Opcode op->type = M680X_OP_CONSTANT; op->const_val = (MI->Opcode & 0x0e) >> 1; direct_hdlr(MI, info, address); relative8_hdlr(MI, info, address); add_reg_to_rw_list(MI, M680X_REG_CC, MODIFY); } static void indexedX0_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { add_indexed_operand(info, M680X_REG_X, false, 0, M680X_OFFSET_NONE, 0, false); } static void indexedX16_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { uint16_t offset = 0; read_word(info, &offset, *address); *address += 2; add_indexed_operand(info, M680X_REG_X, false, 0, M680X_OFFSET_BITS_16, offset, false); } static void imm_rel_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { immediate_hdlr(MI, info, address); relative8_hdlr(MI, info, address); } static void indexedS_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { uint8_t offset = 0; read_byte(info, &offset, (*address)++); add_indexed_operand(info, M680X_REG_S, false, 0, M680X_OFFSET_BITS_8, (uint16_t)offset, false); } static void indexedS16_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { uint16_t offset = 0; read_word(info, &offset, *address); address += 2; add_indexed_operand(info, M680X_REG_S, false, 0, M680X_OFFSET_BITS_16, offset, false); } static void indexedX0p_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { add_indexed_operand(info, M680X_REG_X, true, 1, M680X_OFFSET_NONE, 0, true); } static void indexedXp_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { uint8_t offset = 0; read_byte(info, &offset, (*address)++); add_indexed_operand(info, M680X_REG_X, true, 1, M680X_OFFSET_BITS_8, (uint16_t)offset, false); } static void imm_idx12_x_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { cs_m680x *m680x = &info->m680x; cs_m680x_op *op = &m680x->operands[m680x->op_count++]; indexed12_hdlr(MI, info, address); op->type = M680X_OP_IMMEDIATE; if (info->insn == M680X_INS_MOVW) { uint16_t imm16 = 0; read_word(info, &imm16, *address); op->imm = (int16_t)imm16; op->size = 2; } else { uint8_t imm8 = 0; read_byte(info, &imm8, *address); op->imm = (int8_t)imm8; op->size = 1; } set_operand_size(info, op, 1); } static void ext_idx12_x_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { cs_m680x *m680x = &info->m680x; cs_m680x_op *op0 = &m680x->operands[m680x->op_count++]; uint16_t imm16 = 0; indexed12_hdlr(MI, info, address); read_word(info, &imm16, *address); op0->type = M680X_OP_EXTENDED; op0->ext.address = (int16_t)imm16; set_operand_size(info, op0, 1); } // handler for CPU12 DBEQ/DNBE/IBEQ/IBNE/TBEQ/TBNE instructions. // Example: DBNE X,$1000 static void loop_hdlr(MCInst *MI, m680x_info *info, uint16_t *address) { static const m680x_reg index_to_reg_id[] = { M680X_REG_A, M680X_REG_B, M680X_REG_INVALID, M680X_REG_INVALID, M680X_REG_D, M680X_REG_X, M680X_REG_Y, M680X_REG_S, }; static const m680x_insn index_to_insn_id[] = { M680X_INS_DBEQ, M680X_INS_DBNE, M680X_INS_TBEQ, M680X_INS_TBNE, M680X_INS_IBEQ, M680X_INS_IBNE, M680X_INS_ILLGL, M680X_INS_ILLGL }; cs_m680x *m680x = &info->m680x; uint8_t post_byte = 0; uint8_t rel = 0; cs_m680x_op *op; read_byte(info, &post_byte, (*address)++); info->insn = index_to_insn_id[(post_byte >> 5) & 0x07]; if (info->insn == M680X_INS_ILLGL) { fprintf(stderr, "Internal error: Unexpected post byte " "in loop instruction %02X.\n", post_byte); illegal_hdlr(MI, info, address); }; read_byte(info, &rel, (*address)++); add_reg_operand(info, index_to_reg_id[post_byte & 0x07]); op = &m680x->operands[m680x->op_count++]; op->type = M680X_OP_RELATIVE; op->rel.offset = (post_byte & 0x10) ? 0xff00 | rel : rel; op->rel.address = *address + op->rel.offset; add_insn_group(MI->flat_insn->detail, M680X_GRP_BRAREL); } static void (*const g_insn_handler[])(MCInst *, m680x_info *, uint16_t *) = { illegal_hdlr, relative8_hdlr, relative16_hdlr, immediate_hdlr, // 8-bit immediate_hdlr, // 16-bit immediate_hdlr, // 32-bit direct_hdlr, extended_hdlr, indexedX_hdlr, indexedY_hdlr, indexed09_hdlr, inherent_hdlr, reg_reg09_hdlr, reg_bits_hdlr, bit_move_hdlr, tfm_hdlr, opidx_hdlr, opidx_dir_rel_hdlr, indexedX0_hdlr, indexedX16_hdlr, imm_rel_hdlr, indexedS_hdlr, indexedS16_hdlr, indexedXp_hdlr, indexedX0p_hdlr, indexed12_hdlr, indexed12_hdlr, // subset of indexed12 reg_reg12_hdlr, loop_hdlr, index_hdlr, imm_idx12_x_hdlr, imm_idx12_x_hdlr, ext_idx12_x_hdlr, }; /* handler function pointers */ /* Disasemble one instruction at address and store in str_buff */ static unsigned int m680x_disassemble(MCInst *MI, m680x_info *info, uint16_t address) { cs_m680x *m680x = &info->m680x; cs_detail *detail = MI->flat_insn->detail; uint16_t base_address = address; insn_desc insn_description; e_access_mode access_mode; if (detail != NULL) { memset(detail, 0, offsetof(cs_detail, m680x)+sizeof(cs_m680x)); } memset(&insn_description, 0, sizeof(insn_description)); memset(m680x, 0, sizeof(*m680x)); info->insn_size = 1; if (decode_insn(info, address, &insn_description)) { m680x_reg reg; if (insn_description.opcode > 0xff) address += 2; // 8-bit opcode + page prefix else address++; // 8-bit opcode only info->insn = insn_description.insn; MCInst_setOpcode(MI, insn_description.opcode); reg = g_insn_props[info->insn].reg0; if (reg != M680X_REG_INVALID) { if (reg == M680X_REG_HX && (!info->cpu->reg_byte_size[reg])) reg = M680X_REG_X; add_reg_operand(info, reg); // First (or second) operand is a register which is // part of the mnemonic m680x->flags |= M680X_FIRST_OP_IN_MNEM; reg = g_insn_props[info->insn].reg1; if (reg != M680X_REG_INVALID) { if (reg == M680X_REG_HX && (!info->cpu->reg_byte_size[reg])) reg = M680X_REG_X; add_reg_operand(info, reg); m680x->flags |= M680X_SECOND_OP_IN_MNEM; } } // Call addressing mode specific instruction handler (g_insn_handler[insn_description.hid[0]])(MI, info, &address); (g_insn_handler[insn_description.hid[1]])(MI, info, &address); add_insn_group(detail, g_insn_props[info->insn].group); if (g_insn_props[info->insn].cc_modified && (info->cpu->insn_cc_not_modified[0] != info->insn) && (info->cpu->insn_cc_not_modified[1] != info->insn)) add_reg_to_rw_list(MI, M680X_REG_CC, MODIFY); access_mode = g_insn_props[info->insn].access_mode; // Fix for M6805 BSET/BCLR. It has a differnt operand order // in comparison to the M6811 if ((info->cpu->insn_cc_not_modified[0] == info->insn) || (info->cpu->insn_cc_not_modified[1] == info->insn)) access_mode = rmmm; build_regs_read_write_counts(MI, info, access_mode); add_operators_access(MI, info, access_mode); if (g_insn_props[info->insn].update_reg_access) set_changed_regs_read_write_counts(MI, info); info->insn_size = insn_description.insn_size; return info->insn_size; } else MCInst_setOpcode(MI, insn_description.opcode); // Illegal instruction address = base_address; illegal_hdlr(MI, info, &address); return 1; } // Tables to get the byte size of a register on the CPU // based on an enum m680x_reg value. // Invalid registers return 0. static const uint8_t g_m6800_reg_byte_size[22] = { // A B E F 0 D W CC DP MD HX H X Y S U V Q PC T2 T3 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 2, 0, 0 }; static const uint8_t g_m6805_reg_byte_size[22] = { // A B E F 0 D W CC DP MD HX H X Y S U V Q PC T2 T3 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 2, 0, 0 }; static const uint8_t g_m6808_reg_byte_size[22] = { // A B E F 0 D W CC DP MD HX H X Y S U V Q PC T2 T3 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 1, 1, 0, 2, 0, 0, 0, 2, 0, 0 }; static const uint8_t g_m6801_reg_byte_size[22] = { // A B E F 0 D W CC DP MD HX H X Y S U V Q PC T2 T3 0, 1, 1, 0, 0, 0, 2, 0, 1, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 2, 0, 0 }; static const uint8_t g_m6811_reg_byte_size[22] = { // A B E F 0 D W CC DP MD HX H X Y S U V Q PC T2 T3 0, 1, 1, 0, 0, 0, 2, 0, 1, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 2, 0, 0 }; static const uint8_t g_cpu12_reg_byte_size[22] = { // A B E F 0 D W CC DP MD HX H X Y S U V Q PC T2 T3 0, 1, 1, 0, 0, 0, 2, 0, 1, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 2, 2, 2 }; static const uint8_t g_m6809_reg_byte_size[22] = { // A B E F 0 D W CC DP MD HX H X Y S U V Q PC T2 T3 0, 1, 1, 0, 0, 0, 2, 0, 1, 1, 0, 0, 0, 2, 2, 2, 2, 0, 0, 2, 0, 0 }; static const uint8_t g_hd6309_reg_byte_size[22] = { // A B E F 0 D W CC DP MD HX H X Y S U V Q PC T2 T3 0, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 0, 0, 2, 2, 2, 2, 2, 4, 2, 0, 0 }; // Table to check for a valid register nibble on the M6809 CPU // used for TFR and EXG instruction. static const bool m6809_tfr_reg_valid[16] = { true, true, true, true, true, true, false, false, true, true, true, true, false, false, false, false, }; static const cpu_tables g_cpu_tables[] = { { // M680X_CPU_TYPE_INVALID NULL, { NULL, NULL }, { 0, 0 }, { 0x00, 0x00, 0x00 }, { NULL, NULL, NULL }, { 0, 0, 0 }, NULL, NULL, { M680X_INS_INVLD, M680X_INS_INVLD } }, { // M680X_CPU_TYPE_6301 &g_m6800_inst_page1_table[0], { &g_m6801_inst_overlay_table[0], &g_hd6301_inst_overlay_table[0] }, { ARR_SIZE(g_m6801_inst_overlay_table), ARR_SIZE(g_hd6301_inst_overlay_table) }, { 0x00, 0x00, 0x00 }, { NULL, NULL, NULL }, { 0, 0, 0 }, &g_m6801_reg_byte_size[0], NULL, { M680X_INS_INVLD, M680X_INS_INVLD } }, { // M680X_CPU_TYPE_6309 &g_m6809_inst_page1_table[0], { &g_hd6309_inst_overlay_table[0], NULL }, { ARR_SIZE(g_hd6309_inst_overlay_table), 0 }, { 0x10, 0x11, 0x00 }, { &g_hd6309_inst_page2_table[0], &g_hd6309_inst_page3_table[0], NULL }, { ARR_SIZE(g_hd6309_inst_page2_table), ARR_SIZE(g_hd6309_inst_page3_table), 0 }, &g_hd6309_reg_byte_size[0], NULL, { M680X_INS_INVLD, M680X_INS_INVLD } }, { // M680X_CPU_TYPE_6800 &g_m6800_inst_page1_table[0], { NULL, NULL }, { 0, 0 }, { 0x00, 0x00, 0x00 }, { NULL, NULL, NULL }, { 0, 0, 0 }, &g_m6800_reg_byte_size[0], NULL, { M680X_INS_INVLD, M680X_INS_INVLD } }, { // M680X_CPU_TYPE_6801 &g_m6800_inst_page1_table[0], { &g_m6801_inst_overlay_table[0], NULL }, { ARR_SIZE(g_m6801_inst_overlay_table), 0 }, { 0x00, 0x00, 0x00 }, { NULL, NULL, NULL }, { 0, 0, 0 }, &g_m6801_reg_byte_size[0], NULL, { M680X_INS_INVLD, M680X_INS_INVLD } }, { // M680X_CPU_TYPE_6805 &g_m6805_inst_page1_table[0], { NULL, NULL }, { 0, 0 }, { 0x00, 0x00, 0x00 }, { NULL, NULL, NULL }, { 0, 0, 0 }, &g_m6805_reg_byte_size[0], NULL, { M680X_INS_BCLR, M680X_INS_BSET } }, { // M680X_CPU_TYPE_6808 &g_m6805_inst_page1_table[0], { &g_m6808_inst_overlay_table[0], NULL }, { ARR_SIZE(g_m6808_inst_overlay_table), 0 }, { 0x9E, 0x00, 0x00 }, { &g_m6808_inst_page2_table[0], NULL, NULL }, { ARR_SIZE(g_m6808_inst_page2_table), 0, 0 }, &g_m6808_reg_byte_size[0], NULL, { M680X_INS_BCLR, M680X_INS_BSET } }, { // M680X_CPU_TYPE_6809 &g_m6809_inst_page1_table[0], { NULL, NULL }, { 0, 0 }, { 0x10, 0x11, 0x00 }, { &g_m6809_inst_page2_table[0], &g_m6809_inst_page3_table[0], NULL }, { ARR_SIZE(g_m6809_inst_page2_table), ARR_SIZE(g_m6809_inst_page3_table), 0 }, &g_m6809_reg_byte_size[0], &m6809_tfr_reg_valid[0], { M680X_INS_INVLD, M680X_INS_INVLD } }, { // M680X_CPU_TYPE_6811 &g_m6800_inst_page1_table[0], { &g_m6801_inst_overlay_table[0], &g_m6811_inst_overlay_table[0] }, { ARR_SIZE(g_m6801_inst_overlay_table), ARR_SIZE(g_m6811_inst_overlay_table) }, { 0x18, 0x1A, 0xCD }, { &g_m6811_inst_page2_table[0], &g_m6811_inst_page3_table[0], &g_m6811_inst_page4_table[0] }, { ARR_SIZE(g_m6811_inst_page2_table), ARR_SIZE(g_m6811_inst_page3_table), ARR_SIZE(g_m6811_inst_page4_table) }, &g_m6811_reg_byte_size[0], NULL, { M680X_INS_INVLD, M680X_INS_INVLD } }, { // M680X_CPU_TYPE_CPU12 &g_cpu12_inst_page1_table[0], { NULL, NULL }, { 0, 0 }, { 0x18, 0x00, 0x00 }, { &g_cpu12_inst_page2_table[0], NULL, NULL }, { ARR_SIZE(g_cpu12_inst_page2_table), 0, 0 }, &g_cpu12_reg_byte_size[0], NULL, { M680X_INS_INVLD, M680X_INS_INVLD } }, { // M680X_CPU_TYPE_HCS08 &g_m6805_inst_page1_table[0], { &g_m6808_inst_overlay_table[0], &g_hcs08_inst_overlay_table[0] }, { ARR_SIZE(g_m6808_inst_overlay_table), ARR_SIZE(g_hcs08_inst_overlay_table) }, { 0x9E, 0x00, 0x00 }, { &g_hcs08_inst_page2_table[0], NULL, NULL }, { ARR_SIZE(g_hcs08_inst_page2_table), 0, 0 }, &g_m6808_reg_byte_size[0], NULL, { M680X_INS_BCLR, M680X_INS_BSET } }, }; static const char *s_cpu_type[] = { "INVALID", "6301", "6309", "6800", "6801", "6805", "6808", "6809", "6811", "CPU12", "HCS08", }; static bool m680x_setup_internals(m680x_info *info, e_cpu_type cpu_type, uint16_t address, const uint8_t *code, uint16_t code_len) { if (cpu_type == M680X_CPU_TYPE_INVALID) { fprintf(stderr, "M680X_CPU_TYPE_%s is not suppported\n", s_cpu_type[cpu_type]); return false; } info->code = code; info->size = code_len; info->offset = address; info->cpu_type = cpu_type; info->cpu = &g_cpu_tables[info->cpu_type]; return true; } bool M680X_getInstruction(csh ud, const uint8_t *code, size_t code_len, MCInst *MI, uint16_t *size, uint64_t address, void *inst_info) { unsigned int insn_size = 0; e_cpu_type cpu_type = M680X_CPU_TYPE_INVALID; // No default CPU type cs_struct *handle = (cs_struct *)ud; m680x_info *info = (m680x_info *)handle->printer_info; MCInst_clear(MI); if (handle->mode & CS_MODE_M680X_6800) cpu_type = M680X_CPU_TYPE_6800; else if (handle->mode & CS_MODE_M680X_6801) cpu_type = M680X_CPU_TYPE_6801; else if (handle->mode & CS_MODE_M680X_6805) cpu_type = M680X_CPU_TYPE_6805; else if (handle->mode & CS_MODE_M680X_6808) cpu_type = M680X_CPU_TYPE_6808; else if (handle->mode & CS_MODE_M680X_HCS08) cpu_type = M680X_CPU_TYPE_HCS08; else if (handle->mode & CS_MODE_M680X_6809) cpu_type = M680X_CPU_TYPE_6809; else if (handle->mode & CS_MODE_M680X_6301) cpu_type = M680X_CPU_TYPE_6301; else if (handle->mode & CS_MODE_M680X_6309) cpu_type = M680X_CPU_TYPE_6309; else if (handle->mode & CS_MODE_M680X_6811) cpu_type = M680X_CPU_TYPE_6811; else if (handle->mode & CS_MODE_M680X_CPU12) cpu_type = M680X_CPU_TYPE_CPU12; if (cpu_type != M680X_CPU_TYPE_INVALID && m680x_setup_internals(info, cpu_type, (uint16_t)address, code, code_len)) insn_size = m680x_disassemble(MI, info, (uint16_t)address); if (insn_size == 0) { *size = 1; return false; } // Make sure we always stay within range if (insn_size > code_len) { *size = (uint16_t)code_len; return false; } else *size = (uint16_t)insn_size; return true; } cs_err M680X_disassembler_init(cs_struct *ud) { if (M680X_REG_ENDING != ARR_SIZE(g_m6800_reg_byte_size)) { fprintf(stderr, "Internal error: Size mismatch in enum " "m680x_reg and g_m6800_reg_byte_size\n"); return CS_ERR_MODE; } if (M680X_REG_ENDING != ARR_SIZE(g_m6801_reg_byte_size)) { fprintf(stderr, "Internal error: Size mismatch in enum " "m680x_reg and g_m6801_reg_byte_size\n"); return CS_ERR_MODE; } if (M680X_REG_ENDING != ARR_SIZE(g_m6805_reg_byte_size)) { fprintf(stderr, "Internal error: Size mismatch in enum " "m680x_reg and g_m6805_reg_byte_size\n"); return CS_ERR_MODE; } if (M680X_REG_ENDING != ARR_SIZE(g_m6808_reg_byte_size)) { fprintf(stderr, "Internal error: Size mismatch in enum " "m680x_reg and g_m6808_reg_byte_size\n"); return CS_ERR_MODE; } if (M680X_REG_ENDING != ARR_SIZE(g_m6811_reg_byte_size)) { fprintf(stderr, "Internal error: Size mismatch in enum " "m680x_reg and g_m6811_reg_byte_size\n"); return CS_ERR_MODE; } if (M680X_REG_ENDING != ARR_SIZE(g_cpu12_reg_byte_size)) { fprintf(stderr, "Internal error: Size mismatch in enum " "m680x_reg and g_cpu12_reg_byte_size\n"); return CS_ERR_MODE; } if (M680X_REG_ENDING != ARR_SIZE(g_m6809_reg_byte_size)) { fprintf(stderr, "Internal error: Size mismatch in enum " "m680x_reg and g_m6809_reg_byte_size\n"); return CS_ERR_MODE; } if (M680X_INS_ENDING != ARR_SIZE(g_insn_props)) { fprintf(stderr, "Internal error: Size mismatch in enum " "m680x_insn and g_insn_props\n"); return CS_ERR_MODE; } if (M680X_CPU_TYPE_ENDING != ARR_SIZE(s_cpu_type)) { fprintf(stderr, "Internal error: Size mismatch in enum " "e_cpu_type and s_cpu_type\n"); return CS_ERR_MODE; } if (M680X_CPU_TYPE_ENDING != ARR_SIZE(g_cpu_tables)) { fprintf(stderr, "Internal error: Size mismatch in enum " "e_cpu_type and g_cpu_tables\n"); return CS_ERR_MODE; } if (HANDLER_ID_ENDING != ARR_SIZE(g_insn_handler)) { fprintf(stderr, "Internal error: Size mismatch in enum " "insn_hdlr_id and g_insn_handler\n"); return CS_ERR_MODE; } if (ACCESS_MODE_ENDING != MATRIX_SIZE(g_access_mode_to_access)) { fprintf(stderr, "Internal error: Size mismatch in enum " "e_access_mode and g_access_mode_to_access\n"); return CS_ERR_MODE; } return CS_ERR_OK; } #ifndef CAPSTONE_DIET void M680X_reg_access(const cs_insn *insn, cs_regs regs_read, uint8_t *regs_read_count, cs_regs regs_write, uint8_t *regs_write_count) { if (insn->detail == NULL) { *regs_read_count = 0; *regs_write_count = 0; } else { *regs_read_count = insn->detail->regs_read_count; *regs_write_count = insn->detail->regs_write_count; memcpy(regs_read, insn->detail->regs_read, *regs_read_count * sizeof(insn->detail->regs_read[0])); memcpy(regs_write, insn->detail->regs_write, *regs_write_count * sizeof(insn->detail->regs_write[0])); } } #endif #endif