diff --git a/decompiler/Disasm/Instruction.cpp b/decompiler/Disasm/Instruction.cpp index 6317ff20c7..b78c738016 100644 --- a/decompiler/Disasm/Instruction.cpp +++ b/decompiler/Disasm/Instruction.cpp @@ -6,6 +6,7 @@ #include "Instruction.h" #include "decompiler/ObjectFile/LinkedObjectFile.h" +#include "third-party/fmt/core.h" #include namespace decompiler { @@ -26,6 +27,9 @@ std::string InstructionAtom::to_string(const std::vector& label return "Q"; case IMM_SYM: return sym; + case VF_FIELD: + assert(imm >= 0 && imm < 4); + return fmt::format(".{}", "xyzw"[imm]); default: throw std::runtime_error("Unsupported InstructionAtom"); } @@ -77,6 +81,15 @@ void InstructionAtom::set_sym(std::string _sym) { sym = std::move(_sym); } +/*! + * Make this atom a field (x,y,z,w) of a vf. + */ +void InstructionAtom::set_vf_field(uint32_t value) { + kind = VF_FIELD; + imm = value; + assert(value < 4); +} + /*! * Get as register, or error if not a register. */ @@ -224,7 +237,14 @@ std::string Instruction::to_string(const std::vector& labels) c } for (uint8_t i = 0; i < n_src; i++) { - result += " " + src[i].to_string(labels) + ","; + if (src[i].kind == InstructionAtom::VF_FIELD) { + if (end_comma) { + result.pop_back(); + } + result += src[i].to_string(labels) + ","; + } else { + result += " " + src[i].to_string(labels) + ","; + } end_comma = true; } diff --git a/decompiler/Disasm/Instruction.h b/decompiler/Disasm/Instruction.h index ff5e7ec7cf..419020286b 100644 --- a/decompiler/Disasm/Instruction.h +++ b/decompiler/Disasm/Instruction.h @@ -16,7 +16,7 @@ namespace decompiler { struct DecompilerLabel; -constexpr int MAX_INSTRUCTION_SOURCE = 3; +constexpr int MAX_INSTRUCTION_SOURCE = 4; constexpr int MAX_INTRUCTION_DEST = 1; // An "atom", representing a single register, immediate, etc... for use in an Instruction. @@ -28,6 +28,7 @@ struct InstructionAtom { LABEL, // A label in a LinkedObjectFile VU_ACC, // The VU0 Accumulator VU_Q, // The VU0 Q Register + VF_FIELD, // Field specifier INVALID } kind = INVALID; @@ -37,6 +38,7 @@ struct InstructionAtom { void set_vu_q(); void set_vu_acc(); void set_sym(std::string _sym); + void set_vf_field(uint32_t value); Register get_reg() const; int32_t get_imm() const; diff --git a/decompiler/Disasm/InstructionDecode.cpp b/decompiler/Disasm/InstructionDecode.cpp index 2a98258d2f..552dc5f7a7 100644 --- a/decompiler/Disasm/InstructionDecode.cpp +++ b/decompiler/Disasm/InstructionDecode.cpp @@ -40,11 +40,8 @@ struct OpcodeFields { //////////////// int32_t simm16() { return (int16_t)(data); } - int32_t zimm16() { return (uint16_t)(data); } - uint32_t imm5() { return (data >> 6) & 0x1f; } - uint32_t imm15() { return (data >> 6) & 0b111111111111111; } //////////////////// @@ -52,11 +49,8 @@ struct OpcodeFields { //////////////////// uint32_t cop_func() { return (data >> 21) & 0x1f; } - uint32_t ft() { return (data >> 16) & 0x1f; } - uint32_t fs() { return (data >> 11) & 0x1f; } - uint32_t fd() { return (data >> 6) & 0x1f; } //////////// @@ -64,16 +58,13 @@ struct OpcodeFields { //////////// uint32_t pcreg() { return (data >> 1) & 0x1f; } - uint32_t syscall() { return (data >> 6) & 0xfffff; } - uint32_t MMI_func() { return (data >> 6) & 0x1f; } - uint32_t lower11() { return (uint32_t)(data & 0x7ff); } - uint32_t lower6() { return (uint32_t)(data & 0b111111); } - uint32_t dest() { return (data >> 21) & 0b1111; } + uint32_t fs_f() { return (data >> 21) & 0b11; } + uint32_t ft_f() { return (data >> 23) & 0b11; } uint32_t data; }; @@ -1072,6 +1063,12 @@ Instruction decode_instruction(LinkedWord& word, LinkedObjectFile& file, int seg case FieldType::ZERO: value = 0; break; + case FieldType::FT_F: + value = fields.ft_f(); + break; + case FieldType::FS_F: + value = fields.fs_f(); + break; default: assert(false); } @@ -1112,6 +1109,9 @@ Instruction decode_instruction(LinkedWord& word, LinkedObjectFile& file, int seg case DecodeType::BRANCH_TARGET: atom.set_label(file.get_label_id_for(seg_id, (word_id + value + 1) * 4)); break; + case DecodeType::VF_F: + atom.set_vf_field(value); + break; // NOTE - these change a property of the instruction and don't add an atom. case DecodeType::IL: diff --git a/decompiler/Disasm/OpcodeInfo.cpp b/decompiler/Disasm/OpcodeInfo.cpp index 6e0fc6d4b6..35a03e24f3 100644 --- a/decompiler/Disasm/OpcodeInfo.cpp +++ b/decompiler/Disasm/OpcodeInfo.cpp @@ -405,13 +405,15 @@ void init_opcode_info() { def(IK::VDIV, "vdiv") .dst(FT::ZERO, DT::VU_Q) .src_vf(FT::FS) + .src(FT::FS_F, DT::VF_F) .src_vf(FT::FT) - .src(FT::BC, DT::BC); // todo + .src(FT::FT_F, DT::VF_F); def(IK::VRSQRT, "vrsqrt") .dst(FT::ZERO, DT::VU_Q) .src_vf(FT::FS) + .src(FT::FS_F, DT::VF_F) .src_vf(FT::FT) - .src(FT::BC, DT::BC); // todo + .src(FT::FT_F, DT::VF_F); def(IK::VCLIP, "vclip").src(FT::DEST, DT::DEST).src_vf(FT::FS).src_vf(FT::FT); def(IK::VMULAQ, "vmulaq") .src(FT::DEST, DT::DEST) @@ -430,7 +432,7 @@ void init_opcode_info() { def(IK::QMFC2, "qmfc2").src(FT::IL, DT::IL).dst_gpr(FT::RT).src_vf(FT::FS); def(IK::QMTC2, "qmtc2").src(FT::IL, DT::IL).src_gpr(FT::RT).dst_vf(FT::FS); - def(IK::VSQRT, "vsqrt").src(FT::BC, DT::BC).dst(FT::ZERO, DT::VU_Q).src_vf(FT::FT); + def(IK::VSQRT, "vsqrt").dst(FT::ZERO, DT::VU_Q).src_vf(FT::FT).src(FT::FT_F, DT::VF_F); def(IK::VRXOR, "vrxor").src(FT::BC, DT::BC).src_vf(FT::FS); def(IK::VRNEXT, "vrnext").src(FT::DEST, DT::DEST).dst_vf(FT::FT); def(IK::CTC2, "ctc2").src(FT::IL, DT::IL).src_gpr(FT::RT).dst(FT::RD, DT::VI); diff --git a/decompiler/Disasm/OpcodeInfo.h b/decompiler/Disasm/OpcodeInfo.h index 2d89684917..b369fb830b 100644 --- a/decompiler/Disasm/OpcodeInfo.h +++ b/decompiler/Disasm/OpcodeInfo.h @@ -289,6 +289,8 @@ enum class FieldType { IMM5, IMM15, IL, + FS_F, + FT_F, ZERO }; @@ -305,6 +307,7 @@ enum class DecodeType { VCALLMS_TARGET, DEST, BC, + VF_F, VU_Q, VU_ACC, IL diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index adff240132..49f7e4ce2e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -22,6 +22,7 @@ add_executable(goalc-test ${CMAKE_CURRENT_LIST_DIR}/decompiler/test_FormBeforeExpressions.cpp ${CMAKE_CURRENT_LIST_DIR}/decompiler/test_FormExpressionBuild.cpp ${CMAKE_CURRENT_LIST_DIR}/decompiler/test_FormExpressionBuildLong.cpp + ${CMAKE_CURRENT_LIST_DIR}/decompiler/test_InstructionDecode.cpp ${CMAKE_CURRENT_LIST_DIR}/decompiler/test_InstructionParser.cpp ${CMAKE_CURRENT_LIST_DIR}/decompiler/test_gkernel_decomp.cpp ${CMAKE_CURRENT_LIST_DIR}/decompiler/test_math_decomp.cpp diff --git a/test/decompiler/test_InstructionDecode.cpp b/test/decompiler/test_InstructionDecode.cpp new file mode 100644 index 0000000000..38b5a57ac8 --- /dev/null +++ b/test/decompiler/test_InstructionDecode.cpp @@ -0,0 +1,36 @@ +#include "gtest/gtest.h" +#include "decompiler/Disasm/InstructionDecode.h" +#include "decompiler/Disasm/OpcodeInfo.h" +#include "decompiler/ObjectFile/LinkedObjectFile.h" + +using namespace decompiler; + +TEST(InstructionDecode, VDIV) { + init_opcode_info(); + LinkedObjectFile file; + u32 vdiv_test = 0b010010'1'10'01'10100'01010'01110111100; + // ...... z y 20 10 + LinkedWord vdiv_word(vdiv_test); + auto instr = decode_instruction(vdiv_word, file, 0, 0); + EXPECT_EQ(instr.to_string({}), "vdiv Q, vf10.y, vf20.z"); +} + +TEST(InstructionDecode, VRSQRT) { + init_opcode_info(); + LinkedObjectFile file; + u32 vdiv_test = 0b010010'1'10'01'10100'01010'01110111110; + // ...... z y 20 10 + LinkedWord vdiv_word(vdiv_test); + auto instr = decode_instruction(vdiv_word, file, 0, 0); + EXPECT_EQ(instr.to_string({}), "vrsqrt Q, vf10.y, vf20.z"); +} + +TEST(InstructionDecode, VSQRT) { + init_opcode_info(); + LinkedObjectFile file; + u32 vdiv_test = 0b010010'1'10'00'10100'00000'01110111101; + // ...... z X 20 X + LinkedWord vdiv_word(vdiv_test); + auto instr = decode_instruction(vdiv_word, file, 0, 0); + EXPECT_EQ(instr.to_string({}), "vsqrt Q, vf20.z"); +} \ No newline at end of file