441 lines
10 KiB
C
441 lines
10 KiB
C
/* Capstone Disassembly Engine */
|
|
/* By Yoshinori Sato, 2022 */
|
|
|
|
#include <string.h>
|
|
|
|
#include "../../Mapping.h"
|
|
#include "SHInstPrinter.h"
|
|
|
|
|
|
#ifndef CAPSTONE_DIET
|
|
static const char* const s_reg_names[] = {
|
|
"invalid",
|
|
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
|
|
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
|
|
"r0_bank", "r1_bank", "r2_bank", "r3_bank",
|
|
"r4_bank", "r5_bank", "r6_bank", "r7_bank",
|
|
"fr0", "fr1", "fr2", "fr3", "fr4", "fr5", "fr6", "fr7",
|
|
"fr8", "fr9", "fr10", "fr11", "fr12", "fr13", "fr14", "fr15",
|
|
"dr0", "dr2", "dr4", "dr6", "dr8", "dr10", "dr12", "dr14",
|
|
"xd0", "xd2", "xd4", "xd6", "xd8", "xd10", "xd12", "xd14",
|
|
"xf0", "xf1", "xf2", "xf3", "xf4", "xf5", "xf6", "xf7",
|
|
"xf8", "xf9", "xf10", "xf11", "xf12", "xf13", "xf14", "xf15",
|
|
"fv0", "fv4", "fv8", "fv12",
|
|
"xmtrx",
|
|
"pc", "pr", "mach", "macl",
|
|
"sr", "gbr", "ssr", "spc", "sgr", "dbr", "vbr", "tbr",
|
|
"rs", "re", "mod",
|
|
"fpul", "fpscr",
|
|
"x0", "x1", "y0", "y1", "a0", "a1", "a0g", "a1g", "m0", "m1",
|
|
"dsr",
|
|
"0x0", "0x1", "0x2", "0x3", "0x4", "0x5", "0x6", "0x7",
|
|
"0x8", "0x9", "0xa", "0xb", "0xc", "0xd", "0xe", "0xf",
|
|
};
|
|
#endif
|
|
|
|
const char* SH_reg_name(csh handle, unsigned int reg)
|
|
{
|
|
#ifdef CAPSTONE_DIET
|
|
return NULL;
|
|
#else
|
|
if (reg >= ARR_SIZE(s_reg_names)) {
|
|
return NULL;
|
|
}
|
|
return s_reg_names[(int)reg];
|
|
#endif
|
|
}
|
|
|
|
|
|
void SH_get_insn_id(cs_struct* h, cs_insn* insn, unsigned int id)
|
|
{
|
|
insn->id = id; // These id's matches for sh
|
|
}
|
|
|
|
#ifndef CAPSTONE_DIET
|
|
static const char* const s_insn_names[] = {
|
|
"unknown",
|
|
"add", "add", "addc", "addv", "and",
|
|
"band", "bandnot", "bclr",
|
|
"bf", "bf/s", "bld", "bldnot", "bor", "bornot", "bra", "braf",
|
|
"bset", "bsr", "bsrf", "bst", "bt", "bt/s", "bxor",
|
|
"clips", "clipu",
|
|
"clrdmxy",
|
|
"clrmac", "clrs", "clrt",
|
|
"cmp/eq", "cmp/ge", "cmp/gt", "cmp/hi", "cmp/hs", "cmp/pl",
|
|
"cmp/pz", "cmp/str",
|
|
"div0s", "div0u", "div1",
|
|
"divs", "divu",
|
|
"dmuls.l", "dmulu.l",
|
|
"dt",
|
|
"exts", "exts", "extu", "extu",
|
|
"fabs", "fadd", "fcmp/eq", "fcmp/gt",
|
|
"fcnvds", "fcnvsd", "fdiv",
|
|
"fipr", "fldi0", "fldi1", "flds", "float",
|
|
"fmac", "fmov", "fmul", "fneg", "fpchg",
|
|
"frchg", "fsca", "fschg", "fsqrt", "fsrra",
|
|
"fsts", "fsub", "ftrc", "ftrv",
|
|
"icbi",
|
|
"jmp", "jsr", "jsr/n",
|
|
"ldbank",
|
|
"ldc", "ldrc", "ldre", "ldrs", "lds",
|
|
"ldtlb",
|
|
"mac.l", "mac.w",
|
|
"mov", "mova", "movca", "movco", "movi20", "movi20s",
|
|
"movli", "movml", "movmu", "movrt", "movt", "movu", "movua",
|
|
"mul.l", "mulr", "muls", "mulu",
|
|
"neg", "negc",
|
|
"nop",
|
|
"not", "nott",
|
|
"ocbi", "ocbp", "ocbwb",
|
|
"or",
|
|
"pref", "prefi",
|
|
"resbank",
|
|
"rotcl", "rotcr", "rotl", "rotr",
|
|
"rte", "rts", "rts/n", "rtv/n",
|
|
"setdmx", "setdmy", "setrc",
|
|
"sets", "sett",
|
|
"shad", "shal", "shar", "shld", "shll",
|
|
"shll16", "shll2", "shll8",
|
|
"shlr", "shlr16", "shlr2", "shlr8",
|
|
"sleep",
|
|
"stbank",
|
|
"stc", "sts",
|
|
"sub", "subc", "subv",
|
|
"swap", "swap",
|
|
"synco",
|
|
"tas",
|
|
"trapa",
|
|
"tst",
|
|
"xor",
|
|
"xtrct",
|
|
};
|
|
#endif
|
|
|
|
const char* SH_insn_name(csh handle, unsigned int id)
|
|
{
|
|
#ifdef CAPSTONE_DIET
|
|
return NULL;
|
|
#else
|
|
if (id >= ARR_SIZE(s_insn_names)) {
|
|
return NULL;
|
|
}
|
|
return s_insn_names[id];
|
|
#endif
|
|
}
|
|
|
|
#ifndef CAPSTONE_DIET
|
|
#endif
|
|
|
|
#ifndef CAPSTONE_DIET
|
|
static void print_dsp_double(SStream *O, sh_info *info, int xy)
|
|
{
|
|
char suffix_xy = 'x' + xy;
|
|
int i;
|
|
if (info->op.operands[xy].dsp.insn == SH_INS_DSP_NOP) {
|
|
if ((info->op.operands[0].dsp.insn == SH_INS_DSP_NOP) &&
|
|
(info->op.operands[1].dsp.insn == SH_INS_DSP_NOP)) {
|
|
SStream_concat(O, "nop%c", suffix_xy);
|
|
}
|
|
} else {
|
|
SStream_concat(O, "mov%c", suffix_xy);
|
|
switch(info->op.operands[xy].dsp.size) {
|
|
case 16:
|
|
SStream_concat0(O, ".w ");
|
|
break;
|
|
case 32:
|
|
SStream_concat0(O, ".l ");
|
|
break;
|
|
}
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
switch(info->op.operands[xy].dsp.operand[i]) {
|
|
default:
|
|
break;
|
|
case SH_OP_DSP_REG_IND:
|
|
SStream_concat(O, "@%s", s_reg_names[info->op.operands[xy].dsp.r[i]]);
|
|
break;
|
|
case SH_OP_DSP_REG_POST:
|
|
SStream_concat(O, "@%s+", s_reg_names[info->op.operands[xy].dsp.r[i]]);
|
|
break;
|
|
case SH_OP_DSP_REG_INDEX:
|
|
SStream_concat(O, "@%s+%s", s_reg_names[info->op.operands[xy].dsp.r[i]], s_reg_names[SH_REG_R8 + xy]);
|
|
break;
|
|
case SH_OP_DSP_REG:
|
|
SStream_concat(O, "%s", s_reg_names[info->op.operands[xy].dsp.r[i]]);
|
|
break;
|
|
}
|
|
if (i == 0)
|
|
SStream_concat0(O, ",");
|
|
}
|
|
}
|
|
if (xy == 0)
|
|
SStream_concat0(O, " ");
|
|
}
|
|
|
|
static const char *s_dsp_insns[] = {
|
|
"invalid",
|
|
"nop",
|
|
"mov",
|
|
"pshl",
|
|
"psha",
|
|
"pmuls",
|
|
"pclr_pmuls",
|
|
"psub_pmuls",
|
|
"padd_pmuls",
|
|
"psubc",
|
|
"paddc",
|
|
"pcmp",
|
|
"pabs",
|
|
"prnd",
|
|
"psub",
|
|
"psub",
|
|
"padd",
|
|
"pand",
|
|
"pxor",
|
|
"por",
|
|
"pdec",
|
|
"pinc",
|
|
"pclr",
|
|
"pdmsb",
|
|
"pneg",
|
|
"pcopy",
|
|
"psts",
|
|
"plds",
|
|
"pswap",
|
|
"pwad",
|
|
"pwsb",
|
|
};
|
|
|
|
static void print_dsp(SStream *O, sh_info *info)
|
|
{
|
|
int i;
|
|
switch(info->op.op_count) {
|
|
case 1:
|
|
// single transfer
|
|
SStream_concat0(O, "movs");
|
|
switch(info->op.operands[0].dsp.size) {
|
|
case 16:
|
|
SStream_concat0(O, ".w ");
|
|
break;
|
|
case 32:
|
|
SStream_concat0(O, ".l ");
|
|
break;
|
|
}
|
|
for (i = 0; i < 2; i++) {
|
|
switch(info->op.operands[0].dsp.operand[i]) {
|
|
default:
|
|
break;
|
|
case SH_OP_DSP_REG_PRE:
|
|
SStream_concat(O, "@-%s", s_reg_names[info->op.operands[0].dsp.r[i]]);
|
|
break;
|
|
case SH_OP_DSP_REG_IND:
|
|
SStream_concat(O, "@%s", s_reg_names[info->op.operands[0].dsp.r[i]]);
|
|
break;
|
|
case SH_OP_DSP_REG_POST:
|
|
SStream_concat(O, "@%s+", s_reg_names[info->op.operands[0].dsp.r[i]]);
|
|
break;
|
|
case SH_OP_DSP_REG_INDEX:
|
|
SStream_concat(O, "@%s+%s", s_reg_names[info->op.operands[0].dsp.r[i]],s_reg_names[SH_REG_R8]);
|
|
break;
|
|
case SH_OP_DSP_REG:
|
|
SStream_concat(O, "%s", s_reg_names[info->op.operands[0].dsp.r[i]]);
|
|
}
|
|
if (i == 0)
|
|
SStream_concat0(O, ",");
|
|
}
|
|
break;
|
|
case 2: // Double transfer
|
|
print_dsp_double(O, info, 0);
|
|
print_dsp_double(O, info, 1);
|
|
break;
|
|
case 3: // Parallel
|
|
switch(info->op.operands[2].dsp.cc) {
|
|
default:
|
|
break;
|
|
case SH_DSP_CC_DCT:
|
|
SStream_concat0(O,"dct ");
|
|
break;
|
|
case SH_DSP_CC_DCF:
|
|
SStream_concat0(O,"dcf ");
|
|
break;
|
|
}
|
|
switch(info->op.operands[2].dsp.insn) {
|
|
case SH_INS_DSP_PSUB_PMULS:
|
|
case SH_INS_DSP_PADD_PMULS:
|
|
switch(info->op.operands[2].dsp.insn) {
|
|
default:
|
|
break;
|
|
case SH_INS_DSP_PSUB_PMULS:
|
|
SStream_concat0(O, "psub ");
|
|
break;
|
|
case SH_INS_DSP_PADD_PMULS:
|
|
SStream_concat0(O, "padd ");
|
|
break;
|
|
}
|
|
for (i = 0; i < 6; i++) {
|
|
SStream_concat(O, "%s", s_reg_names[info->op.operands[2].dsp.r[i]]);
|
|
if ((i % 3) < 2)
|
|
SStream_concat0(O, ",");
|
|
if (i == 2)
|
|
SStream_concat(O, " %s ", s_dsp_insns[SH_INS_DSP_PMULS]);
|
|
}
|
|
break;
|
|
case SH_INS_DSP_PCLR_PMULS:
|
|
SStream_concat0(O, s_dsp_insns[SH_INS_DSP_PCLR]);
|
|
SStream_concat(O, " %s ", s_reg_names[info->op.operands[2].dsp.r[3]]);
|
|
SStream_concat(O, "%s ", s_dsp_insns[SH_INS_DSP_PMULS]);
|
|
for (i = 0; i < 3; i++) {
|
|
SStream_concat(O, "%s", s_reg_names[info->op.operands[2].dsp.r[i]]);
|
|
if (i < 2)
|
|
SStream_concat0(O, ",");
|
|
}
|
|
break;
|
|
|
|
default:
|
|
SStream_concat0(O, s_dsp_insns[info->op.operands[2].dsp.insn]);
|
|
SStream_concat0(O, " ");
|
|
for (i = 0; i < 3; i++) {
|
|
if (info->op.operands[2].dsp.r[i] == SH_REG_INVALID) {
|
|
if (i == 0) {
|
|
SStream_concat(O, "#%d", info->op.operands[2].dsp.imm);
|
|
}
|
|
} else
|
|
SStream_concat(O, "%s", s_reg_names[info->op.operands[2].dsp.r[i]]);
|
|
if (i < 2 && info->op.operands[2].dsp.r[i + 1] != SH_REG_INVALID)
|
|
SStream_concat0(O, ",");
|
|
}
|
|
}
|
|
|
|
if (info->op.operands[0].dsp.insn != SH_INS_DSP_NOP) {
|
|
SStream_concat0(O, " ");
|
|
print_dsp_double(O, info, 0);
|
|
}
|
|
if (info->op.operands[1].dsp.insn != SH_INS_DSP_NOP) {
|
|
SStream_concat0(O, " ");
|
|
print_dsp_double(O, info, 1);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void PrintMemop(SStream *O, sh_op_mem *op) {
|
|
switch(op->address) {
|
|
case SH_OP_MEM_INVALID:
|
|
break;
|
|
case SH_OP_MEM_REG_IND:
|
|
SStream_concat(O, "@%s", s_reg_names[op->reg]);
|
|
break;
|
|
case SH_OP_MEM_REG_POST:
|
|
SStream_concat(O, "@%s+", s_reg_names[op->reg]);
|
|
break;
|
|
case SH_OP_MEM_REG_PRE:
|
|
SStream_concat(O, "@-%s", s_reg_names[op->reg]);
|
|
break;
|
|
case SH_OP_MEM_REG_DISP:
|
|
SStream_concat(O, "@(%d,%s)", op->disp, s_reg_names[op->reg]);
|
|
break;
|
|
case SH_OP_MEM_REG_R0: /// <= R0 indexed
|
|
SStream_concat(O, "@(%s,%s)",
|
|
s_reg_names[SH_REG_R0], s_reg_names[op->reg]);
|
|
break;
|
|
case SH_OP_MEM_GBR_DISP: /// <= GBR based displaysment
|
|
SStream_concat(O, "@(%d,%s)",
|
|
op->disp, s_reg_names[SH_REG_GBR]);
|
|
break;
|
|
case SH_OP_MEM_GBR_R0: /// <= GBR based R0 indexed
|
|
SStream_concat(O, "@(%s,%s)",
|
|
s_reg_names[SH_REG_R0], s_reg_names[SH_REG_GBR]);
|
|
break;
|
|
case SH_OP_MEM_PCR: /// <= PC relative
|
|
SStream_concat(O, "0x%x", op->disp);
|
|
break;
|
|
case SH_OP_MEM_TBR_DISP: /// <= GBR based displaysment
|
|
SStream_concat(O, "@@(%d,%s)",
|
|
op->disp, s_reg_names[SH_REG_TBR]);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void SH_printInst(MCInst* MI, SStream* O, void* PrinterInfo)
|
|
{
|
|
#ifndef CAPSTONE_DIET
|
|
sh_info *info = (sh_info *)PrinterInfo;
|
|
int i;
|
|
int imm;
|
|
|
|
if (MI->Opcode == SH_INS_DSP) {
|
|
print_dsp(O, info);
|
|
return;
|
|
}
|
|
|
|
SStream_concat0(O, (char*)s_insn_names[MI->Opcode]);
|
|
switch(info->op.size) {
|
|
case 8:
|
|
SStream_concat0(O, ".b");
|
|
break;
|
|
case 16:
|
|
SStream_concat0(O, ".w");
|
|
break;
|
|
case 32:
|
|
SStream_concat0(O, ".l");
|
|
break;
|
|
case 64:
|
|
SStream_concat0(O, ".d");
|
|
break;
|
|
}
|
|
SStream_concat0(O, " ");
|
|
for (i = 0; i < info->op.op_count; i++) {
|
|
switch(info->op.operands[i].type) {
|
|
case SH_OP_INVALID:
|
|
break;
|
|
case SH_OP_REG:
|
|
SStream_concat0(O, s_reg_names[info->op.operands[i].reg]);
|
|
break;
|
|
case SH_OP_IMM:
|
|
imm = info->op.operands[i].imm;
|
|
SStream_concat(O, "#%d", imm);
|
|
break;
|
|
case SH_OP_MEM:
|
|
PrintMemop(O, &info->op.operands[i].mem);
|
|
break;
|
|
}
|
|
if (i < (info->op.op_count - 1)) {
|
|
SStream_concat0(O, ",");
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifndef CAPSTONE_DIET
|
|
static const name_map group_name_maps[] = {
|
|
{ SH_GRP_INVALID , NULL },
|
|
{ SH_GRP_JUMP, "jump" },
|
|
{ SH_GRP_CALL, "call" },
|
|
{ SH_GRP_INT, "int" },
|
|
{ SH_GRP_RET , "ret" },
|
|
{ SH_GRP_IRET, "iret" },
|
|
{ SH_GRP_PRIVILEGE, "privilege" },
|
|
{ SH_GRP_BRANCH_RELATIVE, "branch_relative" },
|
|
{ SH_GRP_SH2, "SH2" },
|
|
{ SH_GRP_SH2E, "SH2E" },
|
|
{ SH_GRP_SH2DSP, "SH2-DSP" },
|
|
{ SH_GRP_SH2A, "SH2A" },
|
|
{ SH_GRP_SH2AFPU, "SH2A-FPU" },
|
|
{ SH_GRP_SH3, "SH3" },
|
|
{ SH_GRP_SH3DSP, "SH3-DSP" },
|
|
{ SH_GRP_SH4, "SH4" },
|
|
{ SH_GRP_SH4A, "SH4A" },
|
|
};
|
|
#endif
|
|
|
|
const char *SH_group_name(csh handle, unsigned int id)
|
|
{
|
|
#ifndef CAPSTONE_DIET
|
|
return id2name(group_name_maps, ARR_SIZE(group_name_maps), id);
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|