1788 lines
58 KiB
C
1788 lines
58 KiB
C
// SPDX-FileCopyrightText: 2022 Dhruv Maroo <dhruvmaru007@gmail.com>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include "sh_il.h"
|
|
#include <rz_il/rz_il_opbuilder_begin.h>
|
|
#include "regs.h"
|
|
|
|
/**
|
|
* \file sh_il.c
|
|
*
|
|
* Converts SuperH-4 instructions to RzIL statements
|
|
* References:
|
|
* - https://www.st.com/resource/en/user_manual/cd00147165-sh-4-32-bit-cpu-core-architecture-stmicroelectronics.pdf (SH-4 32-bit architecture manual)
|
|
* - https://www.renesas.com/in/en/document/mas/sh-4-software-manual?language=en (SH-4 manual by Renesas)
|
|
*
|
|
* Both the above references are almost the same
|
|
*
|
|
* \note Some things to know before working on this code:
|
|
* - I have used the terms "operand(s)" and "param(s)" interchangeably, and both of them refer to the arguments/params/operands of the instruction
|
|
* - `op` doesn NOT mean operand. It is more akin to instruction or opcode. In majority of the places, it means the type of instruction
|
|
* - I have used the term "pure" in function names very loosely and shouldn't be used in a literal sense. Refer to the Doxygen documentation to
|
|
* know what a function does, do not infer it from the function name
|
|
*/
|
|
|
|
#define SH_U_ADDR(x) UN(SH_ADDR_SIZE, x)
|
|
#define SH_S_ADDR(x) SN(SH_ADDR_SIZE, x)
|
|
#define SH_U_REG(x) UN(SH_REG_SIZE, (x))
|
|
#define SH_S_REG(x) SN(SH_REG_SIZE, (x))
|
|
#define SH_BIT(x) UN(1, x)
|
|
#define SH_TRUE SH_U_REG(1)
|
|
#define SH_FALSE SH_U_REG(0)
|
|
|
|
#define sh_il_get_pure_param(x) \
|
|
sh_il_get_param(op->param[x], op->scaling).pure
|
|
|
|
#define sh_il_set_pure_param(x, val) \
|
|
sh_il_set_param(op->param[x], val, op->scaling)
|
|
|
|
#define sh_il_get_effective_addr_param(x) \
|
|
sh_il_get_effective_addr(op->param[x], op->scaling)
|
|
|
|
/* Utilities */
|
|
|
|
static bool sh_valid_gpr(ut16 reg) {
|
|
return reg < SH_GPR_COUNT;
|
|
}
|
|
|
|
static bool sh_banked_reg(ut16 reg) {
|
|
return reg < SH_BANKED_REG_COUNT;
|
|
}
|
|
|
|
/**
|
|
* Registers available as global variables in the IL
|
|
*/
|
|
static const char *sh_global_registers[] = {
|
|
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", ///< bank 0 registers (user mode)
|
|
"r0b", "r1b", "r2b", "r3b", "r4b", "r5b", "r6b", "r7b", ///< bank 1 registers (privileged mode)
|
|
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "sr",
|
|
SH_SR_T, SH_SR_S, SH_SR_I, SH_SR_I, SH_SR_Q, SH_SR_M, SH_SR_F, SH_SR_B, SH_SR_R, SH_SR_D, ///< status register bits
|
|
"gbr", "ssr", "spc", "sgr", "dbr", "vbr", "mach", "macl", "pr"
|
|
};
|
|
|
|
/**
|
|
* \brief Cast \p val to \p len bits
|
|
* This uses a local temp variable \p temp_var to store the value \p val
|
|
* and then casts the local variable \p temp_var to its signed value
|
|
* and stores that value in the local variable \p cast_var
|
|
*
|
|
* The purpose of this function is to remove the redundant IL block introduced
|
|
* by `SIGNED` opbuilder macro (`MSB` of \p val for fill bits, and \p val for value),
|
|
* and instead use a local variable to refer to \p val in both the above places,
|
|
* making the IL dump much more concise and readable
|
|
*
|
|
* TODO: Ideally this should be integrated in `SIGNED` itself (need to be clever about it though)
|
|
*
|
|
* \param len
|
|
* \param val
|
|
* \param cast_var Casted variable name ; Use this variable to access the casted value in the caller
|
|
* \param temp_var Temp variable name ; Do NOT use this variable outside this function
|
|
* \return RzILOpEffect* Effect corresponding to setting the local variables
|
|
*/
|
|
RzILOpEffect *sh_il_signed(unsigned int len, RZ_OWN RzILOpPure *val, const char *cast_var, const char *temp_var) {
|
|
RzILOpEffect *init = SETL(temp_var, val);
|
|
RzILOpPure *cast = rz_il_op_new_cast(len, MSB(VARL(temp_var)), VARL(temp_var));
|
|
RzILOpEffect *set = SETL(cast_var, cast);
|
|
|
|
return SEQ2(init, set);
|
|
}
|
|
|
|
/**
|
|
* \brief Get the register name for \p reg in bank \p bank
|
|
*
|
|
* \param reg Register index
|
|
* \param bank Bank number
|
|
* \return const char* IL global variable name
|
|
*/
|
|
static const char *sh_get_banked_reg(ut16 reg, ut8 bank) {
|
|
if (!sh_banked_reg(reg) || bank > 1) {
|
|
return NULL;
|
|
}
|
|
return sh_global_registers[reg + bank * SH_BANKED_REG_COUNT];
|
|
}
|
|
|
|
/**
|
|
* \brief Convert \p b to SH_TRUE or SH_FALSE (bool zero-extended to SH_REG_SIZE)
|
|
*
|
|
* \param b RzILOpBool to be converted
|
|
* \return RzILOpBitVector* Zero extended bitvector value
|
|
*/
|
|
static RzILOpBitVector *sh_il_bool_to_bv(RzILOpBool *b) {
|
|
return ITE(b, SH_TRUE, SH_FALSE);
|
|
}
|
|
|
|
/**
|
|
* \brief We need this because sometimes we would want an `RzILOpBitvector` back
|
|
* when we ask for a status reg bit, so this returns us an RzILOpBitvector instead
|
|
* of the `RzILOpBool` returned when using `VARG`
|
|
*
|
|
* \param bit The status register bit global variable name
|
|
* \return RzILOpBitVector* Zero extended bitvector value
|
|
*/
|
|
static RzILOpBitVector *sh_il_get_status_reg_bit(const char *bit) {
|
|
return sh_il_bool_to_bv(VARG(bit));
|
|
}
|
|
|
|
/**
|
|
* \brief Return the status register (sr), calculated by shifting all the status register bits at the correct offsets
|
|
*
|
|
* \return RzILOpPure* The status register IL bitvector
|
|
*/
|
|
static RzILOpPure *sh_il_get_status_reg() {
|
|
RzILOpPure *val = SH_U_REG(0);
|
|
val = LOGOR(sh_il_get_status_reg_bit(SH_SR_D), val);
|
|
val = SHIFTL0(val, SH_U_REG(1));
|
|
val = LOGOR(sh_il_get_status_reg_bit(SH_SR_R), val);
|
|
val = SHIFTL0(val, SH_U_REG(1));
|
|
val = LOGOR(sh_il_get_status_reg_bit(SH_SR_B), val);
|
|
val = SHIFTL0(val, SH_U_REG(13));
|
|
val = LOGOR(sh_il_get_status_reg_bit(SH_SR_F), val);
|
|
val = SHIFTL0(val, SH_U_REG(6));
|
|
val = LOGOR(sh_il_get_status_reg_bit(SH_SR_M), val);
|
|
val = SHIFTL0(val, SH_U_REG(1));
|
|
val = LOGOR(sh_il_get_status_reg_bit(SH_SR_Q), val);
|
|
val = SHIFTL0(val, SH_U_REG(4));
|
|
// VARG(SH_SR_I) is of 4 bits
|
|
val = LOGOR(UNSIGNED(SH_REG_SIZE, VARG(SH_SR_I)), val);
|
|
val = SHIFTL0(val, SH_U_REG(3));
|
|
val = LOGOR(sh_il_get_status_reg_bit(SH_SR_S), val);
|
|
val = SHIFTL0(val, SH_U_REG(1));
|
|
val = LOGOR(sh_il_get_status_reg_bit(SH_SR_T), val);
|
|
|
|
return val;
|
|
}
|
|
|
|
/**
|
|
* \brief Set the value of the status register (sr) to \p val by setting the values of the individual status register bits
|
|
*
|
|
* \param val
|
|
* \return RzILOpEffect*
|
|
*/
|
|
static RzILOpEffect *sh_il_set_status_reg(RZ_OWN RzILOpPure *val) {
|
|
RzILOpEffect *sreg = SETL("_sreg", val);
|
|
RzILOpEffect *eff = SETG(SH_SR_T, LSB(VARL("_sreg")));
|
|
|
|
eff = SEQ2(eff, SETL("_sreg", SHIFTR0(VARL("_sreg"), SH_U_REG(1))));
|
|
eff = SEQ2(eff, SETG(SH_SR_S, LSB(VARL("_sreg"))));
|
|
eff = SEQ2(eff, SETL("_sreg", SHIFTR0(VARL("_sreg"), SH_U_REG(3))));
|
|
eff = SEQ2(eff, SETG(SH_SR_I, LOGAND(UN(4, 0xf), UNSIGNED(4, VARL("_sreg")))));
|
|
eff = SEQ2(eff, SETL("_sreg", SHIFTR0(VARL("_sreg"), SH_U_REG(4))));
|
|
eff = SEQ2(eff, SETG(SH_SR_Q, LSB(VARL("_sreg"))));
|
|
eff = SEQ2(eff, SETL("_sreg", SHIFTR0(VARL("_sreg"), SH_U_REG(1))));
|
|
eff = SEQ2(eff, SETG(SH_SR_M, LSB(VARL("_sreg"))));
|
|
eff = SEQ2(eff, SETL("_sreg", SHIFTR0(VARL("_sreg"), SH_U_REG(6))));
|
|
eff = SEQ2(eff, SETG(SH_SR_F, LSB(VARL("_sreg"))));
|
|
eff = SEQ2(eff, SETL("_sreg", SHIFTR0(VARL("_sreg"), SH_U_REG(13))));
|
|
eff = SEQ2(eff, SETG(SH_SR_B, LSB(VARL("_sreg"))));
|
|
eff = SEQ2(eff, SETL("_sreg", SHIFTR0(VARL("_sreg"), SH_U_REG(1))));
|
|
eff = SEQ2(eff, SETG(SH_SR_R, LSB(VARL("_sreg"))));
|
|
eff = SEQ2(eff, SETL("_sreg", SHIFTR0(VARL("_sreg"), SH_U_REG(1))));
|
|
eff = SEQ2(eff, SETG(SH_SR_D, LSB(VARL("_sreg"))));
|
|
|
|
return SEQ2(sreg, eff);
|
|
}
|
|
|
|
/**
|
|
* \brief Set the value of the local variable "_priv"
|
|
* This exists so that the privilege mode IL doesn't have to be duplicated everywhere,
|
|
* instead one can directly use the local variable
|
|
*
|
|
* \return RzILOpEffect*
|
|
*/
|
|
static RzILOpEffect *sh_il_initialize_privilege() {
|
|
return SETL("_priv", AND(VARG(SH_SR_D), VARG(SH_SR_R)));
|
|
}
|
|
|
|
/**
|
|
* \brief Get the privilege mode
|
|
* Do NOT call this before initializing privilege through `sh_il_initialize_privilege`
|
|
* Otherwise, the local variable would not have been initialized
|
|
* For all the liftings, this is taken care of in `rz_sh_il_opcode`
|
|
*
|
|
* \param ctx SHILContext instance used to store the whether privilege was checked or not
|
|
* \return RzILOpPure* (RzILOpBool*) IL_TRUE if in privilege mode ; IL_FALSE otherwise
|
|
*/
|
|
static RzILOpPure *sh_il_get_privilege_ctx(SHILContext *ctx) {
|
|
if (ctx) {
|
|
ctx->privilege_check = true;
|
|
}
|
|
return VARL("_priv");
|
|
}
|
|
|
|
#define sh_il_get_privilege() sh_il_get_privilege_ctx(ctx)
|
|
|
|
/**
|
|
* \brief Get register corresponding to \p reg index
|
|
* This function is smart enough to give the correct register in case of banked registers or status register
|
|
*
|
|
* \param reg
|
|
* \param ctx SHILContext instance
|
|
* \return RzILOpPure*
|
|
*/
|
|
static RzILOpPure *sh_il_get_reg_ctx(ut16 reg, SHILContext *ctx) {
|
|
if (!sh_banked_reg(reg) || !ctx->use_banked) {
|
|
if (reg == SH_REG_IND_SR) {
|
|
return sh_il_get_status_reg();
|
|
}
|
|
return VARG(sh_registers[reg]);
|
|
}
|
|
|
|
// check if both SR.MD = 1 and SR.RB = 1
|
|
return ITE(sh_il_get_privilege(), VARG(sh_get_banked_reg(reg, 1)), VARG(sh_get_banked_reg(reg, 0)));
|
|
}
|
|
|
|
#define sh_il_get_reg(reg) sh_il_get_reg_ctx(reg, ctx)
|
|
|
|
/**
|
|
* \brief Set the value of the register corresponding to index \p reg to value \p val
|
|
* This function is smart enough to set values correctly in case of banked registers or status register
|
|
*
|
|
* \param reg
|
|
* \param val
|
|
* \param ctx SHILContext instance
|
|
* \return RzILOpEffect*
|
|
*/
|
|
static RzILOpEffect *sh_il_set_reg_ctx(ut16 reg, RZ_OWN RzILOpPure *val, SHILContext *ctx) {
|
|
if (!sh_banked_reg(reg) || !ctx->use_banked) {
|
|
if (reg == SH_REG_IND_SR) {
|
|
return sh_il_set_status_reg(val);
|
|
}
|
|
return SETG(sh_registers[reg], val);
|
|
}
|
|
|
|
return SEQ2(SETL("_regv", val), BRANCH(sh_il_get_privilege(), SETG(sh_get_banked_reg(reg, 1), VARL("_regv")), SETG(sh_get_banked_reg(reg, 0), VARL("_regv"))));
|
|
}
|
|
|
|
#define sh_il_set_reg(reg, val) sh_il_set_reg_ctx(reg, val, ctx)
|
|
|
|
/**
|
|
* \brief Helper struct to take care of converting operands to IL
|
|
*/
|
|
typedef struct sh_param_helper_t {
|
|
RzILOpEffect *pre; ///< pre effect for the operand
|
|
RzILOpPure *pure; ///< pure effect for the operand
|
|
RzILOpEffect *post; ///< post effect for the operand
|
|
} SHParamHelper;
|
|
|
|
/**
|
|
* \brief Convert an unsigned 12 bit \p num to signed 12 bit num
|
|
* I have used 16 bits to represent the numbers involved here,
|
|
* but the 4 most significant bits are the same, so for all purposes
|
|
* these are basically 12 bit numbers extended to 16 bits
|
|
*
|
|
* \param num
|
|
* \return st16
|
|
*/
|
|
st16 convert_to_st12(ut16 num) {
|
|
return (num << 4) >> 4;
|
|
}
|
|
|
|
/**
|
|
* \brief Get the effective address obtained from the given \p param and \p scaling
|
|
*
|
|
* \param param
|
|
* \param scaling
|
|
* \param pc Program counter
|
|
* \param ctx SHILContext instance
|
|
* \return RzILOpPure*
|
|
*/
|
|
static RzILOpPure *sh_il_get_effective_addr_pc_ctx(SHParam param, SHScaling scaling, ut64 pc, SHILContext *ctx) {
|
|
switch (param.mode) {
|
|
case SH_REG_INDIRECT:
|
|
case SH_REG_INDIRECT_I:
|
|
case SH_REG_INDIRECT_D:
|
|
return sh_il_get_reg(param.param[0]);
|
|
case SH_REG_INDIRECT_DISP:
|
|
return ADD(sh_il_get_reg(param.param[0]), MUL(SH_U_ADDR(param.param[1]), SH_U_ADDR(sh_scaling_size[scaling])));
|
|
case SH_REG_INDIRECT_INDEXED:
|
|
return ADD(sh_il_get_reg(SH_REG_IND_R0), sh_il_get_reg(param.param[0]));
|
|
case SH_GBR_INDIRECT_DISP:
|
|
return ADD(VARG("gbr"), MUL(SH_U_ADDR(param.param[0]), SH_U_ADDR(sh_scaling_size[scaling])));
|
|
case SH_GBR_INDIRECT_INDEXED:
|
|
return ADD(VARG("gbr"), sh_il_get_reg(SH_REG_IND_R0));
|
|
case SH_PC_RELATIVE_DISP: {
|
|
RzILOpBitVector *pcbv = SH_U_ADDR(pc);
|
|
// mask lower 2 bits if long word
|
|
if (scaling == SH_SCALING_L) {
|
|
pcbv = LOGAND(pcbv, SH_U_ADDR(0xfffffffc));
|
|
}
|
|
pcbv = ADD(pcbv, SH_U_ADDR(4));
|
|
return ADD(pcbv, MUL(SH_U_ADDR(param.param[0]), SH_U_ADDR(sh_scaling_size[scaling])));
|
|
}
|
|
case SH_PC_RELATIVE8: {
|
|
// sign-extended for 8 bits and shifted left by 1 (i.e. multiplied by 2)
|
|
RzILOpBitVector *relative = SHIFTL0(SH_S_ADDR((st8)param.param[0]), U32(1));
|
|
return ADD(ADD(SH_U_ADDR(pc), SH_U_ADDR(4)), relative);
|
|
}
|
|
case SH_PC_RELATIVE12: {
|
|
// sign-extended for 12 bits and shifted left by 1 (i.e. multiplied by 2)
|
|
RzILOpBitVector *relative = SHIFTL0(SH_S_ADDR(convert_to_st12(param.param[0])), U32(1));
|
|
return ADD(ADD(SH_U_ADDR(pc), SH_U_ADDR(4)), relative);
|
|
}
|
|
case SH_PC_RELATIVE_REG:
|
|
return ADD(ADD(SH_U_ADDR(pc), SH_U_ADDR(4)), sh_il_get_reg(param.param[0]));
|
|
default:
|
|
RZ_LOG_WARN("RzIL: SuperH: No effective address for this mode: %u\n", param.mode);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#define sh_il_get_effective_addr(x, y) sh_il_get_effective_addr_pc_ctx(x, y, pc, ctx)
|
|
|
|
/**
|
|
* \brief Convert the \p param with \p scaling to it's IL representation
|
|
*
|
|
* \param param
|
|
* \param scaling
|
|
* \param pc Program counter
|
|
* \param ctx SHILContext instance
|
|
* \return SHParamHelper Consists of the value of the param and the pre, post effects
|
|
*/
|
|
static SHParamHelper sh_il_get_param_pc_ctx(SHParam param, SHScaling scaling, ut64 pc, SHILContext *ctx) {
|
|
SHParamHelper ret = {
|
|
.pre = NULL,
|
|
.pure = NULL,
|
|
.post = NULL
|
|
};
|
|
|
|
/* In case of invalid scaling, just default to `SH_SCALING_L`, and assume param width of 32 bits (4 bytes)
|
|
This makes the param calculation process simpler and doesn't lead to any functional difference */
|
|
if (scaling == SH_SCALING_INVALID) {
|
|
scaling = SH_SCALING_L;
|
|
}
|
|
|
|
switch (param.mode) {
|
|
case SH_REG_DIRECT:
|
|
if (scaling == SH_SCALING_L) {
|
|
ret.pure = sh_il_get_reg(param.param[0]);
|
|
} else {
|
|
ret.pure = UNSIGNED(BITS_PER_BYTE * sh_scaling_size[scaling], sh_il_get_reg(param.param[0]));
|
|
}
|
|
break;
|
|
case SH_REG_INDIRECT_I:
|
|
ret.post = sh_il_set_reg(param.param[0], ADD(sh_il_get_reg(param.param[0]), SH_U_ADDR(sh_scaling_size[scaling])));
|
|
goto set_pure;
|
|
case SH_REG_INDIRECT_D:
|
|
ret.pre = sh_il_set_reg(param.param[0], SUB(sh_il_get_reg(param.param[0]), SH_U_ADDR(sh_scaling_size[scaling])));
|
|
goto set_pure;
|
|
case SH_REG_INDIRECT:
|
|
case SH_REG_INDIRECT_DISP:
|
|
case SH_REG_INDIRECT_INDEXED:
|
|
case SH_GBR_INDIRECT_DISP:
|
|
case SH_GBR_INDIRECT_INDEXED:
|
|
case SH_PC_RELATIVE_DISP:
|
|
case SH_PC_RELATIVE8:
|
|
case SH_PC_RELATIVE12:
|
|
case SH_PC_RELATIVE_REG:
|
|
set_pure:
|
|
ret.pure = LOADW(BITS_PER_BYTE * sh_scaling_size[scaling], sh_il_get_effective_addr(param, scaling));
|
|
break;
|
|
case SH_IMM_U:
|
|
ret.pure = UN(sh_scaling_size[scaling] * BITS_PER_BYTE, param.param[0]);
|
|
break;
|
|
case SH_IMM_S:
|
|
ret.pure = SN(sh_scaling_size[scaling] * BITS_PER_BYTE, param.param[0]);
|
|
break;
|
|
default:
|
|
RZ_LOG_ERROR("RzIL: SuperH: Invalid addressing mode\n");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#define sh_il_get_param(x, y) sh_il_get_param_pc_ctx(x, y, pc, ctx)
|
|
|
|
/**
|
|
* \brief Apply the effects in order: \p pre, \p target, \p post
|
|
* The good thing about this function is that any of the arguments can be NULL
|
|
* which implies that they do not exist/matter, and the final effect woulds
|
|
* be calculated without these NULL arguments (keeping in mind the above order)
|
|
*
|
|
* \param target
|
|
* \param pre
|
|
* \param post
|
|
* \return RzILOpEffect*
|
|
*/
|
|
static RzILOpEffect *sh_apply_effects(RZ_NULLABLE RzILOpEffect *target, RZ_NULLABLE RzILOpEffect *pre, RZ_NULLABLE RzILOpEffect *post) {
|
|
if (!target) {
|
|
if (pre) {
|
|
target = pre;
|
|
goto append;
|
|
} else if (post) {
|
|
return post;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
if (pre) {
|
|
target = SEQ2(pre, target);
|
|
}
|
|
append:
|
|
if (post) {
|
|
target = SEQ2(target, post);
|
|
}
|
|
|
|
return target;
|
|
}
|
|
|
|
/**
|
|
* \brief Set the value of the \p param at \p scaling to \p val
|
|
* This function is smart enough to also apply any effects corresponding to the \p param
|
|
*
|
|
* \param param
|
|
* \param val
|
|
* \param scaling
|
|
* \param pc Program counter
|
|
* \param ctx SHILContext instance
|
|
* \return RzILOpEffect*
|
|
*/
|
|
static RzILOpEffect *sh_il_set_param_pc_ctx(SHParam param, RZ_OWN RzILOpPure *val, SHScaling scaling, ut64 pc, SHILContext *ctx) {
|
|
RzILOpEffect *ret = NULL, *pre = NULL, *post = NULL;
|
|
switch (param.mode) {
|
|
case SH_REG_DIRECT:
|
|
if (scaling == SH_SCALING_INVALID || scaling == SH_SCALING_L) {
|
|
ret = sh_il_set_reg(param.param[0], val);
|
|
} else {
|
|
/* We don't need to worry about sizes not matching up when calling `sh_il_signed` two times in an effect.
|
|
This is because within an effect, the scaling will stay the same, so all the time `sh_il_signed` is called,
|
|
it will be setting the local variables "_sign" and "_temp" to the same bitvector size.
|
|
Thus, there will be no IL validation errors. */
|
|
RzILOpEffect *cast = sh_il_signed(SH_REG_SIZE, val, "_sign", "_temp");
|
|
ret = SEQ2(cast, sh_il_set_reg(param.param[0], VARL("_sign")));
|
|
}
|
|
break;
|
|
case SH_REG_INDIRECT:
|
|
case SH_REG_INDIRECT_I:
|
|
case SH_REG_INDIRECT_D:
|
|
case SH_REG_INDIRECT_DISP:
|
|
case SH_REG_INDIRECT_INDEXED:
|
|
case SH_GBR_INDIRECT_DISP:
|
|
case SH_GBR_INDIRECT_INDEXED:
|
|
case SH_PC_RELATIVE_DISP:
|
|
case SH_PC_RELATIVE8:
|
|
case SH_PC_RELATIVE12:
|
|
case SH_PC_RELATIVE_REG:
|
|
break;
|
|
case SH_IMM_U:
|
|
case SH_IMM_S:
|
|
default:
|
|
RZ_LOG_ERROR("RzIL: SuperH: Cannot set value for addressing mode: %u\n", param.mode);
|
|
return NULL;
|
|
}
|
|
|
|
if (!ret) {
|
|
SHParamHelper ret_h = sh_il_get_param(param, scaling);
|
|
RZ_FREE(ret_h.pure);
|
|
RzILOpPure *eff_addr = sh_il_get_effective_addr(param, scaling);
|
|
ret = STOREW(eff_addr, val);
|
|
pre = ret_h.pre;
|
|
post = ret_h.post;
|
|
}
|
|
|
|
return sh_apply_effects(ret, pre, post);
|
|
}
|
|
|
|
#define sh_il_set_param(x, y, z) sh_il_set_param_pc_ctx(x, y, z, pc, ctx)
|
|
|
|
/**
|
|
* \brief Check if there was a carry in the addition of \p x and \p y to get \p res
|
|
* Here \p res = \p x + \p y (+ 1, optional)
|
|
* This function can also be used of there was a carry bit added as well
|
|
*
|
|
* Pass in local variables to this function because otherwise the `DUP`s inside it will
|
|
* lead to an unnecessarily long IL
|
|
*
|
|
* \param res
|
|
* \param x
|
|
* \param y
|
|
* \return RzILOpBool* IL_TRUE if carry during addition ; IL_FALSE otherwise
|
|
*/
|
|
static RzILOpBool *sh_il_is_add_carry(RZ_OWN RzILOpPure *res, RZ_OWN RzILOpPure *x, RZ_OWN RzILOpPure *y) {
|
|
// res = x + y
|
|
RzILOpBool *xmsb = MSB(x);
|
|
RzILOpBool *ymsb = MSB(y);
|
|
RzILOpBool *resmsb = MSB(res);
|
|
|
|
// x & y
|
|
RzILOpBool *xy = AND(xmsb, ymsb);
|
|
RzILOpBool *nres = INV(resmsb);
|
|
|
|
// !res & y
|
|
RzILOpBool *ry = AND(nres, DUP(ymsb));
|
|
// x & !res
|
|
RzILOpBool *xr = AND(DUP(xmsb), DUP(nres));
|
|
|
|
// bit = xy | ry | xr
|
|
RzILOpBool *or = OR(xy, ry);
|
|
or = OR(or, xr);
|
|
|
|
return or;
|
|
}
|
|
|
|
/**
|
|
* \brief Check if there was a borrow in the subtraction of \p x and \p y to get \p res
|
|
* Here \p res = \p x - \p y (- 1, optional)
|
|
* This function can also be used of there was a borrow bit added as well
|
|
*
|
|
* Pass in local variables to this function because otherwise the `DUP`s inside it will
|
|
* lead to an unnecessarily long IL
|
|
*
|
|
* \param res
|
|
* \param x
|
|
* \param y
|
|
* \return RzILOpBool* IL_TRUE if borrow during subtraction ; IL_FALSE otherwise
|
|
*/
|
|
static RzILOpBool *sh_il_is_sub_borrow(RZ_OWN RzILOpPure *res, RZ_OWN RzILOpPure *x, RZ_OWN RzILOpPure *y) {
|
|
// res = x - y
|
|
RzILOpBool *xmsb = MSB(x);
|
|
RzILOpBool *ymsb = MSB(y);
|
|
RzILOpBool *resmsb = MSB(res);
|
|
|
|
// !x & y
|
|
RzILOpBool *nx = INV(xmsb);
|
|
RzILOpBool *nxy = AND(nx, ymsb);
|
|
|
|
// y & res
|
|
RzILOpBool *rny = AND(DUP(ymsb), resmsb);
|
|
// res & !x
|
|
RzILOpBool *rnx = AND(DUP(resmsb), DUP(nx));
|
|
|
|
// bit = nxy | rny | rnx
|
|
RzILOpBool *or = OR(nxy, rny);
|
|
or = OR(or, rnx);
|
|
|
|
return or;
|
|
}
|
|
|
|
/**
|
|
* \brief Check if there was a overflow in the addition of \p x and \p y to get \p res
|
|
* Here \p res = \p x + \p y
|
|
*
|
|
* Pass in local variables to this function because otherwise the `DUP`s inside it will
|
|
* lead to an unnecessarily long IL
|
|
*
|
|
* \param res
|
|
* \param x
|
|
* \param y
|
|
* \return RzILOpBool* IL_TRUE if overflow during addition ; IL_FALSE otherwise
|
|
*/
|
|
static RzILOpBool *sh_il_is_add_overflow(RZ_OWN RzILOpPure *res, RZ_OWN RzILOpPure *x, RZ_OWN RzILOpPure *y) {
|
|
// res = x + y
|
|
RzILOpBool *xmsb = MSB(x);
|
|
RzILOpBool *ymsb = MSB(y);
|
|
RzILOpBool *resmsb = MSB(res);
|
|
|
|
// !res & x & y
|
|
RzILOpBool *nrxy = AND(AND(INV(resmsb), xmsb), ymsb);
|
|
// res & !x & !y
|
|
RzILOpBool *rnxny = AND(AND(DUP(resmsb), INV(DUP(xmsb))), INV(DUP(ymsb)));
|
|
// or = nrxy | rnxny
|
|
RzILOpBool *or = OR(nrxy, rnxny);
|
|
|
|
return or;
|
|
}
|
|
|
|
/**
|
|
* \brief Check if there was a underflow in the subtraction of \p x and \p y to get \p res
|
|
* Here \p res = \p x - \p y
|
|
*
|
|
* Pass in local variables to this function because otherwise the `DUP`s inside it will
|
|
* lead to an unnecessarily long IL
|
|
*
|
|
* \param res
|
|
* \param x
|
|
* \param y
|
|
* \return RzILOpBool* IL_TRUE if underflow during subtraction ; IL_FALSE otherwise
|
|
*/
|
|
static RzILOpBool *sh_il_is_sub_underflow(RZ_OWN RzILOpPure *res, RZ_OWN RzILOpPure *x, RZ_OWN RzILOpPure *y) {
|
|
// res = x - y
|
|
RzILOpBool *xmsb = MSB(x);
|
|
RzILOpBool *ymsb = MSB(y);
|
|
RzILOpBool *resmsb = MSB(res);
|
|
|
|
// !res & x & !y
|
|
RzILOpBool *nrxny = AND(AND(INV(resmsb), xmsb), INV(ymsb));
|
|
// res & !x & y
|
|
RzILOpBool *rnxy = AND(AND(DUP(resmsb), INV(DUP(xmsb))), DUP(ymsb));
|
|
// or = nrxny | rnxy
|
|
RzILOpBool *or = OR(nrxny, rnxy);
|
|
|
|
return or;
|
|
}
|
|
|
|
/* Instruction implementations */
|
|
|
|
/**
|
|
* Unknown instruction
|
|
*/
|
|
static RzILOpEffect *sh_il_invalid(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* MOV family instructions
|
|
*/
|
|
static RzILOpEffect *sh_il_mov(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
SHParamHelper shp = sh_il_get_param(op->param[0], op->scaling);
|
|
return sh_apply_effects(sh_il_set_pure_param(1, shp.pure), shp.pre, shp.post);
|
|
}
|
|
|
|
/**
|
|
* MOVT Rn
|
|
* T -> Rn
|
|
* 0000nnnn00101001
|
|
*/
|
|
static RzILOpEffect *sh_il_movt(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return sh_il_set_pure_param(0, UNSIGNED(SH_REG_SIZE, sh_il_get_status_reg_bit(SH_SR_T)));
|
|
}
|
|
|
|
/**
|
|
* SWAP.B Rm, Rn
|
|
* Rm -> swap lower 2 bytes -> REG
|
|
* 0110nnnnmmmm1000
|
|
*
|
|
* SWAP.W Rm, Rn
|
|
* Rm -> swap upper/lower words -> Rn
|
|
* 0110nnnnmmmm1001
|
|
*/
|
|
static RzILOpEffect *sh_il_swap(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
/* We won't be using `sh_il_{get,set}_param_pure`, because it will cast the pure value to the scaling size,
|
|
but we want the whole register, which is why we need to call `sh_il_{get,set}_param directly` */
|
|
if (op->scaling == SH_SCALING_B) {
|
|
// swap lower two bytes
|
|
RzILOpPure *lower_byte = LOGAND(sh_il_get_param(op->param[0], SH_SCALING_L).pure, SH_U_REG(0xff));
|
|
RzILOpPure *new_lower_byte = LOGAND(SHIFTR0(sh_il_get_param(op->param[0], SH_SCALING_L).pure, SH_U_REG(BITS_PER_BYTE)), SH_U_REG(0xff));
|
|
RzILOpPure *new_upper_byte = SHIFTL0(lower_byte, SH_U_REG(BITS_PER_BYTE));
|
|
RzILOpPure *upper_word = LOGAND(sh_il_get_param(op->param[0], SH_SCALING_L).pure, SH_U_REG(0xffff0000));
|
|
return sh_il_set_param(op->param[1], LOGOR(upper_word, LOGOR(new_upper_byte, new_lower_byte)), SH_SCALING_L);
|
|
} else if (op->scaling == SH_SCALING_W) {
|
|
// swap upper and lower words and store in dst
|
|
RzILOpPure *high = SHIFTL0(sh_il_get_param(op->param[0], SH_SCALING_L).pure, SH_U_REG(BITS_PER_BYTE * 2));
|
|
RzILOpPure *low = SHIFTR0(sh_il_get_param(op->param[0], SH_SCALING_L).pure, SH_U_REG(BITS_PER_BYTE * 2));
|
|
return sh_il_set_param(op->param[1], LOGOR(high, low), SH_SCALING_L);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* XTRCT Rm, Rn
|
|
* Rm:Rn middle 32 bits -> Rn
|
|
* 0010nnnnmmmm1101
|
|
*/
|
|
static RzILOpEffect *sh_il_xtrct(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpPure *high = SHIFTL0(sh_il_get_pure_param(0), SH_U_REG(BITS_PER_BYTE * 2));
|
|
RzILOpPure *low = SHIFTR0(sh_il_get_pure_param(1), SH_U_REG(BITS_PER_BYTE * 2));
|
|
return sh_il_set_pure_param(1, LOGOR(high, low));
|
|
}
|
|
|
|
/**
|
|
* ADD Rm, Rn
|
|
* Rn + Rm -> Rn
|
|
* 0011nnnnmmmm1100
|
|
*
|
|
* ADD #imm, Rn
|
|
* Rn + imm -> Rn
|
|
* 0111nnnniiiiiiii
|
|
*/
|
|
static RzILOpEffect *sh_il_add(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return sh_il_set_pure_param(1, ADD(sh_il_get_pure_param(0), sh_il_get_pure_param(1)));
|
|
}
|
|
|
|
/**
|
|
* ADDC Rm, Rn
|
|
* Rn + Rm + T -> Rn
|
|
* carry -> T
|
|
* 0011nnnnmmmm1110
|
|
*/
|
|
static RzILOpEffect *sh_il_addc(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpPure *sum = ADD(sh_il_get_pure_param(0), sh_il_get_pure_param(1));
|
|
RzILOpEffect *local_sum = SETL("sum", ADD(sum, UNSIGNED(SH_REG_SIZE, sh_il_get_status_reg_bit(SH_SR_T))));
|
|
|
|
RzILOpEffect *tbit = SETG(SH_SR_T, sh_il_is_add_carry(VARL("sum"), sh_il_get_pure_param(0), sh_il_get_pure_param(1)));
|
|
RzILOpEffect *ret = sh_il_set_pure_param(1, VARL("sum"));
|
|
return SEQ3(local_sum, tbit, ret);
|
|
}
|
|
|
|
/**
|
|
* ADDV Rm, Rn
|
|
* Rn + Rm -> Rn
|
|
* overflow -> T
|
|
* 0011nnnnmmmm1111
|
|
*/
|
|
static RzILOpEffect *sh_il_addv(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpPure *sum = ADD(sh_il_get_pure_param(0), sh_il_get_pure_param(1));
|
|
RzILOpEffect *local_sum = SETL("sum", sum);
|
|
|
|
RzILOpEffect *tbit = SETG(SH_SR_T, sh_il_is_add_overflow(VARL("sum"), sh_il_get_pure_param(0), sh_il_get_pure_param(1)));
|
|
RzILOpEffect *ret = sh_il_set_pure_param(1, VARL("sum"));
|
|
return SEQ3(local_sum, tbit, ret);
|
|
}
|
|
|
|
/**
|
|
* CMP/EQ #imm, R0
|
|
* When R0 = imm, 1 -> T ; Otherwise, 0 -> T
|
|
* 10001000iiiiiiii
|
|
*
|
|
* CMP/EQ Rm, Rn
|
|
* When Rn = Rm, 1 -> T ; Otherwise, 0 -> T
|
|
* 0011nnnnmmmm0000
|
|
*/
|
|
static RzILOpEffect *sh_il_cmp_eq(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return SETG(SH_SR_T, EQ(sh_il_get_pure_param(0), sh_il_get_pure_param(1)));
|
|
}
|
|
|
|
/**
|
|
* CMP/HS Rm, Rn
|
|
* When Rn >= Rm (unsigned), 1 -> T ; Otherwise, 0 -> T
|
|
* 0011nnnnmmmm0010
|
|
*/
|
|
static RzILOpEffect *sh_il_cmp_hs(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return SETG(SH_SR_T, UGE(sh_il_get_pure_param(1), sh_il_get_pure_param(0)));
|
|
}
|
|
|
|
/**
|
|
* CMP/GE Rm, Rn
|
|
* When Rn >= Rm (signed), 1 -> T ; Otherwise, 0 -> T
|
|
* 0011nnnnmmmm0011
|
|
*/
|
|
static RzILOpEffect *sh_il_cmp_ge(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return SETG(SH_SR_T, SGE(sh_il_get_pure_param(1), sh_il_get_pure_param(0)));
|
|
}
|
|
|
|
/**
|
|
* CMP/HI Rm, Rn
|
|
* When Rn > Rm (unsigned), 1 -> T ; Otherwise, 0 -> T
|
|
* 0011nnnnmmmm0110
|
|
*/
|
|
static RzILOpEffect *sh_il_cmp_hi(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return SETG(SH_SR_T, UGT(sh_il_get_pure_param(1), sh_il_get_pure_param(0)));
|
|
}
|
|
|
|
/**
|
|
* CMP/GT Rm, Rn
|
|
* When Rn > Rm (signed), 1 -> T ; Otherwise, 0 -> T
|
|
* 0011nnnnmmmm0111
|
|
*/
|
|
static RzILOpEffect *sh_il_cmp_gt(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return SETG(SH_SR_T, SGT(sh_il_get_pure_param(1), sh_il_get_pure_param(0)));
|
|
}
|
|
|
|
/**
|
|
* CMP/PZ Rn
|
|
* When Rn >= 0, 1 -> T ; Otherwise, 0 -> T
|
|
* 0100nnnn00010001
|
|
*/
|
|
static RzILOpEffect *sh_il_cmp_pz(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return SETG(SH_SR_T, SGE(sh_il_get_pure_param(0), SH_S_REG(0)));
|
|
}
|
|
|
|
/**
|
|
* CMP/PL Rn
|
|
* When Rn > 0, 1 -> T ; Otherwise, 0 -> T
|
|
* 0100nnnn00010101
|
|
*/
|
|
static RzILOpEffect *sh_il_cmp_pl(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return SETG(SH_SR_T, SGT(sh_il_get_pure_param(0), SH_S_REG(0)));
|
|
}
|
|
|
|
/**
|
|
* CMP/STR Rm, Rn
|
|
* When any bytes are equal, 1 -> T ; Otherwise, 0 -> T
|
|
* 0010nnnnmmmm1100
|
|
*/
|
|
static RzILOpEffect *sh_il_cmp_str(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpPure *full_xor = LOGXOR(sh_il_get_pure_param(0), sh_il_get_pure_param(1));
|
|
RzILOpEffect *eff = SETL("xor", full_xor);
|
|
|
|
eff = SEQ2(eff, SETL("eq", EQ(LOGAND(VARL("xor"), SH_U_REG(0xff)), SH_U_REG(0x0))));
|
|
eff = SEQ2(eff, SETL("xor", SHIFTR0(VARL("xor"), SH_U_REG(BITS_PER_BYTE))));
|
|
eff = SEQ2(eff, SETL("eq", OR(VARL("eq"), EQ(LOGAND(VARL("xor"), SH_U_REG(0xff)), SH_U_REG(0x0)))));
|
|
eff = SEQ2(eff, SETL("xor", SHIFTR0(VARL("xor"), SH_U_REG(BITS_PER_BYTE))));
|
|
eff = SEQ2(eff, SETL("eq", OR(VARL("eq"), EQ(LOGAND(VARL("xor"), SH_U_REG(0xff)), SH_U_REG(0x0)))));
|
|
eff = SEQ2(eff, SETL("xor", SHIFTR0(VARL("xor"), SH_U_REG(BITS_PER_BYTE))));
|
|
eff = SEQ2(eff, SETL("eq", OR(VARL("eq"), EQ(LOGAND(VARL("xor"), SH_U_REG(0xff)), SH_U_REG(0x0)))));
|
|
|
|
return SEQ2(eff, SETG(SH_SR_T, VARL("eq")));
|
|
}
|
|
|
|
/**
|
|
* DIV1 Rm, Rn
|
|
* 1-step division (Rn ÷ Rm) ; Calculation result -> T
|
|
* 0011nnnnmmmm0100
|
|
*/
|
|
static RzILOpEffect *sh_il_div1(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpEffect *ret = NULL;
|
|
RzILOpEffect *q = SETL("q", VARG(SH_SR_Q));
|
|
RzILOpEffect *m = SETL("m", VARG(SH_SR_M));
|
|
RzILOpEffect *t = SETL("t", VARG(SH_SR_T));
|
|
ret = SEQ3(q, m, t);
|
|
|
|
RzILOpEffect *op1 = SETL("op1", sh_il_get_pure_param(0));
|
|
RzILOpEffect *op2 = SETL("op2", sh_il_get_pure_param(1));
|
|
RzILOpEffect *old_q = SETL("old_q", VARL("q"));
|
|
ret = SEQ4(ret, op1, op2, old_q);
|
|
|
|
// Get 31st bit of var q
|
|
q = SETL("q", NON_ZERO(LOGAND(VARL("op2"), SH_U_REG(0x80000000))));
|
|
op2 = SETL("op2", LOGOR(SHIFTL0(VARL("op2"), SH_U_REG(1)), sh_il_bool_to_bv(VARL("t"))));
|
|
ret = SEQ3(ret, q, op2);
|
|
|
|
RzILOpEffect *true_eff = SETL("op2", SUB(VARL("op2"), VARL("op1")));
|
|
RzILOpEffect *false_eff = SETL("op2", ADD(VARL("op2"), VARL("op1")));
|
|
RzILOpEffect *cond = BRANCH(EQ(sh_il_bool_to_bv(VARL("old_q")), sh_il_bool_to_bv(VARL("m"))), true_eff, false_eff);
|
|
ret = SEQ2(ret, cond);
|
|
|
|
// q = q ^ m ^ msb(op2)
|
|
q = SETL("q", XOR(XOR(VARL("q"), VARL("m")), MSB(VARL("op2"))));
|
|
t = SETL("t", NON_ZERO(SUB(SH_U_REG(1), LOGXOR(sh_il_bool_to_bv(VARL("q")), sh_il_bool_to_bv(VARL("m"))))));
|
|
ret = SEQ3(ret, q, t);
|
|
|
|
RzILOpEffect *rn = sh_il_set_pure_param(1, VARL("op2"));
|
|
q = SETG(SH_SR_Q, VARL("q"));
|
|
t = SETG(SH_SR_T, VARL("t"));
|
|
ret = SEQ4(ret, rn, q, t);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* DIV0S Rm, Rn
|
|
* MSB of Rn -> Q ; MSB of Rm -> M, M^Q -> T
|
|
* 0010nnnnmmmm0111
|
|
*/
|
|
static RzILOpEffect *sh_il_div0s(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpEffect *setq = SETG(SH_SR_Q, MSB(sh_il_get_pure_param(1)));
|
|
RzILOpEffect *setm = SETG(SH_SR_M, MSB(sh_il_get_pure_param(0)));
|
|
RzILOpEffect *sett = SETG(SH_SR_T, XOR(VARG(SH_SR_M), VARG(SH_SR_Q)));
|
|
|
|
return SEQ3(setq, setm, sett);
|
|
}
|
|
|
|
/**
|
|
* DIV0U Rm, Rn
|
|
* 0 -> M/Q/T
|
|
* 0000000000011001
|
|
*/
|
|
static RzILOpEffect *sh_il_div0u(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return SEQ3(SETG(SH_SR_M, IL_FALSE), SETG(SH_SR_Q, IL_FALSE), SETG(SH_SR_T, IL_FALSE));
|
|
}
|
|
|
|
/**
|
|
* DMULS.L Rm, Rn
|
|
* Signed, Rn * Rm -> MAC ; 32 * 32 -> 64 bits
|
|
* 0011nnnnmmmm1101
|
|
*/
|
|
static RzILOpEffect *sh_il_dmuls(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpEffect *eff = SETL("res_wide", MUL(SIGNED(2 * SH_REG_SIZE, sh_il_get_pure_param(0)), SIGNED(2 * SH_REG_SIZE, sh_il_get_pure_param(1))));
|
|
RzILOpPure *lower_bits = UNSIGNED(SH_REG_SIZE, LOGAND(VARL("res_wide"), UN(2 * SH_REG_SIZE, 0xffffffff)));
|
|
RzILOpPure *higher_bits = UNSIGNED(SH_REG_SIZE, SHIFTR0(VARL("res_wide"), SH_U_REG(SH_REG_SIZE)));
|
|
return SEQ3(eff, SETG("macl", lower_bits), SETG("mach", higher_bits));
|
|
}
|
|
|
|
/**
|
|
* DMULU.L Rm, Rn
|
|
* Unsigned, Rn * Rm -> MAC ; 32 * 32 -> 64 bits
|
|
* 0011nnnnmmmm0101
|
|
*/
|
|
static RzILOpEffect *sh_il_dmulu(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpEffect *eff = SETL("res_wide", MUL(UNSIGNED(2 * SH_REG_SIZE, sh_il_get_pure_param(0)), UNSIGNED(2 * SH_REG_SIZE, sh_il_get_pure_param(1))));
|
|
RzILOpPure *lower_bits = UNSIGNED(SH_REG_SIZE, LOGAND(VARL("res_wide"), UN(2 * SH_REG_SIZE, 0xffffffff)));
|
|
RzILOpPure *higher_bits = UNSIGNED(SH_REG_SIZE, SHIFTR0(VARL("res_wide"), SH_U_REG(SH_REG_SIZE)));
|
|
return SEQ3(eff, SETG("macl", lower_bits), SETG("mach", higher_bits));
|
|
}
|
|
|
|
/**
|
|
* DT Rn
|
|
* Rn - 1 -> Rn ; When Rn = 0, 1 -> T ; Otherwise 0 -> T
|
|
* 0100nnnn00010000
|
|
*/
|
|
static RzILOpEffect *sh_il_dt(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return SEQ2(sh_il_set_pure_param(0, SUB(sh_il_get_pure_param(0), SH_U_REG(1))), SETG(SH_SR_T, IS_ZERO(sh_il_get_pure_param(0))));
|
|
}
|
|
|
|
/**
|
|
* EXTS.B Rm, Rn
|
|
* Rm sign-extended from byte -> Rn
|
|
* 0110nnnnmmmm1110
|
|
*
|
|
* EXTS.W Rm, Rn
|
|
* Rm sign-extended from word -> Rn
|
|
* 0110nnnnmmmm1111
|
|
*/
|
|
static RzILOpEffect *sh_il_exts(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return sh_il_set_pure_param(1, sh_il_get_pure_param(0));
|
|
}
|
|
|
|
/**
|
|
* EXTU.B Rm, Rn
|
|
* Rm zero-extended from byte -> Rn
|
|
* 0110nnnnmmmm1100
|
|
*
|
|
* EXTU.W Rm, Rn
|
|
* Rm zero-extended from word -> Rn
|
|
* 0110nnnnmmmm1101
|
|
*/
|
|
static RzILOpEffect *sh_il_extu(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
/* Do not use `sh_il_set_pure_param` here since that will sign extend the Rn value, but we want
|
|
it extend it unsigned, which is why we need to directly call `sh_il_set_reg` */
|
|
return sh_il_set_reg(op->param[1].param[0], UNSIGNED(SH_REG_SIZE, sh_il_get_pure_param(0)));
|
|
}
|
|
|
|
/**
|
|
* MAC.L @Rm+, @Rn+
|
|
* Rn * Rm + MAC -> MAC (Signed) (32 * 32 + 64 -> 64 bits)
|
|
* Rn + 4 -> Rn ; Rm + 4 -> Rm
|
|
* 0000nnnnmmmm1111
|
|
*
|
|
* When S bit is enabled, the MAC addition is a saturation operation of 48 bits
|
|
* So only the lower 48 bits of result and MAC are considered
|
|
*
|
|
* MAC.W @Rm+, @Rn+
|
|
* Rn * Rm + MAC -> MAC (Signed) (16 * 16 + 64 -> 64 bits)
|
|
* Rn + 2 -> Rn ; Rm + 2 -> Rm
|
|
* 0000nnnnmmmm1111
|
|
*
|
|
* When S bit is enabled, the MAC addition is a saturation operation of 32 bits
|
|
* So only the lower 32 bits of result and MAC are considered (which is basically MACL register)
|
|
*/
|
|
static RzILOpEffect *sh_il_mac(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
SHParamHelper shp_rm = sh_il_get_param(op->param[0], op->scaling);
|
|
SHParamHelper shp_rn = sh_il_get_param(op->param[1], op->scaling);
|
|
RzILOpEffect *eff = NULL;
|
|
|
|
if (op->scaling == SH_SCALING_L) {
|
|
RzILOpEffect *mac = SETL("mac", LOGOR(SHIFTL0((UNSIGNED(2 * SH_REG_SIZE, VARG("mach"))), SH_U_REG(SH_REG_SIZE)), UNSIGNED(2 * SH_REG_SIZE, VARG("macl"))));
|
|
RzILOpEffect *rm = SETL("rm", shp_rm.pure);
|
|
RzILOpEffect *rn = SETL("rn", shp_rn.pure);
|
|
eff = SEQ2(rm, rn);
|
|
|
|
RzILOpEffect *mul = SETL("mul", MUL(SIGNED(2 * SH_REG_SIZE, VARL("rm")), SIGNED(2 * SH_REG_SIZE, VARL("rn"))));
|
|
RzILOpEffect *add = SETL("add", ADD(VARL("mul"), VARL("mac")));
|
|
RzILOpPure *lower_bits = UNSIGNED(SH_REG_SIZE, LOGAND(VARL("add"), UN(2 * SH_REG_SIZE, 0xffffffff)));
|
|
RzILOpEffect *higher_bits = SETL("high", UNSIGNED(SH_REG_SIZE, SHIFTR0(VARL("add"), SH_U_REG(SH_REG_SIZE))));
|
|
|
|
eff = SEQ7(eff, mac, mul, add, SETG("macl", lower_bits), higher_bits, BRANCH(VARG(SH_SR_S), SETG("mach", LOGAND(VARL("high"), SH_U_REG(0xffff))), SETG("mach", VARL("high"))));
|
|
} else if (op->scaling == SH_SCALING_W) {
|
|
RzILOpEffect *mac = SETL("mac", LOGOR(SHIFTL0((UNSIGNED(2 * SH_REG_SIZE, VARG("mach"))), SH_U_REG(SH_REG_SIZE)), UNSIGNED(2 * SH_REG_SIZE, VARG("macl"))));
|
|
RzILOpEffect *rm = SETL("rm", shp_rm.pure);
|
|
RzILOpEffect *rn = SETL("rn", shp_rn.pure);
|
|
eff = SEQ2(rm, rn);
|
|
|
|
RzILOpEffect *mul = SETL("mul", UNSIGNED(2 * SH_REG_SIZE, MUL(SIGNED(SH_REG_SIZE, VARL("rm")), SIGNED(SH_REG_SIZE, VARL("rn")))));
|
|
RzILOpEffect *add = SETL("add", ADD(VARL("mul"), VARL("mac")));
|
|
RzILOpEffect *lower_bits = SETL("low", UNSIGNED(SH_REG_SIZE, LOGAND(VARL("add"), UN(2 * SH_REG_SIZE, 0xffffffff))));
|
|
RzILOpPure *higher_bits = UNSIGNED(SH_REG_SIZE, SHIFTR0(VARL("add"), SH_U_REG(SH_REG_SIZE)));
|
|
|
|
eff = SEQ6(eff, mac, mul, add, lower_bits, BRANCH(VARG(SH_SR_S), SETG("macl", VARL("low")), SEQ2(SETG("macl", VARL("low")), SETG("mach", higher_bits))));
|
|
}
|
|
|
|
eff = SEQ3(eff, shp_rn.post, shp_rm.post);
|
|
return eff;
|
|
}
|
|
|
|
/**
|
|
* MUL.L Rm, Rn
|
|
* Rn * Rm -> MACL (32 * 32 -> 32 bits)
|
|
* 0000nnnnmmmm0111
|
|
*/
|
|
static RzILOpEffect *sh_il_mul(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return SETG("macl", MUL(sh_il_get_pure_param(0), sh_il_get_pure_param(1)));
|
|
}
|
|
|
|
/**
|
|
* MULS.W Rm, Rn
|
|
* Rn * Rm -> MACL (Signed) (16 * 16 -> 32 bits)
|
|
* 0010nnnnmmmm1111
|
|
*/
|
|
static RzILOpEffect *sh_il_muls(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpEffect *rm = SETL("rm", sh_il_get_pure_param(0));
|
|
RzILOpEffect *rn = SETL("rn", sh_il_get_pure_param(1));
|
|
RzILOpPure *m = SIGNED(SH_REG_SIZE, VARL("rm"));
|
|
RzILOpPure *n = SIGNED(SH_REG_SIZE, VARL("rn"));
|
|
return SEQ3(rm, rn, SETG("macl", MUL(m, n)));
|
|
}
|
|
|
|
/**
|
|
* MULU.W Rm, Rn
|
|
* Rn * Rm -> MACL (Unsigned) (16 * 16 -> 32 bits)
|
|
* 0010nnnnmmmm1110
|
|
*/
|
|
static RzILOpEffect *sh_il_mulu(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpPure *m = UNSIGNED(SH_REG_SIZE, sh_il_get_pure_param(0));
|
|
RzILOpPure *n = UNSIGNED(SH_REG_SIZE, sh_il_get_pure_param(1));
|
|
return SETG("macl", MUL(m, n));
|
|
}
|
|
|
|
/**
|
|
* NEG Rm, Rn
|
|
* 0 - Rm -> Rn
|
|
* 0110nnnnmmmm1011
|
|
*/
|
|
static RzILOpEffect *sh_il_neg(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpPure *sub = SUB(SH_U_REG(0), sh_il_get_pure_param(0));
|
|
return sh_il_set_pure_param(1, sub);
|
|
}
|
|
|
|
/**
|
|
* NEGC Rm, Rn
|
|
* 0 - Rm - T -> Rn ; borrow -> T
|
|
* 0110nnnnmmmm1010
|
|
*/
|
|
static RzILOpEffect *sh_il_negc(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpPure *sub = SUB(SH_U_REG(0), sh_il_get_pure_param(0));
|
|
RzILOpEffect *subvar = SETL("sub", SUB(sub, sh_il_get_status_reg_bit(SH_SR_T)));
|
|
return SEQ3(subvar, sh_il_set_pure_param(1, VARL("sub")), SETG(SH_SR_T, sh_il_is_sub_borrow(VARL("sub"), SH_U_REG(0), sh_il_get_pure_param(0))));
|
|
}
|
|
|
|
/**
|
|
* SUB Rm, Rn
|
|
* Rn - Rm -> Rn
|
|
* 0011nnnnmmmm1000
|
|
*/
|
|
static RzILOpEffect *sh_il_sub(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return sh_il_set_pure_param(1, SUB(sh_il_get_pure_param(1), sh_il_get_pure_param(0)));
|
|
}
|
|
|
|
/**
|
|
* SUBC Rm, Rn
|
|
* Rn - Rm - T -> Rn ; borrow -> T
|
|
* 0011nnnnmmmm1010
|
|
*/
|
|
static RzILOpEffect *sh_il_subc(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpPure *dif = SUB(sh_il_get_pure_param(1), sh_il_get_pure_param(0));
|
|
dif = SUB(dif, sh_il_get_status_reg_bit(SH_SR_T));
|
|
RzILOpEffect *local_dif = SETL("dif", dif);
|
|
|
|
RzILOpEffect *ret = sh_il_set_pure_param(1, VARL("dif"));
|
|
RzILOpEffect *tbit = SETG(SH_SR_T, sh_il_is_sub_borrow(VARL("dif"), sh_il_get_pure_param(0), sh_il_get_pure_param(1)));
|
|
return SEQ3(local_dif, ret, tbit);
|
|
}
|
|
|
|
/**
|
|
* SUBV Rm, Rn
|
|
* Rn - Rm -> Rn ; underflow -> T
|
|
* 0011nnnnmmmm1011
|
|
*/
|
|
static RzILOpEffect *sh_il_subv(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpPure *dif = SUB(sh_il_get_pure_param(1), sh_il_get_pure_param(0));
|
|
RzILOpEffect *local_dif = SETL("dif", dif);
|
|
|
|
RzILOpEffect *ret = sh_il_set_pure_param(1, VARL("dif"));
|
|
RzILOpEffect *tbit = SETG(SH_SR_T, sh_il_is_sub_underflow(VARL("dif"), sh_il_get_pure_param(0), sh_il_get_pure_param(1)));
|
|
return SEQ3(local_dif, ret, tbit);
|
|
}
|
|
|
|
/**
|
|
* AND Rm, Rn
|
|
* Rn & Rm -> Rn
|
|
* 0010nnnnmmmm1001
|
|
*
|
|
* AND #imm, R0
|
|
* R0 & imm -> R0
|
|
* 11001001iiiiiiii
|
|
*
|
|
* AND.B #imm, @(R0, GBR)
|
|
* (R0 + GBR) & imm -> (R0 + GBR)
|
|
* 11001101iiiiiiii
|
|
*/
|
|
static RzILOpEffect *sh_il_and(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return sh_il_set_pure_param(1, LOGAND(sh_il_get_pure_param(0), sh_il_get_pure_param(1)));
|
|
}
|
|
|
|
/**
|
|
* NOT Rm, Rn
|
|
* ~Rm -> Rn
|
|
* 0110nnnnmmmm0111
|
|
*/
|
|
static RzILOpEffect *sh_il_not(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return sh_il_set_pure_param(1, LOGNOT(sh_il_get_pure_param(0)));
|
|
}
|
|
|
|
/**
|
|
* OR Rm, Rn
|
|
* Rn | Rm -> Rn
|
|
* 0010nnnnmmmm1011
|
|
*
|
|
* OR #imm, R0
|
|
* R0 | imm -> R0
|
|
* 11001011iiiiiiii
|
|
*
|
|
* OR.B #imm, @(R0, GBR)
|
|
* (R0 + GBR) | imm -> (R0 + GBR)
|
|
* 11001111iiiiiiii
|
|
*/
|
|
static RzILOpEffect *sh_il_or(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return sh_il_set_pure_param(1, LOGOR(sh_il_get_pure_param(0), sh_il_get_pure_param(1)));
|
|
}
|
|
|
|
/**
|
|
* TAS.B @Rn
|
|
* If (Rn) = 0, 1 -> T ; Otherwise 0 -> T
|
|
* 1 -> MSB of (Rn)
|
|
* 0110nnnnmmmm0111
|
|
*/
|
|
static RzILOpEffect *sh_il_tas(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpPure *mem = sh_il_get_pure_param(0);
|
|
RzILOpEffect *tbit = SETG(SH_SR_T, IS_ZERO(mem));
|
|
return SEQ2(tbit, sh_il_set_pure_param(0, LOGOR(DUP(mem), UN(8, 0x80))));
|
|
}
|
|
|
|
/**
|
|
* TST Rm, Rn
|
|
* If Rn & Rm = 0, 1 -> T ; Otherwise 0 -> T
|
|
* 0010nnnnmmmm1000
|
|
*
|
|
* TST #imm, R0
|
|
* If R0 & imm = 0, 1 -> T ; Otherwise 0 -> T
|
|
* 11001000iiiiiiii
|
|
*
|
|
* TST.B #imm, @(R0, GBR)
|
|
* If (R0 + GBR) & imm = 0, 1 -> T ; Otherwise 0 -> T
|
|
* 11001100iiiiiiii
|
|
*/
|
|
static RzILOpEffect *sh_il_tst(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return SETG(SH_SR_T, IS_ZERO(LOGAND(sh_il_get_pure_param(0), sh_il_get_pure_param(1))));
|
|
}
|
|
|
|
/**
|
|
* XOR Rm, Rn
|
|
* Rn ^ Rm -> Rn
|
|
* 0010nnnnmmmm1010
|
|
*
|
|
* XOR #imm, R0
|
|
* R0 ^ imm -> R0
|
|
* 11001010iiiiiiii
|
|
*
|
|
* XOR.B #imm, @(R0, GBR)
|
|
* (R0 + GBR) ^ imm -> (R0 + GBR)
|
|
* 11001110iiiiiiii
|
|
*/
|
|
static RzILOpEffect *sh_il_xor(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return sh_il_set_pure_param(1, LOGXOR(sh_il_get_pure_param(0), sh_il_get_pure_param(1)));
|
|
}
|
|
|
|
/**
|
|
* ROTL Rn
|
|
* T <- Rn <- MSB
|
|
* 0100nnnn00000100
|
|
*/
|
|
static RzILOpEffect *sh_il_rotl(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpEffect *msb = SETL("msb_", MSB(sh_il_get_pure_param(0)));
|
|
RzILOpEffect *tbit = SETG(SH_SR_T, VARL("msb_"));
|
|
RzILOpEffect *shl = SETL("shl_", SHIFTL0(sh_il_get_pure_param(0), SH_U_REG(1)));
|
|
RzILOpPure *lsb = ITE(VARL("msb_"), LOGOR(VARL("shl_"), SH_U_REG(1)), VARL("shl_"));
|
|
return SEQ4(msb, tbit, shl, sh_il_set_pure_param(0, lsb));
|
|
}
|
|
|
|
/**
|
|
* ROTR Rn
|
|
* LSB -> Rn -> T
|
|
* 0100nnnn00000101
|
|
*/
|
|
static RzILOpEffect *sh_il_rotr(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpEffect *lsb = SETL("lsb_", LSB(sh_il_get_pure_param(0)));
|
|
RzILOpEffect *tbit = SETG(SH_SR_T, VARL("lsb_"));
|
|
RzILOpEffect *shr = SETL("shr_", SHIFTR0(sh_il_get_pure_param(0), SH_U_REG(1)));
|
|
RzILOpPure *msb = ITE(VARL("lsb_"), LOGOR(VARL("shr_"), SH_U_REG(0x80000000)), VARL("shr_"));
|
|
return SEQ4(lsb, tbit, shr, sh_il_set_pure_param(0, msb));
|
|
}
|
|
|
|
/**
|
|
* ROTCL Rn
|
|
* T <- Rn <- T
|
|
* 0100nnnn00100100
|
|
*/
|
|
static RzILOpEffect *sh_il_rotcl(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpEffect *msb = SETL("msb_", MSB(sh_il_get_pure_param(0)));
|
|
RzILOpEffect *shl = SETL("shl_", SHIFTL0(sh_il_get_pure_param(0), SH_U_REG(1)));
|
|
RzILOpPure *lsb = ITE(VARG(SH_SR_T), LOGOR(VARL("shl_"), SH_U_REG(1)), VARL("shl_"));
|
|
RzILOpEffect *tbit = SETG(SH_SR_T, VARL("msb_"));
|
|
return SEQ4(msb, shl, sh_il_set_pure_param(0, lsb), tbit);
|
|
}
|
|
|
|
/**
|
|
* ROTCR Rn
|
|
* T -> Rn -> T
|
|
* 0100nnnn00100101
|
|
*/
|
|
static RzILOpEffect *sh_il_rotcr(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpEffect *lsb = SETL("lsb_", LSB(sh_il_get_pure_param(0)));
|
|
RzILOpEffect *shr = SETL("shr_", SHIFTR0(sh_il_get_pure_param(0), SH_U_REG(1)));
|
|
RzILOpPure *msb = ITE(VARG(SH_SR_T), LOGOR(VARL("shr_"), SH_U_REG(0x80000000)), VARL("shr_"));
|
|
RzILOpEffect *tbit = SETG(SH_SR_T, VARL("lsb_"));
|
|
return SEQ4(lsb, shr, sh_il_set_pure_param(0, msb), tbit);
|
|
}
|
|
|
|
/**
|
|
* SHAD Rm, Rn
|
|
* If Rn >= 0, Rn << Rm -> Rn
|
|
* If Rn < 0, Rn >> Rm -> [MSB -> Rn]
|
|
* MSB -> Rn
|
|
* 0100nnnnmmmm1100
|
|
*/
|
|
static RzILOpEffect *sh_il_shad(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpEffect *shift_amount = SETL("shift_", UNSIGNED(5, sh_il_get_pure_param(0)));
|
|
|
|
RzILOpPure *shl = SHIFTL0(sh_il_get_pure_param(1), VARL("shift_"));
|
|
RzILOpPure *shr = SHIFTRA(sh_il_get_pure_param(1), NEG(VARL("shift_")));
|
|
|
|
return SEQ2(shift_amount, BRANCH(SGE(sh_il_get_pure_param(0), SN(32, 0)), sh_il_set_pure_param(1, shl), sh_il_set_pure_param(1, shr)));
|
|
}
|
|
|
|
/**
|
|
* SHAL Rn
|
|
* T <- Rn <- 0
|
|
* 0100nnnn00100000
|
|
*/
|
|
static RzILOpEffect *sh_il_shal(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpPure *msb = MSB(sh_il_get_pure_param(0));
|
|
RzILOpPure *shl = SHIFTL0(sh_il_get_pure_param(0), SH_U_REG(1));
|
|
return SEQ2(SETG(SH_SR_T, msb), sh_il_set_pure_param(0, shl));
|
|
}
|
|
|
|
/**
|
|
* SHAR Rn
|
|
* MSB -> Rn -> T
|
|
* 0100nnnn00100001
|
|
*/
|
|
static RzILOpEffect *sh_il_shar(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpPure *lsb = LSB(sh_il_get_pure_param(0));
|
|
RzILOpPure *shl = SHIFTRA(sh_il_get_pure_param(0), SH_U_REG(1));
|
|
return SEQ2(SETG(SH_SR_T, lsb), sh_il_set_pure_param(0, shl));
|
|
}
|
|
|
|
/**
|
|
* SHLD Rm, Rn
|
|
* If Rn >= 0, Rn << Rm -> Rn
|
|
* If Rn < 0, Rn >> Rm -> [0 -> Rn]
|
|
* MSB -> Rn
|
|
* 0100nnnnmmmm1101
|
|
*/
|
|
static RzILOpEffect *sh_il_shld(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpEffect *shift_amount = SETL("shift_", UNSIGNED(5, sh_il_get_pure_param(0)));
|
|
|
|
RzILOpPure *shl = SHIFTL0(sh_il_get_pure_param(1), VARL("shift_"));
|
|
RzILOpPure *shr = SHIFTR0(sh_il_get_pure_param(1), NEG(VARL("shift_")));
|
|
|
|
return SEQ2(shift_amount, BRANCH(SGE(sh_il_get_pure_param(0), SN(32, 0)), sh_il_set_pure_param(1, shl), sh_il_set_pure_param(1, shr)));
|
|
}
|
|
|
|
/**
|
|
* SHLL Rn
|
|
* T <- Rn <- 0
|
|
* 0100nnnn00000000
|
|
*/
|
|
static RzILOpEffect *sh_il_shll(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpPure *msb = MSB(sh_il_get_pure_param(0));
|
|
RzILOpPure *shl = SHIFTL0(sh_il_get_pure_param(0), SH_U_REG(1));
|
|
return SEQ2(SETG(SH_SR_T, msb), sh_il_set_pure_param(0, shl));
|
|
}
|
|
|
|
/**
|
|
* SHLR Rn
|
|
* 0 -> Rn -> T
|
|
* 0100nnnn00000001
|
|
*/
|
|
static RzILOpEffect *sh_il_shlr(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpPure *lsb = LSB(sh_il_get_pure_param(0));
|
|
RzILOpPure *shr = SHIFTR0(sh_il_get_pure_param(0), SH_U_REG(1));
|
|
return SEQ2(SETG(SH_SR_T, lsb), sh_il_set_pure_param(0, shr));
|
|
}
|
|
|
|
/**
|
|
* SHLL2 Rn
|
|
* Rn << 2 -> Rn
|
|
* 0100nnnn00001000
|
|
*/
|
|
static RzILOpEffect *sh_il_shll2(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return sh_il_set_pure_param(0, SHIFTL0(sh_il_get_pure_param(0), SH_U_REG(2)));
|
|
}
|
|
|
|
/**
|
|
* SHLR2 Rn
|
|
* Rn >> 2 -> Rn
|
|
* 0100nnnn00001001
|
|
*/
|
|
static RzILOpEffect *sh_il_shlr2(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return sh_il_set_pure_param(0, SHIFTR0(sh_il_get_pure_param(0), SH_U_REG(2)));
|
|
}
|
|
|
|
/**
|
|
* SHLL8 Rn
|
|
* Rn << 8 -> Rn
|
|
* 0100nnnn00011000
|
|
*/
|
|
static RzILOpEffect *sh_il_shll8(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return sh_il_set_pure_param(0, SHIFTL0(sh_il_get_pure_param(0), SH_U_REG(8)));
|
|
}
|
|
|
|
/**
|
|
* SHLR8 Rn
|
|
* Rn >> 8 -> Rn
|
|
* 0100nnnn00011001
|
|
*/
|
|
static RzILOpEffect *sh_il_shlr8(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return sh_il_set_pure_param(0, SHIFTR0(sh_il_get_pure_param(0), SH_U_REG(8)));
|
|
}
|
|
|
|
/**
|
|
* SHLL16 Rn
|
|
* Rn << 16 -> Rn
|
|
* 0100nnnn00101000
|
|
*/
|
|
static RzILOpEffect *sh_il_shll16(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return sh_il_set_pure_param(0, SHIFTL0(sh_il_get_pure_param(0), SH_U_REG(16)));
|
|
}
|
|
|
|
/**
|
|
* SHLR16 Rn
|
|
* Rn >> 16 -> Rn
|
|
* 0100nnnn00101001
|
|
*/
|
|
static RzILOpEffect *sh_il_shlr16(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return sh_il_set_pure_param(0, SHIFTR0(sh_il_get_pure_param(0), SH_U_REG(16)));
|
|
}
|
|
|
|
/**
|
|
* BF label
|
|
* if T = 0, disp * 2 + PC + 4 -> PC ; otherwise (T = 1) NOP
|
|
* 10001011dddddddd
|
|
*/
|
|
static RzILOpEffect *sh_il_bf(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpPure *new_pc = sh_il_get_effective_addr_param(0);
|
|
return BRANCH(VARG(SH_SR_T), JMP(new_pc), NOP());
|
|
}
|
|
|
|
/**
|
|
* BF/S label
|
|
* if T = 0, disp * 2 + PC + 4 -> PC ; otherwise (T = 1) NOP ; delayed branch
|
|
* 10001111dddddddd
|
|
* TODO: Implement delayed branch
|
|
*/
|
|
static RzILOpEffect *sh_il_bfs(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpPure *new_pc = sh_il_get_effective_addr_param(0);
|
|
return BRANCH(VARG(SH_SR_T), JMP(new_pc), NOP());
|
|
}
|
|
|
|
/**
|
|
* BT label
|
|
* if T = 1, disp * 2 + PC + 4 -> PC ; otherwise (T = 0) NOP
|
|
* 10001001dddddddd
|
|
*/
|
|
static RzILOpEffect *sh_il_bt(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpPure *new_pc = sh_il_get_effective_addr_param(0);
|
|
return BRANCH(VARG(SH_SR_T), JMP(new_pc), NOP());
|
|
}
|
|
|
|
/**
|
|
* BT/S label
|
|
* if T = 1, disp * 2 + PC + 4 -> PC ; otherwise (T = 0) NOP ; delayed branch
|
|
* 10001101dddddddd
|
|
* TODO: Implement delayed branch
|
|
*/
|
|
static RzILOpEffect *sh_il_bts(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RzILOpPure *new_pc = sh_il_get_effective_addr_param(0);
|
|
return BRANCH(VARG(SH_SR_T), JMP(new_pc), NOP());
|
|
}
|
|
|
|
/**
|
|
* BRA label
|
|
* disp * 2 + PC + 4 -> PC ; delayed branch
|
|
* 1010dddddddddddd
|
|
* TODO: Implement delayed branch
|
|
*/
|
|
static RzILOpEffect *sh_il_bra(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return JMP(sh_il_get_effective_addr_param(0));
|
|
}
|
|
|
|
/**
|
|
* BRAF Rn
|
|
* Rn + PC + 4 -> PC ; delayed branch
|
|
* 0000nnnn00100011
|
|
* TODO: Implement delayed branch
|
|
*/
|
|
static RzILOpEffect *sh_il_braf(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return JMP(sh_il_get_effective_addr_param(0));
|
|
}
|
|
|
|
/**
|
|
* BSR label
|
|
* PC + 4 -> PR ; disp * 2 + PC + 4 -> PC ; delayed branch
|
|
* 1011dddddddddddd
|
|
* TODO: Implement delayed branch
|
|
*/
|
|
static RzILOpEffect *sh_il_bsr(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return SEQ2(SETG("pr", ADD(SH_U_ADDR(pc), SH_U_ADDR(4))), JMP(sh_il_get_effective_addr_param(0)));
|
|
}
|
|
|
|
/**
|
|
* BSRF Rn
|
|
* PC + 4 -> PR ; Rn + PC + 4 -> PC ; delayed branch
|
|
* 0000nnnn00000011
|
|
* TODO: Implement delayed branch
|
|
*/
|
|
static RzILOpEffect *sh_il_bsrf(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return SEQ2(SETG("pr", ADD(SH_U_ADDR(pc), SH_U_ADDR(4))), JMP(sh_il_get_effective_addr_param(0)));
|
|
}
|
|
|
|
/**
|
|
* JMP @Rn
|
|
* Rn -> PC ; delayed branch
|
|
* 0100nnnn00101011
|
|
* TODO: Implement delayed branch
|
|
*/
|
|
static RzILOpEffect *sh_il_jmp(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return JMP(sh_il_get_effective_addr_param(0));
|
|
}
|
|
|
|
/**
|
|
* JSR @Rn
|
|
* PC + 4 -> PR ; Rn -> PC ; delayed branch
|
|
* 0100nnnn00001011
|
|
* TODO: Implement delayed branch
|
|
*/
|
|
static RzILOpEffect *sh_il_jsr(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return SEQ2(SETG("pr", ADD(SH_U_ADDR(pc), SH_U_ADDR(4))), JMP(sh_il_get_effective_addr_param(0)));
|
|
}
|
|
|
|
/**
|
|
* RTS
|
|
* PR -> PC ; delayed branch
|
|
* 0000000000001011
|
|
* TODO: Implement delayed branch
|
|
*/
|
|
static RzILOpEffect *sh_il_rts(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return JMP(VARG("pr"));
|
|
}
|
|
|
|
/**
|
|
* CLRMAC
|
|
* 0 -> MACH, MACL
|
|
* 0000000000101000
|
|
*/
|
|
static RzILOpEffect *sh_il_clrmac(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return SEQ2(SETG("mach", UN(SH_REG_SIZE, 0)), SETG("macl", UN(SH_REG_SIZE, 0)));
|
|
}
|
|
|
|
/**
|
|
* CLRS
|
|
* 0 -> S
|
|
* 0000000001001000
|
|
*/
|
|
static RzILOpEffect *sh_il_clrs(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return SETG(SH_SR_S, IL_FALSE);
|
|
}
|
|
|
|
/**
|
|
* CLRT
|
|
* 0 -> T
|
|
* 0000000000001000
|
|
*/
|
|
static RzILOpEffect *sh_il_clrt(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return SETG(SH_SR_T, IL_FALSE);
|
|
}
|
|
|
|
/**
|
|
* LDC Rm, REG
|
|
* REG := SR/GBR/VBR/SSR/SPC/DBR/Rn_BANK
|
|
* Rm -> REG
|
|
* PRIVILEGED (Only GBR is not privileged)
|
|
*
|
|
* LDC.L @Rm+, REG
|
|
* REG := SR/GBR/VBR/SSR/SPC/DBR/Rn_BANK
|
|
* (Rm) -> REG ; Rm + 4 -> Rm
|
|
* PRIVILEGED (Only GBR is not privileged)
|
|
*/
|
|
static RzILOpEffect *sh_il_ldc(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
/* We won't be using banked registers for these instructions (except for unprivileged GBR) */
|
|
// TODO: Check what the correct implementation is
|
|
ctx->use_banked = false;
|
|
if (op->param[1].param[0] == SH_REG_IND_GBR) {
|
|
ctx->use_banked = true;
|
|
}
|
|
|
|
RzILOpEffect *eff = NULL;
|
|
if (op->scaling == SH_SCALING_INVALID) {
|
|
if (sh_valid_gpr(op->param[1].param[0])) {
|
|
eff = SETG(sh_get_banked_reg(op->param[1].param[0], 1), sh_il_get_pure_param(0));
|
|
} else {
|
|
eff = sh_il_set_pure_param(1, sh_il_get_pure_param(0));
|
|
}
|
|
} else if (op->scaling == SH_SCALING_L) {
|
|
SHParamHelper rm = sh_il_get_param(op->param[0], op->scaling);
|
|
if (sh_valid_gpr(op->param[1].param[0])) {
|
|
eff = SEQ2(SETG(sh_get_banked_reg(op->param[1].param[0], 1), rm.pure), rm.post);
|
|
} else {
|
|
eff = SEQ2(sh_il_set_pure_param(1, rm.pure), rm.post);
|
|
}
|
|
}
|
|
|
|
if (op->param[1].param[0] != SH_REG_IND_GBR) {
|
|
eff = BRANCH(sh_il_get_privilege(), eff, EMPTY());
|
|
}
|
|
return eff;
|
|
}
|
|
|
|
/**
|
|
* LDS Rm, REG
|
|
* REG := MACH/MACL/PR
|
|
* Rm -> REG
|
|
*
|
|
* LDS.L @Rm+, REG
|
|
* REG := MACH/MACL/PR
|
|
* (Rm) -> REG ; Rm + 4 -> Rm
|
|
*/
|
|
static RzILOpEffect *sh_il_lds(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
if (op->scaling == SH_SCALING_INVALID) {
|
|
return sh_il_set_pure_param(1, sh_il_get_pure_param(0));
|
|
} else if (op->scaling == SH_SCALING_L) {
|
|
SHParamHelper rm = sh_il_get_param(op->param[0], op->scaling);
|
|
return SEQ2(sh_il_set_pure_param(1, rm.pure), rm.post);
|
|
}
|
|
return NOP();
|
|
}
|
|
|
|
/**
|
|
* MOVCA.L R0, @Rn
|
|
* R0 -> (Rn) (without fetching cache block)
|
|
* 0000nnnn11000011
|
|
*/
|
|
static RzILOpEffect *sh_il_movca(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return sh_il_set_pure_param(1, sh_il_get_pure_param(0));
|
|
}
|
|
|
|
/**
|
|
* NOP
|
|
* No operation
|
|
* 0000000000001001
|
|
*/
|
|
static RzILOpEffect *sh_il_nop(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return NOP();
|
|
}
|
|
|
|
/**
|
|
* RTE
|
|
* SSR -> SR ; SPC -> PC ; delayed branch
|
|
* 0000000000101011
|
|
* PRIVILEGED
|
|
* TODO: Implement delayed branch
|
|
*/
|
|
static RzILOpEffect *sh_il_rte(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return BRANCH(sh_il_get_privilege(), SEQ2(sh_il_set_status_reg(VARG("ssr")), JMP(VARG("spc"))), EMPTY());
|
|
}
|
|
|
|
/**
|
|
* SETS
|
|
* 1 -> S
|
|
* 0000000001011000
|
|
*/
|
|
static RzILOpEffect *sh_il_sets(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return SETG(SH_SR_S, IL_TRUE);
|
|
}
|
|
|
|
/**
|
|
* SETT
|
|
* 1 -> T
|
|
* 0000000000011000
|
|
*/
|
|
static RzILOpEffect *sh_il_sett(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return SETG(SH_SR_T, IL_TRUE);
|
|
}
|
|
|
|
/**
|
|
* SLEEP
|
|
* Sleep or standby (so effectively, just a NOP)
|
|
* 0000000000011011
|
|
* PRIVILEGED
|
|
*/
|
|
static RzILOpEffect *sh_il_sleep(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return BRANCH(sh_il_get_privilege(), NOP(), EMPTY());
|
|
}
|
|
|
|
/**
|
|
* STC REG, Rn
|
|
* REG := SR/GBR/VBR/SSR/SPC/SGR/DBR/Rn_BANK
|
|
* REG -> Rn
|
|
* PRIVILEGED (Only GBR is not privileged)
|
|
*
|
|
* STC.L REG, @-Rn
|
|
* REG := SR/GBR/VBR/SSR/SPC/SGR/DBR/Rn_BANK
|
|
* Rn - 4 -> Rn ; REG -> (Rn)
|
|
* PRIVILEGED (Only GBR is not privileged)
|
|
*/
|
|
static RzILOpEffect *sh_il_stc(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
/* We won't be using banked registers for these instructions (except for unprivileged GBR) */
|
|
// TODO: Check what the correct implementation is
|
|
ctx->use_banked = false;
|
|
if (op->param[0].param[0] == SH_REG_IND_GBR) {
|
|
ctx->use_banked = true;
|
|
}
|
|
|
|
RzILOpEffect *eff = NULL;
|
|
if (sh_valid_gpr(op->param[0].param[0])) { // REG = Rn_BANK
|
|
eff = sh_il_set_pure_param(1, VARG(sh_get_banked_reg(op->param[0].param[0], 1)));
|
|
} else {
|
|
eff = sh_il_set_pure_param(1, sh_il_get_pure_param(0));
|
|
}
|
|
if (op->param[0].param[0] != SH_REG_IND_GBR) {
|
|
eff = BRANCH(sh_il_get_privilege(), eff, EMPTY());
|
|
}
|
|
return eff;
|
|
}
|
|
|
|
/**
|
|
* STS REG, Rn
|
|
* REG := MACH/MACL/PR
|
|
* REG -> Rn
|
|
*
|
|
* STS.L REG, @-Rn
|
|
* REG := MACH/MACL/PR
|
|
* Rn + 4 -> Rn ; REG -> (Rn)
|
|
*/
|
|
static RzILOpEffect *sh_il_sts(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
return sh_il_set_pure_param(1, sh_il_get_pure_param(0));
|
|
}
|
|
|
|
/**
|
|
* \brief Unimplemented instruction/opcode
|
|
* To be used for valid SuperH-4 instruction which yet haven't been lifted to the IL
|
|
*/
|
|
static RzILOpEffect *sh_il_unimpl(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx) {
|
|
RZ_LOG_DEBUG("SuperH: Instruction with opcode 0x%04x is unimplemented\n", op->opcode);
|
|
return EMPTY();
|
|
}
|
|
|
|
#include <rz_il/rz_il_opbuilder_end.h>
|
|
|
|
typedef RzILOpEffect *(*sh_il_op)(const SHOp *op, ut64 pc, RzAnalysis *analysis, SHILContext *ctx);
|
|
|
|
/**
|
|
* \brief Lookup table for the IL lifting handlers for the various instructions
|
|
*/
|
|
static const sh_il_op sh_ops[SH_OP_SIZE] = {
|
|
[SH_OP_INVALID] = sh_il_invalid,
|
|
[SH_OP_MOV] = sh_il_mov,
|
|
[SH_OP_MOVT] = sh_il_movt,
|
|
[SH_OP_SWAP] = sh_il_swap,
|
|
[SH_OP_XTRCT] = sh_il_xtrct,
|
|
[SH_OP_ADD] = sh_il_add,
|
|
[SH_OP_ADDC] = sh_il_addc,
|
|
[SH_OP_ADDV] = sh_il_addv,
|
|
[SH_OP_CMP_EQ] = sh_il_cmp_eq,
|
|
[SH_OP_CMP_HS] = sh_il_cmp_hs,
|
|
[SH_OP_CMP_GE] = sh_il_cmp_ge,
|
|
[SH_OP_CMP_HI] = sh_il_cmp_hi,
|
|
[SH_OP_CMP_GT] = sh_il_cmp_gt,
|
|
[SH_OP_CMP_PZ] = sh_il_cmp_pz,
|
|
[SH_OP_CMP_PL] = sh_il_cmp_pl,
|
|
[SH_OP_CMP_STR] = sh_il_cmp_str,
|
|
[SH_OP_DIV1] = sh_il_div1,
|
|
[SH_OP_DIV0S] = sh_il_div0s,
|
|
[SH_OP_DIV0U] = sh_il_div0u,
|
|
[SH_OP_DMULS] = sh_il_dmuls,
|
|
[SH_OP_DMULU] = sh_il_dmulu,
|
|
[SH_OP_DT] = sh_il_dt,
|
|
[SH_OP_EXTS] = sh_il_exts,
|
|
[SH_OP_EXTU] = sh_il_extu,
|
|
[SH_OP_MAC] = sh_il_mac,
|
|
[SH_OP_MUL] = sh_il_mul,
|
|
[SH_OP_MULS] = sh_il_muls,
|
|
[SH_OP_MULU] = sh_il_mulu,
|
|
[SH_OP_NEG] = sh_il_neg,
|
|
[SH_OP_NEGC] = sh_il_negc,
|
|
[SH_OP_SUB] = sh_il_sub,
|
|
[SH_OP_SUBC] = sh_il_subc,
|
|
[SH_OP_SUBV] = sh_il_subv,
|
|
[SH_OP_AND] = sh_il_and,
|
|
[SH_OP_NOT] = sh_il_not,
|
|
[SH_OP_OR] = sh_il_or,
|
|
[SH_OP_TAS] = sh_il_tas,
|
|
[SH_OP_TST] = sh_il_tst,
|
|
[SH_OP_XOR] = sh_il_xor,
|
|
[SH_OP_ROTL] = sh_il_rotl,
|
|
[SH_OP_ROTR] = sh_il_rotr,
|
|
[SH_OP_ROTCL] = sh_il_rotcl,
|
|
[SH_OP_ROTCR] = sh_il_rotcr,
|
|
[SH_OP_SHAD] = sh_il_shad,
|
|
[SH_OP_SHAL] = sh_il_shal,
|
|
[SH_OP_SHAR] = sh_il_shar,
|
|
[SH_OP_SHLD] = sh_il_shld,
|
|
[SH_OP_SHLL] = sh_il_shll,
|
|
[SH_OP_SHLR] = sh_il_shlr,
|
|
[SH_OP_SHLL2] = sh_il_shll2,
|
|
[SH_OP_SHLR2] = sh_il_shlr2,
|
|
[SH_OP_SHLL8] = sh_il_shll8,
|
|
[SH_OP_SHLR8] = sh_il_shlr8,
|
|
[SH_OP_SHLL16] = sh_il_shll16,
|
|
[SH_OP_SHLR16] = sh_il_shlr16,
|
|
[SH_OP_BF] = sh_il_bf,
|
|
[SH_OP_BFS] = sh_il_bfs,
|
|
[SH_OP_BT] = sh_il_bt,
|
|
[SH_OP_BTS] = sh_il_bts,
|
|
[SH_OP_BRA] = sh_il_bra,
|
|
[SH_OP_BRAF] = sh_il_braf,
|
|
[SH_OP_BSR] = sh_il_bsr,
|
|
[SH_OP_BSRF] = sh_il_bsrf,
|
|
[SH_OP_JMP] = sh_il_jmp,
|
|
[SH_OP_JSR] = sh_il_jsr,
|
|
[SH_OP_RTS] = sh_il_rts,
|
|
[SH_OP_CLRMAC] = sh_il_clrmac,
|
|
[SH_OP_CLRS] = sh_il_clrs,
|
|
[SH_OP_CLRT] = sh_il_clrt,
|
|
[SH_OP_LDC] = sh_il_ldc,
|
|
[SH_OP_LDS] = sh_il_lds,
|
|
[SH_OP_MOVCA] = sh_il_movca,
|
|
[SH_OP_NOP] = sh_il_nop,
|
|
[SH_OP_RTE] = sh_il_rte,
|
|
[SH_OP_SETS] = sh_il_sets,
|
|
[SH_OP_SETT] = sh_il_sett,
|
|
[SH_OP_SLEEP] = sh_il_sleep,
|
|
[SH_OP_STC] = sh_il_stc,
|
|
[SH_OP_STS] = sh_il_sts,
|
|
[SH_OP_UNIMPL] = sh_il_unimpl
|
|
};
|
|
|
|
/**
|
|
* \brief Store the lifted IL for \p op in \p aop
|
|
* This function also takes care of initializing and adding the privilege mode local variable if required
|
|
*
|
|
* \param analysis RzAnalysis instance
|
|
* \param aop
|
|
* \param pc Program counter
|
|
* \param op
|
|
* \param ctx Context variables for the current IL lifting
|
|
* \return bool True if successful ; false otherwise
|
|
*/
|
|
RZ_IPI bool rz_sh_il_opcode(RZ_NONNULL RzAnalysis *analysis, RZ_NONNULL RzAnalysisOp *aop, ut64 pc, RZ_BORROW RZ_NONNULL const SHOp *op, RZ_NULLABLE SHILContext *ctx) {
|
|
rz_return_val_if_fail(analysis && aop && op, false);
|
|
if (op->mnemonic >= SH_OP_SIZE) {
|
|
RZ_LOG_ERROR("RzIL: SuperH: out of bounds op\n");
|
|
return false;
|
|
}
|
|
|
|
sh_il_op create_op = sh_ops[op->mnemonic];
|
|
RzILOpEffect *lifted = create_op(op, pc, analysis, ctx);
|
|
|
|
// If the privilege was checked, then we need to set the local variable before the IL lifting
|
|
if (ctx && ctx->privilege_check) {
|
|
lifted = sh_apply_effects(lifted, sh_il_initialize_privilege(), NULL);
|
|
}
|
|
|
|
aop->il_op = lifted;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Initialize new config for the SuperH IL
|
|
*
|
|
* \param analysis RzAnalysis instance
|
|
* \return RzAnalysisILConfig* RzIL config for SuperH ISA
|
|
*/
|
|
RZ_IPI RzAnalysisILConfig *rz_sh_il_config(RZ_NONNULL RzAnalysis *analysis) {
|
|
rz_return_val_if_fail(analysis, NULL);
|
|
|
|
RzAnalysisILConfig *r = rz_analysis_il_config_new(SH_ADDR_SIZE, analysis->big_endian, SH_ADDR_SIZE);
|
|
return r;
|
|
}
|