mirror of
https://github.com/capstone-engine/capstone.git
synced 2024-11-23 21:49:46 +00:00
2222 lines
60 KiB
C
2222 lines
60 KiB
C
/* Capstone Disassembly Engine */
|
|
/* By Yoshinori Sato, 2022 */
|
|
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include "../../cs_priv.h"
|
|
#include "../../MCInst.h"
|
|
#include "../../MCDisassembler.h"
|
|
#include "../../utils.h"
|
|
#include "SHDisassembler.h"
|
|
#include "capstone/sh.h"
|
|
|
|
#define regs_read(_detail, _reg) \
|
|
if (_detail) \
|
|
_detail->regs_read[_detail->regs_read_count++] = _reg
|
|
#define regs_write(_detail, _reg) \
|
|
if (_detail) \
|
|
_detail->regs_write[_detail->regs_write_count++] = _reg
|
|
|
|
enum direction {read, write};
|
|
|
|
static void regs_rw(cs_detail *detail, enum direction rw, sh_reg reg)
|
|
{
|
|
switch(rw) {
|
|
case read:
|
|
regs_read(detail, reg);
|
|
break;
|
|
case write:
|
|
regs_write(detail, reg);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void set_reg_n(sh_info *info, sh_reg reg, int pos,
|
|
enum direction rw, cs_detail *detail)
|
|
{
|
|
info->op.operands[pos].type = SH_OP_REG;
|
|
info->op.operands[pos].reg = reg;
|
|
regs_rw(detail, rw, reg);
|
|
}
|
|
|
|
static void set_reg(sh_info *info, sh_reg reg, enum direction rw,
|
|
cs_detail *detail)
|
|
{
|
|
set_reg_n(info, reg, info->op.op_count, rw, detail);
|
|
info->op.op_count++;
|
|
}
|
|
|
|
static void set_mem_n(sh_info *info, sh_op_mem_type address,
|
|
sh_reg reg, uint32_t disp, int sz, int pos,
|
|
cs_detail *detail)
|
|
{
|
|
info->op.operands[pos].type = SH_OP_MEM;
|
|
info->op.operands[pos].mem.address = address;
|
|
info->op.operands[pos].mem.reg = reg;
|
|
info->op.operands[pos].mem.disp = disp;
|
|
if (sz > 0)
|
|
info->op.size = sz;
|
|
switch (address) {
|
|
case SH_OP_MEM_REG_POST:
|
|
case SH_OP_MEM_REG_PRE:
|
|
regs_write(detail, reg);
|
|
break;
|
|
case SH_OP_MEM_GBR_R0:
|
|
regs_read(detail, SH_REG_GBR);
|
|
regs_read(detail, SH_REG_R0);
|
|
break;
|
|
case SH_OP_MEM_REG_R0:
|
|
regs_read(detail, SH_REG_R0);
|
|
regs_read(detail, reg);
|
|
break;
|
|
case SH_OP_MEM_PCR:
|
|
break;
|
|
default:
|
|
regs_read(detail, reg);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void set_mem(sh_info *info, sh_op_mem_type address,
|
|
sh_reg reg, uint32_t disp, int sz, cs_detail *detail)
|
|
{
|
|
set_mem_n(info, address, reg, disp, sz, info->op.op_count, detail);
|
|
info->op.op_count++;
|
|
}
|
|
|
|
static void set_imm(sh_info *info, int sign, uint64_t imm)
|
|
{
|
|
info->op.operands[info->op.op_count].type = SH_OP_IMM;
|
|
if (sign && imm >= 128)
|
|
imm = -256 + imm;
|
|
info->op.operands[info->op.op_count].imm = imm;
|
|
info->op.op_count++;
|
|
}
|
|
|
|
static void set_groups(cs_detail *detail, int n, ...)
|
|
{
|
|
va_list g;
|
|
va_start(g, n);
|
|
while (n > 0) {
|
|
sh_insn_group grp;
|
|
grp = va_arg(g, sh_insn_group);
|
|
if (detail) {
|
|
detail->groups[detail->groups_count] = grp;
|
|
detail->groups_count++;
|
|
}
|
|
n--;
|
|
}
|
|
va_end(g);
|
|
}
|
|
|
|
enum {
|
|
ISA_ALL = 1,
|
|
ISA_SH2 = 2,
|
|
ISA_SH2A = 3,
|
|
ISA_SH3 = 4,
|
|
ISA_SH4 = 5,
|
|
ISA_SH4A = 6,
|
|
ISA_MAX = 7,
|
|
};
|
|
|
|
static int isalevel(cs_mode mode)
|
|
{
|
|
int level;
|
|
mode >>= 1; /* skip endian */
|
|
for (level = 2; level < ISA_MAX; level++) {
|
|
if (mode & 1)
|
|
return level;
|
|
mode >>= 1;
|
|
}
|
|
return ISA_ALL;
|
|
}
|
|
|
|
enum co_processor {none, shfpu, shdsp};
|
|
typedef union reg_insn {
|
|
sh_reg reg;
|
|
sh_insn insn;
|
|
} reg_insn;
|
|
struct ri_list {
|
|
int no;
|
|
int /* reg_insn */ri;
|
|
int level;
|
|
enum co_processor cp;
|
|
};
|
|
|
|
static const struct ri_list ldc_stc_regs[] = {
|
|
{0, SH_REG_SR, ISA_ALL, none},
|
|
{1, SH_REG_GBR, ISA_ALL, none},
|
|
{2, SH_REG_VBR, ISA_ALL, none},
|
|
{3, SH_REG_SSR, ISA_SH3, none},
|
|
{4, SH_REG_SPC, ISA_SH3, none},
|
|
{5, SH_REG_MOD, ISA_ALL, shdsp},
|
|
{6, SH_REG_RS, ISA_ALL, shdsp},
|
|
{7, SH_REG_RE, ISA_ALL, shdsp},
|
|
{8, SH_REG_R0_BANK, ISA_SH3, none},
|
|
{9, SH_REG_R1_BANK, ISA_SH3, none},
|
|
{10, SH_REG_R2_BANK, ISA_SH3, none},
|
|
{11, SH_REG_R3_BANK, ISA_SH3, none},
|
|
{12, SH_REG_R4_BANK, ISA_SH3, none},
|
|
{13, SH_REG_R5_BANK, ISA_SH3, none},
|
|
{14, SH_REG_R6_BANK, ISA_SH3, none},
|
|
{15, SH_REG_R7_BANK, ISA_SH3, none},
|
|
{-1, SH_REG_INVALID, ISA_ALL, none},
|
|
};
|
|
|
|
static sh_insn lookup_insn(const struct ri_list *list,
|
|
int no, cs_mode mode)
|
|
{
|
|
int level = isalevel(mode);
|
|
sh_insn error = SH_INS_INVALID;
|
|
for(; list->no >= 0; list++) {
|
|
if (no != list->no)
|
|
continue;
|
|
if (((level >= 0) && (level < list->level)) ||
|
|
((level < 0) && (-(level) != list->level)))
|
|
continue;
|
|
if ((list->cp == none) ||
|
|
((list->cp == shfpu) && (mode & CS_MODE_SHFPU)) ||
|
|
((list->cp == shdsp) && (mode & CS_MODE_SHDSP))) {
|
|
return list->ri;
|
|
}
|
|
}
|
|
return error;
|
|
}
|
|
|
|
static sh_reg lookup_regs(const struct ri_list *list,
|
|
int no, cs_mode mode)
|
|
{
|
|
int level = isalevel(mode);
|
|
sh_reg error = SH_REG_INVALID;
|
|
for(; list->no >= 0; list++) {
|
|
if (no != list->no)
|
|
continue;
|
|
if (((level >= 0) && (level < list->level)) ||
|
|
((level < 0) && (-(level) != list->level)))
|
|
continue;
|
|
if ((list->cp == none) ||
|
|
((list->cp == shfpu) && (mode & CS_MODE_SHFPU)) ||
|
|
((list->cp == shdsp) && (mode & CS_MODE_SHDSP))) {
|
|
return list->ri;
|
|
}
|
|
}
|
|
return error;
|
|
}
|
|
|
|
// #define lookup_regs(list, no, mode) ((reg_insn)(lookup(reg, list, no, mode).reg))
|
|
// #define lookup_insn(list, no, mode) ((sh_insn)(lookup(insn, list, no, mode).insn))
|
|
|
|
static sh_reg opSTCsrc(uint16_t code, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int s = (code >> 4) & 0x0f;
|
|
int d = (code >> 8) & 0x0f;
|
|
sh_reg sreg;
|
|
MCInst_setOpcode(MI, SH_INS_STC);
|
|
sreg = lookup_regs(ldc_stc_regs, s, mode);
|
|
if (sreg != SH_REG_INVALID) {
|
|
set_reg(info, sreg, read, detail);
|
|
return SH_REG_R0 + d;
|
|
} else {
|
|
return SH_REG_INVALID;
|
|
}
|
|
}
|
|
|
|
static bool opSTC(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
sh_reg d;
|
|
d = opSTCsrc(code, MI, mode, info, detail);
|
|
if (d != SH_REG_INVALID) {
|
|
set_reg(info, d, write, detail);
|
|
return MCDisassembler_Success;
|
|
} else {
|
|
return MCDisassembler_Fail;
|
|
}
|
|
}
|
|
|
|
static bool op0xx3(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int r = (code >> 8) & 0x0f;
|
|
int insn_code = (code >> 4) & 0x0f;
|
|
static const struct ri_list list[] = {
|
|
{0, SH_INS_BSRF, ISA_SH2, none},
|
|
{2, SH_INS_BRAF, ISA_SH2, none},
|
|
{6, SH_INS_MOVLI, ISA_SH4A, none},
|
|
{7, SH_INS_MOVCO, ISA_SH4A, none},
|
|
{8, SH_INS_PREF, ISA_SH2A, none},
|
|
{9, SH_INS_OCBI, ISA_SH4, none},
|
|
{10, SH_INS_OCBP, ISA_SH4, none},
|
|
{11, SH_INS_OCBWB, ISA_SH4, none},
|
|
{12, SH_INS_MOVCA, ISA_SH4, none},
|
|
{13, SH_INS_PREFI, ISA_SH4A, none},
|
|
{14, SH_INS_ICBI, ISA_SH4A, none},
|
|
{-1, SH_INS_INVALID, ISA_ALL, none},
|
|
};
|
|
sh_insn insn = lookup_insn(list, insn_code, mode);
|
|
|
|
if (insn != SH_INS_INVALID) {
|
|
MCInst_setOpcode(MI, insn);
|
|
switch (insn_code) {
|
|
case 0: /// bsrf Rn
|
|
case 2: /// braf Rn
|
|
set_reg(info, SH_REG_R0 + r, read, detail);
|
|
if (detail)
|
|
set_groups(detail, 2,
|
|
SH_GRP_JUMP,
|
|
SH_GRP_BRANCH_RELATIVE);
|
|
break;
|
|
case 8: /// pref @Rn
|
|
case 9: /// ocbi @Rn
|
|
case 10: /// ocbp @Rn
|
|
case 11: /// ocbwb @Rn
|
|
case 13: /// prefi @Rn
|
|
case 14: /// icbi @Rn
|
|
set_mem(info, SH_OP_MEM_REG_IND,
|
|
SH_REG_R0 + r, 0, 0, detail);
|
|
break;
|
|
case 6: /// movli @Rn, R0
|
|
set_mem(info, SH_OP_MEM_REG_IND,
|
|
SH_REG_R0 + r, 0, 32, detail);
|
|
set_reg(info, SH_REG_R0, write, detail);
|
|
break;
|
|
case 7: /// movco R0,@Rn
|
|
case 12: /// movca R0,@Rn
|
|
set_reg(info, SH_REG_R0, read, detail);
|
|
set_mem(info, SH_OP_MEM_REG_IND,
|
|
SH_REG_R0 + r, 0, 32, detail);
|
|
break;
|
|
}
|
|
return MCDisassembler_Success;
|
|
} else {
|
|
return MCDisassembler_Fail;
|
|
}
|
|
}
|
|
|
|
#define nm(code, dir) \
|
|
int m, n; \
|
|
m = (code >> (4 * (dir + 1))) & 0x0f; \
|
|
n = (code >> (8 - 4 * dir)) & 0x0f
|
|
|
|
static bool opMOVx(uint16_t code, uint64_t address, MCInst *MI,
|
|
cs_mode mode, int size, sh_info *info, cs_detail *detail)
|
|
{
|
|
int ad = ((code >> 10) & 0x3c) | ((code >> 2) & 0x03);
|
|
enum direction rw;
|
|
MCInst_setOpcode(MI, SH_INS_MOV);
|
|
switch (ad) {
|
|
case 0x01: /// mov.X Rs,@(R0, Rd)
|
|
case 0x03: /// mov.X @(R0, Rs), Rd
|
|
rw = (ad >> 1);
|
|
{
|
|
nm(code, rw);
|
|
set_reg_n(info, SH_REG_R0 + m, rw, rw, detail);
|
|
set_mem_n(info, SH_OP_MEM_REG_R0, SH_REG_R0 + n,
|
|
0, size, 1 - rw, detail);
|
|
info->op.op_count = 2;
|
|
}
|
|
break;
|
|
case 0x20: /// mov.X Rs,@-Rd
|
|
case 0x60: /// mov.X @Rs+,Rd
|
|
rw = (ad >> 6) & 1;
|
|
{
|
|
nm(code, rw);
|
|
set_reg_n(info, SH_REG_R0 + m, rw, rw, detail);
|
|
set_mem_n(info, SH_OP_MEM_REG_PRE, SH_REG_R0 + n,
|
|
0, size, 1 - rw, detail);
|
|
}
|
|
break;
|
|
default:
|
|
return MCDisassembler_Fail;
|
|
}
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
static bool opMOV_B(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
return opMOVx(code, address, MI, mode, 8, info, detail);
|
|
}
|
|
|
|
static bool opMOV_W(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
return opMOVx(code, address, MI, mode, 16, info, detail);
|
|
}
|
|
|
|
static bool opMOV_L(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
return opMOVx(code, address, MI, mode, 32, info, detail);
|
|
}
|
|
|
|
static bool opRRfn(uint16_t code, MCInst *MI, sh_insn insn, cs_mode mode,
|
|
int size, int level, sh_info *info, cs_detail *detail)
|
|
{
|
|
int m = (code >> 4) & 0x0f;
|
|
int n = (code >> 8) & 0x0f;
|
|
if (level > isalevel(mode))
|
|
return MCDisassembler_Fail;
|
|
MCInst_setOpcode(MI, insn);
|
|
set_reg(info, SH_REG_R0 + m, read, detail);
|
|
set_reg(info, SH_REG_R0 + n, write, detail);
|
|
info->op.size = size;
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
#define opRR(level, __insn, __size) \
|
|
static bool op##__insn(uint16_t code, uint64_t address, MCInst *MI, \
|
|
cs_mode mode, sh_info *info, cs_detail *detail) \
|
|
{ \
|
|
return opRRfn(code, MI, SH_INS_##__insn, mode, __size, level, \
|
|
info, detail); \
|
|
}
|
|
|
|
/* mul.l - SH2 */
|
|
opRR(ISA_SH2, MUL_L, 0)
|
|
|
|
static bool op0xx8(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int insn_code = (code >> 4) & 0xf;
|
|
static const struct ri_list list[] = {
|
|
{0, SH_INS_CLRT, ISA_ALL, none},
|
|
{1, SH_INS_SETT, ISA_ALL, none},
|
|
{2, SH_INS_CLRMAC, ISA_ALL, none},
|
|
{3, SH_INS_LDTLB, ISA_SH3, none},
|
|
{4, SH_INS_CLRS, ISA_SH3, none},
|
|
{5, SH_INS_SETS, ISA_SH3, none},
|
|
{6, SH_INS_NOTT, -(ISA_SH2A), none},
|
|
{8, SH_INS_CLRDMXY, ISA_SH4A, shdsp},
|
|
{9, SH_INS_SETDMX, ISA_SH4A, shdsp},
|
|
{12, SH_INS_SETDMY, ISA_SH4A, shdsp},
|
|
{-1, SH_INS_INVALID, ISA_ALL, none},
|
|
};
|
|
|
|
sh_insn insn = lookup_insn(list, insn_code, mode);
|
|
if (code & 0x0f00)
|
|
return MCDisassembler_Fail;
|
|
|
|
if (insn != SH_INS_INVALID) {
|
|
MCInst_setOpcode(MI, insn);
|
|
return MCDisassembler_Success;
|
|
} else {
|
|
return MCDisassembler_Fail;
|
|
}
|
|
}
|
|
|
|
static bool op0xx9(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int insn_code = (code >> 4) & 0x0f;
|
|
int r = (code >> 8) & 0x0f;
|
|
static const struct ri_list list[] = {
|
|
{0, SH_INS_NOP, ISA_ALL, none},
|
|
{1, SH_INS_DIV0U, ISA_ALL, none},
|
|
{2, SH_INS_MOVT, ISA_ALL, none},
|
|
{3, SH_INS_MOVRT, -(ISA_SH2A), none},
|
|
{-1, SH_INS_INVALID, ISA_ALL, none},
|
|
};
|
|
sh_insn insn = lookup_insn(list, insn_code, mode);
|
|
if (insn != SH_INS_INVALID) {
|
|
if (insn_code >= 2) {
|
|
/// movt / movrt Rn
|
|
set_reg(info, SH_REG_R0 + r, write, detail);
|
|
} else if (r > 0) {
|
|
insn = SH_INS_INVALID;
|
|
}
|
|
}
|
|
if (insn != SH_INS_INVALID) {
|
|
MCInst_setOpcode(MI, insn);
|
|
return MCDisassembler_Success;
|
|
} else {
|
|
return MCDisassembler_Fail;
|
|
}
|
|
}
|
|
|
|
static const struct ri_list sts_lds_regs[] = {
|
|
{0, SH_REG_MACH, ISA_ALL, none},
|
|
{1, SH_REG_MACL, ISA_ALL, none},
|
|
{2, SH_REG_PR, ISA_ALL, none},
|
|
{3, SH_REG_SGR, ISA_SH4, none},
|
|
{4, SH_REG_TBR, -(ISA_SH2A), none},
|
|
{5, SH_REG_FPUL, ISA_ALL, shfpu},
|
|
{6, SH_REG_FPSCR, ISA_ALL, shfpu},
|
|
{6, SH_REG_DSP_DSR, ISA_ALL, shdsp},
|
|
{7, SH_REG_DSP_A0, ISA_ALL, shdsp},
|
|
{8, SH_REG_DSP_X0, ISA_ALL, shdsp},
|
|
{9, SH_REG_DSP_X1, ISA_ALL, shdsp},
|
|
{10, SH_REG_DSP_Y0, ISA_ALL, shdsp},
|
|
{11, SH_REG_DSP_Y1, ISA_ALL, shdsp},
|
|
{15, SH_REG_DBR, ISA_SH4, none},
|
|
{-1, SH_REG_INVALID, ISA_ALL, none},
|
|
};
|
|
|
|
static sh_reg opSTCSTS(uint16_t code, MCInst *MI, cs_mode mode, sh_info *info,
|
|
cs_detail *detail)
|
|
{
|
|
int s = (code >> 4) & 0x0f;
|
|
int d = (code >> 8) & 0x0f;
|
|
sh_reg reg;
|
|
sh_insn insn;
|
|
|
|
reg = lookup_regs(sts_lds_regs, s, mode);
|
|
if (reg != SH_REG_INVALID) {
|
|
if (s == 3 || s == 4 || s == 15) {
|
|
insn = SH_INS_STC;
|
|
} else {
|
|
insn = SH_INS_STS;
|
|
}
|
|
MCInst_setOpcode(MI, insn);
|
|
set_reg(info, reg, read, detail);
|
|
return SH_REG_R0 + d;
|
|
} else {
|
|
return SH_REG_INVALID;
|
|
}
|
|
}
|
|
|
|
static bool op0xxa(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
sh_reg r = opSTCSTS(code, MI, mode, info, detail);
|
|
if (r != SH_REG_INVALID) {
|
|
set_reg(info, r, write, detail);
|
|
return MCDisassembler_Success;
|
|
} else
|
|
return MCDisassembler_Fail;
|
|
}
|
|
|
|
static bool op0xxb(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int insn_code = (code >> 4) & 0x0f;
|
|
int r = (code >> 8) & 0x0f;
|
|
static const struct ri_list list[] = {
|
|
{0, SH_INS_RTS, ISA_ALL, none},
|
|
{1, SH_INS_SLEEP, ISA_ALL, none},
|
|
{2, SH_INS_RTE, ISA_ALL, none},
|
|
{5, SH_INS_RESBANK, -(ISA_SH2A), none},
|
|
{6, SH_INS_RTS_N, -(ISA_SH2A), none},
|
|
{7, SH_INS_RTV_N, -(ISA_SH2A), none},
|
|
{10, SH_INS_SYNCO, -(ISA_SH4A), none},
|
|
{-1, SH_INS_INVALID, ISA_ALL, none},
|
|
};
|
|
|
|
sh_insn insn = lookup_insn(list, insn_code, mode);
|
|
if (insn_code == 7) {
|
|
set_reg(info, SH_REG_R0 + r, read, detail);
|
|
regs_write(detail, SH_REG_R0);
|
|
} else if (r > 0) {
|
|
insn = SH_INS_INVALID;
|
|
}
|
|
if (insn != SH_INS_INVALID) {
|
|
MCInst_setOpcode(MI, insn);
|
|
return MCDisassembler_Success;
|
|
} else {
|
|
return MCDisassembler_Fail;
|
|
}
|
|
}
|
|
|
|
static bool opMAC(uint16_t code, sh_insn op, MCInst *MI, sh_info *info,
|
|
cs_detail *detail)
|
|
{
|
|
nm(code, 0);
|
|
MCInst_setOpcode(MI, op);
|
|
set_mem(info, SH_OP_MEM_REG_POST, SH_REG_R0 + m, 0, 0, detail);
|
|
set_mem(info, SH_OP_MEM_REG_POST, SH_REG_R0 + n, 0, 0, detail);
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
/// mac.l - sh2+
|
|
static bool opMAC_L(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
if (isalevel(mode) < ISA_SH2)
|
|
return MCDisassembler_Fail;
|
|
return opMAC(code, SH_INS_MAC_L, MI, info, detail);
|
|
}
|
|
|
|
static bool opMAC_W(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
return opMAC(code, SH_INS_MAC_W, MI, info, detail);
|
|
}
|
|
|
|
static bool opMOV_L_dsp(uint16_t code, uint64_t address, MCInst *MI,
|
|
cs_mode mode, sh_info *info, cs_detail *detail)
|
|
{
|
|
int dsp = (code & 0x0f) * 4;
|
|
int rw = (code >> 14) & 1;
|
|
nm(code, rw);
|
|
MCInst_setOpcode(MI, SH_INS_MOV);
|
|
set_mem_n(info, SH_OP_MEM_REG_DISP, SH_REG_R0 + n, dsp,
|
|
32, 1 - rw, detail);
|
|
set_reg_n(info, SH_REG_R0 + m, rw, rw, detail);
|
|
info->op.op_count = 2;
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
static bool opMOV_rind(uint16_t code, uint64_t address, MCInst *MI,
|
|
cs_mode mode, sh_info *info, cs_detail *detail)
|
|
{
|
|
int sz = (code & 0x03);
|
|
int rw = (code >> 14) & 1;
|
|
nm(code, rw);
|
|
MCInst_setOpcode(MI, SH_INS_MOV);
|
|
sz = 8 << sz;
|
|
set_mem_n(info, SH_OP_MEM_REG_IND, SH_REG_R0 + n, 0,
|
|
sz, 1 - rw, detail);
|
|
set_reg_n(info, SH_REG_R0 + m, rw, rw, detail);
|
|
info->op.op_count = 2;
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
static bool opMOV_rpd(uint16_t code, uint64_t address, MCInst *MI,
|
|
cs_mode mode, sh_info *info, cs_detail *detail)
|
|
{
|
|
nm(code, 0);
|
|
int sz = (code & 0x03);
|
|
MCInst_setOpcode(MI, SH_INS_MOV);
|
|
set_reg(info, SH_REG_R0 + m, read, detail);
|
|
set_mem(info, SH_OP_MEM_REG_PRE, SH_REG_R0 + n, 0, 8 << sz, detail);
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
opRR(ISA_ALL, TST, 0)
|
|
opRR(ISA_ALL, AND, 0)
|
|
opRR(ISA_ALL, XOR, 0)
|
|
opRR(ISA_ALL, OR, 0)
|
|
opRR(ISA_ALL, CMP_STR, 0)
|
|
opRR(ISA_ALL, XTRCT, 0)
|
|
opRR(ISA_ALL, MULU_W, 16)
|
|
opRR(ISA_ALL, MULS_W, 16)
|
|
opRR(ISA_ALL, CMP_EQ, 0)
|
|
opRR(ISA_ALL, CMP_HI, 0)
|
|
opRR(ISA_ALL, CMP_HS, 0)
|
|
opRR(ISA_ALL, CMP_GE, 0)
|
|
opRR(ISA_ALL, CMP_GT, 0)
|
|
opRR(ISA_ALL, SUB, 0)
|
|
opRR(ISA_ALL, SUBC, 0)
|
|
opRR(ISA_ALL, SUBV, 0)
|
|
opRR(ISA_ALL, ADD_r, 0)
|
|
opRR(ISA_ALL, ADDC, 0)
|
|
opRR(ISA_ALL, ADDV, 0)
|
|
opRR(ISA_ALL, DIV0S, 0)
|
|
opRR(ISA_ALL, DIV1, 0)
|
|
/// DMULS / DMULU - SH2
|
|
opRR(ISA_SH2, DMULS_L, 0)
|
|
opRR(ISA_SH2, DMULU_L, 0)
|
|
|
|
static bool op4xx0(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int insn_code = (code >> 4) & 0x0f;
|
|
int r = (code >> 8) & 0x0f;
|
|
static const struct ri_list list[] = {
|
|
{0, SH_INS_SHLL, ISA_ALL, none},
|
|
{1, SH_INS_DT, ISA_SH2, none},
|
|
{2, SH_INS_SHAL, ISA_ALL, none},
|
|
{8, SH_INS_MULR, -(ISA_SH2A), none},
|
|
{15, SH_INS_MOVMU, -(ISA_SH2A), none},
|
|
{-1, SH_INS_INVALID, ISA_ALL, none},
|
|
};
|
|
sh_insn insn = lookup_insn(list, insn_code,mode);
|
|
if (insn != SH_INS_INVALID) {
|
|
MCInst_setOpcode(MI, insn);
|
|
if (insn_code < 8) {
|
|
set_reg(info, SH_REG_R0 + r, write, detail);
|
|
} else {
|
|
switch(insn_code) {
|
|
case 0x08:
|
|
set_reg(info, SH_REG_R0, read, detail);
|
|
set_reg(info, SH_REG_R0 + r, write, detail);
|
|
break;
|
|
case 0x0f:
|
|
set_reg(info, SH_REG_R0 + r, read, detail);
|
|
set_mem(info, SH_OP_MEM_REG_PRE, SH_REG_R15, 0, 32, detail);
|
|
break;
|
|
}
|
|
}
|
|
return MCDisassembler_Success;
|
|
} else {
|
|
return MCDisassembler_Fail;
|
|
}
|
|
}
|
|
|
|
static bool op4xx1(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int insn_code = (code >> 4) & 0x0f;
|
|
int r = (code >> 8) & 0x0f;
|
|
static const struct ri_list list[] = {
|
|
{0, SH_INS_SHLR, ISA_ALL, none},
|
|
{1, SH_INS_CMP_PZ, ISA_ALL, none},
|
|
{2, SH_INS_SHAR, ISA_ALL, none},
|
|
{8, SH_INS_CLIPU, -(ISA_SH2A), none},
|
|
{9, SH_INS_CLIPS, -(ISA_SH2A), none},
|
|
{14, SH_INS_STBANK, -(ISA_SH2A), none},
|
|
{15, SH_INS_MOVML, -(ISA_SH2A), none},
|
|
{-1, SH_INS_INVALID, ISA_ALL, none},
|
|
};
|
|
sh_insn insn = lookup_insn(list, insn_code,mode);
|
|
if (insn != SH_INS_INVALID) {
|
|
MCInst_setOpcode(MI, insn);
|
|
switch(insn_code) {
|
|
case 14:
|
|
set_reg(info, SH_REG_R0, read, detail);
|
|
set_mem(info, SH_OP_MEM_REG_IND, SH_REG_R0 + r, 0,
|
|
0, detail);
|
|
break;
|
|
case 15:
|
|
set_reg(info, SH_REG_R0 + r, read, detail);
|
|
set_mem(info, SH_OP_MEM_REG_PRE, SH_REG_R15, 0,
|
|
32, detail);
|
|
break;
|
|
default:
|
|
set_reg(info, SH_REG_R0 + r, write, detail);
|
|
if (insn_code >= 8)
|
|
info->op.size = 8;
|
|
break;
|
|
}
|
|
return MCDisassembler_Success;
|
|
} else {
|
|
return MCDisassembler_Fail;
|
|
}
|
|
}
|
|
|
|
static bool op4xx2(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
sh_reg r = opSTCSTS(code, MI, mode, info, detail);
|
|
if (r != SH_REG_INVALID) {
|
|
set_mem(info, SH_OP_MEM_REG_PRE, r, 0, 32, detail);
|
|
return MCDisassembler_Success;
|
|
} else {
|
|
return MCDisassembler_Fail;
|
|
}
|
|
}
|
|
|
|
static bool opSTC_L(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
sh_reg r = opSTCsrc(code, MI, mode, info, detail);
|
|
if (r != SH_REG_INVALID) {
|
|
set_mem(info, SH_OP_MEM_REG_PRE, r, 0, 32, detail);
|
|
return MCDisassembler_Success;
|
|
} else {
|
|
return MCDisassembler_Fail;
|
|
}
|
|
}
|
|
|
|
static bool op4xx4(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int r = (code >> 8) & 0x0f;
|
|
int insn_code = (code >> 4) & 0x0f;
|
|
static const struct ri_list list[] = {
|
|
{0, SH_INS_ROTL, ISA_ALL, none},
|
|
{1, SH_INS_SETRC, ISA_ALL, shdsp},
|
|
{2, SH_INS_ROTCL, ISA_ALL, none},
|
|
{3, SH_INS_LDRC, ISA_ALL, shdsp},
|
|
{8, SH_INS_DIVU, -(ISA_SH2A), none},
|
|
{9, SH_INS_DIVS, -(ISA_SH2A), none},
|
|
{15, SH_INS_MOVMU, -(ISA_SH2A), none},
|
|
{-1, SH_INS_INVALID, ISA_ALL, none},
|
|
};
|
|
sh_insn insn = lookup_insn(list, insn_code, mode);
|
|
if (insn != SH_INS_INVALID) {
|
|
MCInst_setOpcode(MI, insn);
|
|
switch(insn_code) {
|
|
case 8:
|
|
case 9:
|
|
set_reg(info, SH_REG_R0, read, detail);
|
|
break;
|
|
case 15:
|
|
set_mem(info, SH_OP_MEM_REG_POST, SH_REG_R15, 0,
|
|
32, detail);
|
|
set_reg(info, SH_REG_R0 + r, read, detail);
|
|
return MCDisassembler_Success;
|
|
}
|
|
set_reg(info, SH_REG_R0 + r, write, detail);
|
|
return MCDisassembler_Success;
|
|
} else {
|
|
return MCDisassembler_Fail;
|
|
}
|
|
}
|
|
|
|
static bool op4xx5(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int r = (code >> 8) & 0x0f;
|
|
enum direction rw = read;
|
|
static const struct ri_list list[] = {
|
|
{0, SH_INS_ROTR, ISA_ALL, none},
|
|
{1, SH_INS_CMP_PL, ISA_ALL, none},
|
|
{2, SH_INS_ROTCR, ISA_ALL, none},
|
|
{8, SH_INS_CLIPU, -(ISA_SH2A), none},
|
|
{9, SH_INS_CLIPS, -(ISA_SH2A), none},
|
|
{14, SH_INS_LDBANK, -(ISA_SH2A), none},
|
|
{15, SH_INS_MOVML, -(ISA_SH2A), none},
|
|
{-1, SH_INS_INVALID, ISA_ALL, none},
|
|
};
|
|
int insn_code = (code >> 4) & 0x0f;
|
|
sh_insn insn = lookup_insn(list, insn_code,mode);
|
|
if (insn != SH_INS_INVALID) {
|
|
MCInst_setOpcode(MI, insn);
|
|
switch (insn_code) {
|
|
case 0:
|
|
case 2:
|
|
rw = write;
|
|
break;
|
|
case 1:
|
|
rw = read;
|
|
break;
|
|
case 8:
|
|
case 9:
|
|
info->op.size = 16;
|
|
rw = write;
|
|
break;
|
|
case 0x0e:
|
|
set_mem(info, SH_OP_MEM_REG_IND, SH_REG_R0 + r, 0,
|
|
0, detail);
|
|
set_reg(info, SH_REG_R0, write, detail);
|
|
return MCDisassembler_Success;
|
|
case 0x0f:
|
|
set_mem(info, SH_OP_MEM_REG_POST, SH_REG_R15, 0,
|
|
32, detail);
|
|
set_reg(info, SH_REG_R0 + r, write, detail);
|
|
return MCDisassembler_Success;
|
|
}
|
|
set_reg(info, SH_REG_R0 + r, rw, detail);
|
|
return MCDisassembler_Success;
|
|
} else {
|
|
return MCDisassembler_Fail;
|
|
}
|
|
}
|
|
|
|
static bool opLDCLDS(uint16_t code, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int d = (code >> 4) & 0x0f;
|
|
sh_reg reg = lookup_regs(sts_lds_regs, d, mode);
|
|
sh_insn insn;
|
|
if (reg != SH_REG_INVALID) {
|
|
if (d == 3 || d == 4 || d == 15) {
|
|
insn = SH_INS_LDC;
|
|
} else {
|
|
insn = SH_INS_LDS;
|
|
}
|
|
MCInst_setOpcode(MI, insn);
|
|
set_reg(info, reg, write, detail);
|
|
return MCDisassembler_Success;
|
|
} else {
|
|
return MCDisassembler_Fail;
|
|
}
|
|
}
|
|
|
|
static bool op4xx6(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int r = (code >> 8) & 0x0f;
|
|
set_mem(info, SH_OP_MEM_REG_POST, SH_REG_R0 + r, 0, 32, detail);
|
|
return opLDCLDS(code, MI, mode, info, detail);
|
|
}
|
|
|
|
static bool opLDCdst(uint16_t code, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int d = (code >> 4) & 0x0f;
|
|
sh_reg dreg = lookup_regs(ldc_stc_regs, d, mode);
|
|
if (dreg == SH_REG_INVALID)
|
|
return MCDisassembler_Fail;
|
|
MCInst_setOpcode(MI, SH_INS_LDC);
|
|
set_reg(info, dreg, write, detail);
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
static bool opLDC_L(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int s = (code >> 8) & 0x0f;
|
|
set_mem(info, SH_OP_MEM_REG_POST, SH_REG_R0 + s, 0, 32, detail);
|
|
return opLDCdst(code, MI, mode, info, detail);
|
|
|
|
}
|
|
|
|
static bool op4xx8(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int r = (code >> 8) & 0x0f;
|
|
sh_insn insn[] = { SH_INS_SHLL2, SH_INS_SHLL8, SH_INS_SHLL16};
|
|
int size = (code >> 4) & 0x0f;
|
|
if (size >= ARR_SIZE(insn)) {
|
|
return MCDisassembler_Fail;
|
|
}
|
|
MCInst_setOpcode(MI, insn[size]);
|
|
set_reg(info, SH_REG_R0 + r, write, detail);
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
static bool op4xx9(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int r = (code >> 8) & 0x0f;
|
|
static const struct ri_list list[] = {
|
|
{0, SH_INS_SHLR2, ISA_ALL, none},
|
|
{1, SH_INS_SHLR8, ISA_ALL, none},
|
|
{2, SH_INS_SHLR16, ISA_ALL, none},
|
|
{10, SH_INS_MOVUA, -(ISA_SH4A), none},
|
|
{14, SH_INS_MOVUA, -(ISA_SH4A), none},
|
|
{-1, SH_INS_INVALID, ISA_ALL, none},
|
|
};
|
|
int op = (code >> 4) & 0x0f;
|
|
sh_insn insn = lookup_insn(list, op, mode);
|
|
sh_op_mem_type memop = SH_OP_MEM_INVALID;
|
|
if (insn != SH_INS_INVALID) {
|
|
MCInst_setOpcode(MI, insn);
|
|
if (op < 8) {
|
|
set_reg(info, SH_REG_R0 + r, write, detail);
|
|
} else {
|
|
memop = (op&4)?SH_OP_MEM_REG_POST:SH_OP_MEM_REG_IND;
|
|
set_mem(info, memop, SH_REG_R0 + r, 0, 32, detail);
|
|
set_reg(info, SH_REG_R0, write, detail);
|
|
}
|
|
return MCDisassembler_Success;
|
|
} else {
|
|
return MCDisassembler_Fail;
|
|
}
|
|
}
|
|
|
|
static bool op4xxa(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int r = (code >> 8) & 0x0f;
|
|
set_reg(info, SH_REG_R0 + r, read, detail);
|
|
return opLDCLDS(code, MI, mode, info, detail);
|
|
}
|
|
|
|
static bool op4xxb(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int r = (code >> 8) & 0x0f;
|
|
int insn_code = (code >> 4) & 0x0f;
|
|
int sz = 0;
|
|
int grp = SH_GRP_INVALID;
|
|
sh_op_mem_type memop = SH_OP_MEM_INVALID;
|
|
enum direction rw = read;
|
|
static const struct ri_list list[] = {
|
|
{0, SH_INS_JSR, ISA_ALL, none},
|
|
{1, SH_INS_TAS, ISA_ALL, none},
|
|
{2, SH_INS_JMP, ISA_ALL, none},
|
|
{4, SH_INS_JSR_N, -(ISA_SH2A), none},
|
|
{8, SH_INS_MOV, -(ISA_SH2A), none},
|
|
{9, SH_INS_MOV, -(ISA_SH2A), none},
|
|
{10, SH_INS_MOV, -(ISA_SH2A), none},
|
|
{12, SH_INS_MOV, -(ISA_SH2A), none},
|
|
{13, SH_INS_MOV, -(ISA_SH2A), none},
|
|
{14, SH_INS_MOV, -(ISA_SH2A), none},
|
|
{-1, SH_INS_INVALID, ISA_ALL, none},
|
|
};
|
|
sh_insn insn = lookup_insn(list, insn_code, mode);
|
|
if (insn != SH_INS_INVALID) {
|
|
MCInst_setOpcode(MI, insn);
|
|
sz = 8 << ((code >> 4) & 3);
|
|
switch (insn_code) {
|
|
case 0:
|
|
case 4:
|
|
memop = SH_OP_MEM_REG_IND;
|
|
grp = SH_GRP_CALL;
|
|
break;
|
|
case 1:
|
|
memop = SH_OP_MEM_REG_IND;
|
|
sz = 8;
|
|
rw = write;
|
|
break;
|
|
case 2:
|
|
insn = SH_INS_JMP;
|
|
grp = SH_GRP_JUMP;
|
|
break;
|
|
case 8:
|
|
case 9:
|
|
case 10:
|
|
memop = SH_OP_MEM_REG_POST;
|
|
rw = read;
|
|
break;
|
|
case 12:
|
|
case 13:
|
|
case 14:
|
|
memop = SH_OP_MEM_REG_PRE;
|
|
rw = write;
|
|
break;
|
|
}
|
|
if (grp != SH_GRP_INVALID) {
|
|
set_mem(info, SH_OP_MEM_REG_IND, SH_REG_R0 + r, 0,
|
|
0, detail);
|
|
if (detail)
|
|
set_groups(detail, 1, grp);
|
|
} else {
|
|
if (insn_code != 1) {
|
|
set_reg_n(info, SH_REG_R0, rw, rw, detail);
|
|
info->op.op_count++;
|
|
}
|
|
set_mem_n(info, memop, SH_REG_R0 + r, 0, sz,
|
|
1 - rw, detail);
|
|
info->op.op_count++;
|
|
}
|
|
return MCDisassembler_Success;
|
|
} else {
|
|
return MCDisassembler_Fail;
|
|
}
|
|
}
|
|
|
|
/* SHAD / SHLD - SH2A */
|
|
opRR(ISA_SH2A, SHAD, 0)
|
|
opRR(ISA_SH2A, SHLD, 0)
|
|
|
|
static bool opLDC(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int s = (code >> 8) & 0x0f;
|
|
set_reg(info, SH_REG_R0 + s, read, detail);
|
|
return opLDCdst(code, MI, mode, info, detail);
|
|
}
|
|
|
|
opRR(ISA_ALL, MOV, 0)
|
|
|
|
static bool opMOV_rpi(uint16_t code, uint64_t address, MCInst *MI,
|
|
cs_mode mode, sh_info *info, cs_detail *detail)
|
|
{
|
|
int sz = (code & 0x03);
|
|
nm(code, 0);
|
|
MCInst_setOpcode(MI, SH_INS_MOV);
|
|
set_mem(info, SH_OP_MEM_REG_POST, SH_REG_R0 + m, 0, 8 << sz, detail);
|
|
set_reg(info, SH_REG_R0 + n, write, detail);
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
opRR(ISA_ALL, NOT, 0)
|
|
opRR(ISA_ALL, SWAP_B, 8)
|
|
opRR(ISA_ALL, SWAP_W, 16)
|
|
opRR(ISA_ALL, NEGC, 0)
|
|
opRR(ISA_ALL, NEG, 0)
|
|
opRR(ISA_ALL, EXTU_B, 8)
|
|
opRR(ISA_ALL, EXTU_W, 16)
|
|
opRR(ISA_ALL, EXTS_B, 8)
|
|
opRR(ISA_ALL, EXTS_W, 16)
|
|
|
|
static bool opADD_i(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int r = (code >> 8) & 0x0f;
|
|
MCInst_setOpcode(MI, SH_INS_ADD);
|
|
set_imm(info, 1, code & 0xff);
|
|
set_reg(info, SH_REG_R0 + r, write, detail);
|
|
return MCDisassembler_Success;
|
|
|
|
}
|
|
|
|
static bool opMOV_BW_dsp(uint16_t code, uint64_t address, MCInst *MI,
|
|
cs_mode mode, sh_info *info, cs_detail *detail)
|
|
{
|
|
int dsp = (code & 0x0f);
|
|
int r = (code >> 4) & 0x0f;
|
|
int size = 1 + ((code >> 8) & 1);
|
|
int rw = (code >> 10) & 1;
|
|
MCInst_setOpcode(MI, SH_INS_MOV);
|
|
set_mem_n(info, SH_OP_MEM_REG_DISP, SH_REG_R0 + r, dsp * size,
|
|
8 * size, 1 - rw, detail);
|
|
set_reg_n(info, SH_REG_R0, rw, rw, detail);
|
|
info->op.op_count = 2;
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
static bool opSETRC(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int imm = code & 0xff;
|
|
if (!(mode & CS_MODE_SHDSP))
|
|
return MCDisassembler_Fail;
|
|
MCInst_setOpcode(MI, SH_INS_SETRC);
|
|
set_imm(info, 0, imm);
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
static bool opJSR_N(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int dsp = code & 0xff;
|
|
if (isalevel(mode) != ISA_SH2A)
|
|
return MCDisassembler_Fail;
|
|
MCInst_setOpcode(MI, SH_INS_JSR_N);
|
|
set_mem(info, SH_OP_MEM_TBR_DISP, SH_REG_INVALID, dsp * 4, 0, detail);
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
#define boperand(_code, _op, _imm, _reg) \
|
|
int _op = (code >> 3) & 1; \
|
|
int _imm = code & 7; \
|
|
int _reg = (code >> 4) & 0x0f
|
|
|
|
static bool op86xx(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
static const sh_insn bop[] = {SH_INS_BCLR, SH_INS_BSET};
|
|
boperand(code, op, imm, reg);
|
|
if (isalevel(mode) != ISA_SH2A)
|
|
return MCDisassembler_Fail;
|
|
MCInst_setOpcode(MI, bop[op]);
|
|
set_imm(info, 0, imm);
|
|
set_reg(info, SH_REG_R0 + reg, write, detail);
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
static bool op87xx(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
static const sh_insn bop[] = {SH_INS_BST, SH_INS_BLD};
|
|
boperand(code, op, imm, reg);
|
|
if (isalevel(mode) != ISA_SH2A)
|
|
return MCDisassembler_Fail;
|
|
MCInst_setOpcode(MI, bop[op]);
|
|
set_imm(info, 0, imm);
|
|
set_reg(info, SH_REG_R0 + reg, op?read:write, detail);
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
static bool opCMP_EQi(uint16_t code, uint64_t address, MCInst *MI,
|
|
cs_mode mode, sh_info *info, cs_detail *detail)
|
|
{
|
|
MCInst_setOpcode(MI, SH_INS_CMP_EQ);
|
|
set_imm(info, 1, code & 0x00ff);
|
|
set_reg(info, SH_REG_R0, read, detail);
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
#define opBranch(level, insn) \
|
|
static bool op##insn(uint16_t code, uint64_t address, MCInst *MI, \
|
|
cs_mode mode, sh_info *info, cs_detail *detail) \
|
|
{ \
|
|
int dsp = code & 0x00ff; \
|
|
if (level > isalevel(mode)) \
|
|
return MCDisassembler_Fail; \
|
|
if (dsp >= 0x80) \
|
|
dsp = -256 + dsp; \
|
|
MCInst_setOpcode(MI, SH_INS_##insn); \
|
|
set_mem(info, SH_OP_MEM_PCR, SH_REG_INVALID, address + 4 + dsp * 2, \
|
|
0, detail); \
|
|
if (detail) \
|
|
set_groups(detail, 2, SH_GRP_JUMP, SH_GRP_BRANCH_RELATIVE); \
|
|
return MCDisassembler_Success; \
|
|
}
|
|
|
|
opBranch(ISA_ALL, BT)
|
|
opBranch(ISA_ALL, BF)
|
|
/* bt/s / bf/s - SH2 */
|
|
opBranch(ISA_SH2, BT_S)
|
|
opBranch(ISA_SH2, BF_S)
|
|
|
|
#define opLDRSE(insn) \
|
|
static bool op##insn(uint16_t code, uint64_t address, MCInst *MI, \
|
|
cs_mode mode, sh_info *info, cs_detail *detail) \
|
|
{ \
|
|
int dsp = code & 0xff; \
|
|
if (!(mode & CS_MODE_SHDSP)) \
|
|
return MCDisassembler_Fail; \
|
|
MCInst_setOpcode(MI, SH_INS_##insn); \
|
|
set_mem(info, SH_OP_MEM_PCR, SH_REG_INVALID, address + 4 + dsp * 2, \
|
|
0, detail); \
|
|
return MCDisassembler_Success;\
|
|
}
|
|
|
|
static bool opLDRC(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int imm = code & 0xff;
|
|
if (!(mode & CS_MODE_SHDSP) || isalevel(mode) != ISA_SH4A)
|
|
return MCDisassembler_Fail;
|
|
MCInst_setOpcode(MI, SH_INS_LDRC);
|
|
set_imm(info, 0, imm);
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
opLDRSE(LDRS)
|
|
opLDRSE(LDRE)
|
|
|
|
#define opImmR0(insn) \
|
|
static bool op##insn##_i(uint16_t code, uint64_t address, MCInst *MI, \
|
|
cs_mode mode, sh_info *info, cs_detail *detail) \
|
|
{ \
|
|
MCInst_setOpcode(MI, SH_INS_##insn); \
|
|
set_imm(info, 0, code & 0xff); \
|
|
set_reg(info, SH_REG_R0, write, detail); \
|
|
return MCDisassembler_Success; \
|
|
}
|
|
|
|
opImmR0(TST)
|
|
opImmR0(AND)
|
|
opImmR0(XOR)
|
|
opImmR0(OR)
|
|
|
|
#define opImmMem(insn) \
|
|
static bool op##insn##_B(uint16_t code, uint64_t address, MCInst *MI, \
|
|
cs_mode mode, sh_info *info, cs_detail *detail) \
|
|
{ \
|
|
MCInst_setOpcode(MI, SH_INS_##insn); \
|
|
set_imm(info, 0, code & 0xff); \
|
|
set_mem(info, SH_OP_MEM_GBR_R0, SH_REG_R0, 0, 8, detail); \
|
|
return MCDisassembler_Success; \
|
|
}
|
|
|
|
opImmMem(TST)
|
|
opImmMem(AND)
|
|
opImmMem(XOR)
|
|
opImmMem(OR)
|
|
|
|
static bool opMOV_pc(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int sz = 16 << ((code >> 14) & 1);
|
|
int dsp = (code & 0x00ff) * (sz / 8);
|
|
int r = (code >> 8) & 0x0f;
|
|
MCInst_setOpcode(MI, SH_INS_MOV);
|
|
if (sz == 32)
|
|
address &= ~3;
|
|
set_mem(info, SH_OP_MEM_PCR, SH_REG_INVALID, address + 4 + dsp,
|
|
sz, detail);
|
|
set_reg(info, SH_REG_R0 + r, write, detail);
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
#define opBxx(insn, grp) \
|
|
static bool op##insn(uint16_t code, uint64_t address, MCInst *MI, \
|
|
cs_mode mode, sh_info *info, cs_detail *detail) \
|
|
{ \
|
|
int dsp = (code & 0x0fff); \
|
|
if (dsp >= 0x800) \
|
|
dsp = -0x1000 + dsp; \
|
|
MCInst_setOpcode(MI, SH_INS_##insn); \
|
|
set_mem(info, SH_OP_MEM_PCR, SH_REG_INVALID, address + 4 + dsp * 2, \
|
|
0, detail); \
|
|
if (detail) \
|
|
set_groups(detail, 2, grp, SH_GRP_BRANCH_RELATIVE); \
|
|
return MCDisassembler_Success; \
|
|
}
|
|
|
|
opBxx(BRA, SH_GRP_JUMP)
|
|
opBxx(BSR, SH_GRP_CALL)
|
|
|
|
static bool opMOV_gbr(uint16_t code, uint64_t address, MCInst *MI,
|
|
cs_mode mode, sh_info *info, cs_detail *detail)
|
|
{
|
|
int sz = 8 << ((code >> 8) & 0x03);
|
|
int dsp = (code & 0x00ff) * (sz / 8);
|
|
int rw = (code >> 10) & 1;
|
|
MCInst_setOpcode(MI, SH_INS_MOV);
|
|
set_mem_n(info, SH_OP_MEM_GBR_DISP, SH_REG_GBR, dsp, sz,
|
|
1 - rw, detail);
|
|
set_reg_n(info, SH_REG_R0, rw, rw, detail);
|
|
info->op.op_count = 2;
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
static bool opTRAPA(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
MCInst_setOpcode(MI, SH_INS_TRAPA);
|
|
set_imm(info, 0, code & 0xff);
|
|
if (detail)
|
|
set_groups(detail, 1, SH_GRP_INT);
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
static bool opMOVA(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int dsp = (code & 0x00ff) * 4;
|
|
MCInst_setOpcode(MI, SH_INS_MOVA);
|
|
set_mem(info, SH_OP_MEM_PCR, SH_REG_INVALID, (address & ~3) + 4 + dsp,
|
|
0, detail);
|
|
set_reg(info, SH_REG_R0, write, detail);
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
static bool opMOV_i(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int imm = (code & 0x00ff);
|
|
int r = (code >> 8) & 0x0f;
|
|
MCInst_setOpcode(MI, SH_INS_MOV);
|
|
set_imm(info, 1, imm);
|
|
set_reg(info, SH_REG_R0 + r, write, detail);
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
/* FPU instructions */
|
|
#define opFRR(insn) \
|
|
static bool op##insn(uint16_t code, uint64_t address, MCInst *MI, \
|
|
cs_mode mode, sh_info *info, cs_detail *detail) \
|
|
{ \
|
|
int m = (code >> 4) & 0x0f; \
|
|
int n = (code >> 8) & 0x0f; \
|
|
MCInst_setOpcode(MI, SH_INS_##insn); \
|
|
set_reg(info, SH_REG_FR0 + m, read, detail); \
|
|
set_reg(info, SH_REG_FR0 + n, write, detail); \
|
|
return MCDisassembler_Success; \
|
|
}
|
|
|
|
#define opFRRcmp(insn) \
|
|
static bool op##insn(uint16_t code, uint64_t address, MCInst *MI, \
|
|
cs_mode mode, sh_info *info, cs_detail *detail) \
|
|
{ \
|
|
int m = (code >> 4) & 0x0f; \
|
|
int n = (code >> 8) & 0x0f; \
|
|
MCInst_setOpcode(MI, SH_INS_##insn); \
|
|
set_reg(info, SH_REG_FR0 + m, read, detail); \
|
|
set_reg(info, SH_REG_FR0 + n, read, detail); \
|
|
return MCDisassembler_Success; \
|
|
}
|
|
|
|
opFRR(FADD)
|
|
opFRR(FSUB)
|
|
opFRR(FMUL)
|
|
opFRR(FDIV)
|
|
opFRRcmp(FCMP_EQ)
|
|
opFRRcmp(FCMP_GT)
|
|
|
|
static bool opFMOVm(MCInst *MI, enum direction rw, uint16_t code,
|
|
sh_op_mem_type address, sh_info *info, cs_detail *detail)
|
|
{
|
|
nm(code, (1 - rw));
|
|
MCInst_setOpcode(MI, SH_INS_FMOV);
|
|
set_mem_n(info, address, SH_REG_R0 + m, 0, 0, 1 - rw, detail);
|
|
set_reg_n(info, SH_REG_FR0 + n, rw, rw, detail);
|
|
info->op.op_count = 2;
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
static bool opfxx6(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
return opFMOVm(MI, write, code, SH_OP_MEM_REG_R0, info, detail);
|
|
}
|
|
|
|
static bool opfxx7(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
return opFMOVm(MI, read, code, SH_OP_MEM_REG_R0, info, detail);
|
|
}
|
|
|
|
static bool opfxx8(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
return opFMOVm(MI, write, code, SH_OP_MEM_REG_IND, info, detail);
|
|
}
|
|
|
|
static bool opfxx9(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
return opFMOVm(MI, write, code, SH_OP_MEM_REG_POST, info, detail);
|
|
}
|
|
|
|
static bool opfxxa(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
return opFMOVm(MI, read, code, SH_OP_MEM_REG_IND, info, detail);
|
|
}
|
|
|
|
static bool opfxxb(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
return opFMOVm(MI, read, code, SH_OP_MEM_REG_PRE, info, detail);
|
|
}
|
|
|
|
static bool opFMOV(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
nm(code, 0);
|
|
MCInst_setOpcode(MI, SH_INS_FMOV);
|
|
set_reg(info, SH_REG_FR0 + m, read, detail);
|
|
set_reg(info, SH_REG_FR0 + n, write, detail);
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
static bool opfxxd(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int fr = (code >> 8) & 0x0f;
|
|
int dr = (code >> 9) & 0x07;
|
|
int fvn = (code >> 10) & 0x03;
|
|
int fvm = (code >> 8) & 0x03;
|
|
sh_insn insn = SH_INS_INVALID;
|
|
sh_reg s, d;
|
|
static const struct ri_list list[] = {
|
|
{0, SH_INS_FSTS, ISA_ALL, shfpu},
|
|
{1, SH_INS_FLDS, ISA_ALL, shfpu},
|
|
{2, SH_INS_FLOAT, ISA_ALL, shfpu},
|
|
{3, SH_INS_FTRC, ISA_ALL, shfpu},
|
|
{4, SH_INS_FNEG, ISA_ALL, shfpu},
|
|
{5, SH_INS_FABS, ISA_ALL, shfpu},
|
|
{6, SH_INS_FSQRT, ISA_ALL, shfpu},
|
|
{7, SH_INS_FSRRA, ISA_ALL, shfpu},
|
|
{8, SH_INS_FLDI0, ISA_ALL, shfpu},
|
|
{9, SH_INS_FLDI1, ISA_ALL, shfpu},
|
|
{10, SH_INS_FCNVSD, ISA_SH4A, shfpu},
|
|
{11, SH_INS_FCNVDS, ISA_SH4A, shfpu},
|
|
{14, SH_INS_FIPR, ISA_SH4A, shfpu},
|
|
{-1, SH_INS_INVALID, ISA_ALL, none},
|
|
};
|
|
static const sh_insn chg[] = {
|
|
SH_INS_FSCHG, SH_INS_FPCHG, SH_INS_FRCHG, SH_INS_INVALID
|
|
};
|
|
insn = lookup_insn(list, (code >> 4) & 0x0f, mode);
|
|
s = d = SH_REG_FPUL;
|
|
if (insn != SH_INS_INVALID) {
|
|
switch((code >> 4) & 0x0f) {
|
|
case 0:
|
|
case 2:
|
|
d = SH_REG_FR0 + fr;
|
|
break;
|
|
case 1:
|
|
case 3:
|
|
s = SH_REG_FR0 + fr;
|
|
break;
|
|
case 10:
|
|
d = SH_REG_DR0 + dr;
|
|
break;
|
|
case 11:
|
|
s = SH_REG_DR0 + dr;
|
|
break;
|
|
case 14:
|
|
s = SH_REG_FV0 + fvm;
|
|
d = SH_REG_FV0 + fvn;
|
|
break;
|
|
default:
|
|
s = SH_REG_FR0 + fr;
|
|
d = SH_REG_INVALID;
|
|
break;
|
|
}
|
|
} else if ((code & 0x00f0) == 0x00f0) {
|
|
if ((code & 0x01ff) == 0x00fd) {
|
|
insn = SH_INS_FSCA;
|
|
d = SH_REG_DR0 + dr;
|
|
}
|
|
if ((code & 0x03ff) == 0x01fd) {
|
|
insn = SH_INS_FTRV;
|
|
s = SH_REG_XMATRX;
|
|
d = SH_REG_FV0 + fvn;
|
|
}
|
|
if ((code & 0x03ff) == 0x03fd) {
|
|
insn = chg[(code >> 10) & 3];
|
|
s = d = SH_REG_INVALID;
|
|
}
|
|
}
|
|
if (insn == SH_INS_INVALID) {
|
|
return MCDisassembler_Fail;
|
|
}
|
|
MCInst_setOpcode(MI, insn);
|
|
if (s != SH_REG_INVALID) {
|
|
set_reg(info, s, read, detail);
|
|
}
|
|
if (d != SH_REG_INVALID) {
|
|
set_reg(info, d, write, detail);
|
|
}
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
static bool opFMAC(uint16_t code, uint64_t address, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int m = (code >> 4) & 0x0f;
|
|
int n = (code >> 8) & 0x0f;
|
|
MCInst_setOpcode(MI, SH_INS_FMAC);
|
|
set_reg(info, SH_REG_FR0, read, detail);
|
|
set_reg(info, SH_REG_FR0 + m, read, detail);
|
|
set_reg(info, SH_REG_FR0 + n, write, detail);
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
#include "SHInsnTable.inc"
|
|
|
|
static bool decode_long(uint32_t code, uint64_t address, MCInst *MI,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
uint32_t imm;
|
|
sh_insn insn = SH_INS_INVALID;
|
|
int m,n;
|
|
int dsp;
|
|
int sz;
|
|
static const sh_insn bop[] = {
|
|
SH_INS_BCLR, SH_INS_BSET, SH_INS_BST, SH_INS_BLD,
|
|
SH_INS_BAND, SH_INS_BOR, SH_INS_BXOR, SH_INS_INVALID,
|
|
SH_INS_INVALID, SH_INS_INVALID, SH_INS_INVALID, SH_INS_BLDNOT,
|
|
SH_INS_BANDNOT, SH_INS_BORNOT, SH_INS_INVALID, SH_INS_INVALID,
|
|
};
|
|
switch (code >> 28) {
|
|
case 0x0:
|
|
imm = ((code >> 4) & 0x000f0000) | (code & 0xffff);
|
|
n = (code >> 24) & 0x0f;
|
|
if (code & 0x00010000) {
|
|
// movi20s #imm,
|
|
imm <<= 8;
|
|
if (imm >= 1 << 27)
|
|
imm = -((1 << 28) - imm);
|
|
insn = SH_INS_MOVI20S;
|
|
} else {
|
|
// MOVI20
|
|
if (imm >= 1 << 19)
|
|
imm = -((1 << 20) - imm);
|
|
insn = SH_INS_MOVI20;
|
|
}
|
|
set_imm(info, 0, imm);
|
|
set_reg(info, SH_REG_R0 + n, write, detail);
|
|
break;
|
|
case 0x3:
|
|
n = (code >> 24) & 0x0f;
|
|
m = (code >> 20) & 0x0f;
|
|
sz = (code >> 12) & 0x03;
|
|
dsp = code & 0xfff;
|
|
if (!(code & 0x80000)) {
|
|
dsp <<= sz;
|
|
switch((code >> 14) & 0x3) {
|
|
case 0: // mov.[bwl] Rm,@(disp,Rn)
|
|
// fmov.s DRm,@(disp,Rn)
|
|
if (sz < 3) {
|
|
insn = SH_INS_MOV;
|
|
set_reg(info, SH_REG_R0 + m,
|
|
read, detail);
|
|
} else {
|
|
insn = SH_INS_FMOV;
|
|
set_reg(info, SH_REG_DR0 + (m >> 1),
|
|
read, detail);
|
|
}
|
|
set_mem(info, SH_OP_MEM_REG_DISP,
|
|
SH_REG_R0 + n, dsp, 8 << sz, detail);
|
|
break;
|
|
case 1: // mov.[bwl] @(disp,Rm),Rn
|
|
// fmov.s @(disp,Rm),DRn
|
|
set_mem(info, SH_OP_MEM_REG_DISP,
|
|
SH_REG_R0 + m, dsp, 8 << sz, detail);
|
|
if (sz < 3) {
|
|
insn = SH_INS_MOV;
|
|
set_reg(info, SH_REG_R0 + n,
|
|
write, detail);
|
|
} else {
|
|
insn = SH_INS_FMOV;
|
|
set_reg(info, SH_REG_DR0 + (n >> 1),
|
|
write, detail);
|
|
}
|
|
break;
|
|
case 2: // movu.[bwl] @(disp,Rm),Rn
|
|
if (sz < 2) {
|
|
insn = SH_INS_MOVU;
|
|
set_mem(info, SH_OP_MEM_REG_DISP,
|
|
SH_REG_R0 + m, dsp,
|
|
8 << sz, detail);
|
|
set_reg(info, SH_REG_R0 + n,
|
|
write, detail);
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
// bitop #imm,@(disp,Rn)
|
|
insn = bop[(code >> 12) & 0x0f];
|
|
set_imm(info, 0, m & 7);
|
|
set_mem(info, SH_OP_MEM_REG_DISP, SH_REG_R0 + n,
|
|
dsp, 8, detail);
|
|
}
|
|
}
|
|
if (insn != SH_INS_INVALID) {
|
|
MCInst_setOpcode(MI, insn);
|
|
return MCDisassembler_Success;
|
|
} else {
|
|
return MCDisassembler_Fail;
|
|
}
|
|
}
|
|
|
|
static const sh_reg dsp_areg[2][4] = {
|
|
{SH_REG_R4, SH_REG_R0, SH_REG_R5, SH_REG_R1},
|
|
{SH_REG_R6, SH_REG_R7, SH_REG_R2, SH_REG_R3},
|
|
};
|
|
|
|
static bool decode_dsp_xy(sh_info *info, int xy, uint16_t code,
|
|
cs_detail *detail)
|
|
{
|
|
int a = (code >> 8) & 3;
|
|
int d = (code >> 6) & 3;
|
|
int dir;
|
|
int sz;
|
|
int op;
|
|
|
|
static const sh_reg dreg[4][4] = {
|
|
{SH_REG_DSP_A0, SH_REG_DSP_X0, SH_REG_DSP_A1, SH_REG_DSP_X1},
|
|
{SH_REG_DSP_A0, SH_REG_DSP_A1, SH_REG_DSP_Y0, SH_REG_DSP_Y1},
|
|
{SH_REG_DSP_X0, SH_REG_DSP_Y0, SH_REG_DSP_X1, SH_REG_DSP_Y1},
|
|
{SH_REG_DSP_Y0, SH_REG_DSP_Y1, SH_REG_DSP_X0, SH_REG_DSP_X1},
|
|
};
|
|
|
|
if (xy) {
|
|
op = code & 3;
|
|
dir = 1 - ((code >> 4) & 1);
|
|
sz = (code >> 5) & 1;
|
|
if (code & 0x0c) {
|
|
info->op.operands[xy].dsp.insn = SH_INS_DSP_NOP;
|
|
return MCDisassembler_Success;
|
|
}
|
|
} else {
|
|
op = (code >> 2) & 3;
|
|
dir = 1 - ((code >> 5) & 1);
|
|
sz = (code >> 4) & 1;
|
|
if (code & 0x03) {
|
|
info->op.operands[xy].dsp.insn = SH_INS_DSP_NOP;
|
|
return MCDisassembler_Success;
|
|
}
|
|
}
|
|
info->op.operands[xy].dsp.size = 16 << sz;
|
|
info->op.operands[xy].dsp.insn = SH_INS_DSP_MOV;
|
|
info->op.operands[xy].dsp.operand[1 - dir] =
|
|
SH_OP_DSP_REG_IND + (op - 1);
|
|
info->op.operands[xy].dsp.operand[dir] = SH_OP_DSP_REG;
|
|
info->op.operands[xy].dsp.r[1 - dir] = dsp_areg[xy][a];
|
|
info->op.operands[xy].dsp.size = 16 << sz;
|
|
regs_rw(detail, dir,
|
|
info->op.operands[xy].dsp.r[dir] = dreg[xy * 2 + dir][d]);
|
|
switch(op) {
|
|
case 0x03:
|
|
regs_read(detail, SH_REG_R8 + xy);
|
|
// Fail through
|
|
case 0x02:
|
|
regs_write(detail, dsp_areg[xy][a]);
|
|
break;
|
|
case 0x01:
|
|
regs_read(detail, dsp_areg[xy][a]);
|
|
break;
|
|
default:
|
|
return MCDisassembler_Fail;
|
|
}
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
static bool set_dsp_move_d(sh_info *info, int xy, uint16_t code, cs_mode mode, cs_detail *detail)
|
|
{
|
|
int a;
|
|
int d;
|
|
int dir;
|
|
int op;
|
|
static const sh_reg base[] = {SH_REG_DSP_A0, SH_REG_DSP_X0};
|
|
switch (xy) {
|
|
case 0:
|
|
op = (code >> 2) & 3;
|
|
dir = 1 - ((code >> 5) & 1);
|
|
d = (code >> 7) & 1;
|
|
a = (code >> 9) & 1;
|
|
break;
|
|
case 1:
|
|
op = (code >> 0) & 3;
|
|
dir = 1 - ((code >> 4) & 1);
|
|
d = (code >> 6) & 1;
|
|
a = (code >> 8) & 1;
|
|
break;
|
|
}
|
|
if (op == 0x00) {
|
|
if ((a || d || dir) && !(code & 0x0f))
|
|
return MCDisassembler_Fail;
|
|
info->op.operands[xy].dsp.insn = SH_INS_DSP_NOP;
|
|
} else {
|
|
info->op.operands[xy].dsp.insn = SH_INS_DSP_MOV;
|
|
info->op.operands[xy].dsp.operand[1 - dir] =
|
|
SH_OP_DSP_REG_IND + (op - 1);
|
|
info->op.operands[xy].dsp.operand[dir] = SH_OP_DSP_REG;
|
|
info->op.operands[xy].dsp.r[1 - dir] = SH_REG_R4 + xy * 2 + a;
|
|
info->op.operands[xy].dsp.size = 16;
|
|
regs_rw(detail, dir,
|
|
info->op.operands[xy].dsp.r[dir] =
|
|
base[dir] + d + dir?(xy * 2):0);
|
|
switch(op) {
|
|
case 0x03:
|
|
regs_read(detail, SH_REG_R8 + a);
|
|
// Fail through
|
|
case 0x02:
|
|
regs_write(detail, SH_REG_R4 + xy * 2 + a);
|
|
break;
|
|
case 0x01:
|
|
regs_read(detail, SH_REG_R4 + xy * 2 + a);
|
|
break;
|
|
}
|
|
}
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
static bool decode_dsp_d(const uint16_t code, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
bool ret, dsp_long;
|
|
MCInst_setOpcode(MI, SH_INS_DSP);
|
|
if ((code & 0x3ff) == 0) {
|
|
info->op.operands[0].dsp.insn =
|
|
info->op.operands[1].dsp.insn = SH_INS_DSP_NOP;
|
|
info->op.op_count = 2;
|
|
return MCDisassembler_Success;
|
|
}
|
|
dsp_long = false;
|
|
if (isalevel(mode) == ISA_SH4A) {
|
|
if (!(code & 0x03) && (code & 0x0f) >= 0x04) {
|
|
ret = decode_dsp_xy(info, 0, code, detail);
|
|
ret &= set_dsp_move_d(info, 1, code, mode, detail);
|
|
dsp_long |= true;
|
|
}
|
|
if ((code & 0x0f) <= 0x03 && (code & 0xff)) {
|
|
ret = decode_dsp_xy(info, 1, code, detail);
|
|
ret &= set_dsp_move_d(info, 0, code, mode, detail);
|
|
dsp_long |= true;
|
|
}
|
|
}
|
|
if (!dsp_long) {
|
|
/* X op */
|
|
ret = set_dsp_move_d(info, 0, code, mode, detail);
|
|
/* Y op */
|
|
ret &= set_dsp_move_d(info, 1, code, mode, detail);
|
|
}
|
|
|
|
info->op.op_count = 2;
|
|
return ret;
|
|
}
|
|
|
|
static bool decode_dsp_s(const uint16_t code, MCInst *MI,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int d = code & 1;
|
|
int s = (code >> 1) & 1;
|
|
int opr = (code >> 2) & 3;
|
|
int as = (code >> 8) & 3;
|
|
int ds = (code >> 4) & 0x0f;
|
|
static const sh_reg regs[] = {
|
|
SH_REG_DSP_RSV0, SH_REG_DSP_RSV1, SH_REG_DSP_RSV2,
|
|
SH_REG_DSP_RSV3,
|
|
SH_REG_DSP_RSV4, SH_REG_DSP_A1, SH_REG_DSP_RSV6, SH_REG_DSP_A0,
|
|
SH_REG_DSP_X0, SH_REG_DSP_X1, SH_REG_DSP_Y0, SH_REG_DSP_Y1,
|
|
SH_REG_DSP_M0, SH_REG_DSP_A1G, SH_REG_DSP_M1, SH_REG_DSP_A0G,
|
|
};
|
|
|
|
if (regs[ds] == SH_REG_INVALID)
|
|
return MCDisassembler_Fail;
|
|
|
|
MCInst_setOpcode(MI, SH_INS_DSP);
|
|
info->op.operands[0].dsp.insn = SH_INS_DSP_MOV;
|
|
info->op.operands[0].dsp.operand[1 - d] = SH_OP_DSP_REG;
|
|
info->op.operands[0].dsp.operand[d] = SH_OP_DSP_REG_PRE + opr;
|
|
info->op.operands[0].dsp.r[1 - d] = regs[ds];
|
|
info->op.operands[0].dsp.r[d] = SH_REG_R2 + ((as < 2)?(as+2):(as-2));
|
|
switch (opr) {
|
|
case 3:
|
|
regs_read(detail, SH_REG_R8);
|
|
/* Fail through */
|
|
case 1:
|
|
regs_read(detail, info->op.operands[0].dsp.r[d]);
|
|
break;
|
|
case 0:
|
|
case 2:
|
|
regs_write(detail, info->op.operands[0].dsp.r[d]);
|
|
}
|
|
regs_rw(detail, d, regs[ds]);
|
|
info->op.operands[0].dsp.size = 16 << s;
|
|
info->op.op_count = 1;
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
static const sh_reg dsp_reg_sd[6][4] = {
|
|
{SH_REG_DSP_X0, SH_REG_DSP_X1, SH_REG_DSP_Y0, SH_REG_DSP_A1},
|
|
{SH_REG_DSP_Y0, SH_REG_DSP_Y1, SH_REG_DSP_X0, SH_REG_DSP_A1},
|
|
{SH_REG_DSP_X0, SH_REG_DSP_X1, SH_REG_DSP_A0, SH_REG_DSP_A1},
|
|
{SH_REG_DSP_Y0, SH_REG_DSP_Y1, SH_REG_DSP_M0, SH_REG_DSP_M1},
|
|
{SH_REG_DSP_M0, SH_REG_DSP_M1, SH_REG_DSP_A0, SH_REG_DSP_A1},
|
|
{SH_REG_DSP_X0, SH_REG_DSP_Y0, SH_REG_DSP_A0, SH_REG_DSP_A1},
|
|
};
|
|
typedef enum {f_se, f_sf, f_sx, f_sy, f_dg, f_du} dsp_reg_opr;
|
|
static void set_reg_dsp_read(sh_info *info, int pos, dsp_reg_opr f, int r,
|
|
cs_detail *detail)
|
|
{
|
|
info->op.operands[2].dsp.r[pos] = dsp_reg_sd[f][r];
|
|
regs_read(detail, dsp_reg_sd[f][r]);
|
|
}
|
|
|
|
static void set_reg_dsp_write_gu(sh_info *info, int pos, dsp_reg_opr f, int r,
|
|
cs_detail *detail)
|
|
{
|
|
info->op.operands[2].dsp.r[pos] = dsp_reg_sd[f][r];
|
|
regs_write(detail, dsp_reg_sd[f][r]);
|
|
}
|
|
|
|
static const sh_reg regs_dz[] = {
|
|
SH_REG_DSP_RSV0, SH_REG_DSP_RSV1, SH_REG_DSP_RSV2, SH_REG_DSP_RSV3,
|
|
SH_REG_DSP_RSV4, SH_REG_DSP_A1, SH_REG_DSP_RSV6, SH_REG_DSP_A0,
|
|
SH_REG_DSP_X0, SH_REG_DSP_X1, SH_REG_DSP_Y0, SH_REG_DSP_Y1,
|
|
SH_REG_DSP_M0, SH_REG_DSP_A1G, SH_REG_DSP_M1, SH_REG_DSP_A0G,
|
|
};
|
|
|
|
static void set_reg_dsp_write_z(sh_info *info, int pos, int r,
|
|
cs_detail *detail)
|
|
{
|
|
info->op.operands[2].dsp.r[pos] = regs_dz[r];
|
|
regs_write(detail, regs_dz[r]);
|
|
}
|
|
|
|
static bool dsp_op_cc_3opr(uint32_t code, sh_info *info, sh_dsp_insn insn,
|
|
sh_dsp_insn_type insn2, cs_detail *detail)
|
|
{
|
|
info->op.operands[2].dsp.cc = (code >> 8) & 3;
|
|
if (info->op.operands[2].dsp.cc > 0) {
|
|
info->op.operands[2].dsp.insn = insn;
|
|
} else {
|
|
if (insn2 != SH_INS_DSP_INVALID)
|
|
info->op.operands[2].dsp.insn = (sh_dsp_insn) insn2;
|
|
else
|
|
return MCDisassembler_Fail;
|
|
}
|
|
if (info->op.operands[2].dsp.insn != SH_INS_DSP_PSUBr) {
|
|
set_reg_dsp_read(info, 0, f_sx, (code >> 6) & 3, detail);
|
|
set_reg_dsp_read(info, 1, f_sy, (code >> 4) & 3, detail);
|
|
} else {
|
|
set_reg_dsp_read(info, 1, f_sx, (code >> 6) & 3, detail);
|
|
set_reg_dsp_read(info, 0, f_sy, (code >> 4) & 3, detail);
|
|
}
|
|
set_reg_dsp_write_z(info, 2, code & 0x0f, detail);
|
|
info->op.op_count = 3;
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
static bool dsp_op_cc_2opr(uint32_t code, sh_info *info, sh_dsp_insn insn,
|
|
int xy, int b, cs_detail *detail)
|
|
{
|
|
if (((code >> 8) & 3) == 0)
|
|
return MCDisassembler_Fail;
|
|
info->op.operands[2].dsp.insn = (sh_dsp_insn) insn;
|
|
set_reg_dsp_read(info, 0, xy, (code >> b) & 3, detail);
|
|
set_reg_dsp_write_z(info, 2, code & 0x0f, detail);
|
|
info->op.operands[2].dsp.cc = (code >> 8) & 3;
|
|
info->op.op_count = 3;
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
static bool dsp_op_cc0_2opr(uint32_t code, sh_info *info, sh_dsp_insn insn,
|
|
int xy, int b, cs_detail *detail)
|
|
{
|
|
info->op.operands[2].dsp.insn = (sh_dsp_insn) insn;
|
|
set_reg_dsp_read(info, 0, xy, (code >> b) & 3, detail);
|
|
set_reg_dsp_write_z(info, 2, code & 0x0f, detail);
|
|
info->op.operands[2].dsp.cc = (code >> 8) & 3;
|
|
if (info->op.operands[2].dsp.cc == 1)
|
|
return MCDisassembler_Fail;
|
|
if (info->op.operands[2].dsp.cc == 0)
|
|
info->op.operands[2].dsp.cc = SH_DSP_CC_NONE;
|
|
info->op.op_count = 3;
|
|
return MCDisassembler_Success;
|
|
}
|
|
|
|
static bool decode_dsp_3op(const uint32_t code, sh_info *info,
|
|
cs_detail *detail)
|
|
{
|
|
int cc = (code >> 8) & 3;
|
|
int sx = (code >> 6) & 3;
|
|
int sy = (code >> 4) & 3;
|
|
int dz = (code >> 0) & 0x0f;
|
|
|
|
if ((code & 0xef00) == 0x8000)
|
|
return MCDisassembler_Fail;
|
|
switch((code >> 10) & 0x1f) {
|
|
case 0x00:
|
|
return dsp_op_cc_3opr(code, info,
|
|
SH_INS_DSP_PSHL, SH_INS_DSP_INVALID,
|
|
detail);
|
|
case 0x01:
|
|
if (cc == 0) {
|
|
info->op.operands[2].dsp.insn = SH_INS_DSP_PCMP;
|
|
set_reg_dsp_read(info, 0, f_sx, sx, detail);
|
|
set_reg_dsp_read(info, 1, f_sy, sy, detail);
|
|
info->op.op_count = 3;
|
|
return MCDisassembler_Success;
|
|
} else {
|
|
return dsp_op_cc_3opr(code, info,
|
|
SH_INS_DSP_PSUBr,
|
|
SH_INS_DSP_INVALID, detail);
|
|
}
|
|
case 0x02:
|
|
switch (sy) {
|
|
case 0:
|
|
if(cc == 0) {
|
|
info->op.operands[2].dsp.insn = SH_INS_DSP_PABS;
|
|
set_reg_dsp_read(info, 0, f_sx, sx, detail);
|
|
set_reg_dsp_write_z(info, 1, dz, detail);
|
|
info->op.op_count = 3;
|
|
return MCDisassembler_Success;
|
|
} else {
|
|
return dsp_op_cc_2opr(code, info,
|
|
SH_INS_DSP_PDEC,
|
|
f_sx, 6, detail);
|
|
}
|
|
case 1:
|
|
return dsp_op_cc0_2opr(code, info,
|
|
SH_INS_DSP_PABS,
|
|
f_sx, 6, detail);
|
|
default:
|
|
return MCDisassembler_Fail;
|
|
}
|
|
case 0x03:
|
|
if (cc != 0) {
|
|
info->op.operands[2].dsp.insn = SH_INS_DSP_PCLR;
|
|
info->op.operands[2].dsp.cc = cc;
|
|
set_reg_dsp_write_z(info, 0, dz, detail);
|
|
info->op.op_count = 3;
|
|
return MCDisassembler_Success;
|
|
} else
|
|
return MCDisassembler_Fail;
|
|
case 0x04:
|
|
return dsp_op_cc_3opr(code, info,
|
|
SH_INS_DSP_PSHA, SH_INS_DSP_INVALID,
|
|
detail);
|
|
case 0x05:
|
|
return dsp_op_cc_3opr(code, info,
|
|
SH_INS_DSP_PAND, SH_INS_DSP_INVALID,
|
|
detail);
|
|
case 0x06:
|
|
switch (sy) {
|
|
case 0:
|
|
if (cc == 0) {
|
|
info->op.operands[2].dsp.insn = SH_INS_DSP_PRND;
|
|
set_reg_dsp_read(info, 0, f_sx, sx, detail);
|
|
set_reg_dsp_write_z(info, 1, dz, detail);
|
|
info->op.op_count = 3;
|
|
return MCDisassembler_Success;
|
|
} else {
|
|
return dsp_op_cc_2opr(code, info,
|
|
SH_INS_DSP_PINC,
|
|
f_sx, 6, detail);
|
|
}
|
|
case 1:
|
|
return dsp_op_cc0_2opr(code, info,
|
|
SH_INS_DSP_PRND,
|
|
f_sx, 6, detail);
|
|
default:
|
|
return MCDisassembler_Fail;
|
|
}
|
|
case 0x07:
|
|
switch(sy) {
|
|
case 0:
|
|
return dsp_op_cc_2opr(code, info,
|
|
SH_INS_DSP_PDMSB,
|
|
f_sx, 6, detail);
|
|
case 1:
|
|
return dsp_op_cc_2opr(code, info,
|
|
SH_INS_DSP_PSWAP,
|
|
f_sx, 6, detail);
|
|
default:
|
|
return MCDisassembler_Fail;
|
|
}
|
|
case 0x08:
|
|
return dsp_op_cc_3opr(code, info,
|
|
SH_INS_DSP_PSUB, (sh_dsp_insn_type) SH_INS_DSP_PSUBC,
|
|
detail);
|
|
case 0x09:
|
|
return dsp_op_cc_3opr(code, info,
|
|
SH_INS_DSP_PXOR, (sh_dsp_insn_type) SH_INS_DSP_PWSB,
|
|
detail);
|
|
case 0x0a:
|
|
switch(sx) {
|
|
case 0:
|
|
if (cc == 0) {
|
|
info->op.operands[2].dsp.insn = SH_INS_DSP_PABS;
|
|
set_reg_dsp_read(info, 0, f_sy, sy, detail);
|
|
set_reg_dsp_write_z(info, 1, dz, detail);
|
|
info->op.op_count = 3;
|
|
return MCDisassembler_Success;
|
|
} else {
|
|
return dsp_op_cc_2opr(code, info,
|
|
SH_INS_DSP_PDEC,
|
|
f_sy, 4, detail);
|
|
}
|
|
case 1:
|
|
return dsp_op_cc_2opr(code, info,
|
|
SH_INS_DSP_PABS,
|
|
f_sy, 4, detail);
|
|
default:
|
|
return MCDisassembler_Fail;
|
|
}
|
|
case 0x0c:
|
|
if (cc == 0) {
|
|
info->op.operands[2].dsp.insn
|
|
= SH_INS_DSP_PADDC;
|
|
set_reg_dsp_read(info, 0, f_sx, sx, detail);
|
|
set_reg_dsp_read(info, 1, f_sy, sy, detail);
|
|
set_reg_dsp_write_z(info, 2, dz, detail);
|
|
info->op.op_count = 3;
|
|
return MCDisassembler_Success;
|
|
} else {
|
|
return dsp_op_cc_3opr(code, info,
|
|
SH_INS_DSP_PADD,
|
|
SH_INS_DSP_INVALID, detail);
|
|
}
|
|
case 0x0d:
|
|
return dsp_op_cc_3opr(code, info,
|
|
SH_INS_DSP_POR,
|
|
(sh_dsp_insn_type) SH_INS_DSP_PWAD,
|
|
detail);
|
|
case 0x0e:
|
|
if (cc == 0) {
|
|
if (sx != 0)
|
|
return MCDisassembler_Fail;
|
|
info->op.operands[2].dsp.insn = SH_INS_DSP_PRND;
|
|
set_reg_dsp_read(info, 0, f_sy, sy, detail);
|
|
set_reg_dsp_write_z(info, 1, dz, detail);
|
|
info->op.op_count = 3;
|
|
return MCDisassembler_Success;
|
|
} else {
|
|
switch(sx) {
|
|
case 0:
|
|
return dsp_op_cc_2opr(code, info,
|
|
SH_INS_DSP_PINC,
|
|
f_sy, 4, detail);
|
|
case 1:
|
|
return dsp_op_cc_2opr(code, info,
|
|
SH_INS_DSP_PRND,
|
|
f_sy, 4, detail);
|
|
default:
|
|
return MCDisassembler_Fail;
|
|
}
|
|
}
|
|
case 0x0f:
|
|
switch(sx) {
|
|
case 0:
|
|
return dsp_op_cc_2opr(code, info,
|
|
SH_INS_DSP_PDMSB,
|
|
f_sy, 4, detail);
|
|
case 1:
|
|
return dsp_op_cc_2opr(code, info,
|
|
SH_INS_DSP_PSWAP,
|
|
f_sy, 4, detail);
|
|
default:
|
|
return MCDisassembler_Fail;
|
|
}
|
|
case 0x12:
|
|
return dsp_op_cc_2opr(code, info,
|
|
SH_INS_DSP_PNEG, f_sx, 6, detail);
|
|
case 0x13:
|
|
case 0x17:
|
|
if (cc > 0) {
|
|
info->op.operands[2].dsp.insn = SH_INS_DSP_PSTS;
|
|
info->op.operands[2].dsp.cc = cc;
|
|
regs_read(detail,
|
|
info->op.operands[2].dsp.r[0]
|
|
= SH_REG_MACH + ((code >> 12) & 1));
|
|
set_reg_dsp_write_z(info, 1, dz, detail);
|
|
info->op.op_count = 3;
|
|
return MCDisassembler_Success;
|
|
} else {
|
|
return MCDisassembler_Fail;
|
|
}
|
|
case 0x16:
|
|
return dsp_op_cc_2opr(code, info,
|
|
SH_INS_DSP_PCOPY, f_sx, 6, detail);
|
|
case 0x1a:
|
|
return dsp_op_cc_2opr(code, info,
|
|
SH_INS_DSP_PNEG, f_sy, 4, detail);
|
|
case 0x1b:
|
|
case 0x1f:
|
|
if (cc > 0) {
|
|
info->op.operands[2].dsp.insn = SH_INS_DSP_PLDS;
|
|
info->op.operands[2].dsp.cc = cc;
|
|
info->op.operands[2].dsp.r[0] = regs_dz[dz];
|
|
regs_read(detail, regs_dz[dz]);
|
|
regs_write(detail,
|
|
info->op.operands[2].dsp.r[1]
|
|
= SH_REG_MACH + ((code >> 12) & 1));
|
|
info->op.op_count = 3;
|
|
return MCDisassembler_Success;
|
|
} else {
|
|
return MCDisassembler_Fail;
|
|
}
|
|
case 0x1e:
|
|
return dsp_op_cc_2opr(code, info, SH_INS_DSP_PCOPY, f_sy, 4, detail);
|
|
default:
|
|
return MCDisassembler_Fail;
|
|
}
|
|
}
|
|
|
|
static bool decode_dsp_p(const uint32_t code, MCInst *MI, cs_mode mode,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int dz = code & 0x0f;
|
|
MCInst_setOpcode(MI, SH_INS_DSP);
|
|
if (!decode_dsp_d(code >> 16, MI, mode, info, detail))
|
|
return MCDisassembler_Fail;
|
|
|
|
switch((code >> 12) & 0x0f) {
|
|
case 0x00:
|
|
case 0x01:
|
|
if ((code >> 11) & 1)
|
|
return MCDisassembler_Fail;
|
|
info->op.operands[2].dsp.insn
|
|
= SH_INS_DSP_PSHL + ((code >> 12) & 1);
|
|
info->op.operands[2].dsp.imm = (code >> 4) & 0x7f;
|
|
set_reg_dsp_write_z(info, 1, dz, detail);
|
|
info->op.op_count = 3;
|
|
return MCDisassembler_Success;
|
|
case 0x04:
|
|
if ((((code >> 4) & 1) && isalevel(mode) != ISA_SH4A) ||
|
|
(!((code >> 4) & 1) && (code &3)) ||
|
|
((code >> 4) & 0x0f) >= 2)
|
|
return MCDisassembler_Fail;
|
|
|
|
info->op.operands[2].dsp.insn
|
|
= SH_INS_DSP_PMULS + ((code >> 4) & 1);
|
|
set_reg_dsp_read(info, 0, f_se, (code >> 10) & 3, detail);
|
|
set_reg_dsp_read(info, 1, f_sf, (code >> 8) & 3, detail);
|
|
set_reg_dsp_write_gu(info, 2, f_dg, (code >> 2) & 3, detail);
|
|
if ((code >> 4) & 1)
|
|
set_reg_dsp_write_gu(info, 3, f_du,
|
|
(code >> 0) & 3, detail);
|
|
info->op.op_count = 3;
|
|
return MCDisassembler_Success;
|
|
case 0x06:
|
|
case 0x07:
|
|
info->op.operands[2].dsp.insn
|
|
= SH_INS_DSP_PSUB_PMULS + ((code >> 12) & 1);
|
|
set_reg_dsp_read(info, 0, f_sx, (code >> 6) & 3, detail);
|
|
set_reg_dsp_read(info, 1, f_sy, (code >> 4) & 3, detail);
|
|
set_reg_dsp_write_gu(info, 2, f_du, (code >> 0) & 3, detail);
|
|
set_reg_dsp_read(info, 3, f_se, (code >> 10) & 3, detail);
|
|
set_reg_dsp_read(info, 4, f_sf, (code >> 8) & 3, detail);
|
|
set_reg_dsp_write_gu(info, 5, f_dg, (code >> 2) & 3, detail);
|
|
info->op.op_count = 3;
|
|
return MCDisassembler_Success;
|
|
default:
|
|
if ((code >> 15) & 1)
|
|
return decode_dsp_3op(code, info, detail);
|
|
}
|
|
return MCDisassembler_Fail;
|
|
}
|
|
|
|
static bool sh_disassemble(const uint8_t *code, MCInst *MI, uint64_t address,
|
|
cs_mode mode, uint16_t *size, int code_len,
|
|
sh_info *info, cs_detail *detail)
|
|
{
|
|
int idx;
|
|
uint32_t insn;
|
|
bool dsp_result;
|
|
if (MODE_IS_BIG_ENDIAN(mode)) {
|
|
insn = code[0] << 8 | code[1];
|
|
} else {
|
|
insn = code[1] << 8 | code[0];
|
|
}
|
|
if (mode & CS_MODE_SH2A) {
|
|
/* SH2A 32bit instrcution test */
|
|
if (((insn & 0xf007) == 0x3001 ||
|
|
(insn & 0xf00e) == 0x0000)) {
|
|
if (code_len < 4)
|
|
return MCDisassembler_Fail;
|
|
*size = 4;
|
|
// SH2A is only BIG ENDIAN.
|
|
insn <<= 16;
|
|
insn |= code[2] << 8 | code[3];
|
|
if (decode_long(insn, address, MI, info, detail))
|
|
return MCDisassembler_Success;
|
|
}
|
|
}
|
|
/* Co-processor instructions */
|
|
if ((insn & 0xf000) == 0xf000) {
|
|
if (mode & CS_MODE_SHDSP) {
|
|
dsp_result = MCDisassembler_Fail;
|
|
switch(insn >> 10 & 3) {
|
|
case 0:
|
|
*size = 2;
|
|
dsp_result = decode_dsp_d(insn, MI, mode,
|
|
info, detail);
|
|
break;
|
|
case 1:
|
|
*size = 2;
|
|
dsp_result = decode_dsp_s(insn, MI,
|
|
info, detail);
|
|
break;
|
|
case 2:
|
|
if (code_len < 4)
|
|
return MCDisassembler_Fail;
|
|
*size = 4;
|
|
if (MODE_IS_BIG_ENDIAN(mode)) {
|
|
insn <<= 16;
|
|
insn |= code[2] << 8 | code[3];
|
|
} else
|
|
insn |= (code[3] << 24)
|
|
| (code[2] << 16);
|
|
dsp_result = decode_dsp_p(insn, MI, mode,
|
|
info, detail);
|
|
break;
|
|
}
|
|
return dsp_result;
|
|
}
|
|
if ((mode & CS_MODE_SHFPU) == 0)
|
|
return MCDisassembler_Fail;
|
|
}
|
|
|
|
*size = 2;
|
|
if ((insn & 0xf000) >= 0x8000 && (insn & 0xf000) < 0xf000) {
|
|
idx = insn >> 8;
|
|
} else {
|
|
idx = ((insn >> 8) & 0xf0) | (insn & 0x000f);
|
|
}
|
|
|
|
if (decode[idx]) {
|
|
return decode[idx](insn, address, MI, mode, info, detail);
|
|
} else {
|
|
return MCDisassembler_Fail;
|
|
}
|
|
}
|
|
|
|
bool SH_getInstruction(csh ud, const uint8_t *code, size_t code_len,
|
|
MCInst *MI, uint16_t *size, uint64_t address, void *inst_info)
|
|
{
|
|
|
|
cs_struct* handle = (cs_struct *)ud;
|
|
sh_info *info = (sh_info *)handle->printer_info;
|
|
cs_detail *detail = MI->flat_insn->detail;
|
|
|
|
if (code_len < 2) {
|
|
*size = 0;
|
|
return MCDisassembler_Fail;
|
|
}
|
|
|
|
if (detail) {
|
|
memset(detail, 0, offsetof(cs_detail, sh)+sizeof(cs_sh));
|
|
}
|
|
memset(info, 0, sizeof(sh_info));
|
|
if (sh_disassemble(code, MI, address, handle->mode,
|
|
size, code_len, info, detail) == MCDisassembler_Fail) {
|
|
*size = 0;
|
|
return MCDisassembler_Fail;
|
|
} else {
|
|
return MCDisassembler_Success;
|
|
}
|
|
}
|
|
|
|
#ifndef CAPSTONE_DIET
|
|
void SH_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
|
|
|
|
|