mirror of
https://github.com/Decompollaborate/rabbitizer.git
synced 2025-04-09 18:44:40 +00:00
* setup ALLEGREX * more setup * clo * fix * Implement SPECIAL_RS and SPECIAL_SA instructions * more table placeholders * Implement bshfl instructions * Rename to R4000Allegrex * Implement SPECIAL instructions * Add tests * Remove some duplicated tests * Implement SPECIAL3 instructions * fix bug in test * update * Implement COPz * Implement SPECIAL2 instructions * Implement COP1 * Yeet cop3 * som tests * bvf, bvfl, bvt, bvtl * fix bshfl prefix * need to implement the vfpu registers * implement vt_7? * R4000AllegrexVF -> R4000AllegrexVScalar * Add test suite to compare with the sn toolchain decoding * more vfpu test cases * forgor this * I can't decide how to name these registers * Prepare tables for all register types * Fix typo * Implement vector scalar register operands * Implement quad registers * Fix tests? * svl.q, svr.q * Implement a bunch of vfpu0 instructions * implement registers for `.t` and `.p` instructions * Implement VFPU1 instructions * bleh * VFPU1, VFPU3 and `vcmp.` * Fix wrong register type on some instructions * start vfpu3 * Implement VFPU3 instructions * start categorizing VFPU4 * Categorize VFPU5 * VFPU6 identification * Identify VFPU7 * COP2 is weird * organize COP2 a bit * Add test cases for VFPU4 FMT * VFPU4 FMT2 stuff * VFPU4 FMT3 stuff * VFPU5 stuff * VFPU6 stuff * VFPU7 stuff * Implement COP2 instructions * Implement vmov, vabs and vneg * VPFU4 FMT0 FMT0 FMT0 implemented * VFPU FMT0 FMT0 FMT2 * vnrcp, vnsin, vrexp2 * vrnds, vrndi, vrndf1, vrndf2 * Change tests a bit * vf2h, vh2f, vsbz, vlgb * vuc2ifs, vc2i, vus2i, vs2i, vi2uc, vi2c, vi2us, vi2s * vsrt1, vsrt2, vbfy1, vbfy2, vocp, vsocp, vfad, vavg * vsrt3, vsrt4, vsgn * vmfvc and vmtvc placeholders * vt4444, vt5551, vt5650 * vcst placeholder * vf2in * vf2iz * vf2iu, vf2id, vi2f * vcmovt, vcmovf * vwbn.s, viim.s, vfim.s * vpfxs, vpfxt, vpfxd, vnop, vsync, vflush * vmmov, vmidt, vmzero, vmone * vrot * vmmul, vhtfm2, vtfm2, vhtfm3, vtfm3, vhtfm4, vtfm4, vmscl, vcrsp, vqmul * Implement matrix operands * fix matrix operands * Fix `illegal` tests * hack out a way to check the test cases are assemblable * test-fixing: branches * fix more test cases * fix vmfvc and vmtvc * more test fixing * vdiv and fix operand R323 * more test fixing * Fix matrix operands * implement vcmp comparisons * fix vsync2 * vsqrt and vrndf1 fixes * Implement "constant" operand for `vcst` * Add missing operand of vf2in, vf2iz, vf2iu, vf2id, vi2f * Add missing vcmovt and vcmovf operands * Add missing vwbn operand * Tests cases for vmmul * Fix vtfm2 * Implement "transpose matrix register" * Add placeholders for the remaining missing operands * Implement viim operand * Implement vrot code operand * placeholders for rp and wp operands * test cases for vpfxs, vpfxt and vpfxd * Properly implement rpx, rpy, rpz and rpw * Properly implement wpx, wpy, wpz and wpw operands * Implement vfim * changelog * readme * some cleanup * Restructure some tables * more table restructure * fix tests * more table yeeting * more cleanup * more cleanup * reanming * moar * fmt
726 lines
26 KiB
Rust
726 lines
26 KiB
Rust
/* SPDX-FileCopyrightText: © 2022-2024 Decompollaborate */
|
|
/* SPDX-License-Identifier: MIT */
|
|
|
|
use crate::{
|
|
access_type_enum, instr_category_enum, instr_descriptor, instr_id_enum, instr_id_type_enum,
|
|
instr_suffix_enum, operand_type_enum, registers_enum, utils,
|
|
};
|
|
|
|
#[repr(C)]
|
|
#[derive(Debug, Clone)]
|
|
#[allow(non_camel_case_types)]
|
|
pub struct Instruction {
|
|
word: u32,
|
|
_mandatorybits: u32,
|
|
pub unique_id: instr_id_enum::InstrId,
|
|
descriptor: *const instr_descriptor::InstrDescriptor,
|
|
instr_id_type: instr_id_type_enum::InstrIdType,
|
|
pub vram: u32,
|
|
_handwritten_category: bool,
|
|
pub in_handwritten_function: bool,
|
|
pub category: instr_category_enum::InstrCategory,
|
|
pub flags: u32,
|
|
}
|
|
|
|
#[link(name = "rabbitizer", kind = "static")]
|
|
extern "C" {
|
|
fn RabbitizerInstrId_getOpcodeName(
|
|
unique_id: instr_id_enum::InstrId,
|
|
) -> *const core::ffi::c_char;
|
|
}
|
|
|
|
extern "C" {
|
|
fn RabInstrIdType_getName(id_type: instr_id_type_enum::InstrIdType)
|
|
-> *const core::ffi::c_char;
|
|
}
|
|
|
|
extern "C" {
|
|
fn RabbitizerInstruction_init(self_: *mut Instruction, word: u32, vram: u32);
|
|
fn RabbitizerInstruction_destroy(self_: *mut Instruction);
|
|
|
|
fn RabbitizerInstruction_processUniqueId(self_: *mut Instruction);
|
|
}
|
|
|
|
extern "C" {
|
|
fn RabbitizerInstructionRsp_init(self_: *mut Instruction, word: u32, vram: u32);
|
|
fn RabbitizerInstructionRsp_destroy(self_: *mut Instruction);
|
|
|
|
fn RabbitizerInstructionRsp_processUniqueId(self_: *mut Instruction);
|
|
}
|
|
|
|
extern "C" {
|
|
fn RabbitizerInstructionR3000GTE_init(self_: *mut Instruction, word: u32, vram: u32);
|
|
fn RabbitizerInstructionR3000GTE_destroy(self_: *mut Instruction);
|
|
|
|
fn RabbitizerInstructionR3000GTE_processUniqueId(self_: *mut Instruction);
|
|
}
|
|
|
|
extern "C" {
|
|
fn RabbitizerInstructionR5900_init(self_: *mut Instruction, word: u32, vram: u32);
|
|
fn RabbitizerInstructionR5900_destroy(self_: *mut Instruction);
|
|
|
|
fn RabbitizerInstructionR5900_processUniqueId(self_: *mut Instruction);
|
|
}
|
|
|
|
extern "C" {
|
|
fn RabbitizerInstructionR4000Allegrex_init(self_: *mut Instruction, word: u32, vram: u32);
|
|
fn RabbitizerInstructionR4000Allegrex_destroy(self_: *mut Instruction);
|
|
|
|
fn RabbitizerInstructionR4000Allegrex_processUniqueId(self_: *mut Instruction);
|
|
}
|
|
|
|
extern "C" {
|
|
fn RabbitizerInstruction_getRaw(self_: *const Instruction) -> u32;
|
|
fn RabbitizerInstruction_getProcessedImmediate(self_: *const Instruction) -> i32;
|
|
fn RabbitizerInstruction_getInstrIndexAsVram(self_: *const Instruction) -> u32;
|
|
fn RabbitizerInstruction_getBranchOffset(self_: *const Instruction) -> i32;
|
|
|
|
fn RabbitizerInstruction_getBranchOffsetGeneric(self_: *const Instruction) -> i32;
|
|
fn RabbitizerInstruction_getBranchVramGeneric(self_: *const Instruction) -> u32;
|
|
fn RabbitizerInstruction_getDestinationGpr(self_: *const Instruction) -> i8;
|
|
fn RabbitizerInstruction_outputsToGprZero(self_: *const Instruction) -> bool;
|
|
fn RabbitizerInstruction_blankOut(self_: *mut Instruction);
|
|
fn RabbitizerInstruction_isImplemented(self_: *const Instruction) -> bool;
|
|
fn RabbitizerInstruction_isLikelyHandwritten(self_: *const Instruction) -> bool;
|
|
fn RabbitizerInstruction_isNop(self_: *const Instruction) -> bool;
|
|
fn RabbitizerInstruction_isUnconditionalBranch(self_: *const Instruction) -> bool;
|
|
fn RabbitizerInstruction_isReturn(self_: *const Instruction) -> bool;
|
|
fn RabbitizerInstruction_isJumptableJump(self_: *const Instruction) -> bool;
|
|
|
|
fn RabbitizerInstruction_hasDelaySlot(self_: *const Instruction) -> bool;
|
|
|
|
fn RabbitizerInstruction_sameOpcode(
|
|
self_: *const Instruction,
|
|
other: *const Instruction,
|
|
) -> bool;
|
|
fn RabbitizerInstruction_sameOpcodeButDifferentArguments(
|
|
self_: *const Instruction,
|
|
other: *const Instruction,
|
|
) -> bool;
|
|
fn RabbitizerInstruction_hasOperand(
|
|
self_: *const Instruction,
|
|
operand: operand_type_enum::OperandType,
|
|
) -> bool;
|
|
fn RabbitizerInstruction_hasOperandAlias(
|
|
self_: *const Instruction,
|
|
operand: operand_type_enum::OperandType,
|
|
) -> bool;
|
|
|
|
fn RabbitizerInstruction_isValid(self_: *const Instruction) -> bool;
|
|
|
|
fn RabbitizerInstruction_getSizeForBuffer(
|
|
self_: *const Instruction,
|
|
immOverrideLength: utils::SizeT,
|
|
extraLJust: core::ffi::c_int,
|
|
) -> utils::SizeT;
|
|
fn RabbitizerInstruction_disassemble(
|
|
self_: *const Instruction,
|
|
dst: *mut core::ffi::c_char,
|
|
immOverride: *const core::ffi::c_char,
|
|
immOverrideLength: utils::SizeT,
|
|
extraLJust: core::ffi::c_int,
|
|
) -> utils::SizeT;
|
|
}
|
|
|
|
extern "C" {
|
|
fn RabbitizerInstrDescriptor_instrSuffix(
|
|
self_: *const instr_descriptor::InstrDescriptor,
|
|
) -> instr_suffix_enum::InstrSuffix;
|
|
fn RabbitizerInstrDescriptor_isBranch(self_: *const instr_descriptor::InstrDescriptor) -> bool;
|
|
fn RabbitizerInstrDescriptor_isBranchLikely(
|
|
self_: *const instr_descriptor::InstrDescriptor,
|
|
) -> bool;
|
|
fn RabbitizerInstrDescriptor_isJump(self_: *const instr_descriptor::InstrDescriptor) -> bool;
|
|
fn RabbitizerInstrDescriptor_isJumpWithAddress(
|
|
self_: *const instr_descriptor::InstrDescriptor,
|
|
) -> bool;
|
|
fn RabbitizerInstrDescriptor_isTrap(self_: *const instr_descriptor::InstrDescriptor) -> bool;
|
|
fn RabbitizerInstrDescriptor_isFloat(self_: *const instr_descriptor::InstrDescriptor) -> bool;
|
|
fn RabbitizerInstrDescriptor_isDouble(self_: *const instr_descriptor::InstrDescriptor) -> bool;
|
|
fn RabbitizerInstrDescriptor_isUnsigned(
|
|
self_: *const instr_descriptor::InstrDescriptor,
|
|
) -> bool;
|
|
|
|
fn RabbitizerInstrDescriptor_modifiesRs(
|
|
self_: *const instr_descriptor::InstrDescriptor,
|
|
) -> bool;
|
|
fn RabbitizerInstrDescriptor_modifiesRt(
|
|
self_: *const instr_descriptor::InstrDescriptor,
|
|
) -> bool;
|
|
fn RabbitizerInstrDescriptor_modifiesRd(
|
|
self_: *const instr_descriptor::InstrDescriptor,
|
|
) -> bool;
|
|
fn RabbitizerInstrDescriptor_readsRs(self_: *const instr_descriptor::InstrDescriptor) -> bool;
|
|
fn RabbitizerInstrDescriptor_readsRt(self_: *const instr_descriptor::InstrDescriptor) -> bool;
|
|
fn RabbitizerInstrDescriptor_readsRd(self_: *const instr_descriptor::InstrDescriptor) -> bool;
|
|
|
|
fn RabbitizerInstrDescriptor_readsHI(self_: *const instr_descriptor::InstrDescriptor) -> bool;
|
|
fn RabbitizerInstrDescriptor_readsLO(self_: *const instr_descriptor::InstrDescriptor) -> bool;
|
|
fn RabbitizerInstrDescriptor_modifiesHI(
|
|
self_: *const instr_descriptor::InstrDescriptor,
|
|
) -> bool;
|
|
fn RabbitizerInstrDescriptor_modifiesLO(
|
|
self_: *const instr_descriptor::InstrDescriptor,
|
|
) -> bool;
|
|
|
|
fn RabbitizerInstrDescriptor_modifiesFs(
|
|
self_: *const instr_descriptor::InstrDescriptor,
|
|
) -> bool;
|
|
fn RabbitizerInstrDescriptor_modifiesFt(
|
|
self_: *const instr_descriptor::InstrDescriptor,
|
|
) -> bool;
|
|
fn RabbitizerInstrDescriptor_modifiesFd(
|
|
self_: *const instr_descriptor::InstrDescriptor,
|
|
) -> bool;
|
|
fn RabbitizerInstrDescriptor_readsFs(self_: *const instr_descriptor::InstrDescriptor) -> bool;
|
|
fn RabbitizerInstrDescriptor_readsFt(self_: *const instr_descriptor::InstrDescriptor) -> bool;
|
|
fn RabbitizerInstrDescriptor_readsFd(self_: *const instr_descriptor::InstrDescriptor) -> bool;
|
|
|
|
fn RabbitizerInstrDescriptor_notEmittedByCompilers(
|
|
self_: *const instr_descriptor::InstrDescriptor,
|
|
) -> bool;
|
|
fn RabbitizerInstrDescriptor_canBeHi(self_: *const instr_descriptor::InstrDescriptor) -> bool;
|
|
fn RabbitizerInstrDescriptor_canBeLo(self_: *const instr_descriptor::InstrDescriptor) -> bool;
|
|
fn RabbitizerInstrDescriptor_doesLink(self_: *const instr_descriptor::InstrDescriptor) -> bool;
|
|
fn RabbitizerInstrDescriptor_doesDereference(
|
|
self_: *const instr_descriptor::InstrDescriptor,
|
|
) -> bool;
|
|
fn RabbitizerInstrDescriptor_doesLoad(self_: *const instr_descriptor::InstrDescriptor) -> bool;
|
|
fn RabbitizerInstrDescriptor_doesStore(self_: *const instr_descriptor::InstrDescriptor)
|
|
-> bool;
|
|
fn RabbitizerInstrDescriptor_maybeIsMove(
|
|
self_: *const instr_descriptor::InstrDescriptor,
|
|
) -> bool;
|
|
fn RabbitizerInstrDescriptor_isPseudo(self_: *const instr_descriptor::InstrDescriptor) -> bool;
|
|
fn RabbitizerInstrDescriptor_getAccessType(
|
|
self_: *const instr_descriptor::InstrDescriptor,
|
|
) -> access_type_enum::AccessType;
|
|
fn RabbitizerInstrDescriptor_doesUnsignedMemoryAccess(
|
|
self_: *const instr_descriptor::InstrDescriptor,
|
|
) -> bool;
|
|
}
|
|
|
|
impl Drop for Instruction {
|
|
fn drop(&mut self) {
|
|
unsafe {
|
|
match self.category {
|
|
instr_category_enum::InstrCategory::CPU => {
|
|
RabbitizerInstruction_destroy(self);
|
|
}
|
|
instr_category_enum::InstrCategory::RSP => {
|
|
RabbitizerInstructionRsp_destroy(self);
|
|
}
|
|
instr_category_enum::InstrCategory::R3000GTE => {
|
|
RabbitizerInstructionR3000GTE_destroy(self);
|
|
}
|
|
instr_category_enum::InstrCategory::R5900 => {
|
|
RabbitizerInstructionR5900_destroy(self);
|
|
}
|
|
instr_category_enum::InstrCategory::R4000ALLEGREX => {
|
|
RabbitizerInstructionR4000Allegrex_destroy(self);
|
|
}
|
|
instr_category_enum::InstrCategory::MAX => {
|
|
core::panic!();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Instruction {
|
|
pub fn new(word: u32, vram: u32, instr_cat: instr_category_enum::InstrCategory) -> Self {
|
|
unsafe {
|
|
let mut instr: std::mem::MaybeUninit<Instruction> = std::mem::MaybeUninit::uninit();
|
|
match instr_cat {
|
|
instr_category_enum::InstrCategory::CPU => {
|
|
RabbitizerInstruction_init(instr.as_mut_ptr(), word, vram);
|
|
RabbitizerInstruction_processUniqueId(instr.as_mut_ptr());
|
|
}
|
|
instr_category_enum::InstrCategory::RSP => {
|
|
RabbitizerInstructionRsp_init(instr.as_mut_ptr(), word, vram);
|
|
RabbitizerInstructionRsp_processUniqueId(instr.as_mut_ptr());
|
|
}
|
|
instr_category_enum::InstrCategory::R3000GTE => {
|
|
RabbitizerInstructionR3000GTE_init(instr.as_mut_ptr(), word, vram);
|
|
RabbitizerInstructionR3000GTE_processUniqueId(instr.as_mut_ptr());
|
|
}
|
|
instr_category_enum::InstrCategory::R5900 => {
|
|
RabbitizerInstructionR5900_init(instr.as_mut_ptr(), word, vram);
|
|
RabbitizerInstructionR5900_processUniqueId(instr.as_mut_ptr());
|
|
}
|
|
instr_category_enum::InstrCategory::R4000ALLEGREX => {
|
|
RabbitizerInstructionR4000Allegrex_init(instr.as_mut_ptr(), word, vram);
|
|
RabbitizerInstructionR4000Allegrex_processUniqueId(instr.as_mut_ptr());
|
|
}
|
|
instr_category_enum::InstrCategory::MAX => {
|
|
core::panic!();
|
|
} // _ => not used in purpose
|
|
}
|
|
instr.assume_init()
|
|
}
|
|
}
|
|
|
|
pub fn get_opcode(&self) -> u32 {
|
|
utils::shiftr(self.word, 26, 6)
|
|
}
|
|
|
|
pub fn get_rs(&self) -> u32 {
|
|
if !self.has_operand_alias(operand_type_enum::OperandType::cpu_rs) {
|
|
core::panic!();
|
|
}
|
|
|
|
utils::shiftr(self.word, 21, 5)
|
|
}
|
|
|
|
pub fn get_rs_o32(&self) -> registers_enum::registers::GprO32 {
|
|
self.get_rs().try_into().unwrap()
|
|
}
|
|
|
|
pub fn get_rs_n32(&self) -> registers_enum::registers::GprN32 {
|
|
self.get_rs().try_into().unwrap()
|
|
}
|
|
|
|
pub fn get_rt(&self) -> u32 {
|
|
if !self.has_operand_alias(operand_type_enum::OperandType::cpu_rt) {
|
|
core::panic!();
|
|
}
|
|
|
|
utils::shiftr(self.word, 16, 5)
|
|
}
|
|
|
|
pub fn get_rt_o32(&self) -> registers_enum::registers::GprO32 {
|
|
self.get_rt().try_into().unwrap()
|
|
}
|
|
|
|
pub fn get_rt_n32(&self) -> registers_enum::registers::GprN32 {
|
|
self.get_rt().try_into().unwrap()
|
|
}
|
|
|
|
pub fn get_rd(&self) -> u32 {
|
|
if !self.has_operand_alias(operand_type_enum::OperandType::cpu_rd) {
|
|
core::panic!();
|
|
}
|
|
|
|
utils::shiftr(self.word, 11, 5)
|
|
}
|
|
|
|
pub fn get_rd_o32(&self) -> registers_enum::registers::GprO32 {
|
|
self.get_rd().try_into().unwrap()
|
|
}
|
|
|
|
pub fn get_rd_n32(&self) -> registers_enum::registers::GprN32 {
|
|
self.get_rd().try_into().unwrap()
|
|
}
|
|
|
|
pub fn get_sa(&self) -> u32 {
|
|
if !self.has_operand_alias(operand_type_enum::OperandType::cpu_sa) {
|
|
core::panic!();
|
|
}
|
|
|
|
utils::shiftr(self.word, 6, 5)
|
|
}
|
|
|
|
pub fn get_function(&self) -> u32 {
|
|
//if !self.has_operand_alias(operand_type_enum::OperandType::cpu_function) {
|
|
// core::panic!();
|
|
//}
|
|
|
|
utils::shiftr(self.word, 0, 6)
|
|
}
|
|
|
|
pub fn get_cop0d(&self) -> u32 {
|
|
if !self.has_operand_alias(operand_type_enum::OperandType::cpu_cop0d) {
|
|
core::panic!();
|
|
}
|
|
|
|
utils::shiftr(self.word, 11, 5)
|
|
}
|
|
|
|
pub fn get_cop0d_cop0(&self) -> registers_enum::registers::Cop0 {
|
|
self.get_cop0d().try_into().unwrap()
|
|
}
|
|
|
|
pub fn get_instr_index(&self) -> u32 {
|
|
utils::shiftr(self.word, 0, 26)
|
|
}
|
|
|
|
pub fn get_immediate(&self) -> u16 {
|
|
if !self.has_operand_alias(operand_type_enum::OperandType::cpu_immediate) {
|
|
core::panic!();
|
|
}
|
|
|
|
utils::shiftr(self.word, 0, 16).try_into().unwrap()
|
|
}
|
|
|
|
pub fn get_code(&self) -> u32 {
|
|
if !self.has_operand_alias(operand_type_enum::OperandType::cpu_code) {
|
|
core::panic!();
|
|
}
|
|
|
|
utils::shiftr(self.word, 6, 20)
|
|
}
|
|
|
|
pub fn get_code_upper(&self) -> u32 {
|
|
if !self.has_operand_alias(operand_type_enum::OperandType::cpu_code) {
|
|
core::panic!();
|
|
}
|
|
|
|
utils::shiftr(self.word, 16, 10)
|
|
}
|
|
|
|
pub fn get_code_lower(&self) -> u32 {
|
|
if !self.has_operand_alias(operand_type_enum::OperandType::cpu_code_lower) {
|
|
core::panic!();
|
|
}
|
|
|
|
utils::shiftr(self.word, 6, 10)
|
|
}
|
|
|
|
pub fn get_copraw(&self) -> u32 {
|
|
if !self.has_operand_alias(operand_type_enum::OperandType::cpu_copraw) {
|
|
core::panic!();
|
|
}
|
|
|
|
utils::shiftr(self.word, 0, 25)
|
|
}
|
|
|
|
pub fn get_fs(&self) -> u32 {
|
|
if !self.has_operand_alias(operand_type_enum::OperandType::cpu_fs) {
|
|
core::panic!();
|
|
}
|
|
|
|
utils::shiftr(self.word, 11, 5)
|
|
}
|
|
|
|
pub fn get_fs_o32(&self) -> registers_enum::registers::Cop1O32 {
|
|
self.get_fs().try_into().unwrap()
|
|
}
|
|
|
|
pub fn get_fs_n32(&self) -> registers_enum::registers::Cop1N32 {
|
|
self.get_fs().try_into().unwrap()
|
|
}
|
|
|
|
pub fn get_fs_n64(&self) -> registers_enum::registers::Cop1N64 {
|
|
self.get_fs().try_into().unwrap()
|
|
}
|
|
|
|
pub fn get_ft(&self) -> u32 {
|
|
if !self.has_operand_alias(operand_type_enum::OperandType::cpu_ft) {
|
|
core::panic!();
|
|
}
|
|
|
|
utils::shiftr(self.word, 16, 5)
|
|
}
|
|
|
|
pub fn get_ft_o32(&self) -> registers_enum::registers::Cop1O32 {
|
|
self.get_ft().try_into().unwrap()
|
|
}
|
|
|
|
pub fn get_ft_n32(&self) -> registers_enum::registers::Cop1N32 {
|
|
self.get_ft().try_into().unwrap()
|
|
}
|
|
|
|
pub fn get_ft_n64(&self) -> registers_enum::registers::Cop1N64 {
|
|
self.get_ft().try_into().unwrap()
|
|
}
|
|
|
|
pub fn get_fd(&self) -> u32 {
|
|
if !self.has_operand_alias(operand_type_enum::OperandType::cpu_fd) {
|
|
core::panic!();
|
|
}
|
|
|
|
utils::shiftr(self.word, 6, 5)
|
|
}
|
|
|
|
pub fn get_fd_o32(&self) -> registers_enum::registers::Cop1O32 {
|
|
self.get_fd().try_into().unwrap()
|
|
}
|
|
|
|
pub fn get_fd_n32(&self) -> registers_enum::registers::Cop1N32 {
|
|
self.get_fd().try_into().unwrap()
|
|
}
|
|
|
|
pub fn get_fd_n64(&self) -> registers_enum::registers::Cop1N64 {
|
|
self.get_fd().try_into().unwrap()
|
|
}
|
|
|
|
pub fn get_cop1cs(&self) -> u32 {
|
|
if !self.has_operand_alias(operand_type_enum::OperandType::cpu_cop1cs) {
|
|
core::panic!();
|
|
}
|
|
|
|
utils::shiftr(self.word, 11, 5)
|
|
}
|
|
|
|
pub fn get_cop1cs_cop1control(&self) -> registers_enum::registers::Cop1Control {
|
|
self.get_cop1cs().try_into().unwrap()
|
|
}
|
|
|
|
pub fn get_cop2t(&self) -> u32 {
|
|
if !self.has_operand_alias(operand_type_enum::OperandType::cpu_cop2t) {
|
|
core::panic!();
|
|
}
|
|
|
|
utils::shiftr(self.word, 16, 5)
|
|
}
|
|
|
|
pub fn get_cop2t_cop2(&self) -> registers_enum::registers::Cop2 {
|
|
self.get_cop2t().try_into().unwrap()
|
|
}
|
|
|
|
pub fn flags_get_r5900_disasm_as_data(&self) -> utils::TrinaryValue {
|
|
utils::shiftr(self.flags, 0, 2).try_into().unwrap()
|
|
}
|
|
pub fn flags_set_r5900_disasm_as_data(&mut self, value: utils::TrinaryValue) {
|
|
self.flags = utils::bitrepack(self.flags, value.into(), 0, 2);
|
|
}
|
|
|
|
pub fn flags_get_r5900_use_dollar(&self) -> utils::TrinaryValue {
|
|
utils::shiftr(self.flags, 2, 2).try_into().unwrap()
|
|
}
|
|
pub fn flags_set_r5900_use_dollar(&mut self, value: utils::TrinaryValue) {
|
|
self.flags = utils::bitrepack(self.flags, value.into(), 2, 2);
|
|
}
|
|
|
|
pub fn instr_id_type_name(&self) -> &'static str {
|
|
unsafe { std::ffi::CStr::from_ptr(RabInstrIdType_getName(self.instr_id_type)) }
|
|
.to_str()
|
|
.unwrap()
|
|
}
|
|
|
|
pub fn raw(&self) -> u32 {
|
|
unsafe { RabbitizerInstruction_getRaw(self) }
|
|
}
|
|
pub fn processed_immediate(&self) -> i32 {
|
|
unsafe { RabbitizerInstruction_getProcessedImmediate(self) }
|
|
}
|
|
pub fn instr_index_as_vram(&self) -> u32 {
|
|
unsafe { RabbitizerInstruction_getInstrIndexAsVram(self) }
|
|
}
|
|
pub fn branch_offset(&self) -> i32 {
|
|
unsafe { RabbitizerInstruction_getBranchOffset(self) }
|
|
}
|
|
|
|
pub fn branch_offset_generic(&self) -> i32 {
|
|
unsafe { RabbitizerInstruction_getBranchOffsetGeneric(self) }
|
|
}
|
|
|
|
pub fn branch_vram_generic(&self) -> u32 {
|
|
unsafe { RabbitizerInstruction_getBranchVramGeneric(self) }
|
|
}
|
|
pub fn destination_gpr(&self) -> Option<u32> {
|
|
unsafe {
|
|
let reg: i8 = RabbitizerInstruction_getDestinationGpr(self);
|
|
|
|
if reg < 0 {
|
|
return None;
|
|
}
|
|
Some(reg as u32)
|
|
}
|
|
}
|
|
pub fn outputs_to_gpr_zero(&self) -> bool {
|
|
unsafe { RabbitizerInstruction_outputsToGprZero(self) }
|
|
}
|
|
pub fn opcode_name(&self) -> &'static str {
|
|
unsafe {
|
|
std::ffi::CStr::from_ptr(RabbitizerInstrId_getOpcodeName(self.unique_id))
|
|
.to_str()
|
|
.unwrap()
|
|
}
|
|
}
|
|
|
|
pub fn blank_out(mut self) {
|
|
unsafe { RabbitizerInstruction_blankOut(&mut self) }
|
|
}
|
|
|
|
#[deprecated(since = "1.8.4", note = "please use `is_valid` instead")]
|
|
pub fn is_implemented(&self) -> bool {
|
|
unsafe { RabbitizerInstruction_isImplemented(self) }
|
|
}
|
|
pub fn is_likely_handwritten(&self) -> bool {
|
|
unsafe { RabbitizerInstruction_isLikelyHandwritten(self) }
|
|
}
|
|
pub fn is_nop(&self) -> bool {
|
|
unsafe { RabbitizerInstruction_isNop(self) }
|
|
}
|
|
pub fn is_unconditional_branch(&self) -> bool {
|
|
unsafe { RabbitizerInstruction_isUnconditionalBranch(self) }
|
|
}
|
|
|
|
pub fn is_return(&self) -> bool {
|
|
unsafe { RabbitizerInstruction_isReturn(self) }
|
|
}
|
|
pub fn is_jumptable_jump(&self) -> bool {
|
|
unsafe { RabbitizerInstruction_isJumptableJump(self) }
|
|
}
|
|
|
|
pub fn has_delay_slot(&self) -> bool {
|
|
unsafe { RabbitizerInstruction_hasDelaySlot(self) }
|
|
}
|
|
|
|
pub fn same_opcode(&self, other: &Instruction) -> bool {
|
|
unsafe { RabbitizerInstruction_sameOpcode(self, other) }
|
|
}
|
|
pub fn same_opcode_but_different_arguments(&self, other: &Instruction) -> bool {
|
|
unsafe { RabbitizerInstruction_sameOpcodeButDifferentArguments(self, other) }
|
|
}
|
|
|
|
pub fn has_operand(&self, operand: operand_type_enum::OperandType) -> bool {
|
|
unsafe { RabbitizerInstruction_hasOperand(self, operand) }
|
|
}
|
|
pub fn has_operand_alias(&self, operand: operand_type_enum::OperandType) -> bool {
|
|
unsafe { RabbitizerInstruction_hasOperandAlias(self, operand) }
|
|
}
|
|
|
|
pub fn is_valid(&self) -> bool {
|
|
unsafe { RabbitizerInstruction_isValid(self) }
|
|
}
|
|
|
|
pub fn get_operand_type(&self, index: usize) -> operand_type_enum::OperandType {
|
|
unsafe { &*self.descriptor }.get_operand_type(index)
|
|
}
|
|
|
|
pub fn get_operands_slice(&self) -> &[operand_type_enum::OperandType] {
|
|
unsafe { &*self.descriptor }.operands_slice()
|
|
}
|
|
|
|
pub fn instr_suffix(&self) -> instr_suffix_enum::InstrSuffix {
|
|
unsafe { RabbitizerInstrDescriptor_instrSuffix(self.descriptor) }
|
|
}
|
|
pub fn is_branch(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_isBranch(self.descriptor) }
|
|
}
|
|
pub fn is_branch_likely(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_isBranchLikely(self.descriptor) }
|
|
}
|
|
pub fn is_jump(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_isJump(self.descriptor) }
|
|
}
|
|
pub fn is_jump_with_address(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_isJumpWithAddress(self.descriptor) }
|
|
}
|
|
pub fn is_trap(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_isTrap(self.descriptor) }
|
|
}
|
|
pub fn is_float(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_isFloat(self.descriptor) }
|
|
}
|
|
pub fn is_double(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_isDouble(self.descriptor) }
|
|
}
|
|
pub fn is_unsigned(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_isUnsigned(self.descriptor) }
|
|
}
|
|
pub fn modifies_rs(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_modifiesRs(self.descriptor) }
|
|
}
|
|
pub fn modifies_rt(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_modifiesRt(self.descriptor) }
|
|
}
|
|
pub fn modifies_rd(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_modifiesRd(self.descriptor) }
|
|
}
|
|
pub fn reads_rs(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_readsRs(self.descriptor) }
|
|
}
|
|
pub fn reads_rt(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_readsRt(self.descriptor) }
|
|
}
|
|
pub fn reads_rd(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_readsRd(self.descriptor) }
|
|
}
|
|
pub fn reads_hi(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_readsHI(self.descriptor) }
|
|
}
|
|
pub fn reads_lo(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_readsLO(self.descriptor) }
|
|
}
|
|
pub fn modifies_hi(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_modifiesHI(self.descriptor) }
|
|
}
|
|
pub fn modifies_lo(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_modifiesLO(self.descriptor) }
|
|
}
|
|
pub fn modifies_fs(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_modifiesFs(self.descriptor) }
|
|
}
|
|
pub fn modifies_ft(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_modifiesFt(self.descriptor) }
|
|
}
|
|
pub fn modifies_fd(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_modifiesFd(self.descriptor) }
|
|
}
|
|
pub fn reads_fs(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_readsFs(self.descriptor) }
|
|
}
|
|
pub fn reads_ft(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_readsFt(self.descriptor) }
|
|
}
|
|
pub fn reads_fd(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_readsFd(self.descriptor) }
|
|
}
|
|
|
|
// @deprecated
|
|
pub fn not_emited_by_compilers(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_notEmittedByCompilers(self.descriptor) }
|
|
}
|
|
pub fn not_emitted_by_compilers(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_notEmittedByCompilers(self.descriptor) }
|
|
}
|
|
pub fn can_be_hi(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_canBeHi(self.descriptor) }
|
|
}
|
|
pub fn can_be_lo(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_canBeLo(self.descriptor) }
|
|
}
|
|
pub fn does_link(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_doesLink(self.descriptor) }
|
|
}
|
|
pub fn does_dereference(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_doesDereference(self.descriptor) }
|
|
}
|
|
pub fn does_load(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_doesLoad(self.descriptor) }
|
|
}
|
|
pub fn does_store(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_doesStore(self.descriptor) }
|
|
}
|
|
pub fn maybe_is_move(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_maybeIsMove(self.descriptor) }
|
|
}
|
|
pub fn is_pseudo(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_isPseudo(self.descriptor) }
|
|
}
|
|
pub fn access_type(&self) -> access_type_enum::AccessType {
|
|
unsafe { RabbitizerInstrDescriptor_getAccessType(self.descriptor) }
|
|
}
|
|
pub fn does_unsigned_memory_access(&self) -> bool {
|
|
unsafe { RabbitizerInstrDescriptor_doesUnsignedMemoryAccess(self.descriptor) }
|
|
}
|
|
|
|
pub fn disassemble(
|
|
&self,
|
|
imm_override: Option<&str>,
|
|
extra_l_just: core::ffi::c_int,
|
|
) -> String {
|
|
let (imm_override_ptr, imm_override_len) = utils::c_string_from_str(imm_override);
|
|
|
|
unsafe {
|
|
let buffer_size =
|
|
RabbitizerInstruction_getSizeForBuffer(self, imm_override_len, extra_l_just);
|
|
|
|
let mut buffer: Vec<u8> = vec![0; buffer_size];
|
|
let disassembled_size = RabbitizerInstruction_disassemble(
|
|
self,
|
|
buffer.as_mut_ptr() as *mut core::ffi::c_char,
|
|
imm_override_ptr,
|
|
imm_override_len,
|
|
extra_l_just,
|
|
);
|
|
buffer.truncate(disassembled_size);
|
|
|
|
String::from_utf8(buffer).unwrap()
|
|
}
|
|
}
|
|
}
|