Files
ircolib/external/capstone/arch/M68K/M68KInstPrinter.c
T

589 lines
17 KiB
C

/* Capstone Disassembly Engine */
/* M68K Backend by Daniel Collin <daniel@collin.com> 2015-2016 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "M68KInstPrinter.h"
#include "M68KDisassembler.h"
#include "../../Mapping.h"
#include "../../cs_priv.h"
#include "../../utils.h"
#include "../../MCInst.h"
#include "../../MCInstrDesc.h"
#include "../../MCRegisterInfo.h"
#ifndef CAPSTONE_DIET
static const char s_spacing[] = " ";
static const char *const s_reg_names[] = {
"invalid", "d0", "d1", "d2", "d3", "d4", "d5", "d6",
"d7", "a0", "a1", "a2", "a3", "a4", "a5", "a6",
"a7", "fp0", "fp1", "fp2", "fp3", "fp4", "fp5", "fp6",
"fp7", "pc", "sr", "ccr", "sfc", "dfc", "usp", "vbr",
"cacr", "caar", "msp", "isp", "tc", "itt0", "itt1", "dtt0",
"dtt1", "mmusr", "urp", "srp",
"fpcr", "fpsr", "fpiar",
"tt0", "tt1", "crp",
};
static const char *const s_instruction_names[] = {
"invalid", "abcd", "add", "adda", "addi", "addq",
"addx", "and", "andi", "asl", "asr", "bhs",
"blo", "bhi", "bls", "bcc", "bcs", "bne",
"beq", "bvc", "bvs", "bpl", "bmi", "bge",
"blt", "bgt", "ble", "bra", "bsr", "bchg",
"bclr", "bset", "btst", "bfchg", "bfclr", "bfexts",
"bfextu", "bfffo", "bfins", "bfset", "bftst", "bkpt",
"callm", "cas", "cas2", "chk", "chk2", "clr",
"cmp", "cmpa", "cmpi", "cmpm", "cmp2", "cinvl",
"cinvp", "cinva", "cpushl", "cpushp", "cpusha", "dbt",
"dbf", "dbhi", "dbls", "dbcc", "dbcs", "dbne",
"dbeq", "dbvc", "dbvs", "dbpl", "dbmi", "dbge",
"dblt", "dbgt", "dble", "dbra", "divs", "divsl",
"divu", "divul", "eor", "eori", "exg", "ext",
"extb", "fabs", "fsabs", "fdabs", "facos", "fadd",
"fsadd", "fdadd", "fasin", "fatan", "fatanh", "fbf",
"fbeq", "fbogt", "fboge", "fbolt", "fbole", "fbogl",
"fbor", "fbun", "fbueq", "fbugt", "fbuge", "fbult",
"fbule", "fbne", "fbt", "fbsf", "fbseq", "fbgt",
"fbge", "fblt", "fble", "fbgl", "fbgle", "fbngle",
"fbngl", "fbnle", "fbnlt", "fbnge", "fbngt", "fbsne",
"fbst", "fcmp", "fcos", "fcosh", "fdbf", "fdbeq",
"fdbogt", "fdboge", "fdbolt", "fdbole", "fdbogl", "fdbor",
"fdbun", "fdbueq", "fdbugt", "fdbuge", "fdbult", "fdbule",
"fdbne", "fdbt", "fdbsf", "fdbseq", "fdbgt", "fdbge",
"fdblt", "fdble", "fdbgl", "fdbgle", "fdbngle", "fdbngl",
"fdbnle", "fdbnlt", "fdbnge", "fdbngt", "fdbsne", "fdbst",
"fdiv", "fsdiv", "fddiv", "fetox", "fetoxm1", "fgetexp",
"fgetman", "fint", "fintrz", "flog10", "flog2", "flogn",
"flognp1", "fmod", "fmove", "fsmove", "fdmove", "fmovecr",
"fmovem", "fmul", "fsmul", "fdmul", "fneg", "fsneg",
"fdneg", "fnop", "frem", "frestore", "fsave", "fscale",
"fsgldiv", "fsglmul", "fsin", "fsincos", "fsinh", "fsqrt",
"fssqrt", "fdsqrt", "fsf", "fseq", "fsogt", "fsoge",
"fsolt", "fsole", "fsogl", "fsor", "fsun", "fsueq",
"fsugt", "fsuge", "fsult", "fsule", "fsne", "fst",
"fssf", "fsseq", "fsgt", "fsge", "fslt", "fsle",
"fsgl", "fsgle", "fsngle", "fsngl", "fsnle", "fsnlt",
"fsnge", "fsngt", "fssne", "fsst", "fsub", "fssub",
"fdsub", "ftan", "ftanh", "ftentox", "ftrapf", "ftrapeq",
"ftrapogt", "ftrapoge", "ftrapolt", "ftrapole", "ftrapogl", "ftrapor",
"ftrapun", "ftrapueq", "ftrapugt", "ftrapuge", "ftrapult", "ftrapule",
"ftrapne", "ftrapt", "ftrapsf", "ftrapseq", "ftrapgt", "ftrapge",
"ftraplt", "ftraple", "ftrapgl", "ftrapgle", "ftrapngle", "ftrapngl",
"ftrapnle", "ftrapnlt", "ftrapnge", "ftrapngt", "ftrapsne", "ftrapst",
"ftst", "ftwotox", "halt", "illegal", "jmp", "jsr",
"lea", "link", "lpstop", "lsl", "lsr", "move",
"movea", "movec", "movem", "movep", "moveq", "moves",
"move16", "muls", "mulu", "nbcd", "neg", "negx",
"nop", "not", "or", "ori", "pack", "pea",
"pflush", "pflusha", "pflushan", "pflushn", "ploadr", "ploadw",
"plpar", "plpaw", "pmove", "pmovefd", "ptestr", "ptestw",
"pulse", "rems", "remu", "reset", "rol", "ror",
"roxl", "roxr", "rtd", "rte", "rtm", "rtr",
"rts", "sbcd", "st", "sf", "shi", "sls",
"scc", "shs", "scs", "slo", "sne", "seq",
"svc", "svs", "spl", "smi", "sge", "slt",
"sgt", "sle", "stop", "sub", "suba", "subi",
"subq", "subx", "swap", "tas", "trap", "trapv",
"trapt", "trapf", "traphi", "trapls", "trapcc", "traphs",
"trapcs", "traplo", "trapne", "trapeq", "trapvc", "trapvs",
"trappl", "trapmi", "trapge", "traplt", "trapgt", "traple",
"tst", "unlk", "unpk", "bgnd", "tbls", "tblu",
"tblsn", "tblun",
};
#endif
#ifndef CAPSTONE_DIET
static const char *getRegName(m68k_reg reg)
{
return s_reg_names[(int)reg];
}
static void printRegbits(SStream *O, bool *need_sep, uint32_t data,
const char *prefix)
{
unsigned int first;
int i;
for (i = 0; i < 8; ++i) {
if (!(data & (1 << i)))
continue;
first = i;
while (i < 7 && (data & (1 << (i + 1))))
i++;
if (*need_sep)
SStream_concat1(O, '/');
*need_sep = true;
SStream_concat(O, "%s%" PRIu32, prefix, first);
if ((unsigned int)i > first)
SStream_concat(O, "-%s%" PRIu32, prefix,
(unsigned int)i);
}
}
static void registerBits(SStream *O, const cs_m68k_op *op)
{
unsigned int data = op->register_bits;
bool need_sep = false;
if (!data) {
SStream_concat(O, "%s", "#$0");
return;
}
printRegbits(O, &need_sep, data & 0xff, "d");
printRegbits(O, &need_sep, (data >> 8) & 0xff, "a");
printRegbits(O, &need_sep, (data >> 16) & 0xff, "fp");
}
static void registerPair(SStream *O, const cs_m68k_op *op)
{
SStream_concat(O, "%s:%s", s_reg_names[op->reg_pair.reg_0],
s_reg_names[op->reg_pair.reg_1]);
}
static void printScaleFactor(SStream *O, uint8_t scale, int threshold)
{
if (scale > threshold)
SStream_concat(O, "%s*%s%" PRId8, s_spacing, s_spacing, scale);
}
static void printIndexReg(SStream *O, const cs_m68k_op *op)
{
SStream_concat(O, "%s.%c", getRegName(op->mem.index_reg),
op->mem.index_size ? 'l' : 'w');
}
static void printBitfield(SStream *O, const cs_m68k_op *op)
{
if (!op->mem.bitfield)
return;
SStream_concat0(O, "{");
if (M68K_BF_IS_REG(op->mem.offset))
SStream_concat(O, "d%" PRId8, M68K_BF_REG_NUM(op->mem.offset));
else
SStream_concat(O, "%" PRId8, op->mem.offset);
SStream_concat0(O, ":");
if (M68K_BF_IS_REG(op->mem.width))
SStream_concat(O, "d%" PRId8, M68K_BF_REG_NUM(op->mem.width));
else
SStream_concat(O, "%" PRId8, op->mem.width);
SStream_concat0(O, "}");
}
static void printImmediate(SStream *O, const cs_m68k *inst,
const cs_m68k_op *op)
{
if (inst->op_size.type == M68K_SIZE_TYPE_FPU) {
#if defined(_KERNEL_MODE)
SStream_concat(O, "#<float_point_unsupported>");
return;
#else
if (inst->op_size.fpu_size == M68K_FPU_SIZE_SINGLE)
SStream_concat(O, "#%f", op->simm);
else if (inst->op_size.fpu_size == M68K_FPU_SIZE_DOUBLE)
SStream_concat(O, "#%f", op->dimm);
else
SStream_concat(O, "#<unsupported>");
return;
#endif
}
SStream_concat(O, "#$%" PRIx64, op->imm);
}
static void printIndex8BitDisp(SStream *O, unsigned int pc,
const cs_m68k_op *op)
{
if (op->address_mode == M68K_AM_PCI_INDEX_8_BIT_DISP) {
SStream_concat(O, "$%" PRIx32 "(pc,%s", pc + 2 + op->mem.disp,
s_spacing);
} else {
SStream_concat(O, "%s$%" PRIx16 "(%s,%s",
op->mem.disp < 0 ? "-" : "", abs(op->mem.disp),
getRegName(op->mem.base_reg), s_spacing);
}
printIndexReg(O, op);
printScaleFactor(O, op->mem.scale, 1);
SStream_concat0(O, ")");
}
static void printRegAddrMode(SStream *O, unsigned int pc, const cs_m68k_op *op)
{
switch (op->address_mode) {
case M68K_AM_REG_DIRECT_DATA:
SStream_concat(O, "d%" PRId32, (op->reg - M68K_REG_D0));
break;
case M68K_AM_REG_DIRECT_ADDR:
SStream_concat(O, "a%" PRId32, (op->reg - M68K_REG_A0));
break;
case M68K_AM_REGI_ADDR:
SStream_concat(O, "(a%" PRId32 ")", (op->reg - M68K_REG_A0));
break;
case M68K_AM_REGI_ADDR_POST_INC:
SStream_concat(O, "(a%" PRId32 ")+", (op->reg - M68K_REG_A0));
break;
case M68K_AM_REGI_ADDR_PRE_DEC:
SStream_concat(O, "-(a%" PRId32 ")", (op->reg - M68K_REG_A0));
break;
case M68K_AM_REGI_ADDR_DISP:
SStream_concat(O, "%s$%" PRIx16 "(a%" PRId32 ")",
op->mem.disp < 0 ? "-" : "", abs(op->mem.disp),
(op->mem.base_reg - M68K_REG_A0));
break;
case M68K_AM_PCI_DISP:
SStream_concat(O, "$%" PRIx32 "(pc)", pc + 2 + op->mem.disp);
break;
default:
break;
}
}
static void printBaseDisp(SStream *O, unsigned int pc, const cs_m68k_op *op)
{
int is_pc = (op->address_mode == M68K_AM_PCI_INDEX_BASE_DISP);
if (is_pc) {
SStream_concat(O, "$%" PRIx32, pc + 2 + op->mem.in_disp);
} else if (op->mem.in_disp != 0) {
SStream_concat(O, "%s$%" PRIx32,
op->mem.in_disp >= 0 ? "" : "-",
abs(op->mem.in_disp));
}
SStream_concat0(O, "(");
if (is_pc) {
SStream_concat0(O, "pc");
} else if (op->mem.base_reg != M68K_REG_INVALID) {
SStream_concat(O, "a%" PRId32, op->mem.base_reg - M68K_REG_A0);
}
if ((is_pc || op->mem.base_reg != M68K_REG_INVALID) &&
op->mem.index_reg != M68K_REG_INVALID)
SStream_concat(O, ",%s", s_spacing);
if (op->mem.index_reg != M68K_REG_INVALID) {
printIndexReg(O, op);
printScaleFactor(O, op->mem.scale, 0);
}
SStream_concat0(O, ")");
}
static void printMemIndirect(SStream *O, unsigned int pc, const cs_m68k_op *op)
{
int is_pc = (op->address_mode == M68K_AM_PC_MEMI_POST_INDEX ||
op->address_mode == M68K_AM_PC_MEMI_PRE_INDEX);
int is_post = (op->address_mode == M68K_AM_MEMI_POST_INDEX ||
op->address_mode == M68K_AM_PC_MEMI_POST_INDEX);
int is_pre = (op->address_mode == M68K_AM_MEMI_PRE_INDEX ||
op->address_mode == M68K_AM_PC_MEMI_PRE_INDEX);
SStream_concat0(O, "([");
if (is_pc) {
SStream_concat(O, "$%" PRIx32, pc + 2 + op->mem.in_disp);
} else if (op->mem.in_disp != 0) {
SStream_concat(O, "%s$%" PRIx32,
op->mem.in_disp >= 0 ? "" : "-",
abs(op->mem.in_disp));
}
if (op->mem.base_reg != M68K_REG_INVALID) {
if (op->mem.in_disp != 0)
SStream_concat(O, ",%s%s", s_spacing,
getRegName(op->mem.base_reg));
else
SStream_concat(O, "%s", getRegName(op->mem.base_reg));
}
if (is_post)
SStream_concat0(O, "]");
if (op->mem.index_reg != M68K_REG_INVALID) {
SStream_concat(O, ",%s", s_spacing);
printIndexReg(O, op);
}
printScaleFactor(O, op->mem.scale, 0);
if (is_pre)
SStream_concat0(O, "]");
if (op->mem.out_disp != 0) {
SStream_concat(O, ",%s%s$%" PRIx32, s_spacing,
op->mem.out_disp >= 0 ? "" : "-",
abs(op->mem.out_disp));
}
SStream_concat0(O, ")");
}
static void printAddressingMode(SStream *O, unsigned int pc,
const cs_m68k *inst, const cs_m68k_op *op)
{
switch (op->address_mode) {
case M68K_AM_NONE:
switch (op->type) {
case M68K_OP_REG_BITS:
registerBits(O, op);
break;
case M68K_OP_REG_PAIR:
registerPair(O, op);
break;
case M68K_OP_REG:
SStream_concat(O, "%s", s_reg_names[op->reg]);
break;
default:
break;
}
break;
case M68K_AM_REG_DIRECT_DATA:
case M68K_AM_REG_DIRECT_ADDR:
case M68K_AM_REGI_ADDR:
case M68K_AM_REGI_ADDR_POST_INC:
case M68K_AM_REGI_ADDR_PRE_DEC:
case M68K_AM_REGI_ADDR_DISP:
case M68K_AM_PCI_DISP:
printRegAddrMode(O, pc, op);
break;
case M68K_AM_ABSOLUTE_DATA_SHORT:
SStream_concat(O, "$%" PRIx32 ".w", (uint32_t)op->imm);
break;
case M68K_AM_ABSOLUTE_DATA_LONG:
SStream_concat(O, "$%" PRIx64 ".l", (uint64_t)op->imm);
break;
case M68K_AM_IMMEDIATE:
printImmediate(O, inst, op);
break;
case M68K_AM_PCI_INDEX_8_BIT_DISP:
case M68K_AM_AREGI_INDEX_8_BIT_DISP:
printIndex8BitDisp(O, pc, op);
break;
case M68K_AM_PCI_INDEX_BASE_DISP:
case M68K_AM_AREGI_INDEX_BASE_DISP:
printBaseDisp(O, pc, op);
break;
case M68K_AM_PC_MEMI_POST_INDEX:
case M68K_AM_PC_MEMI_PRE_INDEX:
case M68K_AM_MEMI_PRE_INDEX:
case M68K_AM_MEMI_POST_INDEX:
printMemIndirect(O, pc, op);
break;
case M68K_AM_BRANCH_DISPLACEMENT:
SStream_concat(O, "$%" PRIx32, pc + 2 + op->br_disp.disp);
default:
break;
}
printBitfield(O, op);
}
static void printCAS2(SStream *O, unsigned int pc, const cs_m68k *ext)
{
printAddressingMode(O, pc, ext, &ext->operands[0]);
SStream_concat0(O, ",");
printAddressingMode(O, pc, ext, &ext->operands[1]);
SStream_concat0(O, ",");
SStream_concat(O, "(%s):(%s)",
s_reg_names[ext->operands[2].reg_pair.reg_0],
s_reg_names[ext->operands[2].reg_pair.reg_1]);
}
static void printCacheOp(SStream *O, unsigned int pc, const cs_m68k *ext)
{
static const char *const cache_names[] = { "nc", "dc", "ic", "bc" };
unsigned int sel = (unsigned int)ext->operands[0].imm;
int i;
if (sel < ARR_SIZE(cache_names))
SStream_concat0(O, cache_names[sel]);
else
SStream_concat(O, "#$%" PRIx64, ext->operands[0].imm);
for (i = 1; i < ext->op_count; ++i) {
SStream_concat(O, ",%s", s_spacing);
printAddressingMode(O, pc, ext, &ext->operands[i]);
}
}
#endif
static void printOpSize(SStream *O, const cs_m68k *ext)
{
switch (ext->op_size.type) {
case M68K_SIZE_TYPE_INVALID:
break;
case M68K_SIZE_TYPE_CPU:
switch (ext->op_size.cpu_size) {
case M68K_CPU_SIZE_BYTE:
SStream_concat0(O, ".b");
break;
case M68K_CPU_SIZE_WORD:
SStream_concat0(O, ".w");
break;
case M68K_CPU_SIZE_LONG:
SStream_concat0(O, ".l");
break;
case M68K_CPU_SIZE_NONE:
break;
}
break;
case M68K_SIZE_TYPE_FPU:
switch (ext->op_size.fpu_size) {
case M68K_FPU_SIZE_SINGLE:
SStream_concat0(O, ".s");
break;
case M68K_FPU_SIZE_DOUBLE:
SStream_concat0(O, ".d");
break;
case M68K_FPU_SIZE_EXTENDED:
SStream_concat0(O, ".x");
break;
case M68K_FPU_SIZE_NONE:
break;
}
break;
}
}
void M68K_printInst(MCInst *MI, SStream *O, void *PrinterInfo)
{
#ifndef CAPSTONE_DIET
m68k_info *info = (m68k_info *)PrinterInfo;
cs_m68k *ext = &info->extension;
cs_detail *detail = NULL;
int i = 0;
if (detail_is_set(MI)) {
detail = get_detail(MI);
int regs_read_count = MIN((int)ARR_SIZE(detail->regs_read),
info->regs_read_count);
int regs_write_count = MIN((int)ARR_SIZE(detail->regs_write),
info->regs_write_count);
int groups_count =
MIN((int)ARR_SIZE(detail->groups), info->groups_count);
memcpy(&detail->m68k, ext, sizeof(cs_m68k));
memcpy(&detail->regs_read, &info->regs_read,
regs_read_count * sizeof(info->regs_read[0]));
detail->regs_read_count = regs_read_count;
memcpy(&detail->regs_write, &info->regs_write,
regs_write_count * sizeof(info->regs_write[0]));
detail->regs_write_count = regs_write_count;
memcpy(&detail->groups, &info->groups, groups_count);
detail->groups_count = groups_count;
}
if (MI->Opcode == M68K_INS_INVALID) {
if (ext->op_count)
SStream_concat(O, "dc.w $%" PRIx32,
(uint32_t)ext->operands[0].imm);
else
SStream_concat(O, "dc.w $<unknown>");
return;
}
SStream_concat0(O, (char *)s_instruction_names[MI->Opcode]);
printOpSize(O, ext);
SStream_concat0(O, " ");
if (MI->Opcode == M68K_INS_CAS2) {
printCAS2(O, info->pc, ext);
return;
}
if (MI->Opcode >= M68K_INS_CINVL && MI->Opcode <= M68K_INS_CPUSHA) {
printCacheOp(O, info->pc, ext);
return;
}
for (i = 0; i < ext->op_count; ++i) {
printAddressingMode(O, info->pc, ext, &ext->operands[i]);
if ((i + 1) != ext->op_count)
SStream_concat(O, ",%s", s_spacing);
}
#endif
}
const char *M68K_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 M68K_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id)
{
insn->id = id; // These id's matches for 68k
}
const char *M68K_insn_name(csh handle, unsigned int id)
{
#ifdef CAPSTONE_DIET
return NULL;
#else
return s_instruction_names[id];
#endif
}
#ifndef CAPSTONE_DIET
static const name_map group_name_maps[] = {
{ M68K_GRP_INVALID, NULL },
{ M68K_GRP_JUMP, "jump" },
{ M68K_GRP_RET, "ret" },
{ M68K_GRP_IRET, "iret" },
{ M68K_GRP_BRANCH_RELATIVE, "branch_relative" },
};
#endif
const char *M68K_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
}
#ifndef CAPSTONE_DIET
void M68K_reg_access(const cs_insn *insn, cs_regs regs_read,
uint8_t *regs_read_count, cs_regs regs_write,
uint8_t *regs_write_count)
{
uint8_t read_count, write_count;
read_count = insn->detail->regs_read_count;
write_count = insn->detail->regs_write_count;
// implicit registers
memcpy(regs_read, insn->detail->regs_read,
read_count * sizeof(insn->detail->regs_read[0]));
memcpy(regs_write, insn->detail->regs_write,
write_count * sizeof(insn->detail->regs_write[0]));
*regs_read_count = read_count;
*regs_write_count = write_count;
}
#endif