Files
kaizen/external/capstone/arch/SH/SHDisassembler.c
T
iris 00cc9309cb Squashed 'external/ircolib/' changes from ce3cd726c..de6e324bd
de6e324bd separate emu thread
10d3daf86 Roms List improvements
95d202f37 Let's make the rom list process on a separate thread so the emulator doesnt take ages to load.
fc306967f Wow the ROM Header was just completely busted. Game list view works now
bad1691ee fuck this shit
2b59e5f46 game list in progress
d26417b83 remappable inputs in progress
ac4af8106 input
e72abc240 update readme
430139dc9 Qt6 frontend
3080d4d45 Fix this small bug too
08cd13b85 Cop0 unused functions do not actually pose a threat (as per manual). They don't do anything, so shall we.
61bb4fb44 make idle loop detection a little more specific with where the load goes
b037de4c3 SAZDFsdff
12e81e73e need to figure out why n64-systemtest loops indefinitely at some address that appears to be valid (i think it's me not invalidating the cache properly)
204f0e13b idle skipping seems to work!
cb8bb634a sdkfjlasdf
58e5c89c1 Fix compilation issue on my machine (no idea)
24fb2898e attempting more serious idle skipping
214719577 Place rsp.Step inside cached interpreter. Gains about 3 more fps
bb97dcc23 mmmmm
920b77d38 wjkhasdfjhkasdf
430ccdab4 it's a start...
4f42a673a Cached interpreter plays Mario 64. Start looking into RSP as well
c9a030787 idle skipping works!
5fbda03ce new idea
366637aba Idle skipping... maybe?
609fa2fb0 Cache instructions implemented but broken lmao. Commented out for now
e140a6d12 - Stop using inheritance for CPU, instead use composition. - Introduce KAIZEN_JIT_ENABLED optional define instead of relying on __aarch64__ and the like. - More cache work
68e613057 prep cache impl
811b4d809 fix clang format
fda755f7d idk
d5024ebbf small MI refactor in preparation of (eventually) implementing the RDRAM interface properly
694b45341 Merge commit '206dcdedf195fb320913584180edb12c7731e396' as 'external/SDL'
206dcdedf Squashed 'external/SDL/' content from commit 4d17b99d0a
4d16e1cb4 need to update sdl
848b19920 Fix compilation error
db61b5299 Merge commit 'e94a94559f28e49678fbcf72199a5258137b0fe9' as 'external/imgui'
e94a94559 Squashed 'external/imgui/' content from commit 02e9b8cac
52edb3757 need to update imgui
c1a705e86 Emulate weird JALR behaviour
4b4c32f4b Fix exception for "unusable COP1" in 4 instructions i missed accidentally (again)
df5828142 Bug putting 0s in the log everywhere
f8b580048 Make isviewer a sink to file
8241e9735 Fix exception for "unusable COP1" in 4 instructions i missed accidentally
b29715f20 small changes
d9a620bc1 make use of my new small utility library
0d1aa938e Add 'external/ircolib/' from commit 'ce3cd726c8df8388d554abf8bb55d55020eb4450'
e64eb40b3 Fuck git

git-subtree-dir: external/ircolib
git-subtree-split: de6e324bde
2026-06-15 11:56:38 +02:00

2227 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); // NOLINT(clang-analyzer-valist.Uninitialized)
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:
MCInst_setOpcode(MI, 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 << (28 - 1)))
imm |= ~((1 << 28) - 1);
insn = SH_INS_MOVI20S;
} else {
// MOVI20
if (imm & (1 << (28 - 1)))
imm |= ~((1 << 20) - 1);
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) {
default:
printf("Invalid xy value %" PRId32 "\n", xy);
return MCDisassembler_Fail;
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 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 = 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_INS_DSP_PSUBC,
detail);
case 0x09:
return dsp_op_cc_3opr(code, info,
SH_INS_DSP_PXOR, 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_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 instruction 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 (idx < ARR_SIZE(decode) && 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 {
if (detail)
detail->sh = info->op;
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