rizin/librz/arch/p/analysis/analysis_sh.c
billow 7a77b8f7bd
linter: update clang-format installation to version 20 (#5451)
* fix: format with clang-format-20
* fix: fix clang-format linter with rewrite '^#define.*/\*.*\\$'
* fix: update clang-format to version 20 in workflows and documentation
* fix: add SPDX license information to .git-blame-ignore-revs
2025-10-12 08:02:03 +08:00

1206 lines
54 KiB
C

// SPDX-FileCopyrightText: 2010-2013 eloi <limited-entropy.com>
// SPDX-License-Identifier: LGPL-3.0-only
#include <string.h>
#include <rz_types.h>
#include <rz_lib.h>
#include <rz_asm.h>
#include <rz_analysis.h>
#include "sh/sh_il.h"
#define API static
#define LONG_SIZE 4
#define WORD_SIZE 2
#define BYTE_SIZE 1
/*
* all processor instructions are implemented, but all FPU are missing
* DIV1,MAC.W,MAC.L,rte,rts should be checked
* also tests are written, but still could be some issues, if I misunderstand instructions.
* If you found some bugs, please open an issue
*/
#define BIT_32(x) x ",0x80000000,&"
#define S16_EXT(x) x ",DUP,0x8000,&,?{,0xFFFFFFFFFFFF0000,|,}"
#define S32_EXT(x) x ",DUP,0x80000000,&,?{,0xFFFFFFFF00000000,|,}"
#define IS_T "sr,0x1,&,"
#define SET_T "0x1,sr,|="
#define CLR_T "0xFFFFFFFE,sr,&="
// Macros for different instruction types
#define IS_CLRT(x) x == 0x0008
#define IS_NOP(x) x == 0x0009
#define IS_RTS(x) x == 0x000b
#define IS_SETT(x) x == 0x0018
#define IS_DIV0U(x) x == 0x0019
#define IS_SLEEP(x) x == 0x001b
#define IS_CLRMAC(x) x == 0x0028
#define IS_RTE(x) x == 0x002b
#define IS_STCSR1(x) (((x) & 0xF0CF) == 0x0002) // mask stc Rn,{SR,gbr,VBR,SSR}
#define IS_BSRF(x) ((x) & 0xf0ff) == 0x0003
#define IS_BRAF(x) (((x) & 0xf0ff) == 0x0023)
#define IS_MOVB_REG_TO_R0REL(x) (((x) & 0xF00F) == 0x0004)
#define IS_MOVW_REG_TO_R0REL(x) (((x) & 0xF00F) == 0x0005)
#define IS_MOVL_REG_TO_R0REL(x) (((x) & 0xF00F) == 0x0006)
#define IS_MULL(x) (((x) & 0xF00F) == 0x0007)
#define IS_MOVB_R0REL_TO_REG(x) (((x) & 0xF00F) == 0x000C)
#define IS_MOVW_R0REL_TO_REG(x) (((x) & 0xF00F) == 0x000D)
#define IS_MOVL_R0REL_TO_REG(x) (((x) & 0xF00F) == 0x000E)
#define IS_MACL(x) (((x) & 0xF00F) == 0x000F)
#define IS_MOVT(x) (((x) & 0xF0FF) == 0x0029)
#define IS_STSMACH(x) (((x) & 0xF0FF) == 0x000A)
#define IS_STSMACL(x) (((x) & 0xF0FF) == 0x001A)
#define IS_STSPR(x) (((x) & 0xF0FF) == 0x002A)
// #define IS_STSFPUL(x) (((x) & 0xF0FF) == 0x005A) //FP*: todo maybe someday
// #define IS_STSFPSCR(x) (((x) & 0xF0FF) == 0x006A)
#define IS_MOVB_REG_TO_REGREF(x) (((x) & 0xF00F) == 0x2000)
#define IS_MOVW_REG_TO_REGREF(x) (((x) & 0xF00F) == 0x2001)
#define IS_MOVL_REG_TO_REGREF(x) (((x) & 0xF00F) == 0x2002)
// #define invalid?(x) (((x) & 0xF00F) == 0x2003) //illegal on sh2e
#define IS_PUSHB(x) (((x) & 0xF00F) == 0x2004)
#define IS_PUSHW(x) (((x) & 0xF00F) == 0x2005)
#define IS_PUSHL(x) (((x) & 0xF00F) == 0x2006)
#define IS_DIV0S(x) (((x) & 0xF00F) == 0x2007)
#define IS_TSTRR(x) (((x) & 0xF00F) == 0x2008)
#define IS_AND_REGS(x) (((x) & 0xF00F) == 0x2009)
#define IS_XOR_REGS(x) (((x) & 0xF00F) == 0x200A)
#define IS_OR_REGS(x) (((x) & 0xF00F) == 0x200B)
#define IS_CMPSTR(x) (((x) & 0xF00F) == 0x200C)
#define IS_XTRCT(x) (((x) & 0xF00F) == 0x200D)
#define IS_MULUW(x) (((x) & 0xF00F) == 0x200E)
#define IS_MULSW(x) (((x) & 0xF00F) == 0x200F)
#define IS_CMPEQ(x) (((x) & 0xF00F) == 0x3000)
// #define invalid?(x) (((x) & 0xF00F) == 0x3001)
#define IS_CMPHS(x) (((x) & 0xF00F) == 0x3002)
#define IS_CMPGE(x) (((x) & 0xF00F) == 0x3003)
#define IS_CMPHI(x) (((x) & 0xF00F) == 0x3006)
#define IS_CMPGT(x) (((x) & 0xF00F) == 0x3007)
#define IS_DIV1(x) (((x) & 0xF00F) == 0x3004)
#define IS_DMULU(x) (((x) & 0xF00F) == 0x3005)
#define IS_DMULS(x) (((x) & 0xF00F) == 0x300D)
#define IS_SUB(x) (((x) & 0xF00F) == 0x3008)
// #define invalid?(x) (((x) & 0xF00F) == 0x3009)
#define IS_SUBC(x) (((x) & 0xF00F) == 0x300A)
#define IS_SUBV(x) (((x) & 0xF00F) == 0x300B)
#define IS_ADD(x) (((x) & 0xF00F) == 0x300C)
#define IS_ADDC(x) (((x) & 0xF00F) == 0x300E)
#define IS_ADDV(x) (((x) & 0xF00F) == 0x300F)
#define IS_MACW(x) (((x) & 0xF00F) == 0x400F)
#define IS_JSR(x) (((x) & 0xf0ff) == 0x400b)
#define IS_JMP(x) (((x) & 0xf0ff) == 0x402b)
#define IS_CMPPL(x) (((x) & 0xf0ff) == 0x4015)
#define IS_CMPPZ(x) (((x) & 0xf0ff) == 0x4011)
#define IS_LDCSR(x) (((x) & 0xF0FF) == 0x400E)
#define IS_LDCGBR(x) (((x) & 0xF0FF) == 0x401E)
#define IS_LDCVBR(x) (((x) & 0xF0FF) == 0x402E)
#define IS_LDCLSR(x) (((x) & 0xF0FF) == 0x4007)
#define IS_LDCLSRGBR(x) (((x) & 0xF0FF) == 0x4017)
#define IS_LDCLSRVBR(x) (((x) & 0xF0FF) == 0x4027)
#define IS_LDSMACH(x) (((x) & 0xF0FF) == 0x400A)
#define IS_LDSMACL(x) (((x) & 0xF0FF) == 0x401A)
#define IS_LDSLMACH(x) (((x) & 0xF0FF) == 0x4006)
#define IS_LDSLMACL(x) (((x) & 0xF0FF) == 0x4016)
#define IS_LDSPR(x) (((x) & 0xF0FF) == 0x402A)
#define IS_LDSLPR(x) (((x) & 0xF0FF) == 0x4026)
// #define IS_LDSFPUL(x) (((x) & 0xF0FF) == 0x405A) //FP*: todo maybe someday
// #define IS_LDSFPSCR(x) (((x) & 0xF0FF) == 0x406A)
// #define IS_LDSLFPUL(x) (((x) & 0xF0FF) == 0x4066)
// #define IS_LDSLFPSCR(x) (((x) & 0xF0FF) == 0x4056)
#define IS_ROTCR(x) (((x) & 0xF0FF) == 0x4025)
#define IS_ROTCL(x) (((x) & 0xF0FF) == 0x4024)
#define IS_ROTL(x) (((x) & 0xF0FF) == 0x4004)
#define IS_ROTR(x) (((x) & 0xF0FF) == 0x4005)
// not on sh2e : shad, shld
// #define IS_SHIFT1(x) (((x) & 0xF0DE) == 0x4000) //unused (treated as switch-case)
// other shl{l,r}{,2,8,16} in switch case also.
#define IS_STSLMACL(x) (((x) & 0xF0FF) == 0x4012)
#define IS_STSLMACH(x) (((x) & 0xF0FF) == 0x4002)
#define IS_STCLSR(x) (((x) & 0xF0FF) == 0x4003)
#define IS_STCLGBR(x) (((x) & 0xF0FF) == 0x4013)
#define IS_STCLVBR(x) (((x) & 0xF0FF) == 0x4023)
// todo: other stc.l not on sh2e
#define IS_STSLPR(x) (((x) & 0xF0FF) == 0x4022)
// #define IS_STSLFPUL(x) (((x) & 0xF0FF) == 0x4052)
// #define IS_STSLFPSCR(x) (((x) & 0xF0FF) == 0x4062)
#define IS_TASB(x) (((x) & 0xF0FF) == 0x401B)
#define IS_DT(x) (((x) & 0xF0FF) == 0x4010)
#define IS_MOVB_REGREF_TO_REG(x) (((x) & 0xF00F) == 0x6000)
#define IS_MOVW_REGREF_TO_REG(x) (((x) & 0xF00F) == 0x6001)
#define IS_MOVL_REGREF_TO_REG(x) (((x) & 0xF00F) == 0x6002)
#define IS_MOV_REGS(x) (((x) & 0xf00f) == 0x6003)
#define IS_MOVB_POP(x) (((x) & 0xF00F) == 0x6004)
#define IS_MOVW_POP(x) (((x) & 0xF00F) == 0x6005)
#define IS_MOVL_POP(x) (((x) & 0xF00F) == 0x6006)
#define IS_NOT(x) (((x) & 0xF00F) == 0x6007)
#define IS_SWAPB(x) (((x) & 0xF00F) == 0x6008)
#define IS_SWAPW(x) (((x) & 0xF00F) == 0x6009)
#define IS_NEG(x) (((x) & 0xF00F) == 0x600B)
#define IS_NEGC(x) (((x) & 0xF00F) == 0x600A)
#define IS_EXT(x) (((x) & 0xF00C) == 0x600C) // match ext{s,u}.{b,w}
#define IS_MOVB_R0_REGDISP(x) (((x) & 0xFF00) == 0x8000)
#define IS_MOVW_R0_REGDISP(x) (((x) & 0xFF00) == 0x8100)
// #define illegal?(x) (((x) & 0xF900) == 0x8000) //match 8{2,3,6,7}00
#define IS_MOVB_REGDISP_R0(x) (((x) & 0xFF00) == 0x8400)
#define IS_MOVW_REGDISP_R0(x) (((x) & 0xFF00) == 0x8500)
#define IS_CMPIMM(x) (((x) & 0xFF00) == 0x8800)
// #define illegal?(x) (((x) & 0xFB00) == 0x8A00) //match 8{A,E}00
#define IS_BT(x) (((x) & 0xff00) == 0x8900)
#define IS_BF(x) (((x) & 0xff00) == 0x8B00)
#define IS_BTS(x) (((x) & 0xff00) == 0x8D00)
#define IS_BFS(x) (((x) & 0xff00) == 0x8F00)
#define IS_BT_OR_BF(x) IS_BT(x) || IS_BTS(x) || IS_BF(x) || IS_BFS(x)
#define IS_MOVB_R0_GBRREF(x) (((x) & 0xFF00) == 0xC000)
#define IS_MOVW_R0_GBRREF(x) (((x) & 0xFF00) == 0xC100)
#define IS_MOVL_R0_GBRREF(x) (((x) & 0xFF00) == 0xC200)
#define IS_TRAP(x) (((x) & 0xFF00) == 0xC300)
#define IS_MOVB_GBRREF_R0(x) (((x) & 0xFF00) == 0xC400)
#define IS_MOVW_GBRREF_R0(x) (((x) & 0xFF00) == 0xC500)
#define IS_MOVL_GBRREF_R0(x) (((x) & 0xFF00) == 0xC600)
#define IS_MOVA_PCREL_R0(x) (((x) & 0xFF00) == 0xC700)
#define IS_BINLOGIC_IMM_R0(x) (((x) & 0xFC00) == 0xC800) // match C{8,9,A,B}00
#define IS_BINLOGIC_IMM_GBR(x) (((x) & 0xFC00) == 0xCC00) // match C{C,D,E,F}00 : *.b #imm, @(R0,gbr)
/* Compute PC-relative displacement for branch instructions */
#define GET_BRA_OFFSET(x) ((x) & 0x0fff)
#define GET_BTF_OFFSET(x) ((x) & 0x00ff)
/* Compute reg nr for BRAF,BSR,BSRF,JMP,JSR */
#define GET_TARGET_REG(x) (((x) >> 8) & 0x0f)
#define GET_SOURCE_REG(x) (((x) >> 4) & 0x0f)
/* index of PC reg in regs[] array*/
#define PC_IDX 16
/* for {bra,bsr} only: (sign-extend 12bit offset)<<1 + PC +4 */
static ut64 disarm_12bit_offset(RzAnalysisOp *op, unsigned int insoff) {
ut64 off = insoff;
/* sign extend if higher bit is 1 (0x0800) */
if ((off & 0x0800) == 0x0800) {
off |= ~0xFFF;
}
return (op->addr) + (off << 1) + 4;
}
/* for bt,bf sign-extended offsets : return PC+4+ (exts.b offset)<<1 */
static ut64 disarm_8bit_offset(ut64 pc, ut32 offs) {
/* pc (really, op->addr) is 64 bits, so we need to sign-extend
* to 64 bits instead of the 32 the actual CPU does */
ut64 off = offs;
/* sign extend if higher bit is 1 (0x08) */
if ((off & 0x80) == 0x80) {
off |= ~0xFF;
}
return (off << 1) + pc + 4;
}
static char *regs[] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "pc" };
static RzAnalysisValue *analysis_fill_ai_rg(RzAnalysis *analysis, int idx) {
RzAnalysisValue *ret = rz_analysis_value_new();
ret->type = RZ_ANALYSIS_VAL_REG;
ret->reg = rz_reg_get(analysis->reg, regs[idx], RZ_REG_TYPE_GPR);
return ret;
}
static RzAnalysisValue *analysis_fill_im(RzAnalysis *analysis, st32 v) {
RzAnalysisValue *ret = rz_analysis_value_new();
ret->type = RZ_ANALYSIS_VAL_IMM;
ret->imm = v;
return ret;
}
/* Implements @(disp,Rn) , size=1 for .b, 2 for .w, 4 for .l */
static RzAnalysisValue *analysis_fill_reg_disp_mem(RzAnalysis *analysis, int reg, st64 delta, st64 size) {
RzAnalysisValue *ret = analysis_fill_ai_rg(analysis, reg);
ret->type = RZ_ANALYSIS_VAL_MEM;
ret->memref = size;
ret->delta = delta * size;
return ret;
}
/* Rn */
static RzAnalysisValue *analysis_fill_reg_ref(RzAnalysis *analysis, int reg, st64 size) {
RzAnalysisValue *ret = analysis_fill_ai_rg(analysis, reg);
ret->type = RZ_ANALYSIS_VAL_MEM;
ret->memref = size;
return ret;
}
/* @(R0,Rx) references for all sizes */
static RzAnalysisValue *analysis_fill_r0_reg_ref(RzAnalysis *analysis, int reg, st64 size) {
RzAnalysisValue *ret = analysis_fill_ai_rg(analysis, 0);
ret->type = RZ_ANALYSIS_VAL_MEM;
ret->regdelta = rz_reg_get(analysis->reg, regs[reg], RZ_REG_TYPE_GPR);
ret->memref = size;
return ret;
}
// @(disp,PC) for size=2(.w), size=4(.l). disp is 0-extended
static RzAnalysisValue *analysis_pcrel_disp_mov(RzAnalysis *analysis, RzAnalysisOp *op, ut8 disp, int size) {
RzAnalysisValue *ret = rz_analysis_value_new();
ret->type = RZ_ANALYSIS_VAL_MEM;
if (size == 2) {
ret->base = op->addr + 4;
ret->delta = disp << 1;
} else {
ret->base = (op->addr + 4) & ~0x03;
ret->delta = disp << 2;
}
ret->memref = size;
return ret;
}
//= PC+4+R<reg>
static RzAnalysisValue *analysis_regrel_jump(RzAnalysis *analysis, RzAnalysisOp *op, ut8 reg) {
RzAnalysisValue *ret = rz_analysis_value_new();
ret->type = RZ_ANALYSIS_VAL_REG;
ret->reg = rz_reg_get(analysis->reg, regs[reg], RZ_REG_TYPE_GPR);
ret->base = op->addr + 4;
return ret;
}
/* 16 decoder routines, based on 1st nibble value */
static int first_nibble_is_0(RzAnalysis *analysis, RzAnalysisOp *op, ut16 code) { // STOP
if (IS_BSRF(code)) {
/* Call 'far' subroutine Rn+PC+4 */
op->type = RZ_ANALYSIS_OP_TYPE_UCALL;
op->delay = 1;
op->dst = analysis_regrel_jump(analysis, op, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "1,SETD,pc,2,+,pr,=,r%d,2,+,pc,+=", GET_TARGET_REG(code));
} else if (IS_BRAF(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_UJMP;
op->dst = analysis_regrel_jump(analysis, op, GET_TARGET_REG(code));
op->eob = true;
op->delay = 1;
rz_strbuf_setf(&op->esil, "1,SETD,r%d,2,+,pc,+=", GET_TARGET_REG(code));
} else if (IS_RTS(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_RET;
op->delay = 1;
op->eob = true;
rz_strbuf_setf(&op->esil, "pr,pc,=");
} else if (IS_RTE(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_RET;
op->delay = 1;
op->eob = true;
// rz_strbuf_setf (&op->esil, "1,SETD,r15,[4],4,+,pc,=,r15,4,+,[4],0xFFF0FFF,&,sr,=,8,r15,+=");
// not sure if should be added 4 to pc
rz_strbuf_setf(&op->esil, "1,SETD,r15,[4],pc,=,r15,4,+,[4],0xFFF0FFF,&,sr,=,8,r15,+=");
} else if (IS_MOVB_REG_TO_R0REL(code)) { // 0000nnnnmmmm0100 mov.b <REG_M>,@(R0,<REG_N>)
op->type = RZ_ANALYSIS_OP_TYPE_STORE;
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->dst = analysis_fill_r0_reg_ref(analysis, GET_TARGET_REG(code), BYTE_SIZE);
rz_strbuf_setf(&op->esil, "r%d,0xFF,&,r0,r%d,+,=[1]", GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_MOVW_REG_TO_R0REL(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_STORE;
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->dst = analysis_fill_r0_reg_ref(analysis, GET_TARGET_REG(code), WORD_SIZE);
rz_strbuf_setf(&op->esil, "r%d,0xFFFF,&,r0,r%d,+,=[2]", GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_MOVL_REG_TO_R0REL(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_STORE;
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->dst = analysis_fill_r0_reg_ref(analysis, GET_TARGET_REG(code), LONG_SIZE);
rz_strbuf_setf(&op->esil, "r%d,r0,r%d,+,=[4]", GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_MOVB_R0REL_TO_REG(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_LOAD;
op->src[0] = analysis_fill_r0_reg_ref(analysis, GET_SOURCE_REG(code), BYTE_SIZE);
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "r0,r%d,+,[1],r%d,=,0x000000FF,r%d,&=,r%d,0x80,&,?{,0xFFFFFF00,r%d,|=,}", GET_SOURCE_REG(code), GET_TARGET_REG(code), GET_TARGET_REG(code), GET_TARGET_REG(code), GET_TARGET_REG(code));
} else if (IS_MOVW_R0REL_TO_REG(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_LOAD;
op->src[0] = analysis_fill_r0_reg_ref(analysis, GET_SOURCE_REG(code), WORD_SIZE);
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "r0,r%d,+,[2],r%d,=,0x0000FFFF,r%d,&=,r%d,0x8000,&,?{,0xFFFF0000,r%d,|=,}", GET_SOURCE_REG(code), GET_TARGET_REG(code), GET_TARGET_REG(code), GET_TARGET_REG(code), GET_TARGET_REG(code));
} else if (IS_MOVL_R0REL_TO_REG(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_LOAD;
op->src[0] = analysis_fill_r0_reg_ref(analysis, GET_SOURCE_REG(code), LONG_SIZE);
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "r0,r%d,+,[4],r%d,=", GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_NOP(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_NOP;
rz_strbuf_setf(&op->esil, " ");
} else if (IS_CLRT(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_UNK;
rz_strbuf_setf(&op->esil, "0xFFFFFFFE,sr,&=");
} else if (IS_SETT(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_UNK;
rz_strbuf_setf(&op->esil, "0x1,sr,|=");
} else if (IS_CLRMAC(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_UNK;
rz_strbuf_setf(&op->esil, "0,mach,=,0,macl,=");
} else if (IS_DIV0U(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_DIV;
rz_strbuf_setf(&op->esil, "0xFFFFFCFE,sr,&=");
} else if (IS_MOVT(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "0x1,sr,&,r%d,=", GET_TARGET_REG(code));
} else if (IS_MULL(code)) { // multiply long
op->type = RZ_ANALYSIS_OP_TYPE_MUL;
op->src[0] = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
op->src[1] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
rz_strbuf_setf(&op->esil, "r%d,r%d,*,macl,=", GET_TARGET_REG(code), GET_SOURCE_REG(code));
} else if (IS_SLEEP(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_UNK;
rz_strbuf_setf(&op->esil, "sleep_called,TRAP");
} else if (IS_STSMACH(code)) { // 0000nnnn0000101_ sts MAC*,<REG_N>
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "mach,r%d,=", GET_TARGET_REG(code));
} else if (IS_STSMACL(code)) { // 0000nnnn0000101_ sts MAC*,<REG_N>
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "macl,r%d,=", GET_TARGET_REG(code));
} else if (IS_STSLMACL(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "macl,r%d,=", GET_TARGET_REG(code));
} else if (IS_STCSR1(code)) { // 0000nnnn00010010 stc {sr,gbr,vbr,ssr},<REG_N>
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
// todo: plug in src
switch (GET_SOURCE_REG(code)) {
case 0:
rz_strbuf_setf(&op->esil, "sr,r%d,=", GET_TARGET_REG(code));
break;
case 1:
rz_strbuf_setf(&op->esil, "gbr,r%d,=", GET_TARGET_REG(code));
break;
case 2:
rz_strbuf_setf(&op->esil, "vbr,r%d,=", GET_TARGET_REG(code));
break;
default:
rz_strbuf_setf(&op->esil, "%s", "");
break;
}
} else if (IS_STSPR(code)) { // 0000nnnn00101010 sts PR,<REG_N>
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "pr,r%d,=", GET_TARGET_REG(code));
} else if (IS_MACL(code)) {
rz_strbuf_setf(&op->esil,
"mach,0x80000000,&,!," // mach_old sign (0)
S32_EXT("r%d,[4]") "," //@Rn sign extended
S32_EXT("r%d,[4]") "," //@Rm sign extended
"*," //(1)
"macl,32,mach,<<,|," // macl | (mach << 32)
"+," // MAC+@Rm*@Rn
"32," S32_EXT("r%d,[4]") "," //@Rn sign extended
S32_EXT("r%d,[4]") "," //@Rm sign extended
"*,"
"4,r%d,+=," // Rn+=4
"4,r%d,+=," // Rm+=4
"0xffffffff00000000,&,>>,mach,=," // MACH > mach
"0xffffffff,&,macl,=,"
"0x2,sr,&,!,?{,BREAK,}," // if S==0 BREAK
"0x00007fff,mach,>,"
"0x80000000,mach,&,!,&,"
"?{,0x00007fff,mach,=,0xffffffff,macl,=,}," // if (mach>0x00007fff&&mach>0) mac=0x00007fffffffffff
"0xffff8000,mach,<,"
"0x80000000,mach,&,!,!,&,"
"?{,0xffff8000,mach,=,0x0,macl,=,}," // if (mach>0xffff8000&&mach<0) mac=0xffff800000000000
,
GET_TARGET_REG(code), GET_SOURCE_REG(code),
GET_TARGET_REG(code), GET_SOURCE_REG(code),
GET_TARGET_REG(code), GET_SOURCE_REG(code));
op->type = RZ_ANALYSIS_OP_TYPE_MUL;
}
return op->size;
}
// nibble=1; 0001nnnnmmmmi4*4 mov.l <REG_M>,@(<disp>,<REG_N>)
static int movl_reg_rdisp(RzAnalysis *analysis, RzAnalysisOp *op, ut16 code) {
op->type = RZ_ANALYSIS_OP_TYPE_STORE;
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->dst = analysis_fill_reg_disp_mem(analysis, GET_TARGET_REG(code), code & 0x0F, LONG_SIZE);
rz_strbuf_setf(&op->esil, "r%d,r%d,0x%x,+,=[4]", GET_SOURCE_REG(code), GET_TARGET_REG(code), (code & 0xF) << 2);
return op->size;
}
static int first_nibble_is_2(RzAnalysis *analysis, RzAnalysisOp *op, ut16 code) {
if (IS_MOVB_REG_TO_REGREF(code)) { // 0010nnnnmmmm0000 mov.b <REG_M>,@<REG_N>
op->type = RZ_ANALYSIS_OP_TYPE_STORE;
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->dst = analysis_fill_reg_ref(analysis, GET_TARGET_REG(code), BYTE_SIZE);
rz_strbuf_setf(&op->esil, "r%d,r%d,=[1]", GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_MOVW_REG_TO_REGREF(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_STORE;
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->dst = analysis_fill_reg_ref(analysis, GET_TARGET_REG(code), WORD_SIZE);
rz_strbuf_setf(&op->esil, "r%d,r%d,=[2]", GET_SOURCE_REG(code) & 0xFF, GET_TARGET_REG(code));
} else if (IS_MOVL_REG_TO_REGREF(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_STORE;
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->dst = analysis_fill_reg_ref(analysis, GET_TARGET_REG(code), LONG_SIZE);
rz_strbuf_setf(&op->esil, "r%d,r%d,=[4]", GET_SOURCE_REG(code) & 0xFF, GET_TARGET_REG(code));
} else if (IS_AND_REGS(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_AND;
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "r%d,r%d,&=", GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_XOR_REGS(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_XOR;
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "r%d,r%d,^=", GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_OR_REGS(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_OR;
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "r%d,r%d,|=", GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_PUSHB(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_PUSH;
rz_strbuf_setf(&op->esil, "1,r%d,-=,r%d,r%d,=[1]", GET_TARGET_REG(code), GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_PUSHW(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_PUSH;
rz_strbuf_setf(&op->esil, "2,r%d,-=,r%d,r%d,=[2]", GET_TARGET_REG(code), GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_PUSHL(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_PUSH;
rz_strbuf_setf(&op->esil, "4,r%d,-=,r%d,r%d,=[4]", GET_TARGET_REG(code), GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_TSTRR(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_ACMP;
rz_strbuf_setf(&op->esil, "1,sr,|=,r%d,r%d,&,?{,0xFFFFFFFE,sr,&=,}", GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_CMPSTR(code)) { // 0010nnnnmmmm1100 cmp/str <REG_M>,<REG_N>
op->type = RZ_ANALYSIS_OP_TYPE_ACMP; // maybe not?
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->src[1] = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "0xFFFFFFFE,sr,&=,24,r%d,r%d,^,>>,0xFF,&,!,?{,1,sr,|=,},16,r%d,r%d,^,>>,0xFF,&,!,?{,1,sr,|=,},8,r%d,r%d,^,>>,0xFF,&,!,?{,1,sr,|=,},r%d,r%d,^,0xFF,&,!,?{,1,sr,|=,}", GET_SOURCE_REG(code), GET_TARGET_REG(code), GET_SOURCE_REG(code), GET_TARGET_REG(code), GET_SOURCE_REG(code), GET_TARGET_REG(code), GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_XTRCT(code)) { // 0010nnnnmmmm1101 xtrct <REG_M>,<REG_N>
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->src[1] = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "16,r%d,0xFFFF,&,<<,16,r%d,0xFFFF0000,&,>>,|,r%d,=", GET_SOURCE_REG(code), GET_TARGET_REG(code), GET_TARGET_REG(code));
} else if (IS_DIV0S(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_DIV;
rz_strbuf_setf(&op->esil, "0xFFFFFCFE,sr,&=,r%d,0x80000000,&,?{,0x200,sr,|=,},r%d,0x80000000,&,?{,0x100,sr,|=,},sr,1,sr,<<,^,0x200,&,?{,1,sr,|=,}", GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_MULUW(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_MUL;
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->src[1] = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "r%d,0xFFFF,&,r%d,0xFFFF,&,*,macl,=", GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_MULSW(code)) { // 0010nnnnmmmm111_ mul{s,u}.w <REG_M>,<REG_N>
op->type = RZ_ANALYSIS_OP_TYPE_MUL;
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->src[1] = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, S16_EXT("r%d") "," S16_EXT("r%d") ",*,macl,=", GET_SOURCE_REG(code), GET_TARGET_REG(code));
}
return op->size;
}
static int first_nibble_is_3(RzAnalysis *analysis, RzAnalysisOp *op, ut16 code) {
// TODO Handle carry/overflow , CMP/xx?
if (IS_ADD(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "r%d,r%d,+=", GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_ADDC(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "sr,0x1,&,0xFFFFFFFE,sr,&=,r%d,+=,31,$c,sr,|,sr,:=,r%d,r%d,+=,31,$c,sr,|,sr,:=", GET_TARGET_REG(code), GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_ADDV(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "0xFFFFFFFE,sr,&=,r%d,r%d,+=,31,$o,sr,|=", GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_SUB(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_SUB;
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "r%d,r%d,-=", GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_SUBC(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_SUB;
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "sr,1,&," CLR_T ",r%d,-=,31,$b,sr,|,sr,:=,r%d,r%d,-=,31,$b,sr,|,sr,:=", GET_TARGET_REG(code), GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_SUBV(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_SUB;
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, CLR_T ",r%d,r%d,-=,31,$o,sr,|,sr,:=", GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_CMPEQ(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_CMP;
op->src[0] = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
op->src[1] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
rz_strbuf_setf(&op->esil, "0xFFFFFFFE,sr,&,r%d,r%d,^,!,|,sr,=", GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_CMPGE(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_CMP;
op->src[0] = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
op->src[1] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
rz_strbuf_setf(&op->esil, "0xFFFFFFFE,sr,&=,r%d,r%d,>=,?{,0x1,sr,|=,}", GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_CMPGT(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_CMP;
op->src[0] = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
op->src[1] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
rz_strbuf_setf(&op->esil, "0xFFFFFFFE,sr,&=,r%d,r%d,>,?{,0x1,sr,|=,}", GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_CMPHI(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_CMP;
op->src[0] = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
op->src[1] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
rz_strbuf_setf(&op->esil, "0xFFFFFFFE,sr,&=,r%d,0x100000000,+,r%d,0x100000000,+,>,?{,0x1,sr,|=,}", GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_CMPHS(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_CMP;
op->src[0] = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
op->src[1] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
rz_strbuf_setf(&op->esil, "0xFFFFFFFE,sr,&=,r%d,0x100000000,+,r%d,0x100000000,+,>=,?{,0x1,sr,|=,}", GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_DIV1(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_DIV;
op->src[0] = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
op->src[1] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
rz_strbuf_setf(&op->esil,
"1,sr,>>,sr,^,0x80,&," // old_Q^M
"0xFFFFFF7F,sr,&=,"
"1,r%d,DUP,0x80000000,&,?{,0x80,sr,|=,},<<,sr,0x1,&,|,r%d,=," // shift Q<-Rn<-T
"DUP,!,!,?{,"
"r%d,NUM," // Rn_old (before subtract)
"r%d,r%d,+=,"
"r%d,<,}{," // tmp0
"r%d,NUM," // Rn_old (before subtract)
"r%d,r%d,-=,"
"r%d,>,}," // tmp0
"sr,0x80,&,!,!,^," // Q^tmp0
"sr,0x100,&,?{,!,}," // if (M) !(Q^tmp0)
"0xFFFFFF7F,sr,&=," // Q==0
"?{,0x80,sr,|=,}," // Q=!(Q^tmp0)or(Q^tmp0)
CLR_T ","
"1,sr,>>,sr,^,0x80,&,!,sr,|=", // sr=!Q^M
GET_TARGET_REG(code), GET_TARGET_REG(code),
GET_TARGET_REG(code),
GET_SOURCE_REG(code), GET_TARGET_REG(code),
GET_TARGET_REG(code),
GET_TARGET_REG(code),
GET_SOURCE_REG(code), GET_TARGET_REG(code),
GET_TARGET_REG(code));
} else if (IS_DMULU(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_MUL;
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->src[1] = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "32,r%d,r%d,*,DUP,0xFFFFFFFF,&,macl,=,>>,mach,=", GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_DMULS(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_MUL;
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->src[1] = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "32,r%d,r%d,0x80000000,&,?{,0xFFFFFFFF00000000,+,},r%d,r%d,0x80000000,&,?{,0xFFFFFFFF00000000,+,},*,DUP,0xFFFFFFFF,&,macl,=,>>,mach,=", GET_SOURCE_REG(code), GET_SOURCE_REG(code), GET_TARGET_REG(code), GET_TARGET_REG(code));
}
return op->size;
}
static int first_nibble_is_4(RzAnalysis *analysis, RzAnalysisOp *op, ut16 code) {
switch (code & 0xF0FF) { // TODO: change to common } else if construction
case 0x4020: // shal
op->type = RZ_ANALYSIS_OP_TYPE_SAL;
rz_strbuf_setf(&op->esil, "0xFFFFFFFE,sr,&=,r%d,0x80000000,&,?{,0x1,sr,|=,},1,r%d,<<=", GET_TARGET_REG(code), GET_TARGET_REG(code));
break;
case 0x4021: // shar
op->type = RZ_ANALYSIS_OP_TYPE_SAR;
rz_strbuf_setf(&op->esil, "0xFFFFFFFE,sr,&=,r%d,0x1,&,?{,0x1,sr,|=,},0,r%d,0x80000000,&,?{,0x80000000,+,},1,r%d,>>=,r%d,|=", GET_TARGET_REG(code), GET_TARGET_REG(code), GET_TARGET_REG(code), GET_TARGET_REG(code));
break;
case 0x4000: // shll
op->type = RZ_ANALYSIS_OP_TYPE_SHL;
rz_strbuf_setf(&op->esil, "0xFFFFFFFE,sr,&=,r%d,0x80000000,&,?{,0x1,sr,|=,},1,r%d,<<=", GET_TARGET_REG(code), GET_TARGET_REG(code));
break;
case 0x4008: // shll2
op->type = RZ_ANALYSIS_OP_TYPE_SHL;
rz_strbuf_setf(&op->esil, "2,r%d,<<=", GET_TARGET_REG(code));
break;
case 0x4018: // shll8
op->type = RZ_ANALYSIS_OP_TYPE_SHL;
rz_strbuf_setf(&op->esil, "8,r%d,<<=", GET_TARGET_REG(code));
break;
case 0x4028: // shll16
op->type = RZ_ANALYSIS_OP_TYPE_SHL;
rz_strbuf_setf(&op->esil, "16,r%d,<<=", GET_TARGET_REG(code));
break;
case 0x4001: // shlr
rz_strbuf_setf(&op->esil, "0xFFFFFFFE,sr,&=,r%d,0x1,&,?{,0x1,sr,|=,},1,r%d,>>=", GET_TARGET_REG(code), GET_TARGET_REG(code));
op->type = RZ_ANALYSIS_OP_TYPE_SHR;
break;
case 0x4009: // shlr2
rz_strbuf_setf(&op->esil, "2,r%d,>>=", GET_TARGET_REG(code));
op->type = RZ_ANALYSIS_OP_TYPE_SHR;
break;
case 0x4019: // shlr8
rz_strbuf_setf(&op->esil, "8,r%d,>>=", GET_TARGET_REG(code));
op->type = RZ_ANALYSIS_OP_TYPE_SHR;
break;
case 0x4029: // shlr16
rz_strbuf_setf(&op->esil, "16,r%d,>>=", GET_TARGET_REG(code));
op->type = RZ_ANALYSIS_OP_TYPE_SHR;
break;
default:
break;
}
if (IS_JSR(code)) {
// op->type = RZ_ANALYSIS_OP_TYPE_UCALL; //call to reg
op->type = RZ_ANALYSIS_OP_TYPE_RCALL; // call to reg
op->delay = 1;
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "1,SETD,pc,2,+,pr,=,r%d,pc,=", GET_TARGET_REG(code));
} else if (IS_JMP(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_UJMP; // jmp to reg
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
op->delay = 1;
op->eob = true;
rz_strbuf_setf(&op->esil, "1,SETD,r%d,pc,=", GET_TARGET_REG(code));
} else if (IS_CMPPL(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_CMP;
rz_strbuf_setf(&op->esil, "0xFFFFFFFE,sr,&=,0,r%d,>,?{,0x1,sr,|=,}", GET_TARGET_REG(code));
} else if (IS_CMPPZ(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_CMP;
rz_strbuf_setf(&op->esil, "0xFFFFFFFE,sr,&=,0,r%d,>=,?{,0x1,sr,|=,}", GET_TARGET_REG(code));
} else if (IS_LDCLSR(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_POP;
rz_strbuf_setf(&op->esil, "r%d,[4],0x0FFF0FFF,&,sr,=,4,r%d,+=", GET_TARGET_REG(code), GET_TARGET_REG(code));
} else if (IS_LDCLSRGBR(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_POP;
rz_strbuf_setf(&op->esil, "r%d,[4],gbr,=,4,r%d,+=", GET_TARGET_REG(code), GET_TARGET_REG(code));
} else if (IS_LDCLSRVBR(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_POP;
rz_strbuf_setf(&op->esil, "r%d,[4],vbr,=,4,r%d,+=", GET_TARGET_REG(code), GET_TARGET_REG(code));
// todo ssr?
} else if (IS_LDSLMACH(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_POP;
rz_strbuf_setf(&op->esil, "r%d,[4],mach,=,4,r%d,+=", GET_TARGET_REG(code), GET_TARGET_REG(code));
} else if (IS_LDSLMACL(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_POP;
rz_strbuf_setf(&op->esil, "r%d,[4],macl,=,4,r%d,+=", GET_TARGET_REG(code), GET_TARGET_REG(code));
} else if (IS_LDSLPR(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_POP;
rz_strbuf_setf(&op->esil, "r%d,[4],pr,=,4,r%d,+=", GET_TARGET_REG(code), GET_TARGET_REG(code));
} else if (IS_LDCSR(code)) {
rz_strbuf_setf(&op->esil, "r%d,0x0FFF0FFF,&,sr,=", GET_TARGET_REG(code));
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
} else if (IS_LDCGBR(code)) {
rz_strbuf_setf(&op->esil, "r%d,gbr,=", GET_TARGET_REG(code));
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
} else if (IS_LDCVBR(code)) {
rz_strbuf_setf(&op->esil, "r%d,vbr,=", GET_TARGET_REG(code));
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
} else if (IS_LDSMACH(code)) {
rz_strbuf_setf(&op->esil, "r%d,mach,=", GET_TARGET_REG(code));
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
} else if (IS_LDSMACL(code)) {
rz_strbuf_setf(&op->esil, "r%d,macl,=", GET_TARGET_REG(code));
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
} else if (IS_LDSPR(code)) {
rz_strbuf_setf(&op->esil, "r%d,pr,=", GET_TARGET_REG(code));
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
} else if (IS_ROTR(code)) {
rz_strbuf_setf(&op->esil, "0xFFFFFFFE,sr,&=,r%d,0x1,&,sr,|=,0x1,r%d,>>>,r%d,=", GET_TARGET_REG(code), GET_TARGET_REG(code), GET_TARGET_REG(code));
op->type = (code & 1) ? RZ_ANALYSIS_OP_TYPE_ROR : RZ_ANALYSIS_OP_TYPE_ROL;
} else if (IS_ROTL(code)) {
rz_strbuf_setf(&op->esil, "0xFFFFFFFE,sr,&=,0x1,r%d,<<<,r%d,=,r%d,0x1,&,sr,|=", GET_TARGET_REG(code), GET_TARGET_REG(code), GET_TARGET_REG(code));
op->type = (code & 1) ? RZ_ANALYSIS_OP_TYPE_ROR : RZ_ANALYSIS_OP_TYPE_ROL;
} else if (IS_ROTCR(code)) {
rz_strbuf_setf(&op->esil, "0,sr,0x1,&,?{,0x80000000,},0xFFFFFFFE,sr,&=,r%d,1,&,sr,|=,1,r%d,>>=,r%d,|=", GET_TARGET_REG(code), GET_TARGET_REG(code), GET_TARGET_REG(code));
op->type = (code & 1) ? RZ_ANALYSIS_OP_TYPE_ROR : RZ_ANALYSIS_OP_TYPE_ROL;
} else if (IS_ROTCL(code)) {
rz_strbuf_setf(&op->esil, "sr,0x1,&,0xFFFFFFFE,sr,&=,r%d,0x80000000,&,?{,1,sr,|=,},1,r%d,<<=,r%d,|=", GET_TARGET_REG(code), GET_TARGET_REG(code), GET_TARGET_REG(code));
op->type = (code & 1) ? RZ_ANALYSIS_OP_TYPE_ROR : RZ_ANALYSIS_OP_TYPE_ROL;
} else if (IS_STCLSR(code)) {
rz_strbuf_setf(&op->esil, "4,r%d,-=,sr,r%d,=[4]", GET_TARGET_REG(code), GET_TARGET_REG(code));
op->type = RZ_ANALYSIS_OP_TYPE_PUSH;
} else if (IS_STCLGBR(code)) {
rz_strbuf_setf(&op->esil, "4,r%d,-=,gbr,r%d,=[4]", GET_TARGET_REG(code), GET_TARGET_REG(code));
op->type = RZ_ANALYSIS_OP_TYPE_PUSH;
} else if (IS_STCLVBR(code)) {
rz_strbuf_setf(&op->esil, "4,r%d,-=,vbr,r%d,=[4]", GET_TARGET_REG(code), GET_TARGET_REG(code));
op->type = RZ_ANALYSIS_OP_TYPE_PUSH;
} else if (IS_STSLMACL(code)) {
rz_strbuf_setf(&op->esil, "4,r%d,-=,macl,r%d,=[4]", GET_TARGET_REG(code), GET_TARGET_REG(code));
op->type = RZ_ANALYSIS_OP_TYPE_PUSH;
} else if (IS_STSLMACH(code)) {
rz_strbuf_setf(&op->esil, "4,r%d,-=,mach,r%d,=[4]", GET_TARGET_REG(code), GET_TARGET_REG(code));
op->type = RZ_ANALYSIS_OP_TYPE_PUSH;
} else if (IS_STSLPR(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_PUSH;
rz_strbuf_setf(&op->esil, "4,r%d,-=,pr,r%d,=[4]", GET_TARGET_REG(code), GET_TARGET_REG(code));
} else if (IS_TASB(code)) {
rz_strbuf_setf(&op->esil, "0xFFFFFFFE,sr,&=,r%d,[1],!,?{,0x80,r%d,=[1],1,sr,|=,}", GET_TARGET_REG(code), GET_TARGET_REG(code));
op->type = RZ_ANALYSIS_OP_TYPE_UNK;
} else if (IS_DT(code)) {
rz_strbuf_setf(&op->esil, "0xFFFFFFFE,sr,&=,1,r%d,-=,$z,sr,|,sr,:=", GET_TARGET_REG(code));
op->type = RZ_ANALYSIS_OP_TYPE_UNK;
} else if (IS_MACW(code)) {
rz_strbuf_setf(&op->esil,
"0x2,sr,&,!,?{," // if S==0
S16_EXT("r%d,[2]") "," //@Rn sign extended
S16_EXT("r%d,[2]") "," //@Rm sign extended
"*,"
"macl,32,mach,<<,|," // macl | (mach << 32)
"+," // MAC+@Rm*@Rn
"32," S16_EXT("r%d,[2]") "," //@Rn sign extended
S16_EXT("r%d,[2]") "," //@Rm sign extended
"*,"
"0xffffffff00000000,&,>>,mach,=," // MACH > mach
"0xffffffff,&,macl,=,"
"}{," // if S==1
S16_EXT("r%d,[2]") "," //@Rn sign extended
S16_EXT("r%d,[2]") "," //@Rm sign extended
"*"
"macl,+=," // macl+(@Rm+@Rm)
"31,$o,?{," // if overflow
"macl,0x80000000,&,?{,"
"0x7fffffff,macl,=,"
"}{,"
"0x80000000,macl,=,"
"},"
"},"
"},"
"2,r%d,+=," // Rn+=2
"2,r%d,+=,", // Rm+=2
GET_TARGET_REG(code), GET_SOURCE_REG(code),
GET_TARGET_REG(code), GET_SOURCE_REG(code),
GET_TARGET_REG(code), GET_SOURCE_REG(code),
GET_TARGET_REG(code), GET_SOURCE_REG(code));
op->type = RZ_ANALYSIS_OP_TYPE_MUL;
}
return op->size;
}
// nibble=5; 0101nnnnmmmmi4*4 mov.l @(<disp>,<REG_M>),<REG_N>
static int movl_rdisp_reg(RzAnalysis *analysis, RzAnalysisOp *op, ut16 code) {
op->type = RZ_ANALYSIS_OP_TYPE_LOAD;
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
op->src[0] = analysis_fill_reg_disp_mem(analysis, GET_SOURCE_REG(code), code & 0x0F, LONG_SIZE);
rz_strbuf_setf(&op->esil, "r%d,0x%x,+,[4],r%d,=", GET_SOURCE_REG(code), (code & 0xF) * 4, GET_TARGET_REG(code));
return op->size;
}
static int first_nibble_is_6(RzAnalysis *analysis, RzAnalysisOp *op, ut16 code) {
if (IS_MOV_REGS(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "r%d,r%d,=", GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_MOVB_REGREF_TO_REG(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_LOAD;
op->src[0] = analysis_fill_reg_ref(analysis, GET_SOURCE_REG(code), BYTE_SIZE);
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "0x000000FF,r%d,&=,r%d,[1],DUP,0x80,&,?{,0xFFFFFF00,|=,},r%d,=", GET_TARGET_REG(code), GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_MOVW_REGREF_TO_REG(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_LOAD;
op->src[0] = analysis_fill_reg_ref(analysis, GET_SOURCE_REG(code), WORD_SIZE);
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "0x0000FFFF,r%d,&=,r%d,[2],DUP,0x8000,&,?{,0xFFFF0000,|=,},r%d,=", GET_TARGET_REG(code), GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_MOVL_REGREF_TO_REG(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_LOAD;
op->src[0] = analysis_fill_reg_ref(analysis, GET_SOURCE_REG(code), LONG_SIZE);
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "r%d,[4],r%d,=", GET_SOURCE_REG(code), GET_TARGET_REG(code));
} else if (IS_EXT(code)) {
// ext{s,u}.{b,w} instructs. todo : more detail ?
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
switch (code & 0xF) {
case 0xC: // EXTU.B
rz_strbuf_setf(&op->esil, "r%d,0xFF,&,r%d,=", GET_SOURCE_REG(code), GET_TARGET_REG(code));
break;
case 0xD: // EXTU.W
rz_strbuf_setf(&op->esil, "r%d,0xFFFF,&,r%d,=", GET_SOURCE_REG(code), GET_TARGET_REG(code));
break;
case 0xE: // EXTS.B
rz_strbuf_setf(&op->esil, "r%d,0xFF,&,DUP,0x80,&,?{,0xFFFFFF00,|,},r%d,=", GET_SOURCE_REG(code), GET_TARGET_REG(code));
break;
case 0xF: // EXTS.W
rz_strbuf_setf(&op->esil, "r%d,0xFFFF,&,DUP,0x8000,&,?{,0xFFFF0000,|,},r%d,=", GET_SOURCE_REG(code), GET_TARGET_REG(code));
break;
default:
rz_strbuf_setf(&op->esil, "TODO,NOT IMPLEMENTED");
break;
}
} else if (IS_MOVB_POP(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_POP;
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "r%d,[1],DUP,0x80,&,?{,0xFFFFFF00,|,},r%d,=,1,r%d,+=", GET_SOURCE_REG(code), GET_TARGET_REG(code), GET_SOURCE_REG(code));
} else if (IS_MOVW_POP(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_POP;
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "r%d,[2],DUP,0x8000,&,?{,0xFFFF0000,|,},r%d,=,2,r%d,+=", GET_SOURCE_REG(code), GET_TARGET_REG(code), GET_SOURCE_REG(code));
} else if (IS_MOVL_POP(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_POP;
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "r%d,[4],r%d,=,4,r%d,+=", GET_SOURCE_REG(code), GET_TARGET_REG(code), GET_SOURCE_REG(code));
} else if (IS_NEG(code)) {
// todo: neg and negc details
op->type = RZ_ANALYSIS_OP_TYPE_UNK;
rz_strbuf_setf(&op->esil, "r%d,0,-,r%d,=", GET_SOURCE_REG(code), GET_TARGET_REG(code));
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
} else if (IS_NEGC(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_UNK;
rz_strbuf_setf(&op->esil, "1,sr,&,0xFFFFFFFE,sr,&=,r%d,+,0,-,31,$b,sr,|,sr,=,r%d,=", GET_SOURCE_REG(code), GET_TARGET_REG(code));
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
} else if (IS_NOT(code)) {
// todo : details?
rz_strbuf_setf(&op->esil, "0xFFFFFFFF,r%d,^,r%d,=", GET_SOURCE_REG(code), GET_TARGET_REG(code));
op->type = RZ_ANALYSIS_OP_TYPE_NOT;
op->src[0] = analysis_fill_ai_rg(analysis, GET_SOURCE_REG(code));
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
} else if (IS_SWAPB(code)) {
rz_strbuf_setf(&op->esil, "r%d,0xFFFF0000,&,8,r%d,0xFF,&,<<,|,8,r%d,0xFF00,&,>>,|,r%d,=", GET_SOURCE_REG(code), GET_SOURCE_REG(code), GET_SOURCE_REG(code), GET_TARGET_REG(code));
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
// todo : details
} else if (IS_SWAPW(code)) {
rz_strbuf_setf(&op->esil, "16,r%d,0xFFFF,&,<<,16,r%d,0xFFFF0000,&,>>,|,r%d,=", GET_SOURCE_REG(code), GET_SOURCE_REG(code), GET_TARGET_REG(code));
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
}
return op->size;
}
// nibble=7; 0111nnnni8*1.... add #<imm>,<REG_N>
static int add_imm(RzAnalysis *analysis, RzAnalysisOp *op, ut16 code) {
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
op->src[0] = analysis_fill_im(analysis, (st8)(code & 0xFF)); // Casting to (st8) forces sign-extension.
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
rz_strbuf_setf(&op->esil, "0x%x,DUP,0x80,&,?{,0xFFFFFF00,|,},r%d,+=", code & 0xFF, GET_TARGET_REG(code));
return op->size;
}
static int first_nibble_is_8(RzAnalysis *analysis, RzAnalysisOp *op, ut16 code) {
if (IS_BT_OR_BF(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_CJMP; // Jump if true or jump if false insns
op->jump = disarm_8bit_offset(op->addr, GET_BTF_OFFSET(code));
op->fail = op->addr + 2;
op->eob = true;
if (IS_BT(code)) {
rz_strbuf_setf(&op->esil, "sr,1,&,?{,0x%" PFMT64x ",pc,=,}", op->jump);
} else if (IS_BTS(code)) {
rz_strbuf_setf(&op->esil, "1,SETD,sr,1,&,?{,0x%" PFMT64x ",pc,=,}", op->jump);
op->delay = 1; // Only /S versions have a delay slot
} else if (IS_BFS(code)) {
rz_strbuf_setf(&op->esil, "1,SETD,sr,1,&,!,?{,0x%" PFMT64x ",pc,=,}", op->jump);
op->delay = 1; // Only /S versions have a delay slot
} else if (IS_BF(code)) {
rz_strbuf_setf(&op->esil, "sr,1,&,!,?{,0x%" PFMT64x ",pc,=,}", op->jump);
}
} else if (IS_MOVB_REGDISP_R0(code)) {
// 10000100mmmmi4*1 mov.b @(<disp>,<REG_M>),R0
op->type = RZ_ANALYSIS_OP_TYPE_LOAD;
op->dst = analysis_fill_ai_rg(analysis, 0);
op->src[0] = analysis_fill_reg_disp_mem(analysis, GET_SOURCE_REG(code), code & 0x0F, BYTE_SIZE);
rz_strbuf_setf(&op->esil, "r%d,0x%x,+,[1],DUP,0x80,&,?{,0xFFFFFF00,|,},r0,=", GET_SOURCE_REG(code), code & 0xF);
} else if (IS_MOVW_REGDISP_R0(code)) {
// 10000101mmmmi4*2 mov.w @(<disp>,<REG_M>),R0
op->type = RZ_ANALYSIS_OP_TYPE_LOAD;
op->dst = analysis_fill_ai_rg(analysis, 0);
op->src[0] = analysis_fill_reg_disp_mem(analysis, GET_SOURCE_REG(code), code & 0x0F, WORD_SIZE);
rz_strbuf_setf(&op->esil, "r%d,0x%x,+,[2],DUP,0x8000,&,?{,0xFFFF0000,|,},r0,=", GET_SOURCE_REG(code), (code & 0xF) * 2);
} else if (IS_CMPIMM(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_CMP;
rz_strbuf_setf(&op->esil, "0xFFFFFFFE,sr,&=,0x%x,DUP,0x80,&,?{,0xFFFFFF00,|,},r0,==,$z,sr,|,sr,:=", code & 0xFF);
} else if (IS_MOVB_R0_REGDISP(code)) {
/* 10000000mmmmi4*1 mov.b R0,@(<disp>,<REG_M>)*/
op->type = RZ_ANALYSIS_OP_TYPE_STORE;
op->src[0] = analysis_fill_ai_rg(analysis, 0);
op->dst = analysis_fill_reg_disp_mem(analysis, GET_SOURCE_REG(code), code & 0x0F, BYTE_SIZE);
rz_strbuf_setf(&op->esil, "r0,0xFF,&,0x%x,r%d,+,=[1]", code & 0xF, GET_SOURCE_REG(code));
} else if (IS_MOVW_R0_REGDISP(code)) {
// 10000001mmmmi4*2 mov.w R0,@(<disp>,<REG_M>))
op->type = RZ_ANALYSIS_OP_TYPE_STORE;
op->src[0] = analysis_fill_ai_rg(analysis, 0);
op->dst = analysis_fill_reg_disp_mem(analysis, GET_SOURCE_REG(code), code & 0x0F, WORD_SIZE);
rz_strbuf_setf(&op->esil, "r0,0xFFFF,&,0x%x,r%d,+,=[2]", (code & 0xF) * 2, GET_SOURCE_REG(code));
}
return op->size;
}
// nibble=9; 1001nnnni8p2.... mov.w @(<disp>,PC),<REG_N>
static int movw_pcdisp_reg(RzAnalysis *analysis, RzAnalysisOp *op, ut16 code) {
op->type = RZ_ANALYSIS_OP_TYPE_LOAD;
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
op->src[0] = rz_analysis_value_new();
op->src[0]->base = (code & 0xFF) * 2 + op->addr + 4;
op->src[0]->memref = 1;
rz_strbuf_setf(&op->esil, "0x%" PFMT64x ",[2],r%d,=,r%d,0x8000,&,?{,0xFFFF0000,r%d,|=,}", op->src[0]->base, GET_TARGET_REG(code), GET_TARGET_REG(code), GET_TARGET_REG(code));
return op->size;
}
// nibble=A; 1010i12......... bra <bdisp12>
static int bra(RzAnalysis *analysis, RzAnalysisOp *op, ut16 code) {
/* Unconditional branch, relative to PC */
op->type = RZ_ANALYSIS_OP_TYPE_JMP;
op->delay = 1;
op->jump = disarm_12bit_offset(op, GET_BRA_OFFSET(code));
op->eob = true;
rz_strbuf_setf(&op->esil, "1,SETD,0x%" PFMT64x ",pc,=", op->jump);
return op->size;
}
// nibble=B; 1011i12......... bsr <bdisp12>
static int bsr(RzAnalysis *analysis, RzAnalysisOp *op, ut16 code) {
/* Subroutine call, relative to PC */
op->type = RZ_ANALYSIS_OP_TYPE_CALL;
op->jump = disarm_12bit_offset(op, GET_BRA_OFFSET(code));
op->delay = 1;
rz_strbuf_setf(&op->esil, "1,SETD,pc,2,+,pr,=,0x%" PFMT64x ",pc,=", op->jump);
return op->size;
}
static int first_nibble_is_c(RzAnalysis *analysis, RzAnalysisOp *op, ut16 code) {
if (IS_TRAP(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_SWI;
op->val = (ut8)(code & 0xFF);
rz_strbuf_setf(&op->esil, "4,r15,-=,sr,r15,=[4],4,r15,-=,2,pc,-,r15,=[4],2,0x%x,<<,4,+,vbr,+,pc,=", code & 0xFF);
} else if (IS_MOVA_PCREL_R0(code)) {
// 11000111i8p4.... mova @(<disp>,PC),R0
op->type = RZ_ANALYSIS_OP_TYPE_LEA;
op->src[0] = analysis_pcrel_disp_mov(analysis, op, code & 0xFF, LONG_SIZE); // this is wrong !
op->dst = analysis_fill_ai_rg(analysis, 0); // Always R0
rz_strbuf_setf(&op->esil, "0x%x,pc,+,r0,=", (code & 0xFF) * 4);
} else if (IS_BINLOGIC_IMM_R0(code)) { // 110010__i8 (binop) #imm, R0
op->src[0] = analysis_fill_im(analysis, code & 0xFF);
op->src[1] = analysis_fill_ai_rg(analysis, 0); // Always R0
op->dst = analysis_fill_ai_rg(analysis, 0); // Always R0 except tst #imm, R0
switch (code & 0xFF00) {
case 0xC800: // tst
// TODO : get correct op->dst ! (T flag)
op->type = RZ_ANALYSIS_OP_TYPE_ACMP;
rz_strbuf_setf(&op->esil, "0xFFFFFFFE,sr,&=,r0,0x%x,&,!,?{,1,sr,|=,}", code & 0xFF);
break;
case 0xC900: // and
op->type = RZ_ANALYSIS_OP_TYPE_AND;
rz_strbuf_setf(&op->esil, "0x%x,r0,&=", code & 0xFF);
break;
case 0xCA00: // xor
op->type = RZ_ANALYSIS_OP_TYPE_XOR;
rz_strbuf_setf(&op->esil, "0x%x,r0,^=", code & 0xFF);
break;
case 0xCB00: // or
op->type = RZ_ANALYSIS_OP_TYPE_OR;
rz_strbuf_setf(&op->esil, "0x%x,r0,|=", code & 0xFF);
break;
}
} else if (IS_BINLOGIC_IMM_GBR(code)) { // 110011__i8 (binop).b #imm, @(R0,gbr)
op->src[0] = analysis_fill_im(analysis, code & 0xFF);
switch (code & 0xFF00) {
case 0xCC00: // tst
// TODO : get correct op->dst ! (T flag)
op->type = RZ_ANALYSIS_OP_TYPE_ACMP;
rz_strbuf_setf(&op->esil, "0xFFFFFFFE,sr,&=,r0,gbr,+,[1],0x%x,&,!,?{,1,sr,|=,}", code & 0xFF);
break;
case 0xCD00: // and
op->type = RZ_ANALYSIS_OP_TYPE_AND;
rz_strbuf_setf(&op->esil, "r0,gbr,+,[1],0x%x,&,r0,gbr,+,=[1]", code & 0xFF);
break;
case 0xCE00: // xor
op->type = RZ_ANALYSIS_OP_TYPE_XOR;
rz_strbuf_setf(&op->esil, "r0,gbr,+,[1],0x%x,^,r0,gbr,+,=[1]", code & 0xFF);
break;
case 0xCF00: // or
op->type = RZ_ANALYSIS_OP_TYPE_OR;
rz_strbuf_setf(&op->esil, "r0,gbr,+,[1],0x%x,|,r0,gbr,+,=[1]", code & 0xFF);
break;
}
// TODO : implement @(R0,gbr) dest and src[1]
} else if (IS_MOVB_R0_GBRREF(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_STORE;
op->src[0] = analysis_fill_ai_rg(analysis, 0);
rz_strbuf_setf(&op->esil, "r0,gbr,0x%x,+,=[1]", code & 0xFF);
} else if (IS_MOVW_R0_GBRREF(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_STORE;
op->src[0] = analysis_fill_ai_rg(analysis, 0);
rz_strbuf_setf(&op->esil, "r0,gbr,0x%x,+,=[2]", (code & 0xFF) * 2);
} else if (IS_MOVL_R0_GBRREF(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_STORE;
op->src[0] = analysis_fill_ai_rg(analysis, 0);
rz_strbuf_setf(&op->esil, "r0,gbr,0x%x,+,=[4]", (code & 0xFF) * 4);
} else if (IS_MOVB_GBRREF_R0(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_LOAD;
op->dst = analysis_fill_ai_rg(analysis, 0);
rz_strbuf_setf(&op->esil, "gbr,0x%x,+,[1],DUP,0x80,&,?{,0xFFFFFF00,|,},r0,=", (code & 0xFF));
} else if (IS_MOVW_GBRREF_R0(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_LOAD;
op->dst = analysis_fill_ai_rg(analysis, 0);
rz_strbuf_setf(&op->esil, "gbr,0x%x,+,[2],DUP,0x8000,&,?{,0xFFFF0000,|,},r0,=", (code & 0xFF) * 2);
} else if (IS_MOVL_GBRREF_R0(code)) {
op->type = RZ_ANALYSIS_OP_TYPE_LOAD;
op->dst = analysis_fill_ai_rg(analysis, 0);
rz_strbuf_setf(&op->esil, "gbr,0x%x,+,[4],r0,=", (code & 0xFF) * 4);
}
return op->size;
}
// nibble=d; 1101nnnni8 : mov.l @(<disp>,PC), Rn
static int movl_pcdisp_reg(RzAnalysis *analysis, RzAnalysisOp *op, ut16 code) {
op->type = RZ_ANALYSIS_OP_TYPE_LOAD;
op->src[0] = analysis_pcrel_disp_mov(analysis, op, code & 0xFF, LONG_SIZE);
// TODO: check it
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
// rz_strbuf_setf (&op->esil, "0x%x,[4],r%d,=", (code & 0xFF) * 4 + (op->addr & 0xfffffff3) + 4, GET_TARGET_REG (code));
rz_strbuf_setf(&op->esil, "0x%" PFMT64x ",[4],r%d,=", (code & 0xFF) * 4 + ((op->addr >> 2) << 2) + 4, GET_TARGET_REG(code));
return op->size;
}
// nibble=e; 1110nnnni8*1.... mov #<imm>,<REG_N>
static int mov_imm_reg(RzAnalysis *analysis, RzAnalysisOp *op, ut16 code) {
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
op->dst = analysis_fill_ai_rg(analysis, GET_TARGET_REG(code));
op->src[0] = analysis_fill_im(analysis, (st8)(code & 0xFF));
rz_strbuf_setf(&op->esil, "0x%x,r%d,=,r%d,0x80,&,?{,0xFFFFFF00,r%d,|=,}", code & 0xFF, GET_TARGET_REG(code), GET_TARGET_REG(code), GET_TARGET_REG(code));
return op->size;
}
// nibble=f;
static int fpu_insn(RzAnalysis *analysis, RzAnalysisOp *op, ut16 code) {
// Not interested on FPU stuff for now
op->family = RZ_ANALYSIS_OP_FAMILY_FPU;
return op->size;
}
/* Table of routines for further analysis based on 1st nibble */
static int (*first_nibble_decode[])(RzAnalysis *, RzAnalysisOp *, ut16) = {
first_nibble_is_0,
movl_reg_rdisp,
first_nibble_is_2,
first_nibble_is_3,
first_nibble_is_4,
movl_rdisp_reg,
first_nibble_is_6,
add_imm,
first_nibble_is_8,
movw_pcdisp_reg,
bra,
bsr,
first_nibble_is_c,
movl_pcdisp_reg,
mov_imm_reg,
fpu_insn
};
/* This is the basic operation analysis. Just initialize and jump to
* routines defined in first_nibble_decode table
*/
static int sh_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *data, int len, RzAnalysisOpMask mask) {
ut8 op_MSB, op_LSB;
int ret;
if (!data || len < 2) {
return 0;
}
op->addr = addr;
op->type = RZ_ANALYSIS_OP_TYPE_UNK;
op->size = 2;
op_MSB = analysis->big_endian ? data[0] : data[1];
op_LSB = analysis->big_endian ? data[1] : data[0];
ut16 opcode = (ut16)op_MSB << 8 | op_LSB;
ret = first_nibble_decode[(op_MSB >> 4) & 0x0F](analysis, op, opcode);
// RzIL uplifting
SHOp *ilop = sh_disassembler(opcode);
SHILContext *ctx = RZ_NEW0(SHILContext);
ctx->use_banked = true;
if (ilop) {
rz_sh_il_opcode(analysis, op, addr, ilop, ctx);
}
RZ_FREE(ctx);
RZ_FREE(ilop);
return ret;
}
/* Set the profile register */
static RZ_OWN char *sh_get_reg_profile(RzAnalysis *analysis) {
// TODO Add fpu regs
const char *p =
"=PC pc\n"
"=SN r0\n"
"=SP r15\n"
"=BP r14\n"
"=A0 r4\n"
"=A1 r5\n"
"=A2 r6\n"
"=A3 r7\n"
"=R0 r0\n"
"gpr r0 .32 0 0\n"
"gpr r1 .32 4 0\n"
"gpr r2 .32 8 0\n"
"gpr r3 .32 12 0\n"
"gpr r4 .32 16 0\n"
"gpr r5 .32 20 0\n"
"gpr r6 .32 24 0\n"
"gpr r7 .32 28 0\n"
"gpr r0b .32 32 0\n"
"gpr r1b .32 36 0\n"
"gpr r2b .32 40 0\n"
"gpr r3b .32 44 0\n"
"gpr r4b .32 48 0\n"
"gpr r5b .32 52 0\n"
"gpr r6b .32 56 0\n"
"gpr r7b .32 60 0\n"
"gpr r8 .32 64 0\n"
"gpr r9 .32 68 0\n"
"gpr r10 .32 72 0\n"
"gpr r11 .32 76 0\n"
"gpr r12 .32 80 0\n"
"gpr r13 .32 84 0\n"
"gpr r14 .32 88 0\n"
"gpr r15 .32 92 0\n"
"gpr pc .32 96 0\n"
"gpr sr .32 100 0\n"
"gpr sr_t .1 100.0 0\n"
"gpr sr_s .1 100.1 0\n"
"gpr sr_i .4 100.4 0\n"
"gpr sr_q .1 101.0 0\n"
"gpr sr_m .1 101.1 0\n"
"gpr sr_f .1 101.7 0\n"
"gpr sr_b .1 103.4 0\n"
"gpr sr_r .1 103.5 0\n"
"gpr sr_d .1 103.6 0\n"
"gpr gbr .32 104 0\n"
"gpr ssr .32 108 0\n"
"gpr spc .32 112 0\n"
"gpr sgr .32 116 0\n"
"gpr dbr .32 120 0\n"
"gpr vbr .32 124 0\n"
"gpr mach .32 128 0\n"
"gpr macl .32 132 0\n"
"gpr pr .32 136 0\n";
return rz_str_dup(p);
}
static int archinfo(RzAnalysis *a, RzAnalysisInfoType query) {
switch (query) {
case RZ_ANALYSIS_ARCHINFO_MIN_OP_SIZE:
/* fall-thru */
case RZ_ANALYSIS_ARCHINFO_MAX_OP_SIZE:
/* fall-thru */
case RZ_ANALYSIS_ARCHINFO_TEXT_ALIGN:
/* fall-thru */
case RZ_ANALYSIS_ARCHINFO_DATA_ALIGN:
return 2;
case RZ_ANALYSIS_ARCHINFO_CAN_USE_POINTERS:
return true;
default:
return -1;
}
}
RzAnalysisPlugin rz_analysis_plugin_sh = {
.name = "sh",
.desc = "SH-4 code analysis plugin",
.license = "LGPL3",
.arch = "sh",
.archinfo = archinfo,
.bits = 32,
.op = &sh_op,
.get_reg_profile = &sh_get_reg_profile,
.esil = true,
.il_config = rz_sh_il_config
};