You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2306 lines
56 KiB
2306 lines
56 KiB
3 years ago
|
/* Capstone Disassembly Engine */
|
||
|
/* M680X Backend by Wolfgang Schwotzer <wolfgang.schwotzer@gmx.net> 2017 */
|
||
|
|
||
|
/* ======================================================================== */
|
||
|
/* ================================ INCLUDES ============================== */
|
||
|
/* ======================================================================== */
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#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
|
||
|
|