diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a6fae4..ba2c0a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Add `flag_disasmAsData` member to the `Instruction` class. +- Add `flag_r5900DisasmAsData` member to the `Instruction` class. + - This flag allows to fine-tune R5900 instruction set that are affected by + the global `gnuMode` option. + - Currently these instructions are: `trunc.w.s`, `cvt.w.s`, `vclipw` and + `vsqrt`. - `TrinaryValue.TRUE` forces the instruction to be disassembled as data. - `TrinaryValue.FALSE` bypasses the global checks for disassembling a word as data. A word will still be disassembled as data if it can't be decoded. diff --git a/cplusplus/include/instructions/InstructionBase.hpp b/cplusplus/include/instructions/InstructionBase.hpp index 9af69fd..99908cf 100644 --- a/cplusplus/include/instructions/InstructionBase.hpp +++ b/cplusplus/include/instructions/InstructionBase.hpp @@ -149,8 +149,8 @@ namespace rabbitizer { /* flags */ - TrinaryValue FlagGet_disasmAsData() const; - void FlagSet_disasmAsData(TrinaryValue value); + TrinaryValue FlagGet_r5900DisasmAsData() const; + void FlagSet_r5900DisasmAsData(TrinaryValue value); TrinaryValue FlagGet_r5900UseDollar() const; void FlagSet_r5900UseDollar(TrinaryValue value); diff --git a/cplusplus/src/instructions/InstructionBase.cpp b/cplusplus/src/instructions/InstructionBase.cpp index 070636a..61bb908 100644 --- a/cplusplus/src/instructions/InstructionBase.cpp +++ b/cplusplus/src/instructions/InstructionBase.cpp @@ -648,11 +648,11 @@ void InstructionBase::Set_stype(uint8_t val) { /* flags */ -TrinaryValue InstructionBase::FlagGet_disasmAsData() const { - return static_cast(RAB_INSTR_FLAGS_GET_disasmAsData(&this->instr)); +TrinaryValue InstructionBase::FlagGet_r5900DisasmAsData() const { + return static_cast(RAB_INSTR_FLAGS_GET_r5900DisasmAsData(&this->instr)); } -void InstructionBase::FlagSet_disasmAsData(TrinaryValue value) { - RAB_INSTR_FLAGS_SET_disasmAsData(&this->instr, static_cast(value)); +void InstructionBase::FlagSet_r5900DisasmAsData(TrinaryValue value) { + RAB_INSTR_FLAGS_SET_r5900DisasmAsData(&this->instr, static_cast(value)); } TrinaryValue InstructionBase::FlagGet_r5900UseDollar() const { diff --git a/include/instructions/RabbitizerInstruction.h b/include/instructions/RabbitizerInstruction.h index 69068a8..470d33f 100644 --- a/include/instructions/RabbitizerInstruction.h +++ b/include/instructions/RabbitizerInstruction.h @@ -38,7 +38,7 @@ typedef struct RabbitizerInstruction { * Flags are bitpacked, refer to the `RAB_INSTR_FLAGS_` macros to access them. * * Bit usage: - * - Bits 0 ~ 1: `disasmAsData`. Value of the `RabTrinaryValue` enum. + * - Bits 0 ~ 1: `r5900DisasmAsData`. Value of the `RabTrinaryValue` enum. * - `RAB_TRINARY_VAL_TRUE` forces the instruction to be disassembled as data. * - `RAB_TRINARY_VAL_FALSE` bypasses the global checks for disassembling a word as data. A word will still be disassembled as data if it can't be decoded. * - `RAB_TRINARY_VAL_NONE` leaves this decision to the global settings. @@ -145,8 +145,8 @@ typedef struct RabbitizerInstruction { #define RAB_INSTR_PACK_stype(word, value) (BITREPACK((word), (value), 6, 5)) -#define RAB_INSTR_FLAGS_GET_disasmAsData(self) (RabTrinaryValue)(SHIFTR((self)->flags, 0, 2)) -#define RAB_INSTR_FLAGS_SET_disasmAsData(self, value) ((self)->flags = BITREPACK((self)->flags, (value), 0, 2)) +#define RAB_INSTR_FLAGS_GET_r5900DisasmAsData(self) (RabTrinaryValue)(SHIFTR((self)->flags, 0, 2)) +#define RAB_INSTR_FLAGS_SET_r5900DisasmAsData(self, value) ((self)->flags = BITREPACK((self)->flags, (value), 0, 2)) #define RAB_INSTR_FLAGS_GET_r5900UseDollar(self) (RabTrinaryValue)(SHIFTR((self)->flags, 2, 2)) #define RAB_INSTR_FLAGS_SET_r5900UseDollar(self, value) ((self)->flags = BITREPACK((self)->flags, (value), 2, 2)) diff --git a/pyproject.toml b/pyproject.toml index 727dd26..1c6a915 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ [project] name = "rabbitizer" # Version should be synced with include/common/RabbitizerVersion.h -version = "1.8.0.dev0" +version = "1.8.0" description = "MIPS instruction decoder" # license = "MIT" readme = "README.md" diff --git a/rabbitizer/rabbitizer.pyi b/rabbitizer/rabbitizer.pyi index 806383f..44c770c 100644 --- a/rabbitizer/rabbitizer.pyi +++ b/rabbitizer/rabbitizer.pyi @@ -61,8 +61,11 @@ class Instruction: inHandwrittenFunction: bool = False """Boolean value indicating if the current instruction is used on a handwritten function. This is intended to be determined by the user.""" - flag_disasmAsData: Enum = TrinaryValue.NONE - """Flag to override the disasmAsData global configuration. + flag_r5900DisasmAsData: Enum = TrinaryValue.NONE + """Flag to override the r5900DisasmAsData global configuration. + + - This flag allows to fine-tune R5900 instruction set that are affected by the global `gnuMode` option. + - Currently these instructions are: `trunc.w.s` (r5900 mode), `cvt.w.s` (r5900 mode), `vclipw` and `vsqrt`. - `TrinaryValue.TRUE` forces the instruction to be disassembled as data. - `TrinaryValue.FALSE` bypasses the global checks for disassembling a word as data. A word will still be disassembled as data if it can't be decoded. diff --git a/rabbitizer/rabbitizer_type_Instruction.c b/rabbitizer/rabbitizer_type_Instruction.c index c197024..979a4f4 100644 --- a/rabbitizer/rabbitizer_type_Instruction.c +++ b/rabbitizer/rabbitizer_type_Instruction.c @@ -242,7 +242,7 @@ static PyObject *rabbitizer_type_Instruction_member_get_instrIdType(PyRabbitizer return 0; \ } -DEF_MEMBER_FLAG(disasmAsData) +DEF_MEMBER_FLAG(r5900DisasmAsData) DEF_MEMBER_FLAG(r5900UseDollar) @@ -250,6 +250,8 @@ DEF_MEMBER_FLAG(r5900UseDollar) #define MEMBER_SET(name, docs, closure) { #name, (getter) NULL, (setter) rabbitizer_type_Instruction_member_set_##name, PyDoc_STR(docs), closure } #define MEMBER_GET_SET(name, docs, closure) { #name, (getter) rabbitizer_type_Instruction_member_get_##name, (setter) rabbitizer_type_Instruction_member_set_##name, PyDoc_STR(docs), closure } +#define MEMBER_FLAG_GET_SET(name, docs, closure) { #name, (getter) rabbitizer_type_Instruction_member_get_flag_##name, (setter) rabbitizer_type_Instruction_member_set_flag_##name, PyDoc_STR(docs), closure } + static PyGetSetDef rabbitizer_type_Instruction_getsetters[] = { MEMBER_GET(rs, "", NULL), MEMBER_GET(rt, "", NULL), @@ -261,8 +263,8 @@ static PyGetSetDef rabbitizer_type_Instruction_getsetters[] = { MEMBER_GET(uniqueId, "", NULL), MEMBER_GET(instrIdType, "", NULL), - MEMBER_GET_SET(flag_disasmAsData, "", NULL), - MEMBER_GET_SET(flag_r5900UseDollar, "", NULL), + MEMBER_FLAG_GET_SET(r5900DisasmAsData, "", NULL), + MEMBER_FLAG_GET_SET(r5900UseDollar, "", NULL), { 0 } }; diff --git a/rust/src/instruction.rs b/rust/src/instruction.rs index fe690e6..2d4f455 100644 --- a/rust/src/instruction.rs +++ b/rust/src/instruction.rs @@ -454,10 +454,10 @@ impl Instruction { self.get_cop2t().try_into().unwrap() } - pub fn flags_get_disasm_as_data(&self) -> utils::TrinaryValue { + pub fn flags_get_r5900_disasm_as_data(&self) -> utils::TrinaryValue { utils::shiftr(self.flags, 0, 2).try_into().unwrap() } - pub fn flags_set_disasm_as_data(&mut self, value: utils::TrinaryValue) { + pub fn flags_set_r5900_disasm_as_data(&mut self, value: utils::TrinaryValue) { self.flags = utils::bitrepack(self.flags, value.try_into().unwrap(), 0, 2); } diff --git a/src/instructions/RabbitizerInstruction/RabbitizerInstruction.c b/src/instructions/RabbitizerInstruction/RabbitizerInstruction.c index e83831d..573c62a 100644 --- a/src/instructions/RabbitizerInstruction/RabbitizerInstruction.c +++ b/src/instructions/RabbitizerInstruction/RabbitizerInstruction.c @@ -25,7 +25,7 @@ void RabbitizerInstruction_init(RabbitizerInstruction *self, uint32_t word, uint self->category = RABBITIZER_INSTRCAT_CPU; self->flags = 0; - RAB_INSTR_FLAGS_SET_disasmAsData(self, RAB_TRINARY_VAL_NONE); + RAB_INSTR_FLAGS_SET_r5900DisasmAsData(self, RAB_TRINARY_VAL_NONE); } void RabbitizerInstruction_destroy(UNUSED RabbitizerInstruction *self) { diff --git a/src/instructions/RabbitizerInstruction/RabbitizerInstruction_Disassemble.c b/src/instructions/RabbitizerInstruction/RabbitizerInstruction_Disassemble.c index 783469b..00587f2 100644 --- a/src/instructions/RabbitizerInstruction/RabbitizerInstruction_Disassemble.c +++ b/src/instructions/RabbitizerInstruction/RabbitizerInstruction_Disassemble.c @@ -94,31 +94,29 @@ size_t RabbitizerInstruction_disassembleAsData(const RabbitizerInstruction *self } bool RabbitizerInstruction_mustDisasmAsData(const RabbitizerInstruction *self) { - switch (RAB_INSTR_FLAGS_GET_disasmAsData(self)) { - case RAB_TRINARY_VAL_TRUE: - return true; - - case RAB_TRINARY_VAL_FALSE: + switch (self->uniqueId) { + case RABBITIZER_INSTR_ID_cpu_break: + if (RabbitizerConfig_Cfg.toolchainTweaks.sn64DivFix) { + return true; + } break; - case RAB_TRINARY_VAL_NONE: - if (RabbitizerConfig_Cfg.toolchainTweaks.sn64DivFix) { - if (self->uniqueId == RABBITIZER_INSTR_ID_cpu_break) { - return true; - } - } + case RABBITIZER_INSTR_ID_cpu_trunc_w_s: + case RABBITIZER_INSTR_ID_cpu_cvt_w_s: + if (self->category == RABBITIZER_INSTRCAT_R5900) { + switch (RAB_INSTR_FLAGS_GET_r5900DisasmAsData(self)) { + case RAB_TRINARY_VAL_TRUE: + return true; - if (RabbitizerConfig_Cfg.toolchainTweaks.gnuMode) { - switch (self->uniqueId) { - case RABBITIZER_INSTR_ID_cpu_trunc_w_s: - case RABBITIZER_INSTR_ID_cpu_cvt_w_s: - if (self->category == RABBITIZER_INSTRCAT_R5900) { + case RAB_TRINARY_VAL_FALSE: + break; + + case RAB_TRINARY_VAL_NONE: + if (RabbitizerConfig_Cfg.toolchainTweaks.gnuMode) { /** - * Due to the R5900's FPU being non properly complaint, the instruction cvt.w.s always - * behaves as trunc.w.s because EE can only do round-to-zero. + * Due to the R5900's FPU being non properly complaint, the instruction cvt.w.s always behaves as trunc.w.s because EE can only do round-to-zero. * - * Assemblers like GAS workaround this issue by decoding cvt.w.s as trunc.w.s, but other - * assemblers just use trunc.w.s and cvt.w.s as-is. + * Assemblers like GAS workaround this issue by decoding cvt.w.s as trunc.w.s, but other assemblers just use trunc.w.s and cvt.w.s as-is. * * Here's some reading about the binutils rationale: * - https://sourceware.org/legacy-ml/binutils/2012-11/msg00360.html @@ -128,26 +126,49 @@ bool RabbitizerInstruction_mustDisasmAsData(const RabbitizerInstruction *self) { * - trunc.w.s is built as the cvt.w.s instruction. * - cvt.w.s errors complaining as not being supported by the processor. * - * To ensure the produced disassembly will still match when built with GAS, we decode this - * two instructions as .word + * To ensure the produced disassembly will still match when built with GAS, we decode this two instructions as .word */ return true; } break; - case RABBITIZER_INSTR_ID_r5900_vclipw: - // The vclipw instruction has variants that are undocumented (vclipw.xy, vclipw.z) and don't - // assemble in gnu as - return true; - case RABBITIZER_INSTR_ID_r5900_vsqrt: - // The vclipw instruction seems to be representable in multiple ways, and we only disassemble - // one of them - return true; - - default: - break; } } break; + + case RABBITIZER_INSTR_ID_r5900_vclipw: + switch (RAB_INSTR_FLAGS_GET_r5900DisasmAsData(self)) { + case RAB_TRINARY_VAL_TRUE: + return true; + + case RAB_TRINARY_VAL_FALSE: + break; + + case RAB_TRINARY_VAL_NONE: + if (RabbitizerConfig_Cfg.toolchainTweaks.gnuMode) { + // The vclipw instruction has variants that are undocumented (vclipw.xy, vclipw.z) and don't assemble in gnu as + return true; + } + break; + } + + case RABBITIZER_INSTR_ID_r5900_vsqrt: + switch (RAB_INSTR_FLAGS_GET_r5900DisasmAsData(self)) { + case RAB_TRINARY_VAL_TRUE: + return true; + + case RAB_TRINARY_VAL_FALSE: + break; + + case RAB_TRINARY_VAL_NONE: + if (RabbitizerConfig_Cfg.toolchainTweaks.gnuMode) { + // The vclipw instruction seems to be representable in multiple ways, and we only disassemble one of them + return true; + } + break; + } + + default: + break; } if (!RabbitizerInstruction_isValid(self)) {