rizin/librz/arch/isa/ppc/ppc_il_ops.c

1980 lines
49 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// SPDX-FileCopyrightText: 2022 Rot127 <rot127@posteo.com>
// SPDX-License-Identifier: LGPL-3.0-only
#include "ppc_il.h"
#include "ppc_analysis.h"
#include "rz_types_base.h"
#include <rz_il/rz_il_opcodes.h>
#include <rz_util/rz_assert.h>
#include <capstone/capstone.h>
#include <rz_il/rz_il_opbuilder_begin.h>
static RzILOpEffect *load_op(RZ_BORROW csh handle, RZ_BORROW cs_insn *insn, const cs_mode mode) {
rz_return_val_if_fail(handle && insn, EMPTY());
ut32 id = insn->id;
// READ
const char *rT = cs_reg_name(handle, INSOP(0).reg);
const char *rA = cs_reg_name(handle, INSOP(1).mem.base);
#if CS_NEXT_VERSION < 6
const char *rB = cs_reg_name(handle, INSOP(2).reg);
#else
const char *rB = cs_reg_name(handle, INSOP(1).mem.offset);
#endif
st64 d = INSOP(1).mem.disp; // RA = base ; D = Disposition
st64 sI = INSOP(1).imm; // liX instructions (alias for addX).
bool update_ra = ppc_updates_ra_with_ea(id); // Save ea in RA?
ut32 mem_acc_size = ppc_get_mem_acc_size(id);
RzILOpPure *base;
RzILOpPure *disp;
RzILOpPure *ea;
RzILOpPure *into_rt;
if (mem_acc_size < 0) {
NOT_IMPLEMENTED;
}
// How to read instruction ids:
// Letter Meaning
// L Load
// B/H/W/D/F Byte, Half Word, Word, Double Word, Float
// Z/A/B Zero extend, Algebraic, Byte reversal
// U/R Update (store EA in RA), Reserve indexed
// X X Form instruction (uses RB instead of immediate)
// CIX Caching Inhibited Indexed
// V Vector indexed
// EXEC
switch (id) {
default:
NOT_IMPLEMENTED;
case PPC_INS_LI: // RT = sI
into_rt = EXTEND(PPC_ARCH_BITS, SN(16, sI));
update_ra = false;
break;
case PPC_INS_LIS: // RT = SI << 16
into_rt = EXTEND(PPC_ARCH_BITS, APPEND(SN(16, sI), U16(0)));
update_ra = false;
break;
case PPC_INS_LA: // RT = EA
NOT_IMPLEMENTED;
case PPC_INS_LBZ:
case PPC_INS_LBZX:
case PPC_INS_LBZU:
case PPC_INS_LBZUX:
case PPC_INS_LHZ:
case PPC_INS_LHZX:
case PPC_INS_LHZU:
case PPC_INS_LHZUX:
case PPC_INS_LWZ:
case PPC_INS_LWZU:
case PPC_INS_LWZUX:
case PPC_INS_LWZX:
case PPC_INS_LD:
case PPC_INS_LDX:
case PPC_INS_LDU:
case PPC_INS_LDUX:
case PPC_INS_LHA:
case PPC_INS_LHAX:
case PPC_INS_LHAU:
case PPC_INS_LHAUX:
case PPC_INS_LWA:
case PPC_INS_LWAX:
case PPC_INS_LWAUX:
case PPC_INS_LBZCIX:
case PPC_INS_LHZCIX:
case PPC_INS_LWZCIX:
case PPC_INS_LDCIX:
#if CS_NEXT_VERSION >= 6
base = VARG(rA);
#else
base = rA ? VARG(rA) : NULL;
#endif
if (ppc_is_x_form(id)) {
disp = VARG(rB);
} else {
// "disp << 2" done by capstone.
RzILOpPure *imm = SN(16, d);
disp = EXTEND(PPC_ARCH_BITS, imm);
}
ea = base ? ADD(base, disp) : disp;
RzILOpPure *loadw = LOADW(mem_acc_size, VARLP("ea"));
if (ppc_is_algebraic(id)) {
into_rt = EXTEND(PPC_ARCH_BITS, VARLP("loadw"));
} else {
into_rt = EXTZ(VARLP("loadw"));
}
into_rt = LET("ea", ea, LET("loadw", loadw, into_rt));
break;
case PPC_INS_LWARX:
case PPC_INS_LDARX:
NOT_IMPLEMENTED;
// Byte reverse and reserved indexed
case PPC_INS_LHBRX:
case PPC_INS_LWBRX:
case PPC_INS_LDBRX:
#if CS_NEXT_VERSION >= 6
base = VARG(rA);
#else
base = rA ? VARG(rA) : NULL;
#endif
disp = VARG(rB);
ea = base ? ADD(base, disp) : disp;
#define LB(i) LOADW(8, ADD(VARLP("ea"), UA(i)))
if (IN_BE_MODE) {
if (id == PPC_INS_LHBRX) {
into_rt = LET("ea", ea, APPEND(LB(0), LB(1)));
} else if (id == PPC_INS_LWBRX) {
into_rt = LET("ea", ea, APPEND(LB(0), APPEND(LB(1), APPEND(LB(2), LB(3)))));
} else {
into_rt = LET("ea", ea, APPEND(LB(0), APPEND(LB(1), APPEND(LB(2), APPEND(LB(3), APPEND(LB(4), APPEND(LB(5), APPEND(LB(6), LB(7)))))))));
}
} else {
if (id == PPC_INS_LHBRX) {
into_rt = LET("ea", ea, APPEND(LB(1), LB(0)));
} else if (id == PPC_INS_LWBRX) {
into_rt = LET("ea", ea, APPEND(LB(3), APPEND(LB(2), APPEND(LB(1), LB(0)))));
} else {
into_rt = LET("ea", ea, APPEND(LB(7), APPEND(LB(6), APPEND(LB(5), APPEND(LB(4), APPEND(LB(3), APPEND(LB(2), APPEND(LB(1), LB(0)))))))));
}
}
into_rt = EXTZ(into_rt);
break;
// Floats
case PPC_INS_LFD:
case PPC_INS_LFDX:
case PPC_INS_LFDU:
case PPC_INS_LFDUX:
case PPC_INS_LFIWAX:
case PPC_INS_LFIWZX:
case PPC_INS_LFS:
case PPC_INS_LFSX:
case PPC_INS_LFSU:
case PPC_INS_LFSUX:
NOT_IMPLEMENTED;
// Vector
case PPC_INS_LVEBX:
case PPC_INS_LVEHX:
case PPC_INS_LVEWX:
case PPC_INS_LVSL:
case PPC_INS_LVSR:
case PPC_INS_LVX:
case PPC_INS_LVXL:
NOT_IMPLEMENTED;
// String word
case PPC_INS_LSWI:
NOT_IMPLEMENTED;
// VSX Scalar
case PPC_INS_LXSDX:
// VSX Vector
case PPC_INS_LXVD2X:
case PPC_INS_LXVDSX:
case PPC_INS_LXVW4X:
NOT_IMPLEMENTED;
}
rz_return_val_if_fail(into_rt, NULL);
RzILOpEffect *res = SETG(rT, into_rt);
RzILOpEffect *update = update_ra ? SETG(rA, DUP(ea)) : EMPTY();
return SEQ2(res, update);
}
/**
* \brief Determine log_2( \p v ). \p v must be a multiple of 2.
* See: https://graphics.stanford.edu/~seander/bithacks.html#IntegerLog
*
* \param v Value to determine lg(v) for.
* \return ut32 lg(v)
*/
static inline ut32 ppc_log_2(const ut32 v) {
static const ut32 b[] = { 0xAAAAAAAA, 0xCCCCCCCC, 0xF0F0F0F0,
0xFF00FF00, 0xFFFF0000 };
register ut32 r = (v & b[0]) != 0;
r |= ((v & b[4]) != 0) << 4;
r |= ((v & b[3]) != 0) << 3;
r |= ((v & b[2]) != 0) << 2;
r |= ((v & b[1]) != 0) << 1;
return r;
}
static RzILOpEffect *store_op(RZ_BORROW csh handle, RZ_BORROW cs_insn *insn, const cs_mode mode) {
rz_return_val_if_fail(handle && insn, EMPTY());
ut32 id = insn->id;
// How to read instruction ids:
// Letter Meaning
// ST Store
// B/H/W/D/F Byte, Half Word, Word, Double Word, Float
// BR Byte reversal
// U/X Update (store EA in RA), X Form instruction (uses RB instead of immediate)
// MW/CIX Multiple word, Caching Inhibited Indexed
// V Vector
// READ
const char *rS = cs_reg_name(handle, INSOP(0).reg);
const char *rA = cs_reg_name(handle, INSOP(1).mem.base);
#if CS_NEXT_VERSION < 6
const char *rB = cs_reg_name(handle, INSOP(2).reg);
#else
const char *rB = cs_reg_name(handle, INSOP(1).mem.offset);
#endif
st64 d = INSOP(1).mem.disp; // RA = base ; D = Disposition
bool update_ra = ppc_updates_ra_with_ea(id); // Save ea in RA?
ut32 mem_acc_size = ppc_get_mem_acc_size(id);
RzILOpPure *base;
RzILOpPure *disp;
RzILOpPure *ea;
RzILOpEffect *store;
if (mem_acc_size < 0) {
NOT_IMPLEMENTED;
}
// EXEC
switch (id) {
default:
NOT_IMPLEMENTED;
case PPC_INS_DCBZ: {
ut32 r = ppc_log_2(DCACHE_LINE_SIZE);
#if CS_NEXT_VERSION >= 6
rA = cs_reg_name(handle, INSOP(0).mem.base);
rB = cs_reg_name(handle, INSOP(0).mem.offset);
base = VARG(rA);
#else
rA = cs_reg_name(handle, INSOP(0).reg);
rB = cs_reg_name(handle, INSOP(1).reg);
base = rA ? VARG(rA) : NULL;
#endif
ea = base ? ADD(base, VARG(rB)) : VARG(rB);
// Align EA
ea = LOGAND(ea, SHIFTL0(UA(-1), U8(r)));
//! DCACHE_LINE_SIZE is currently hard coded. Should be replaced by config option.
store = STOREW(ea, UN(DCACHE_LINE_SIZE * 8, 0));
break;
}
case PPC_INS_STB:
case PPC_INS_STH:
case PPC_INS_STW:
case PPC_INS_STD:
case PPC_INS_STBX:
case PPC_INS_STHX:
case PPC_INS_STWX:
case PPC_INS_STDX:
case PPC_INS_STBU:
case PPC_INS_STDU:
case PPC_INS_STHU:
case PPC_INS_STWU:
case PPC_INS_STBUX:
case PPC_INS_STHUX:
case PPC_INS_STWUX:
case PPC_INS_STDUX:
case PPC_INS_STBCIX:
case PPC_INS_STHCIX:
case PPC_INS_STWCIX:
case PPC_INS_STDCIX:
#if CS_NEXT_VERSION >= 6
base = VARG(rA);
#else
base = rA ? VARG(rA) : NULL;
#endif
if (ppc_is_x_form(id)) {
disp = VARG(rB);
} else {
disp = EXTEND(PPC_ARCH_BITS, S16(d));
}
ea = base ? ADD(base, disp) : disp;
store = STOREW(ea, CAST(mem_acc_size, IL_FALSE, VARG(rS)));
break;
case PPC_INS_STDCX:
case PPC_INS_STWCX:
// Float
case PPC_INS_STFD:
case PPC_INS_STFDU:
case PPC_INS_STFDUX:
case PPC_INS_STFDX:
case PPC_INS_STFIWX:
case PPC_INS_STFS:
case PPC_INS_STFSU:
case PPC_INS_STFSUX:
case PPC_INS_STFSX:
NOT_IMPLEMENTED;
// Byte reverse and reserved indexed
case PPC_INS_STHBRX:
case PPC_INS_STWBRX:
case PPC_INS_STDBRX:
NOT_IMPLEMENTED;
// Multiple word
case PPC_INS_STMW:
NOT_IMPLEMENTED;
// String word
case PPC_INS_STSWI:
NOT_IMPLEMENTED;
// Vector
case PPC_INS_STVEBX:
case PPC_INS_STVEHX:
case PPC_INS_STVEWX:
case PPC_INS_STVX:
case PPC_INS_STVXL:
NOT_IMPLEMENTED;
// VSX Vector
case PPC_INS_STXSDX:
case PPC_INS_STXVD2X:
case PPC_INS_STXVW4X:
NOT_IMPLEMENTED;
}
// WRITE
rz_return_val_if_fail(store, NULL);
RzILOpEffect *update = update_ra ? SETG(rA, DUP(ea)) : EMPTY();
return SEQ2(store, update);
}
/**
* NOTE: Instructions which set the 'OV' bit are not yet supported by capstone.
*/
static RzILOpEffect *add_sub_op(RZ_BORROW csh handle, RZ_BORROW cs_insn *insn, bool add, const cs_mode mode) {
rz_return_val_if_fail(handle && insn, EMPTY());
ut32 id = insn->id;
// READ
const char *rT = cs_reg_name(handle, INSOP(0).reg);
const char *rA = cs_reg_name(handle, INSOP(1).reg);
const char *rB = cs_reg_name(handle, INSOP(2).reg);
st64 sI = INSOP(2).imm;
bool set_ca = (id != PPC_INS_ADD && id != PPC_INS_ADDI && id != PPC_INS_ADDIS && id != PPC_INS_SUBF && id != PPC_INS_NEG);
bool cr0 = insn->detail->ppc.update_cr0;
RzILOpPure *a = NULL;
RzILOpPure *b = NULL;
RzILOpPure *c = NULL;
RzILOpPure *res = NULL;
// How to read instruction ids:
// Letter Meaning
// ADD/SUBF Add, Subtract from
// I/M/Z Immediate, Minus one, Zero extend,
// C/E/S Carry (sets it), Extends (adds carry it), Shift immediate
#if CS_NEXT_VERSION >= 6
// Handle Add alias
switch (insn->alias_id) {
default:
break;
case PPC_INS_ALIAS_LI: // RT = sI
return SETG(rT, EXTEND(PPC_ARCH_BITS, SN(16, sI)));
case PPC_INS_ALIAS_LIS: // RT = SI << 16
return SETG(rT, EXTEND(PPC_ARCH_BITS, APPEND(SN(16, sI), U16(0))));
}
#endif
// EXEC
switch (id) {
default:
NOT_IMPLEMENTED;
case PPC_INS_ADD:
case PPC_INS_ADDC:
case PPC_INS_SUBF:
case PPC_INS_SUBFC:
a = add ? VARG(rA) : ADD(LOGNOT(VARG(rA)), UA(1));
b = VARG(rB);
res = ADD(VARL("a"), VARL("b"));
break;
case PPC_INS_ADDE:
case PPC_INS_SUBFE:
a = add ? VARG(rA) : LOGNOT(VARG(rA));
b = VARG(rB);
c = BOOL_TO_BV(VARG("ca"), PPC_ARCH_BITS);
res = ADD(VARL("a"), ADD(VARL("b"), VARL("c")));
break;
case PPC_INS_ADDI:
case PPC_INS_ADDIC:
case PPC_INS_ADDIS:
case PPC_INS_SUBFIC:
a = add ? VARG(rA) : LOGNOT(VARG(rA));
if (id == PPC_INS_ADDIS) {
b = EXTEND(PPC_ARCH_BITS, APPEND(SN(16, sI), U16(0)));
} else if (id == PPC_INS_SUBFIC) {
b = EXTEND(PPC_ARCH_BITS, SN(16, sI));
c = UA(1);
} else {
b = EXTEND(PPC_ARCH_BITS, SN(16, sI));
}
if (c) {
res = ADD(VARL("a"), ADD(VARL("b"), VARL("c")));
break;
}
res = ADD(VARL("a"), VARL("b"));
break;
case PPC_INS_ADDME:
case PPC_INS_ADDZE:
case PPC_INS_SUBFME:
case PPC_INS_SUBFZE:
a = add ? VARG(rA) : LOGNOT(VARG(rA));
b = BOOL_TO_BV(VARG("ca"), PPC_ARCH_BITS);
if (id == PPC_INS_ADDME || id == PPC_INS_SUBFME) {
c = UA(-1);
res = ADD(VARL("a"), ADD(VARL("b"), VARL("c")));
} else {
res = ADD(VARL("a"), VARL("b"));
}
break;
case PPC_INS_NEG:
a = LOGNOT(VARG(rA));
b = UA(1);
res = ADD(VARL("a"), VARL("b"));
}
if (!(a && b)) {
rz_warn_if_reached();
return NULL;
}
RzILOpEffect *set_carry = set_ca ? ppc_set_carry_add_sub(VARL("a"), VARL("b"), c ? VARL("c") : NULL, mode) : EMPTY();
// Instructions which set the OV bit are not supported in capstone.
// See: https://github.com/capstone-engine/capstone/issues/944
RzILOpPure *zero = UA(0);
RzILOpEffect *overflow = EMPTY();
RzILOpEffect *update_cr0 = cr0 ? ppc_cmp_set_cr(res, zero, true, "cr0", mode) : EMPTY();
RzILOpEffect *set = SETG(rT, res);
RzILOpEffect *set_ops = SEQ3(SETL("a", a), SETL("b", b), c ? SETL("c", c) : EMPTY());
rz_il_op_pure_free(zero);
return SEQ5(set_ops, set, set_carry, overflow, update_cr0);
}
static bool is_logical_compare(ut32 id) {
switch (id) {
case PPC_INS_CMPW:
case PPC_INS_CMPD:
case PPC_INS_CMPWI:
case PPC_INS_CMPDI:
return true;
default:
return false;
}
}
static RzILOpEffect *compare_op(RZ_BORROW csh handle, RZ_BORROW cs_insn *insn, const cs_mode mode) {
rz_return_val_if_fail(handle && insn, EMPTY());
ut32 id = insn->id;
const char *crX = NULL;
const char *rA = NULL;
const char *rB = NULL;
st64 imm = 0;
RzILOpPure *left = NULL;
RzILOpPure *right = NULL;
bool signed_cmp = false;
#if CS_API_MAJOR == 5 && CS_NEXT_VERSION < 6
// weird bug on cmp/cmpl in capstone v5
if (id == PPC_INS_CMP) {
if (!strcmp(insn->mnemonic, "cmpw")) {
id = PPC_INS_CMPW;
} else if (!strcmp(insn->mnemonic, "cmpd")) {
id = PPC_INS_CMPD;
} else {
rz_warn_if_reached();
}
} else if (id == PPC_INS_CMPL) {
if (!strcmp(insn->mnemonic, "cmplw")) {
id = PPC_INS_CMPLW;
} else if (!strcmp(insn->mnemonic, "cmpld")) {
id = PPC_INS_CMPLD;
} else {
rz_warn_if_reached();
}
} else if (id == PPC_INS_CMPI) {
if (!strcmp(insn->mnemonic, "cmpwi")) {
id = PPC_INS_CMPWI;
} else if (!strcmp(insn->mnemonic, "cmpdi")) {
id = PPC_INS_CMPDI;
} else {
rz_warn_if_reached();
}
} else if (id == PPC_INS_CMPLI) {
if (!strcmp(insn->mnemonic, "cmplwi")) {
id = PPC_INS_CMPLWI;
} else if (!strcmp(insn->mnemonic, "cmpldi")) {
id = PPC_INS_CMPLDI;
} else {
rz_warn_if_reached();
}
}
#endif
// READ
#if CS_NEXT_VERSION >= 6
// Uses REAL instruction operand set.
crX = cs_reg_name(handle, INSOP(0).reg);
rA = cs_reg_name(handle, INSOP(1).reg);
rB = cs_reg_name(handle, INSOP(2).reg);
imm = INSOP(2).imm;
#else
// cr0 reg is not explicitly stored in the operands list.
if (OP_CNT == 2) {
crX = "cr0";
rA = cs_reg_name(handle, INSOP(0).reg);
rB = cs_reg_name(handle, INSOP(1).reg);
imm = INSOP(1).imm;
} else {
crX = cs_reg_name(handle, INSOP(0).reg);
rA = cs_reg_name(handle, INSOP(1).reg);
rB = cs_reg_name(handle, INSOP(2).reg);
imm = INSOP(2).imm;
}
#endif
// How to read instruction ids:
// Letter Meaning
// CMP Compare
// B/H/W/D Byte, Half Word, Word, Double Word
// I/L Immediate, Logical (unsigned compare)
// EXEC
// Logical <=> unsigned comparisons ; Not logical <=> signed comparison.
signed_cmp = is_logical_compare(id);
// Left operand is always RA
if (id == PPC_INS_CMPW || id == PPC_INS_CMPWI || id == PPC_INS_CMPLW || id == PPC_INS_CMPLWI) {
left = EXTS(CAST(32, IL_FALSE, VARG(rA)));
} else {
left = VARG(rA);
}
// Right operand differs between instructions.
switch (id) {
default:
NOT_IMPLEMENTED;
case PPC_INS_CMPW:
case PPC_INS_CMPLW:
right = EXTS(CAST(32, IL_FALSE, VARG(rB)));
break;
case PPC_INS_CMPD:
case PPC_INS_CMPLD:
right = VARG(rB);
break;
case PPC_INS_CMPWI:
case PPC_INS_CMPLWI:
right = (id == PPC_INS_CMPWI) ? EXTS(S16(imm)) : EXTZ(U16(imm));
break;
case PPC_INS_CMPDI:
case PPC_INS_CMPLDI:
right = (id == PPC_INS_CMPDI) ? EXTEND(64, S16(imm)) : APPEND(U48(0), U16(imm));
break;
}
RzILOpEffect *ret = ppc_cmp_set_cr(left, right, signed_cmp, crX, mode);
rz_il_op_pure_free(left);
rz_il_op_pure_free(right);
return ret;
}
#if CS_API_MAJOR == 5 && CS_NEXT_VERSION < 6
// bug on xori in capstone v5
static bool is_xnop(cs_insn *insn) {
return insn->id == PPC_INS_XNOP &&
INSOP(0).reg == 0 &&
INSOP(1).reg == 0 &&
INSOP(2).imm == 0;
}
#endif
static RzILOpEffect *bitwise_op(RZ_BORROW csh handle, RZ_BORROW cs_insn *insn, const cs_mode mode) {
rz_return_val_if_fail(handle && insn, EMPTY());
#if CS_API_MAJOR == 5 && CS_NEXT_VERSION < 6
if (is_xnop(insn)) {
return NOP();
}
#endif
ut32 id = insn->id;
// READ
const char *rA = cs_reg_name(handle, INSOP(0).reg);
const char *rS = cs_reg_name(handle, INSOP(1).reg);
const char *rB = cs_reg_name(handle, INSOP(2).reg);
st64 uI = INSOP(2).imm;
bool cr0 = insn->detail->ppc.update_cr0;
RzILOpPure *op0;
RzILOpPure *op1;
RzILOpPure *res;
// How to read instruction ids:
// Letter Meaning
// AND/OR/... AND, OR etc.
// B/H/W/D Byte, Half Word, Word, Double Word
// I/C/S Immediate, Complement, Shifted
// EXEC
switch (id) {
default:
NOT_IMPLEMENTED;
case PPC_INS_AND:
case PPC_INS_ANDC:
case PPC_INS_ANDIS:
case PPC_INS_ANDI:
op0 = VARG(rS);
if (id == PPC_INS_AND || id == PPC_INS_ANDC) {
op1 = (id == PPC_INS_AND) ? VARG(rB) : LOGNOT(VARG(rB));
} else {
op1 = (id == PPC_INS_ANDI) ? EXTZ(U16(uI)) : EXTZ(APPEND(U16(uI), U16(0)));
}
res = LOGAND(op0, op1);
break;
case PPC_INS_OR:
case PPC_INS_ORC:
case PPC_INS_ORI:
case PPC_INS_ORIS:
op0 = VARG(rS);
if (id == PPC_INS_OR || id == PPC_INS_ORC) {
op1 = (id == PPC_INS_OR) ? VARG(rB) : LOGNOT(VARG(rB));
} else {
op1 = (id == PPC_INS_ORI) ? EXTZ(U16(uI)) : EXTZ(APPEND(U16(uI), U16(0)));
}
res = LOGOR(op0, op1);
break;
#if CS_NEXT_VERSION < 6
// bug on xori in capstone v5
case PPC_INS_XNOP:
op0 = VARG(rS);
op1 = EXTZ(U16(uI));
res = LOGXOR(op0, op1);
break;
#endif
case PPC_INS_XOR:
case PPC_INS_XORI:
case PPC_INS_XORIS:
op0 = VARG(rS);
if (id == PPC_INS_XOR) {
op1 = VARG(rB);
} else {
op1 = (id == PPC_INS_XORI) ? EXTZ(U16(uI)) : EXTZ(APPEND(U16(uI), U16(0)));
}
res = LOGXOR(op0, op1);
break;
case PPC_INS_NAND:
case PPC_INS_NOR:
op0 = VARG(rS);
op1 = VARG(rB);
res = LOGNOT(
(id == PPC_INS_NAND) ? LOGAND(op0, op1) : LOGOR(op0, op1));
break;
// Compare bytes
case PPC_INS_CMPB: {
// do n = 0 to (64BIT_CPU ? 7 : 3)
// if RS[8×n:8×n+7] = RB[8×n:8×n+7] then
// RA[8×n:8×n+7] ← 0b1111_1111
// else
// RA[8×n:8×n+7] ← 0b0000_0000
ut8 bits = PPC_ARCH_BITS;
RzILOpEffect *loop, *init_n, *init_bitmask, *ones_rA;
RzILOpPure *bitmask_n_n7 = BIT_MASK(bits, MUL(U8(8), VARL("n")), ADD(MUL(U8(8), VARL("n")), U8(7)));
// RS[8×n:8×n+7]
RzILOpPure *rS_8n_8n7 = LOGAND(VARL("bitmask_n_n7"), VARG(rS));
// RB[8×n:8×n+7]
RzILOpPure *rB_8n_8n7 = LOGAND(VARL("bitmask_n_n7"), VARG(rB));
RzILOpPure *b_cond = EQ(rS_8n_8n7, rB_8n_8n7);
init_n = SETL("n", U8(0));
init_bitmask = SETL("bitmask_n_n7", bitmask_n_n7);
// RA[8×n:8×n+7] ← 0b1111_1111
ones_rA = SETL("res", LOGOR(VARL("res"), LOGAND(UA(-1), VARL("bitmask_n_n7"))));
ut8 m = IN_64BIT_MODE ? 8 : 4;
loop = REPEAT(ULT(VARL("n"), U8(m)),
SEQ3(BRANCH(b_cond,
ones_rA, EMPTY()),
SETL("n", ADD(VARL("n"), U8(1))),
SETL("bitmask_n_n7", DUP(bitmask_n_n7))));
return SEQ5(SETL("res", UA(0)), init_n, init_bitmask, loop, SETG(rA, VARL("res")));
}
case PPC_INS_EQV:
op0 = VARG(rS);
op1 = VARG(rB);
res = LOGXOR(op0, LOGNOT(op1));
break;
// Extend
case PPC_INS_EXTSB:
res = EXTS(UNSIGNED(PPC_BYTE, VARG(rS)));
break;
case PPC_INS_EXTSH:
res = EXTS(UNSIGNED(PPC_HWORD, VARG(rS)));
break;
case PPC_INS_EXTSW:
res = EXTS(UNSIGNED(PPC_WORD, VARG(rS)));
break;
// Count leading zeros
case PPC_INS_CNTLZD:
case PPC_INS_CNTLZW: {
RzILOpEffect *n, *set_m;
if ((id == PPC_INS_CNTLZW) && IN_64BIT_MODE) {
n = SETL("n", UA(32));
set_m = SETL("m", UA(32));
} else {
n = SETL("n", UA(0));
set_m = SETL("m", UA(0));
}
RzILOpPure *cond = AND(ULT(VARL("n"), UA(PPC_ARCH_BITS)), INV(BIT_IS_SET(VARG(rS), PPC_ARCH_BITS, VARL("n"))));
RzILOpEffect *cloop = REPEAT(cond, SETL("n", ADD(VARL("n"), UA(1))));
return SEQ4(set_m, n, cloop, SETG(rA, SUB(VARL("n"), VARL("m"))));
}
// Population count
case PPC_INS_POPCNTD:
case PPC_INS_POPCNTW:
NOT_IMPLEMENTED;
}
// WRITE
RzILOpPure *zero = UA(0);
RzILOpPure *old_res = VARL("res");
RzILOpEffect *update_cr0 = cr0 ? ppc_cmp_set_cr(old_res, zero, true, "cr0", mode) : EMPTY();
RzILOpEffect *set = SETG(rA, old_res);
rz_il_op_pure_free(zero);
return SEQ3(SETL("res", res), set, update_cr0);
}
static RzILOpEffect *branch_op(RZ_BORROW csh handle, RZ_BORROW cs_insn *insn, const cs_mode mode) {
rz_return_val_if_fail(handle && insn, EMPTY());
#if CS_NEXT_VERSION >= 6
bool is_conditional = ppc_insn_is_conditional(insn);
#else
ut32 id = insn->id;
bool is_conditional = ppc_is_conditional(id);
#endif
RzILOpEffect *set_cia; // Current instruction address
RzILOpEffect *set_nia; // Next instruction address
RzILOpEffect *set_lr; // Set Link Register
RzILOpEffect *decr_ctr; // Effect to decrement CTR
// How to read instruction ids:
// Letter Meaning
// B Branch
// C/D Conditional, Decrement CTR
// Z/NZ/T/F Branch if CTR is: zero/not zero, branch if CR=1 (true)/CR=0 (false)
// L/A/LR/CTR/TAR Set LR, branch to absolute address, branch to LR, branch to CTR, branch to target address register
// Determine next instruction address.
if (!is_conditional) {
set_nia = SETL("NIA", ppc_get_branch_ta(insn, mode));
} else {
set_nia = SETL("NIA", ITE(ppc_get_branch_cond(handle, insn, mode), ppc_get_branch_ta(insn, mode), ADD(VARL("CIA"), UA(4))));
}
set_cia = SETL("CIA", UA(insn->address));
#if CS_NEXT_VERSION >= 6
set_lr = ppc_insn_sets_lr(insn) ? SETG("lr", ADD(VARL("CIA"), UA(4))) : EMPTY();
#else
set_lr = ppc_sets_lr(id) ? SETG("lr", ADD(VARL("CIA"), UA(4))) : EMPTY();
#endif
decr_ctr = ppc_decrements_ctr(insn, mode) ? SETG("ctr", SUB(VARG("ctr"), UA(1))) : EMPTY();
return SEQ5(set_cia, decr_ctr, set_lr, set_nia, JMP(VARL("NIA")));
}
/**
* NOTE: Instructions which set the 'OV' bit are not yet supported by Capstone.
*/
static RzILOpEffect *div_mul_op(RZ_BORROW csh handle, RZ_BORROW cs_insn *insn, const cs_mode mode) {
rz_return_val_if_fail(handle && insn, EMPTY());
ut32 id = insn->id;
// READ
const char *rT = cs_reg_name(handle, INSOP(0).reg);
const char *rA = cs_reg_name(handle, INSOP(1).reg);
const char *rB = cs_reg_name(handle, INSOP(2).reg);
st64 sI = INSOP(2).imm;
bool set_cr0 = insn->detail->ppc.update_cr0;
RzILOpPure *op0;
RzILOpPure *op1;
RzILOpPure *prod;
// How to read instruction IDs:
// Letter Meaning
// MUL/DIV Multiply, Divide
// W/D/LW/HW/LI Word, double word, low word, high word, Low immediate
// O/U/E Overflow (not supported), Unsigned, Extended
if (id == PPC_INS_MULLI) {
op0 = VARG(rA);
op1 = S16(sI);
} else {
op0 = VARG(rA);
op1 = VARG(rB);
}
if (id == PPC_INS_MULHW || id == PPC_INS_MULLW || id == PPC_INS_DIVW) {
op0 = UNSIGNED(32, op0);
op1 = UNSIGNED(32, op1);
} else if (id == PPC_INS_DIVWU || id == PPC_INS_MULHWU) {
op0 = SIGNED(32, op0);
op1 = SIGNED(32, op1);
}
if (ppc_is_mul_div_u(id) && ppc_is_mul_div_d(id, mode)) {
op0 = UNSIGNED(128, op0);
op1 = UNSIGNED(128, op1);
} else if (ppc_is_mul_div_u(id) && !ppc_is_mul_div_d(id, mode)) {
op0 = UNSIGNED(64, op0);
op1 = UNSIGNED(64, op1);
} else if (!ppc_is_mul_div_u(id) && ppc_is_mul_div_d(id, mode)) {
op0 = SIGNED(128, op0);
op1 = SIGNED(128, op1);
} else {
op0 = SIGNED(64, op0);
op1 = SIGNED(64, op1);
}
switch (id) {
default:
NOT_IMPLEMENTED;
case PPC_INS_MULHD:
case PPC_INS_MULHDU:
case PPC_INS_MULHW:
case PPC_INS_MULHWU:
case PPC_INS_MULLI:
case PPC_INS_MULLD:
case PPC_INS_MULLW:
prod = MUL(op0, op1);
break;
case PPC_INS_DIVWU:
case PPC_INS_DIVDU:
prod = DIV(op0, op1);
break;
case PPC_INS_DIVW:
case PPC_INS_DIVD:
prod = SDIV(op0, op1);
break;
}
// Get high word/double word of product.
if (id == PPC_INS_MULHW || id == PPC_INS_MULHWU) {
prod = SHIFTR0(prod, U8(32));
} else if (id == PPC_INS_MULHD || id == PPC_INS_MULHDU) {
prod = SHIFTR0(prod, U8(64));
}
RzILOpEffect *cr0 = set_cr0 ? ppc_cmp_set_cr(VARG(rT), UA(0), true, "cr0", mode) : EMPTY();
return SEQ2(SETG(rT, CAST(PPC_ARCH_BITS, IL_FALSE, prod)), cr0);
}
static RzILOpEffect *move_from_to_spr_op(RZ_BORROW csh handle, RZ_BORROW cs_insn *insn, const cs_mode mode) {
rz_return_val_if_fail(handle && insn, EMPTY());
ut32 id = insn->id;
#if CS_NEXT_VERSION >= 6
const char *rS;
const char *rT;
if (insn->id == PPC_INS_MFSPR || insn->id == PPC_INS_MTSPR) {
rT = cs_reg_name(handle, INSOP(0).reg);
rS = cs_reg_name(handle, INSOP(1).reg);
} else {
rS = cs_reg_name(handle, INSOP(0).reg);
rT = cs_reg_name(handle, INSOP(0).reg);
}
#else
const char *rS = cs_reg_name(handle, INSOP(0).reg);
const char *rT = cs_reg_name(handle, INSOP(0).reg);
#endif
const char *spr_name = "";
// Some registers need to assemble the value before it is read or written (e.g. XER with all its bits).
// Leave it NULL if the value of the SPR or RS should be used.
RzILOpEffect *set_val = NULL;
// Size of the value written to the target register (SPR or RT)
ut32 size = PPC_ARCH_BITS;
switch (id) {
default:
NOT_IMPLEMENTED;
case PPC_INS_MTDCR:
case PPC_INS_MFDCR:
NOT_IMPLEMENTED;
case PPC_INS_MFVSCR:
case PPC_INS_MTVSCR:
NOT_IMPLEMENTED;
break;
case PPC_INS_MFMSR:
spr_name = "msr";
break;
case PPC_INS_MTMSR:
case PPC_INS_MTMSRD:
NOT_IMPLEMENTED;
case PPC_INS_MTCRF: {
ut32 mask = 0xffffffff;
if (id == PPC_INS_MTCRF) {
ut8 fxm = INSOP(0).imm;
rS = cs_reg_name(handle, INSOP(1).reg);
mask = ppc_fmx_to_mask(fxm);
}
RzILOpEffect *set_cr = SETL("cr", LOGAND(UNSIGNED(32, VARG(rS)), U32(mask)));
return SEQ2(set_cr, ppc_sync_crx_cr(false, mask));
}
case PPC_INS_MFCR:
return SEQ2(ppc_sync_crx_cr(true, 0x0), SETG(rT, EXTZ(VARL("cr"))));
// Note: We do not update CR after the OCRF operations.
case PPC_INS_MTOCRF:
case PPC_INS_MFOCRF:
#if CS_NEXT_VERSION >= 6
rS = cs_reg_name(handle, INSOP(1).reg);
rT = cs_reg_name(handle, INSOP(0).reg);
spr_name = rT;
size = 4;
set_val = SETL("val", VARG(rS));
#else
NOT_IMPLEMENTED;
#endif
break;
// IBM POWER specific Segment Register
case PPC_INS_MTSRIN:
case PPC_INS_MFSRIN:
// Indirect set.
case PPC_INS_MFSR:
case PPC_INS_MTSR:
// Direct set.
NOT_IMPLEMENTED;
case PPC_INS_MFLR:
case PPC_INS_MTLR:
spr_name = "lr";
break;
case PPC_INS_MFCTR:
case PPC_INS_MTCTR:
spr_name = "ctr";
break;
#if CS_NEXT_VERSION < 6
case PPC_INS_MFXER:
case PPC_INS_MTXER:
if (id == PPC_INS_MTXER) {
return ppc_set_xer(VARG(rS), mode);
}
spr_name = "xer";
set_val = SETL("val", ppc_get_xer(mode));
break;
#endif
case PPC_INS_MFSPR:
case PPC_INS_MTSPR: {
#if CS_NEXT_VERSION >= 6
if (insn->alias_id == PPC_INS_ALIAS_MTXER) {
return ppc_set_xer(VARG(rS), mode);
} else if (insn->alias_id == PPC_INS_ALIAS_MFXER) {
set_val = SETL("val", ppc_get_xer(mode));
break;
}
#endif
ut32 spr = INSOP(1).imm;
switch (spr) {
default:
// TODO Invoke different exception handlers. But they are not implemented yet.
NOT_IMPLEMENTED;
case 808:
case 809:
case 810:
case 811:
// Reserved. Treated as No-ops
RZ_LOG_WARN("Reserved SPR instruction encountered at 0x%" PFMT64x "\n", (ut64)insn->address);
return EMPTY();
case 1:
case 3:
case 8:
case 9:
case 13:
case 256:
case 769:
case 770:
case 771:
case 772:
case 773:
case 774:
case 775:
case 776:
case 779:
case 800:
case 801:
case 802:
case 803:
case 804:
case 805:
case 806:
case 898:
case 815:
case 896:
NOT_IMPLEMENTED;
}
break;
}
// WRITE/READ only
case PPC_INS_MTFSB0:
case PPC_INS_MTFSB1:
case PPC_INS_MTFSF:
case PPC_INS_MFFS:
case PPC_INS_MFTB:
#if CS_NEXT_VERSION < 6
case PPC_INS_MFRTCU:
case PPC_INS_MFRTCL:
NOT_IMPLEMENTED;
// Not yet handled
case PPC_INS_MFBR0:
case PPC_INS_MFBR1:
case PPC_INS_MFBR2:
case PPC_INS_MFBR3:
case PPC_INS_MFBR4:
case PPC_INS_MFBR5:
case PPC_INS_MFBR6:
case PPC_INS_MFBR7:
case PPC_INS_MTBR0:
case PPC_INS_MTBR1:
case PPC_INS_MTBR2:
case PPC_INS_MTBR3:
case PPC_INS_MTBR4:
case PPC_INS_MTBR5:
case PPC_INS_MTBR6:
case PPC_INS_MTBR7:
NOT_IMPLEMENTED;
case PPC_INS_MFDSCR:
case PPC_INS_MTDSCR:
NOT_IMPLEMENTED;
case PPC_INS_MFDSISR:
case PPC_INS_MFDAR:
case PPC_INS_MFSRR2:
case PPC_INS_MFSRR3:
case PPC_INS_MFCFAR:
case PPC_INS_MFAMR:
case PPC_INS_MFPID:
case PPC_INS_MFTBLO:
case PPC_INS_MFTBHI:
#if CS_NEXT_VERSION >= 6
case PPC_INS_MFDBATU0:
case PPC_INS_MFDBATL0:
case PPC_INS_MFDBATU1:
case PPC_INS_MFDBATL1:
case PPC_INS_MFDBATU2:
case PPC_INS_MFDBATL2:
case PPC_INS_MFDBATU3:
case PPC_INS_MFDBATL3:
case PPC_INS_MFIBATU0:
case PPC_INS_MFIBATL0:
case PPC_INS_MFIBATU1:
case PPC_INS_MFIBATL1:
case PPC_INS_MFIBATU2:
case PPC_INS_MFIBATL2:
case PPC_INS_MFIBATU3:
case PPC_INS_MFIBATL3:
#endif
case PPC_INS_MFDBATU:
case PPC_INS_MFDBATL:
case PPC_INS_MFIBATU:
case PPC_INS_MFIBATL:
case PPC_INS_MFDCCR:
case PPC_INS_MFICCR:
case PPC_INS_MFDEAR:
case PPC_INS_MFESR:
case PPC_INS_MFSPEFSCR:
case PPC_INS_MFTCR:
case PPC_INS_MFASR:
case PPC_INS_MFPVR:
case PPC_INS_MFTBU:
case PPC_INS_MTDSISR:
case PPC_INS_MTDAR:
case PPC_INS_MTSRR2:
case PPC_INS_MTSRR3:
case PPC_INS_MTCFAR:
case PPC_INS_MTAMR:
case PPC_INS_MTPID:
case PPC_INS_MTTBL:
case PPC_INS_MTTBU:
case PPC_INS_MTTBLO:
case PPC_INS_MTTBHI:
#if CS_NEXT_VERSION >= 6
case PPC_INS_MTDBATU0:
case PPC_INS_MTDBATL0:
case PPC_INS_MTDBATU1:
case PPC_INS_MTDBATL1:
case PPC_INS_MTDBATU2:
case PPC_INS_MTDBATL2:
case PPC_INS_MTDBATU3:
case PPC_INS_MTDBATL3:
case PPC_INS_MTIBATU0:
case PPC_INS_MTIBATL0:
case PPC_INS_MTIBATU1:
case PPC_INS_MTIBATL1:
case PPC_INS_MTIBATU2:
case PPC_INS_MTIBATL2:
case PPC_INS_MTIBATU3:
case PPC_INS_MTIBATL3:
#endif
case PPC_INS_MTDBATU:
case PPC_INS_MTDBATL:
case PPC_INS_MTIBATU:
case PPC_INS_MTIBATL:
case PPC_INS_MTDCCR:
case PPC_INS_MTICCR:
case PPC_INS_MTDEAR:
case PPC_INS_MTESR:
case PPC_INS_MTSPEFSCR:
case PPC_INS_MTTCR:
#endif
NOT_IMPLEMENTED;
}
if (set_val) {
RzILOpEffect *write_spr = ppc_moves_to_spr(id) ? SETG(spr_name, UNSIGNED(size, VARL("val"))) : SETG(rT, EXTZ(VARL("val")));
return SEQ2(set_val, write_spr);
}
return ppc_moves_to_spr(id) ? SETG(spr_name, UNSIGNED(size, VARG(rS))) : SETG(rT, EXTZ(VARG(spr_name)));
}
/**
*
* NOTE: Shift instructions are not implemented as in the programmer reference manual.
* The manual uses rotate, here we simply use SHIFT ops.
*/
static RzILOpEffect *shift_and_rotate(RZ_BORROW csh handle, RZ_BORROW cs_insn *insn, const cs_mode mode) {
rz_return_val_if_fail(handle && insn, EMPTY());
ut32 id = insn->id;
bool sets_cr0 = insn->detail->ppc.update_cr0;
// READ
const char *rA = cs_reg_name(handle, INSOP(0).reg);
const char *rS = cs_reg_name(handle, INSOP(1).reg);
const char *rB = cs_reg_name(handle, INSOP(2).reg);
ut64 sH = INSOP(2).imm;
ut64 mB = INSOP(3).imm;
ut64 mE = INSOP(4).imm;
ut8 b, e; // Mask begin/end
bool all_bits_set = false;
RzILOpPure *n; // Shift/rotate steps
RzILOpPure *r; // Rotate result
RzILOpPure *into_rA = NULL;
RzILOpPure *ca_val; // Arithmetic shift instructions set the ca field.
RzILOpEffect *set_mask = NULL, *set_ca = NULL, *update_cr0 = NULL;
// How to read instruction ids:
// Letter Meaning
// R/S Rotate, Shift
// L/R Left, Right
// W/D Word, Double Word
// A/I Algebraic, Immediate
// C/CL/CR Clear, clear left/right
// M/NM/MI Mask, AND with mask, mask insert
// FIXME: With update to auto-sync ppc arch
#if CS_API_MAJOR == 5 && CS_API_MINOR == 0 && CS_NEXT_VERSION < 6
// weird bug on capstone v5.0
if (id == PPC_INS_CLRLDI && !strcmp(insn->mnemonic, "rldicl")) {
id = PPC_INS_RLDICL;
} else if (id == PPC_INS_CLRLWI && !strcmp(insn->mnemonic, "rlwinm")) {
id = PPC_INS_RLWINM;
}
#endif
#if CS_NEXT_VERSION >= 6
if (insn->alias_id == PPC_INS_ALIAS_SLWI) {
id = PPC_INS_SLWI;
} else if (insn->alias_id == PPC_INS_ALIAS_SRWI) {
id = PPC_INS_SRWI;
} else if (insn->alias_id == PPC_INS_ALIAS_SLDI) {
id = PPC_INS_SLDI;
}
#endif
switch (id) {
default:
NOT_IMPLEMENTED;
#if CS_NEXT_VERSION < 6
case PPC_INS_ROTLW:
case PPC_INS_ROTLWI:
#endif
case PPC_INS_RLWIMI:
case PPC_INS_RLWINM:
case PPC_INS_RLWNM:
#if CS_NEXT_VERSION >= 6
if (insn->alias_id == PPC_INS_ALIAS_CLRLWI ||
insn->alias_id == PPC_INS_ALIAS_CLRLWI_) {
break; // Handle down below
}
if (id == PPC_INS_RLWNM) {
#else
if (id == PPC_INS_RLWNM || id == PPC_INS_ROTLW) {
#endif
n = CAST(6, IL_FALSE, LOGAND(VARG(rB), UA(0x1f)));
} else {
n = U8(sH);
}
r = ROTL32(UNSIGNED(32, VARG(rS)), n);
#if CS_NEXT_VERSION >= 6
b = mB + 32;
e = mE + 32;
#else
if (id == PPC_INS_ROTLW || id == PPC_INS_ROTLWI) {
b = 32; // mb: 0 + 32
e = 63; // me: 31 + 32
} else {
b = mB + 32;
e = mE + 32;
}
#endif
// Mask has all bits set.
all_bits_set = (((b - 1) & 0x3f) == e);
set_mask = all_bits_set ? NULL : SET_MASK(U8(b), U8(e));
into_rA = all_bits_set ? r : LOGAND(r, VARL("mask"));
if (id == PPC_INS_RLWIMI && !all_bits_set) {
into_rA = LOGOR(into_rA, LOGAND(VARG(rA), LOGNOT(VARL("mask"))));
}
break;
#if CS_NEXT_VERSION < 6
case PPC_INS_ROTLD:
case PPC_INS_ROTLDI:
#endif
case PPC_INS_RLDCL:
case PPC_INS_RLDCR:
case PPC_INS_RLDIC:
case PPC_INS_RLDICL:
case PPC_INS_RLDICR:
case PPC_INS_RLDIMI:
#if CS_NEXT_VERSION >= 6
if (insn->alias_id == PPC_INS_ALIAS_CLRLDI ||
insn->alias_id == PPC_INS_ALIAS_CLRLDI_) {
break; // Handle below
}
if (id == PPC_INS_RLDCR || id == PPC_INS_RLDCL) {
#else
if (id == PPC_INS_RLDCR || id == PPC_INS_RLDCL || id == PPC_INS_ROTLD) {
#endif
// For these instruction ME is the third operand, not MB.
mE = INSOP(3).imm;
n = UNSIGNED(8, VARG(rB));
} else if (id == PPC_INS_RLDICR) {
mE = INSOP(3).imm;
n = U8(sH);
} else {
n = U8(sH);
}
n = LOGAND(U8(0x3f), n);
r = ROTL64(VARG(rS), n);
#if CS_NEXT_VERSION >= 6
if (id == PPC_INS_RLDICR || id == PPC_INS_RLDCR) {
b = 0;
e = mE;
} else {
b = mB;
if (id == PPC_INS_RLDCL || id == PPC_INS_RLDICL) {
e = 63;
} else if (id == PPC_INS_RLDIMI) {
e = (63 - sH) & 0x3f;
} else {
e = sH;
}
}
#else
if (id == PPC_INS_RLDICR || id == PPC_INS_RLDCR || id == PPC_INS_ROTLDI || id == PPC_INS_ROTLD) {
b = 0;
if (id == PPC_INS_ROTLDI || id == PPC_INS_ROTLD) {
e = 63;
} else {
e = mE;
}
} else {
b = mB;
if (id == PPC_INS_RLDCL || id == PPC_INS_RLDICL) {
e = 63;
} else if (id == PPC_INS_RLDIMI) {
e = (63 - sH) & 0x3f;
} else {
e = sH;
}
}
#endif
all_bits_set = (((b - 1) & 0x3f) == e);
set_mask = all_bits_set ? NULL : SET_MASK(U8(b), U8(e));
into_rA = all_bits_set ? r : LOGAND(r, VARL("mask"));
if (id == PPC_INS_RLDIMI && !all_bits_set) {
into_rA = LOGOR(into_rA, LOGAND(VARG(rA), LOGNOT(VARL("mask"))));
}
break;
case PPC_INS_SLDI:
#if CS_NEXT_VERSION < 6
// Currently broken in rizins capstone version.
// Immediate is not in instruction.
NOT_IMPLEMENTED;
#endif
case PPC_INS_SLD:
case PPC_INS_SRD:
case PPC_INS_SLWI:
case PPC_INS_SRWI:
if (id == PPC_INS_SLD || id == PPC_INS_SRD) {
n = VARG(rB);
} else {
n = UA(sH);
}
n = LOGAND(UA(0x3f), n);
if (id == PPC_INS_SRD) {
into_rA = SHIFTR0(VARG(rS), n);
} else if (id == PPC_INS_SRWI) {
into_rA = SHIFTR0(LOGAND(VARG(rS), UA(0xffffffff)), n);
} else {
into_rA = SHIFTL0(VARG(rS), n);
}
if (id == PPC_INS_SLWI || id == PPC_INS_SRWI) {
into_rA = LOGAND(into_rA, UA(0xffffffff)); // Clear high 32bits
}
break;
case PPC_INS_SRAD:
case PPC_INS_SRADI:
if (id == PPC_INS_SRAD) {
n = CAST(6, IL_FALSE, LOGAND(VARG(rB), UA(0x3f)));
} else {
n = U8(sH);
}
into_rA = SHIFTRA(VARG(rS), n);
// Set ca to 1 if RS is negative and 1s were shifted out.
ca_val = ITE(AND(MSB(VARG(rS)),
NON_ZERO(MOD(VARG(rS), EXTZ(SHIFTL0(UA(1), DUP(n)))))), // (RS % (1 << n)) != 0
IL_TRUE,
IL_FALSE);
set_ca = SETG("ca", ca_val);
break;
case PPC_INS_SLW:
case PPC_INS_SRW:
into_rA = (id == PPC_INS_SLW) ? SHIFTL0(UNSIGNED(32, VARG(rS)), VARG(rB)) : SHIFTR0(UNSIGNED(32, VARG(rS)), VARG(rB));
if (IN_64BIT_MODE) {
into_rA = APPEND(U32(0), into_rA);
}
break;
case PPC_INS_SRAW:
case PPC_INS_SRAWI:
n = (id == PPC_INS_SRAW) ? CAST(6, IL_FALSE, LOGAND(VARG(rB), UA(0x3f))) : U8(sH);
into_rA = EXTS(SHIFTRA(UNSIGNED(32, VARG(rS)), n));
// Set ca to 1 if RS is negative and 1s were shifted out.
ca_val = ITE(AND(MSB(SIGNED(32, VARG(rS))), // (RS < 0) &&
NON_ZERO(MOD(UNSIGNED(32, VARG(rS)), UNSIGNED(32, SHIFTL0(UA(1), DUP(n)))))), // (RS % (1 << n)) != 0
IL_TRUE,
IL_FALSE);
set_ca = SETG("ca", ca_val);
break;
#if CS_NEXT_VERSION < 6
case PPC_INS_CLRLDI:
case PPC_INS_CLRLWI:
r = VARG(rS);
b = (id == PPC_INS_CLRLWI) ? INSOP(2).imm + 32 : INSOP(2).imm;
e = 63;
all_bits_set = (((b - 1) & 0x3f) == e);
set_mask = all_bits_set ? NULL : SET_MASK(U8(b), U8(e));
into_rA = all_bits_set ? r : LOGAND(r, VARL("mask"));
}
#else
}
if (insn->alias_id == PPC_INS_ALIAS_CLRLDI ||
insn->alias_id == PPC_INS_ALIAS_CLRLWI ||
insn->alias_id == PPC_INS_ALIAS_CLRLDI_ ||
insn->alias_id == PPC_INS_ALIAS_CLRLWI_) {
r = VARG(rS);
b = (insn->alias_id == PPC_INS_ALIAS_CLRLWI) ? INSOP(3).imm + 32 : INSOP(3).imm;
e = 63;
all_bits_set = (((b - 1) & 0x3f) == e);
set_mask = all_bits_set ? NULL : SET_MASK(U8(b), U8(e));
into_rA = all_bits_set ? r : LOGAND(r, VARL("mask"));
}
#endif
RzILOpPure *zero = UA(0);
RzILOpPure *old_res = VARL("result");
update_cr0 = sets_cr0 ? ppc_cmp_set_cr(old_res, zero, true, "cr0", mode) : EMPTY();
set_mask = set_mask ? set_mask : EMPTY();
set_ca = set_ca ? set_ca : EMPTY();
rz_il_op_pure_free(zero);
return SEQ5(set_mask, set_ca, SETL("result", into_rA), SETG(rA, old_res), update_cr0);
}
static RzILOpEffect *sys(RZ_BORROW csh handle, RZ_BORROW cs_insn *insn, const cs_mode mode) {
rz_return_val_if_fail(handle && insn, EMPTY());
ut32 id = insn->id;
switch (id) {
default:
NOT_IMPLEMENTED;
case PPC_INS_SC:
case PPC_INS_TRAP:
case PPC_INS_TW:
case PPC_INS_TD:
case PPC_INS_TWI:
case PPC_INS_TDI:
return NOP();
}
}
static RzILOpEffect *cr_logical(RZ_BORROW csh handle, RZ_BORROW cs_insn *insn, const cs_mode mode) {
rz_return_val_if_fail(handle && insn, EMPTY());
ut32 id = insn->id;
if (id == PPC_INS_MCRF) {
return SETG(cs_reg_name(handle, INSOP(0).reg), VARG(cs_reg_name(handle, INSOP(1).reg)));
}
NOT_IMPLEMENTED;
}
/**
* \brief Returns the RZIL implementation of a given capstone instruction.
* Or NULL if the instruction is not yet implemented.
*
* \param handle The capstone handle.
* \param insn The capstone instruction.
* \param mode The capstone mode.
* \return RzILOpEffect* Sequence of effects which emulate the instruction.
*/
RZ_IPI RzILOpEffect *rz_ppc_cs_get_il_op(RZ_BORROW csh handle, RZ_BORROW cs_insn *insn, const cs_mode mode) {
rz_return_val_if_fail(handle && insn, EMPTY());
rz_return_val_if_fail(insn->detail, EMPTY());
RzILOpEffect *lop;
switch (insn->id) {
default:
NOT_IMPLEMENTED;
case PPC_INS_INVALID:
// TODO Exception
NOT_IMPLEMENTED;
case PPC_INS_NOP:
#if CS_API_MAJOR < 5
// bug on xori in capstone v5
case PPC_INS_XNOP:
#endif
case PPC_INS_DCBT:
case PPC_INS_DCBTST:
// Everything is executed linear => Sync instructions are NOP()s.
case PPC_INS_ISYNC:
case PPC_INS_SYNC:
#if CS_NEXT_VERSION < 6
case PPC_INS_LWSYNC:
case PPC_INS_MSYNC:
case PPC_INS_PTESYNC:
#endif
case PPC_INS_TLBSYNC:
lop = NOP();
break;
case PPC_INS_ADD:
case PPC_INS_ADDC:
case PPC_INS_ADDE:
case PPC_INS_ADDI:
case PPC_INS_ADDIC:
case PPC_INS_ADDIS:
case PPC_INS_ADDME:
case PPC_INS_ADDZE:
lop = add_sub_op(handle, insn, true, mode);
break;
case PPC_INS_NEG:
case PPC_INS_SUBF:
case PPC_INS_SUBFC:
case PPC_INS_SUBFE:
case PPC_INS_SUBFIC:
case PPC_INS_SUBFME:
case PPC_INS_SUBFZE:
lop = add_sub_op(handle, insn, false, mode);
break;
case PPC_INS_DIVD:
case PPC_INS_DIVDU:
case PPC_INS_DIVW:
case PPC_INS_DIVWU:
case PPC_INS_MULHD:
case PPC_INS_MULHDU:
case PPC_INS_MULHW:
case PPC_INS_MULHWU:
case PPC_INS_MULLD:
case PPC_INS_MULLI:
case PPC_INS_MULLW:
lop = div_mul_op(handle, insn, mode);
break;
case PPC_INS_LI:
case PPC_INS_LIS:
case PPC_INS_LA:
case PPC_INS_LBZ:
case PPC_INS_LBZU:
case PPC_INS_LBZUX:
case PPC_INS_LBZX:
case PPC_INS_LD:
case PPC_INS_LDARX:
case PPC_INS_LDBRX:
case PPC_INS_LDU:
case PPC_INS_LDUX:
case PPC_INS_LDX:
case PPC_INS_LHA:
case PPC_INS_LHAU:
case PPC_INS_LHAUX:
case PPC_INS_LHAX:
case PPC_INS_LHBRX:
case PPC_INS_LHZ:
case PPC_INS_LHZU:
case PPC_INS_LHZUX:
case PPC_INS_LHZX:
case PPC_INS_LWA:
case PPC_INS_LWARX:
case PPC_INS_LWAUX:
case PPC_INS_LWAX:
case PPC_INS_LWBRX:
case PPC_INS_LWZ:
case PPC_INS_LWZU:
case PPC_INS_LWZUX:
case PPC_INS_LWZX:
case PPC_INS_LBZCIX:
case PPC_INS_LHZCIX:
case PPC_INS_LWZCIX:
case PPC_INS_LDCIX:
lop = load_op(handle, insn, mode);
break;
case PPC_INS_STB:
case PPC_INS_STBU:
case PPC_INS_STBUX:
case PPC_INS_STBX:
case PPC_INS_STD:
case PPC_INS_STDBRX:
case PPC_INS_STDCX:
case PPC_INS_STDU:
case PPC_INS_STDUX:
case PPC_INS_STDX:
case PPC_INS_STFD:
case PPC_INS_STFDU:
case PPC_INS_STFDUX:
case PPC_INS_STFDX:
case PPC_INS_STFIWX:
case PPC_INS_STFS:
case PPC_INS_STFSU:
case PPC_INS_STFSUX:
case PPC_INS_STFSX:
case PPC_INS_STH:
case PPC_INS_STHBRX:
case PPC_INS_STHU:
case PPC_INS_STHUX:
case PPC_INS_STHX:
case PPC_INS_STMW:
case PPC_INS_STSWI:
case PPC_INS_STVEBX:
case PPC_INS_STVEHX:
case PPC_INS_STVEWX:
case PPC_INS_STVX:
case PPC_INS_STVXL:
case PPC_INS_STW:
case PPC_INS_STWBRX:
case PPC_INS_STWCX:
case PPC_INS_STWU:
case PPC_INS_STWUX:
case PPC_INS_STWX:
case PPC_INS_STXSDX:
case PPC_INS_STXVD2X:
case PPC_INS_STXVW4X:
case PPC_INS_DCBZ:
case PPC_INS_STHCIX:
case PPC_INS_STWCIX:
case PPC_INS_STBCIX:
case PPC_INS_STDCIX:
lop = store_op(handle, insn, mode);
break;
#if CS_NEXT_VERSION < 6
case PPC_INS_MR:
#endif
case PPC_INS_AND:
case PPC_INS_ANDC:
case PPC_INS_ANDIS:
case PPC_INS_ANDI:
case PPC_INS_OR:
case PPC_INS_ORC:
case PPC_INS_ORI:
case PPC_INS_ORIS:
case PPC_INS_NAND:
case PPC_INS_NOR:
#if CS_API_MAJOR == 5 && CS_NEXT_VERSION < 6
// bug on xori in capstone v5
case PPC_INS_XNOP:
#endif
case PPC_INS_XOR:
case PPC_INS_XORI:
#if CS_NEXT_VERSION >= 6
if (insn->is_alias && insn->alias_id == PPC_INS_ALIAS_XNOP) {
return NOP();
}
#endif
// fallthrough
case PPC_INS_XORIS:
case PPC_INS_EQV:
case PPC_INS_EXTSB:
case PPC_INS_EXTSH:
case PPC_INS_EXTSW:
case PPC_INS_CNTLZD:
case PPC_INS_CNTLZW:
case PPC_INS_POPCNTD:
case PPC_INS_POPCNTW:
case PPC_INS_CMPB:
#if CS_API_MAJOR == 5
case PPC_INS_CMPRB:
case PPC_INS_CMPEQB:
#endif
lop = bitwise_op(handle, insn, mode);
break;
#if CS_API_MAJOR == 5 && CS_NEXT_VERSION < 6
case PPC_INS_CMP:
case PPC_INS_CMPI:
case PPC_INS_CMPL:
case PPC_INS_CMPLI:
#endif
case PPC_INS_CMPD:
case PPC_INS_CMPDI:
case PPC_INS_CMPLD:
case PPC_INS_CMPLDI:
case PPC_INS_CMPLW:
case PPC_INS_CMPLWI:
case PPC_INS_CMPW:
case PPC_INS_CMPWI:
lop = compare_op(handle, insn, mode);
break;
case PPC_INS_B:
case PPC_INS_BA:
case PPC_INS_BC:
case PPC_INS_BCCTR:
case PPC_INS_BCCTRL:
case PPC_INS_BCL:
case PPC_INS_BCLR:
case PPC_INS_BCLRL:
case PPC_INS_BCTR:
case PPC_INS_BCTRL:
case PPC_INS_BL:
case PPC_INS_BLA:
case PPC_INS_BLR:
case PPC_INS_BLRL:
case PPC_INS_BCA:
case PPC_INS_BCLA:
#if CS_NEXT_VERSION < 6
case PPC_INS_BDNZ:
case PPC_INS_BDNZA:
case PPC_INS_BDNZL:
case PPC_INS_BDNZLA:
case PPC_INS_BDNZLR:
case PPC_INS_BDNZLRL:
case PPC_INS_BDZ:
case PPC_INS_BDZA:
case PPC_INS_BDZL:
case PPC_INS_BDZLA:
case PPC_INS_BDZLR:
case PPC_INS_BDZLRL:
case PPC_INS_BDNZT:
case PPC_INS_BDNZTL:
case PPC_INS_BDNZTA:
case PPC_INS_BDNZTLA:
case PPC_INS_BDNZF:
case PPC_INS_BDNZFL:
case PPC_INS_BDNZFA:
case PPC_INS_BDNZFLA:
case PPC_INS_BDZT:
case PPC_INS_BDZTA:
case PPC_INS_BDZTL:
case PPC_INS_BDZTLA:
case PPC_INS_BDZF:
case PPC_INS_BDZFA:
case PPC_INS_BDZFL:
case PPC_INS_BDZFLA:
#endif
#if CS_API_MAJOR == 5 && CS_NEXT_VERSION < 6
case PPC_INS_BCDCFN:
case PPC_INS_BCDCFSQ:
case PPC_INS_BCDCFZ:
case PPC_INS_BCDCPSGN:
case PPC_INS_BCDCTN:
case PPC_INS_BCDCTSQ:
case PPC_INS_BCDCTZ:
case PPC_INS_BCDS:
case PPC_INS_BCDSETSGN:
case PPC_INS_BCDSR:
case PPC_INS_BCDTRUNC:
case PPC_INS_BCDUS:
case PPC_INS_BCDUTRUNC:
case PPC_INS_BDNZFLR:
case PPC_INS_BDNZFLRL:
case PPC_INS_BDNZTLR:
case PPC_INS_BDNZTLRL:
case PPC_INS_BDZFLR:
case PPC_INS_BDZFLRL:
case PPC_INS_BDZTLR:
case PPC_INS_BDZTLRL:
case PPC_INS_BEQ:
case PPC_INS_BEQA:
case PPC_INS_BEQCTR:
case PPC_INS_BEQCTRL:
case PPC_INS_BEQL:
case PPC_INS_BEQLA:
case PPC_INS_BEQLR:
case PPC_INS_BEQLRL:
case PPC_INS_BF:
case PPC_INS_BFA:
case PPC_INS_BFCTR:
case PPC_INS_BFCTRL:
case PPC_INS_BFL:
case PPC_INS_BFLA:
case PPC_INS_BFLR:
case PPC_INS_BFLRL:
case PPC_INS_BGE:
case PPC_INS_BGEA:
case PPC_INS_BGECTR:
case PPC_INS_BGECTRL:
case PPC_INS_BGEL:
case PPC_INS_BGELA:
case PPC_INS_BGELR:
case PPC_INS_BGELRL:
case PPC_INS_BGT:
case PPC_INS_BGTA:
case PPC_INS_BGTCTR:
case PPC_INS_BGTCTRL:
case PPC_INS_BGTL:
case PPC_INS_BGTLA:
case PPC_INS_BGTLR:
case PPC_INS_BGTLRL:
case PPC_INS_BLE:
case PPC_INS_BLEA:
case PPC_INS_BLECTR:
case PPC_INS_BLECTRL:
case PPC_INS_BLEL:
case PPC_INS_BLELA:
case PPC_INS_BLELR:
case PPC_INS_BLELRL:
case PPC_INS_BLT:
case PPC_INS_BLTA:
case PPC_INS_BLTCTR:
case PPC_INS_BLTCTRL:
case PPC_INS_BLTL:
case PPC_INS_BLTLA:
case PPC_INS_BLTLR:
case PPC_INS_BLTLRL:
case PPC_INS_BNE:
case PPC_INS_BNEA:
case PPC_INS_BNECTR:
case PPC_INS_BNECTRL:
case PPC_INS_BNEL:
case PPC_INS_BNELA:
case PPC_INS_BNELR:
case PPC_INS_BNELRL:
case PPC_INS_BNG:
case PPC_INS_BNGA:
case PPC_INS_BNGCTR:
case PPC_INS_BNGCTRL:
case PPC_INS_BNGL:
case PPC_INS_BNGLA:
case PPC_INS_BNGLR:
case PPC_INS_BNGLRL:
case PPC_INS_BNL:
case PPC_INS_BNLA:
case PPC_INS_BNLCTR:
case PPC_INS_BNLCTRL:
case PPC_INS_BNLL:
case PPC_INS_BNLLA:
case PPC_INS_BNLLR:
case PPC_INS_BNLLRL:
case PPC_INS_BNS:
case PPC_INS_BNSA:
case PPC_INS_BNSCTR:
case PPC_INS_BNSCTRL:
case PPC_INS_BNSL:
case PPC_INS_BNSLA:
case PPC_INS_BNSLR:
case PPC_INS_BNSLRL:
case PPC_INS_BNU:
case PPC_INS_BNUA:
case PPC_INS_BNUCTR:
case PPC_INS_BNUCTRL:
case PPC_INS_BNUL:
case PPC_INS_BNULA:
case PPC_INS_BNULR:
case PPC_INS_BNULRL:
case PPC_INS_BPERMD:
case PPC_INS_BSO:
case PPC_INS_BSOA:
case PPC_INS_BSOCTR:
case PPC_INS_BSOCTRL:
case PPC_INS_BSOL:
case PPC_INS_BSOLA:
case PPC_INS_BSOLR:
case PPC_INS_BSOLRL:
case PPC_INS_BT:
case PPC_INS_BTA:
case PPC_INS_BTCTR:
case PPC_INS_BTCTRL:
case PPC_INS_BTL:
case PPC_INS_BTLA:
case PPC_INS_BTLR:
case PPC_INS_BTLRL:
case PPC_INS_BUN:
case PPC_INS_BUNA:
case PPC_INS_BUNCTR:
case PPC_INS_BUNCTRL:
case PPC_INS_BUNL:
case PPC_INS_BUNLA:
case PPC_INS_BUNLR:
case PPC_INS_BUNLRL:
#endif
lop = branch_op(handle, insn, mode);
break;
// These instruction are not in the ISA manual v3.1B.
case PPC_INS_BRINC:
NOT_IMPLEMENTED;
case PPC_INS_MFCR:
case PPC_INS_MFCTR:
case PPC_INS_MFDCR:
case PPC_INS_MFFS:
case PPC_INS_MFLR:
case PPC_INS_MFMSR:
case PPC_INS_MFOCRF:
case PPC_INS_MFSPR:
case PPC_INS_MFSR:
case PPC_INS_MFSRIN:
case PPC_INS_MFTB:
case PPC_INS_MFVSCR:
case PPC_INS_MTCRF:
case PPC_INS_MTCTR:
case PPC_INS_MTDCR:
case PPC_INS_MTFSB0:
case PPC_INS_MTFSB1:
case PPC_INS_MTFSF:
case PPC_INS_MTLR:
case PPC_INS_MTMSR:
case PPC_INS_MTMSRD:
case PPC_INS_MTOCRF:
case PPC_INS_MTSPR:
case PPC_INS_MTSR:
case PPC_INS_MTSRIN:
case PPC_INS_MTVSCR:
#if CS_NEXT_VERSION < 6
case PPC_INS_MFBR0:
case PPC_INS_MFBR1:
case PPC_INS_MFBR2:
case PPC_INS_MFBR3:
case PPC_INS_MFBR4:
case PPC_INS_MFBR5:
case PPC_INS_MFBR6:
case PPC_INS_MFBR7:
case PPC_INS_MFXER:
case PPC_INS_MFRTCU:
case PPC_INS_MFRTCL:
case PPC_INS_MFDSCR:
case PPC_INS_MFDSISR:
case PPC_INS_MFDAR:
case PPC_INS_MFSRR2:
case PPC_INS_MFSRR3:
case PPC_INS_MFCFAR:
case PPC_INS_MFAMR:
case PPC_INS_MFPID:
case PPC_INS_MFTBLO:
case PPC_INS_MFTBHI:
case PPC_INS_MFDBATU:
case PPC_INS_MFDBATL:
case PPC_INS_MFIBATU:
case PPC_INS_MFIBATL:
case PPC_INS_MFTBU:
case PPC_INS_MTCR:
case PPC_INS_MTDBATU:
case PPC_INS_MTDBATL:
case PPC_INS_MTIBATU:
case PPC_INS_MTIBATL:
case PPC_INS_MFDCCR:
case PPC_INS_MFICCR:
case PPC_INS_MFDEAR:
case PPC_INS_MFESR:
case PPC_INS_MFSPEFSCR:
case PPC_INS_MFTCR:
case PPC_INS_MFASR:
case PPC_INS_MFPVR:
case PPC_INS_MTBR0:
case PPC_INS_MTBR1:
case PPC_INS_MTBR2:
case PPC_INS_MTBR3:
case PPC_INS_MTBR4:
case PPC_INS_MTBR5:
case PPC_INS_MTBR6:
case PPC_INS_MTBR7:
case PPC_INS_MTXER:
case PPC_INS_MTDSCR:
case PPC_INS_MTDSISR:
case PPC_INS_MTDAR:
case PPC_INS_MTSRR2:
case PPC_INS_MTSRR3:
case PPC_INS_MTCFAR:
case PPC_INS_MTAMR:
case PPC_INS_MTPID:
case PPC_INS_MTTBL:
case PPC_INS_MTTBU:
case PPC_INS_MTTBLO:
case PPC_INS_MTTBHI:
case PPC_INS_MTDCCR:
case PPC_INS_MTICCR:
case PPC_INS_MTDEAR:
case PPC_INS_MTESR:
case PPC_INS_MTSPEFSCR:
case PPC_INS_MTTCR:
#endif
lop = move_from_to_spr_op(handle, insn, mode);
break;
case PPC_INS_ISEL:
NOT_IMPLEMENTED;
break;
case PPC_INS_CREQV:
case PPC_INS_CRXOR:
case PPC_INS_CRAND:
case PPC_INS_CRANDC:
case PPC_INS_CRNAND:
case PPC_INS_CRNOR:
case PPC_INS_CROR:
case PPC_INS_CRORC:
#if CS_NEXT_VERSION < 6
case PPC_INS_CRSET:
case PPC_INS_CRNOT:
case PPC_INS_CRMOVE:
case PPC_INS_CRCLR:
#endif
NOT_IMPLEMENTED;
case PPC_INS_MCRF:
lop = cr_logical(handle, insn, mode);
break;
// Rotate and rotate
case PPC_INS_RLDCL:
case PPC_INS_RLDCR:
case PPC_INS_RLDIC:
case PPC_INS_RLDICL:
case PPC_INS_RLDICR:
case PPC_INS_RLDIMI:
case PPC_INS_RLWIMI:
case PPC_INS_RLWINM:
case PPC_INS_RLWNM:
#if CS_NEXT_VERSION < 6
case PPC_INS_ROTLD:
case PPC_INS_ROTLDI:
case PPC_INS_CLRLDI:
case PPC_INS_ROTLWI:
case PPC_INS_CLRLWI:
case PPC_INS_ROTLW:
#endif
case PPC_INS_SLD:
case PPC_INS_SLW:
case PPC_INS_SRAD:
case PPC_INS_SRADI:
case PPC_INS_SRAW:
case PPC_INS_SRAWI:
case PPC_INS_SRD:
case PPC_INS_SRW:
case PPC_INS_SLWI:
case PPC_INS_SLDI:
case PPC_INS_SRWI:
lop = shift_and_rotate(handle, insn, mode);
break;
case PPC_INS_SC:
case PPC_INS_TRAP:
case PPC_INS_TW:
case PPC_INS_TD:
case PPC_INS_TWI:
case PPC_INS_TDI:
lop = sys(handle, insn, mode);
break;
}
return lop;
}
#include <rz_il/rz_il_opbuilder_end.h>