802798ce3c
git-subtree-dir: external/capstone git-subtree-split: e46f64fadb351e9ecd05264fab26f2772feb0994
425 lines
11 KiB
C
425 lines
11 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
|
|
}
|