Files
rizin/librz/arch/isa/m68k/m68k_cs.c
T
2026-10-11 12:06:06 +00:00

676 lines
18 KiB
C

// SPDX-FileCopyrightText: 2026 RizinOrg <[email protected]>
// SPDX-License-Identifier: LGPL-3.0-only
#include "m68k/m68k_cs.h"
#include <string.h>
#include <rz_util/rz_assert.h>
#define M68K_FPU_EXT_DST(ext) (((ext) >> 7) & 7)
RZ_IPI bool rz_m68k_reg_is_dreg(m68k_reg reg) {
return reg >= M68K_REG_D0 && reg <= M68K_REG_D7;
}
RZ_IPI bool rz_m68k_reg_is_areg(m68k_reg reg) {
return reg >= M68K_REG_A0 && reg <= M68K_REG_A7;
}
RZ_IPI bool rz_m68k_reg_is_gpr(m68k_reg reg) {
return rz_m68k_reg_is_dreg(reg) || rz_m68k_reg_is_areg(reg);
}
RZ_IPI bool rz_m68k_reg_is_control(m68k_reg reg) {
switch (reg) {
case M68K_REG_SFC:
case M68K_REG_DFC:
case M68K_REG_USP:
case M68K_REG_VBR:
case M68K_REG_CACR:
case M68K_REG_CAAR:
case M68K_REG_MSP:
case M68K_REG_ISP:
case M68K_REG_TC:
case M68K_REG_ITT0:
case M68K_REG_ITT1:
case M68K_REG_DTT0:
case M68K_REG_DTT1:
case M68K_REG_MMUSR:
case M68K_REG_URP:
case M68K_REG_SRP:
return true;
default:
return false;
}
}
RZ_IPI bool rz_m68k_op_is_control_reg(RZ_NONNULL const cs_m68k_op *op) {
rz_return_val_if_fail(op, false);
if (op->type != M68K_OP_REG) {
return false;
}
return rz_m68k_reg_is_control(op->reg);
}
RZ_IPI bool rz_m68k_mode_is_coldfire(cs_mode mode) {
#ifdef RZ_CAPSTONE_HAS_M68K_COLDFIRE
return mode == CS_MODE_M68K_CFV1 ||
mode == CS_MODE_M68K_CFV2 ||
mode == CS_MODE_M68K_CFV3 ||
mode == CS_MODE_M68K_CFV4 ||
mode == CS_MODE_M68K_CFV4E ||
mode == CS_MODE_M68K_CFV5 ||
mode == CS_MODE_M68K_COLDFIRE;
#else
(void)mode;
return false;
#endif
}
RZ_IPI bool rz_m68k_mode_has_fsave(cs_mode mode) {
return mode == CS_MODE_M68K_020 || mode == CS_MODE_M68K_030 ||
mode == CS_MODE_M68K_040 || mode == CS_MODE_M68K_060;
}
RZ_IPI bool rz_m68k_mode_has_emac(cs_mode mode) {
#ifdef RZ_CAPSTONE_HAS_M68K_COLDFIRE
return mode == CS_MODE_M68K_CFV4E || mode == CS_MODE_M68K_CFV5;
#else
(void)mode;
return false;
#endif
}
RZ_IPI bool rz_m68k_control_reg_implemented(cs_mode mode, m68k_reg reg) {
if (rz_m68k_mode_is_coldfire(mode)) {
return reg == M68K_REG_CACR || reg == M68K_REG_VBR;
}
switch (reg) {
case M68K_REG_SFC:
case M68K_REG_DFC:
case M68K_REG_USP:
case M68K_REG_VBR:
return mode == CS_MODE_M68K_010 || mode == CS_MODE_M68K_020 ||
mode == CS_MODE_M68K_030 || mode == CS_MODE_M68K_040 ||
mode == CS_MODE_M68K_060
#ifdef RZ_CAPSTONE_HAS_M68K_CPU32
|| mode == CS_MODE_M68K_CPU32
#endif
;
case M68K_REG_CACR:
return mode == CS_MODE_M68K_020 || mode == CS_MODE_M68K_030 ||
mode == CS_MODE_M68K_040 || mode == CS_MODE_M68K_060;
case M68K_REG_CAAR:
return mode == CS_MODE_M68K_020 || mode == CS_MODE_M68K_030;
case M68K_REG_MSP:
case M68K_REG_ISP:
return mode == CS_MODE_M68K_020 || mode == CS_MODE_M68K_030 ||
mode == CS_MODE_M68K_040;
case M68K_REG_TC:
case M68K_REG_ITT0:
case M68K_REG_ITT1:
case M68K_REG_DTT0:
case M68K_REG_DTT1:
case M68K_REG_URP:
case M68K_REG_SRP:
return mode == CS_MODE_M68K_040 || mode == CS_MODE_M68K_060;
case M68K_REG_MMUSR:
return mode == CS_MODE_M68K_040;
default:
return false;
}
}
RZ_IPI bool rz_m68k_reg_name_is_mmu_root_pointer(RZ_NULLABLE const char *name) {
return RZ_STR_EQ(name, "srp") || RZ_STR_EQ(name, "crp");
}
RZ_IPI bool rz_m68k_reg_is_fpu(m68k_reg reg) {
return reg >= M68K_REG_FP0 && reg <= M68K_REG_FP7;
}
RZ_IPI bool rz_m68k_op_is_fpu_reg(RZ_NONNULL const cs_m68k_op *op) {
rz_return_val_if_fail(op, false);
if (op->type != M68K_OP_REG) {
return false;
}
return rz_m68k_reg_is_fpu(op->reg);
}
RZ_IPI bool rz_m68k_reg_is_fpu_control(m68k_reg reg) {
return reg == M68K_REG_FPCR || reg == M68K_REG_FPSR || reg == M68K_REG_FPIAR;
}
RZ_IPI bool rz_m68k_reg_is_acc(m68k_reg reg) {
#ifdef RZ_CAPSTONE_HAS_M68K_COLDFIRE
return reg == M68K_REG_ACC || (reg >= M68K_REG_ACC0 && reg <= M68K_REG_ACC3);
#else
return false;
#endif
}
RZ_IPI bool rz_m68k_op_is_predec_areg(RZ_NONNULL const cs_m68k_op *op) {
rz_return_val_if_fail(op, false);
if (op->address_mode != M68K_AM_REGI_ADDR_PRE_DEC) {
return false;
}
return rz_m68k_reg_is_areg(op->reg);
}
RZ_IPI bool rz_m68k_op_is_data_reg(RZ_NONNULL const cs_m68k_op *op) {
rz_return_val_if_fail(op, false);
if (op->type != M68K_OP_REG) {
return false;
}
return rz_m68k_reg_is_dreg(op->reg);
}
RZ_IPI bool rz_m68k_op_is_addr_reg(RZ_NONNULL const cs_m68k_op *op) {
rz_return_val_if_fail(op, false);
if (op->type != M68K_OP_REG) {
return false;
}
return rz_m68k_reg_is_areg(op->reg);
}
RZ_IPI bool rz_m68k_op_is_gpr(RZ_NONNULL const cs_m68k_op *op) {
rz_return_val_if_fail(op, false);
if (op->type != M68K_OP_REG) {
return false;
}
return rz_m68k_reg_is_gpr(op->reg);
}
RZ_IPI bool rz_m68k_op_is_status_reg(RZ_NONNULL const cs_m68k_op *op) {
rz_return_val_if_fail(op, false);
if (op->type != M68K_OP_REG) {
return false;
}
return op->reg == M68K_REG_CCR || op->reg == M68K_REG_SR;
}
RZ_IPI bool rz_m68k_op_is_acc_reg(RZ_NONNULL const cs_m68k_op *op) {
rz_return_val_if_fail(op, false);
if (op->type != M68K_OP_REG) {
return false;
}
return rz_m68k_reg_is_acc(op->reg);
}
RZ_IPI bool rz_m68k_op_is_fpu_control_reg(RZ_NONNULL const cs_m68k_op *op) {
rz_return_val_if_fail(op, false);
if (op->type != M68K_OP_REG) {
return false;
}
return rz_m68k_reg_is_fpu_control(op->reg);
}
RZ_IPI bool rz_m68k_insn_uses_fpu_operand(RZ_NONNULL const cs_m68k *m68k) {
rz_return_val_if_fail(m68k, false);
for (ut8 i = 0; i < m68k->op_count; i++) {
const cs_m68k_op *op = &m68k->operands[i];
switch (op->type) {
case M68K_OP_INVALID:
// Capstone can leave M68K_OP_INVALID on malformed FPU encodings.
continue;
case M68K_OP_FP_SINGLE:
case M68K_OP_FP_DOUBLE:
#ifdef RZ_CAPSTONE_HAS_M68K_FP_FORMATS
case M68K_OP_FP_EXTENDED:
case M68K_OP_FP_PACKED:
#endif
return true;
case M68K_OP_REG:
if (rz_m68k_reg_is_fpu(op->reg)) {
return true;
}
break;
default:
break;
}
}
return false;
}
RZ_IPI ut32 rz_m68k_detail_op_bits(RZ_NONNULL const cs_m68k *m68k, ut32 fallback_bits) {
rz_return_val_if_fail(m68k, fallback_bits);
if (m68k->op_size.type == M68K_SIZE_TYPE_CPU) {
switch (m68k->op_size.cpu_size) {
case M68K_CPU_SIZE_BYTE:
return 8;
case M68K_CPU_SIZE_WORD:
return 16;
case M68K_CPU_SIZE_LONG:
return 32;
default:
break;
}
}
if (m68k->op_size.type == M68K_SIZE_TYPE_FPU) {
switch (m68k->op_size.fpu_size) {
case M68K_FPU_SIZE_SINGLE:
return 32;
case M68K_FPU_SIZE_DOUBLE:
return 64;
case M68K_FPU_SIZE_EXTENDED:
return 80;
#ifdef RZ_CAPSTONE_HAS_M68K_FP_FORMATS
case M68K_FPU_SIZE_PACKED:
return 80;
#endif
default:
break;
}
}
return fallback_bits;
}
RZ_IPI ut32 rz_m68k_bits_access_bytes(ut32 bits) {
return RZ_MAX(1, bits / 8);
}
RZ_IPI ut32 rz_m68k_reg_stack_access_bytes(cs_mode mode, m68k_reg reg, ut32 bits) {
ut32 bytes = rz_m68k_bits_access_bytes(bits);
// A7 is 32 bits; this is the address-register delta, not its width. A
// byte-sized predecrement/postincrement adjusts A7 by 2 bytes to preserve
// stack word alignment on classic M680x0. ColdFire treats A7 like every
// other address register and adjusts it by one for byte operands (CFPRM).
return reg == M68K_REG_A7 && bytes == 1 && !rz_m68k_mode_is_coldfire(mode) ? 2 : bytes;
}
RZ_IPI m68k_reg rz_m68k_op_base_reg(RZ_NONNULL const cs_m68k_op *op) {
rz_return_val_if_fail(op, M68K_REG_INVALID);
if (op->mem.base_reg != M68K_REG_INVALID) {
return op->mem.base_reg;
}
return rz_m68k_reg_is_areg(op->reg) ? op->reg : M68K_REG_INVALID;
}
RZ_IPI ut32 rz_m68k_op_absolute_address(RZ_NONNULL const cs_m68k_op *op, bool short_addr) {
rz_return_val_if_fail(op, 0);
#ifdef RZ_CAPSTONE_HAS_M68K_COLDFIRE
st64 address = op->mem.address;
#else
st64 address = op->imm ? op->imm : op->mem.disp;
#endif
return short_addr ? (ut32)(st32)(st16)address : (ut32)address;
}
RZ_IPI bool rz_m68k_op_is_mem_addr(RZ_NONNULL const cs_m68k_op *op) {
rz_return_val_if_fail(op, false);
switch (op->address_mode) {
case M68K_AM_REGI_ADDR:
case M68K_AM_REGI_ADDR_POST_INC:
case M68K_AM_REGI_ADDR_PRE_DEC:
case M68K_AM_REGI_ADDR_DISP:
case M68K_AM_AREGI_INDEX_8_BIT_DISP:
case M68K_AM_AREGI_INDEX_BASE_DISP:
case M68K_AM_MEMI_PRE_INDEX:
case M68K_AM_MEMI_POST_INDEX:
case M68K_AM_PCI_DISP:
case M68K_AM_PCI_INDEX_8_BIT_DISP:
case M68K_AM_PCI_INDEX_BASE_DISP:
case M68K_AM_PC_MEMI_PRE_INDEX:
case M68K_AM_PC_MEMI_POST_INDEX:
case M68K_AM_ABSOLUTE_DATA_SHORT:
case M68K_AM_ABSOLUTE_DATA_LONG:
return true;
default:
return op->type == M68K_OP_MEM;
}
}
RZ_IPI ut64 rz_m68k_op_absolute_mem_address(RZ_NONNULL const cs_m68k_op *operand) {
rz_return_val_if_fail(operand, 0);
if (operand->type != M68K_OP_MEM) {
return 0;
}
// M68000PRM 2.2.16: an absolute short address is sign-extended to 32 bits.
if (operand->address_mode == M68K_AM_ABSOLUTE_DATA_SHORT) {
return rz_m68k_op_absolute_address(operand, true);
}
#ifdef RZ_CAPSTONE_HAS_M68K_COLDFIRE
return operand->mem.address;
#else
if (operand->imm) {
return operand->imm;
}
return (ut64)(st64)(operand->mem.disp + operand->mem.in_disp + operand->mem.out_disp);
#endif
}
RZ_IPI bool rz_m68k_op_is_absolute_mem(RZ_NONNULL const cs_m68k_op *operand) {
rz_return_val_if_fail(operand, false);
if (operand->type != M68K_OP_MEM) {
return false;
}
switch (operand->address_mode) {
case M68K_AM_ABSOLUTE_DATA_SHORT:
case M68K_AM_ABSOLUTE_DATA_LONG:
return true;
default:
break;
}
return operand->mem.base_reg == M68K_REG_INVALID &&
operand->mem.index_reg == M68K_REG_INVALID &&
operand->mem.in_base_reg == M68K_REG_INVALID;
}
RZ_IPI bool rz_m68k_insn_cache_requires_address(ut32 insn_id) {
switch (insn_id) {
#ifdef RZ_CAPSTONE_HAS_M68K_COLDFIRE
case M68K_INS_INTOUCH:
#endif
case M68K_INS_CINVL:
case M68K_INS_CINVP:
case M68K_INS_CPUSHL:
case M68K_INS_CPUSHP:
return true;
default:
return false;
}
}
RZ_IPI bool rz_m68k_op_is_data_reg_pair(RZ_NONNULL const cs_m68k_op *op) {
rz_return_val_if_fail(op, false);
if (op->type != M68K_OP_REG_PAIR) {
return false;
}
return rz_m68k_reg_is_dreg(op->reg_pair.reg_0) && rz_m68k_reg_is_dreg(op->reg_pair.reg_1);
}
RZ_IPI bool rz_m68k_op_is_gpr_reg_pair(RZ_NONNULL const cs_m68k_op *op) {
rz_return_val_if_fail(op, false);
if (op->type != M68K_OP_REG_PAIR) {
return false;
}
m68k_reg r0 = op->reg_pair.reg_0;
m68k_reg r1 = op->reg_pair.reg_1;
return rz_m68k_reg_is_gpr(r0) && rz_m68k_reg_is_gpr(r1);
}
#ifdef RZ_CAPSTONE_HAS_M68K_CPU32
RZ_IPI bool rz_m68k_tbl_insn_is_signed(m68k_insn insn) {
return insn == M68K_INS_TBLS || insn == M68K_INS_TBLSN;
}
RZ_IPI bool rz_m68k_tbl_insn_is_unrounded(m68k_insn insn) {
return insn == M68K_INS_TBLSN || insn == M68K_INS_TBLUN;
}
#endif
RZ_IPI bool rz_m68k_fpu_insn_extension_word(RZ_NONNULL const cs_insn *insn, RZ_NONNULL ut16 *extension) {
rz_return_val_if_fail(insn && extension, false);
if (insn->size < 4) {
return false;
}
*extension = ((ut16)insn->bytes[2] << 8) | insn->bytes[3];
return true;
}
RZ_IPI m68k_reg rz_m68k_fpu_insn_extension_dst_reg(RZ_NONNULL const cs_insn *insn) {
rz_return_val_if_fail(insn, M68K_REG_INVALID);
ut16 extension = 0;
if (!rz_m68k_fpu_insn_extension_word(insn, &extension)) {
return M68K_REG_INVALID;
}
return M68K_REG_FP0 + M68K_FPU_EXT_DST(extension);
}
// Capstone collapses some FPU read-modify-write instructions to one operand
// when source and destination are the same (for example "frem fp0"). The
// destination remains encoded in the FPU extension word; materialize it as a
// synthetic second operand so analysis and IL can use a uniform two-operand
// form.
RZ_IPI bool rz_m68k_fpu_insn_hidden_dst_op(RZ_NONNULL const cs_insn *insn, RZ_NONNULL cs_m68k_op *dst) {
rz_return_val_if_fail(insn && dst, false);
m68k_reg reg = rz_m68k_fpu_insn_extension_dst_reg(insn);
if (!rz_m68k_reg_is_fpu(reg)) {
return false;
}
memset(dst, 0, sizeof(*dst));
dst->type = M68K_OP_REG;
dst->reg = reg;
return true;
}
RZ_IPI bool rz_m68k_fpu_op_from_reg(RZ_NONNULL cs_m68k_op *op, m68k_reg reg) {
rz_return_val_if_fail(op, false);
if (!rz_m68k_reg_is_fpu(reg)) {
return false;
}
memset(op, 0, sizeof(*op));
op->type = M68K_OP_REG;
op->reg = reg;
return true;
}
RZ_IPI RZ_NULLABLE const cs_m68k_op *rz_m68k_fpu_insn_second_op_or_hidden_dst(RZ_NONNULL const cs_insn *insn, RZ_NONNULL cs_m68k_op *hidden_dst) {
rz_return_val_if_fail(insn && insn->detail && hidden_dst, NULL);
const cs_m68k *m68k = &insn->detail->m68k;
if (m68k->op_count > 1) {
return &m68k->operands[1];
}
return rz_m68k_fpu_insn_hidden_dst_op(insn, hidden_dst) ? hidden_dst : NULL;
}
RZ_IPI RZ_NULLABLE const cs_m68k_op *rz_m68k_fpu_insn_unary_dst_op(RZ_NONNULL const cs_insn *insn, RZ_NONNULL const cs_m68k_op *src, RZ_NONNULL cs_m68k_op *hidden_dst) {
rz_return_val_if_fail(insn && insn->detail && src && hidden_dst, NULL);
const cs_m68k *m68k = &insn->detail->m68k;
if (m68k->op_count > 1) {
return &m68k->operands[1];
}
if (rz_m68k_op_is_fpu_reg(src)) {
m68k_reg reg = rz_m68k_fpu_insn_extension_dst_reg(insn);
if (rz_m68k_reg_is_fpu(reg) && reg != src->reg && rz_m68k_fpu_insn_hidden_dst_op(insn, hidden_dst)) {
return hidden_dst;
}
return src;
}
return rz_m68k_fpu_insn_hidden_dst_op(insn, hidden_dst) ? hidden_dst : NULL;
}
RZ_IPI bool rz_m68k_fpu_insn_has_data_dst(ut32 insn_id) {
switch (insn_id) {
case M68K_INS_FMOVECR:
case M68K_INS_FABS:
case M68K_INS_FSABS:
case M68K_INS_FDABS:
case M68K_INS_FACOS:
case M68K_INS_FASIN:
case M68K_INS_FATAN:
case M68K_INS_FATANH:
case M68K_INS_FCOS:
case M68K_INS_FCOSH:
case M68K_INS_FETOX:
case M68K_INS_FETOXM1:
case M68K_INS_FLOG10:
case M68K_INS_FLOG2:
case M68K_INS_FLOGN:
case M68K_INS_FLOGNP1:
case M68K_INS_FNEG:
case M68K_INS_FSNEG:
case M68K_INS_FDNEG:
case M68K_INS_FSIN:
case M68K_INS_FSINH:
case M68K_INS_FSQRT:
case M68K_INS_FSSQRT:
case M68K_INS_FDSQRT:
case M68K_INS_FTAN:
case M68K_INS_FTANH:
case M68K_INS_FTENTOX:
case M68K_INS_FTWOTOX:
case M68K_INS_FINT:
case M68K_INS_FINTRZ:
case M68K_INS_FGETEXP:
case M68K_INS_FGETMAN:
case M68K_INS_FADD:
case M68K_INS_FSADD:
case M68K_INS_FDADD:
case M68K_INS_FSUB:
case M68K_INS_FSSUB:
case M68K_INS_FDSUB:
case M68K_INS_FMUL:
case M68K_INS_FSMUL:
case M68K_INS_FDMUL:
case M68K_INS_FSGLMUL:
case M68K_INS_FDIV:
case M68K_INS_FSDIV:
case M68K_INS_FDDIV:
case M68K_INS_FSGLDIV:
case M68K_INS_FMOD:
case M68K_INS_FREM:
case M68K_INS_FSCALE:
case M68K_INS_FCMP:
return true;
default:
return false;
}
}
RZ_IPI bool rz_m68k_fpu_insn_single_op_is_complete(RZ_NONNULL const cs_insn *insn) {
rz_return_val_if_fail(insn && insn->detail, false);
if (insn->detail->m68k.op_count != 1) {
return false;
}
const cs_m68k_op *operand = &insn->detail->m68k.operands[0];
if (!rz_m68k_op_is_fpu_reg(operand)) {
return false;
}
switch (insn->id) {
case M68K_INS_FABS:
case M68K_INS_FSABS:
case M68K_INS_FDABS:
case M68K_INS_FACOS:
case M68K_INS_FASIN:
case M68K_INS_FATAN:
case M68K_INS_FATANH:
case M68K_INS_FCOS:
case M68K_INS_FCOSH:
case M68K_INS_FETOX:
case M68K_INS_FETOXM1:
case M68K_INS_FLOG10:
case M68K_INS_FLOG2:
case M68K_INS_FLOGN:
case M68K_INS_FLOGNP1:
case M68K_INS_FNEG:
case M68K_INS_FSNEG:
case M68K_INS_FDNEG:
case M68K_INS_FSIN:
case M68K_INS_FSINH:
case M68K_INS_FSQRT:
case M68K_INS_FSSQRT:
case M68K_INS_FDSQRT:
case M68K_INS_FTAN:
case M68K_INS_FTANH:
case M68K_INS_FTENTOX:
case M68K_INS_FTWOTOX:
case M68K_INS_FINT:
case M68K_INS_FINTRZ:
case M68K_INS_FGETEXP:
case M68K_INS_FGETMAN:
case M68K_INS_FTST:
return rz_m68k_fpu_insn_extension_dst_reg(insn) == operand->reg;
default:
return false;
}
}
RZ_IPI bool rz_m68k_fpu_insn_needs_hidden_dst(RZ_NONNULL const cs_insn *insn) {
rz_return_val_if_fail(insn && insn->detail, false);
if (insn->detail->m68k.op_count != 1 || !rz_m68k_fpu_insn_has_data_dst(insn->id)) {
return false;
}
if (rz_m68k_fpu_insn_single_op_is_complete(insn)) {
return false;
}
return rz_m68k_reg_is_fpu(rz_m68k_fpu_insn_extension_dst_reg(insn));
}
RZ_IPI bool rz_m68k_fpu_size_is_extended(RZ_NONNULL const cs_m68k *m68k) {
rz_return_val_if_fail(m68k, false);
return m68k->op_size.type == M68K_SIZE_TYPE_FPU && m68k->op_size.fpu_size == M68K_FPU_SIZE_EXTENDED;
}
#ifdef RZ_CAPSTONE_HAS_M68K_FP_FORMATS
RZ_IPI bool rz_m68k_fpu_size_is_packed(RZ_NONNULL const cs_m68k *m68k) {
rz_return_val_if_fail(m68k, false);
return m68k->op_size.type == M68K_SIZE_TYPE_FPU && m68k->op_size.fpu_size == M68K_FPU_SIZE_PACKED;
}
#endif
RZ_IPI bool rz_m68k_op_detail_is_invalid(RZ_NULLABLE const cs_m68k_op *op) {
return !op ||
op->type == M68K_OP_INVALID ||
(op->type == M68K_OP_REG && op->reg == M68K_REG_INVALID) ||
(op->type == M68K_OP_MEM && op->address_mode == M68K_AM_NONE);
}
RZ_IPI bool rz_m68k_fpu_op_is_illegal_read(RZ_NONNULL const cs_m68k *m68k, RZ_NONNULL const cs_m68k_op *op) {
rz_return_val_if_fail(m68k && op, false);
if (rz_m68k_op_is_fpu_reg(op)) {
return false;
}
// Capstone produced no usable operand data; there is nothing to read.
if (rz_m68k_op_detail_is_invalid(op)) {
return true;
}
// A GPR cannot supply an FPU-sized operand (e.g. fmove.x d0, fp0 is illegal).
if (m68k->op_size.type == M68K_SIZE_TYPE_FPU && rz_m68k_op_is_gpr(op)) {
return true;
}
// FP immediates use dedicated M68K_OP_FP_* types; an integer immediate
// cannot encode an FPU-sized value.
if (m68k->op_size.type == M68K_SIZE_TYPE_FPU && op->type == M68K_OP_IMM) {
return true;
}
// An immediate cannot encode an 80-bit extended precision value.
if (op->type == M68K_OP_IMM && rz_m68k_detail_op_bits(m68k, 80) == 80) {
return true;
}
return false;
}
static bool m68k_address_mode_is_pc_relative(m68k_address_mode mode) {
switch (mode) {
case M68K_AM_PCI_DISP:
case M68K_AM_PCI_INDEX_8_BIT_DISP:
case M68K_AM_PCI_INDEX_BASE_DISP:
case M68K_AM_PC_MEMI_PRE_INDEX:
case M68K_AM_PC_MEMI_POST_INDEX:
return true;
default:
return false;
}
}
RZ_IPI bool rz_m68k_fpu_op_is_illegal_write(RZ_NONNULL const cs_m68k *m68k, RZ_NONNULL const cs_m68k_op *op) {
rz_return_val_if_fail(m68k && op, false);
if (rz_m68k_op_is_fpu_reg(op)) {
return false;
}
if (rz_m68k_op_detail_is_invalid(op)) {
return true;
}
// PC-relative addressing is never alterable and cannot be a destination.
if (m68k_address_mode_is_pc_relative(op->address_mode)) {
return true;
}
if (m68k->op_size.type == M68K_SIZE_TYPE_FPU) {
return !rz_m68k_op_is_mem_addr(op);
}
return op->type != M68K_OP_REG && !rz_m68k_op_is_mem_addr(op);
}
RZ_IPI bool rz_m68k_fpu_insn_data_alias_has_dst(RZ_NONNULL const cs_insn *insn) {
rz_return_val_if_fail(insn && insn->detail, false);
if (insn->detail->m68k.op_count < 1) {
return false;
}
cs_m68k_op hidden_dst;
const cs_m68k_op *dst = rz_m68k_fpu_insn_second_op_or_hidden_dst(insn, &hidden_dst);
return dst && rz_m68k_op_is_fpu_reg(dst);
}