diff --git a/ps2xRecomp/CMakeLists.txt b/ps2xRecomp/CMakeLists.txt index e8ba62b..cae5ca8 100644 --- a/ps2xRecomp/CMakeLists.txt +++ b/ps2xRecomp/CMakeLists.txt @@ -1,9 +1,11 @@ cmake_minimum_required(VERSION 3.20) -project(PS2Recomp VERSION 0.1.0 LANGUAGES CXX) +project(PS2Recomp VERSION 0.1.0 LANGUAGES C CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD_REQUIRED ON) include(FetchContent) @@ -54,6 +56,30 @@ FetchContent_MakeAvailable(libdwarf) set(LIBDWARF_INCLUDE_DIR "${libdwarf_SOURCE_DIR}/src/lib/libdwarf") +FetchContent_Declare( + rabbitizer + GIT_REPOSITORY https://github.com/Decompollaborate/rabbitizer.git + GIT_TAG 1.14.3 +) +FetchContent_MakeAvailable(rabbitizer) + +if (NOT TARGET rabbitizer) + file(GLOB_RECURSE RABBITIZER_SOURCES CONFIGURE_DEPENDS + "${rabbitizer_SOURCE_DIR}/src/analysis/*.c" + "${rabbitizer_SOURCE_DIR}/src/common/*.c" + "${rabbitizer_SOURCE_DIR}/src/instructions/*.c" + "${rabbitizer_SOURCE_DIR}/src/instructions/*/*.c" + ) + + add_library(rabbitizer STATIC ${RABBITIZER_SOURCES}) + + target_include_directories(rabbitizer PUBLIC + "${rabbitizer_SOURCE_DIR}/include" + "${rabbitizer_SOURCE_DIR}/tables" + "${rabbitizer_SOURCE_DIR}/tables/tables" + ) +endif() + file(GLOB_RECURSE PS2RECOMP_LIB_SOURCES CONFIGURE_DEPENDS src/lib/*.cpp ) @@ -87,6 +113,7 @@ PUBLIC fmt::fmt toml11::toml11 dwarf + rabbitizer ) file(GLOB_RECURSE PS2RECOMP_EXE_SOURCES CONFIGURE_DEPENDS @@ -108,4 +135,4 @@ install(TARGETS ps2_recomp ps2_recomp_lib install(DIRECTORY include/ DESTINATION include -) \ No newline at end of file +) diff --git a/ps2xRecomp/include/ps2recomp/code_generator.h b/ps2xRecomp/include/ps2recomp/code_generator.h index 2a329f7..540f551 100644 --- a/ps2xRecomp/include/ps2recomp/code_generator.h +++ b/ps2xRecomp/include/ps2recomp/code_generator.h @@ -124,7 +124,7 @@ namespace ps2recomp const Symbol *findSymbolByAddress(uint32_t address) const; std::string getFunctionName(uint32_t address) const; - std::string getGeneratedFunctionName(const Function &function); + std::string sanitizeFunctionName(const std::string& name) const; }; } diff --git a/ps2xRecomp/include/ps2recomp/instructions.h b/ps2xRecomp/include/ps2recomp/instructions.h index 30b767d..74481d2 100644 --- a/ps2xRecomp/include/ps2recomp/instructions.h +++ b/ps2xRecomp/include/ps2recomp/instructions.h @@ -80,8 +80,8 @@ namespace ps2recomp OPCODE_SDC2 = 0x3E, // PS2 specific Store Quadword from Coprocessor 2 (VU0) - Overrides standard MIPS SDC2 OPCODE_SD = 0x3F, // Store Doubleword - // OPCODE_LQC2 = 0x36, - // OPCODE_SQC2 = 0x3E + OPCODE_LQC2 = OPCODE_LDC2, + OPCODE_SQC2 = OPCODE_SDC2 }; // SPECIAL Function (bits 5-0) for OPCODE_SPECIAL @@ -775,4 +775,4 @@ namespace ps2recomp } // namespace ps2recomp -#endif // PS2RECOMP_INSTRUCTIONS_H \ No newline at end of file +#endif // PS2RECOMP_INSTRUCTIONS_H diff --git a/ps2xRecomp/src/lib/code_generator.cpp b/ps2xRecomp/src/lib/code_generator.cpp index 8996e3a..0ed9982 100644 --- a/ps2xRecomp/src/lib/code_generator.cpp +++ b/ps2xRecomp/src/lib/code_generator.cpp @@ -28,6 +28,20 @@ namespace ps2recomp namespace ps2recomp { + static bool isReservedCxxIdentifier(const std::string& name) + { + if (name.size() >= 2 && name[0] == '_' && name[1] == '_') + return true; + if (!name.empty() && name[0] == '_' && std::isupper(static_cast(name[1]))) + return true; + return false; + } + + static bool isReservedCxxKeyword(const std::string& name) + { + return kKeywords.contains(name); + } + CodeGenerator::CodeGenerator(const std::vector &symbols) { for (auto &symbol : symbols) @@ -57,27 +71,13 @@ namespace ps2recomp const Symbol *sym = findSymbolByAddress(address); if (sym && sym->isFunction) { - return sym->name; + return CodeGenerator::sanitizeFunctionName(sym->name); } return ""; - } + } - static bool isReservedCxxIdentifier(const std::string &name) - { - if (name.size() >= 2 && name[0] == '_' && name[1] == '_') - return true; - if (!name.empty() && name[0] == '_' && std::isupper(static_cast(name[1]))) - return true; - return false; - } - - static bool isReservedCxxKeyword(const std::string &name) - { - return kKeywords.contains(name); - } - - static std::string sanitizeFunctionName(const std::string &name) + std::string CodeGenerator::sanitizeFunctionName(const std::string &name) const { std::string sanitized = name; @@ -96,16 +96,6 @@ namespace ps2recomp return "ps2_" + sanitized; } - std::string CodeGenerator::getGeneratedFunctionName(const Function &function) - { - std::string name = getFunctionName(function.start); - if (name.empty()) - { - name = sanitizeFunctionName(function.name); - } - return name; - } - std::string CodeGenerator::handleBranchDelaySlots(const Instruction &branchInst, const Instruction &delaySlot, const Function &function, const std::unordered_set &internalTargets) { @@ -131,10 +121,13 @@ namespace ps2recomp std::string funcName = getFunctionName(target); if (!funcName.empty()) { - ss << " " << funcName << "(rdram, ctx, runtime);\n"; if (branchInst.opcode == OPCODE_J) { - ss << " return;\n"; + ss << " " << funcName << "(rdram, ctx, runtime); return;\n"; + } + else + { + ss << " " << funcName << "(rdram, ctx, runtime);\n"; } } else @@ -402,7 +395,13 @@ namespace ps2recomp ss << "// Function: " << function.name << "\n"; ss << "// Address: 0x" << std::hex << function.start << " - 0x" << function.end << std::dec << "\n"; - std::string sanitizedName = getGeneratedFunctionName(function); + std::string sanitizedName = getFunctionName(function.start); + if (sanitizedName.empty()) + { + std::stringstream nameBuilder; + nameBuilder << "Errorfunc_" << std::hex << function.start; // this should never happen but lets put here just to track + sanitizedName = nameBuilder.str(); + } ss << "void " << sanitizedName << "(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime) {\n\n"; for (size_t i = 0; i < instructions.size(); ++i) @@ -2226,7 +2225,7 @@ namespace ps2recomp if (!function.isRecompiled && !function.isStub) continue; - std::string generatedName = getGeneratedFunctionName(function); + std::string generatedName = getFunctionName(function.start); if (function.isStub) { @@ -2246,28 +2245,28 @@ namespace ps2recomp } ss << " // Register recompiled functions\n"; - for (const auto & [first, second] : normalFunctions) + for (const auto &[first, second] : normalFunctions) { ss << " runtime.registerFunction(0x" << std::hex << first << std::dec << ", " << second << ");\n"; } ss << "\n // Register stub functions\n"; - for (const auto & [first, second] : stubFunctions) + for (const auto &[first, second] : stubFunctions) { ss << " runtime.registerFunction(0x" << std::hex << first << std::dec << ", " << second << ");\n"; } ss << "\n // Register system call stubs\n"; - for (const auto & [first, second] : systemCallFunctions) + for (const auto &[first, second] : systemCallFunctions) { ss << " runtime.registerFunction(0x" << std::hex << first << std::dec << ", " << second << ");\n"; } ss << "\n // Register library stubs\n"; - for (const auto & [first, second] : libraryFunctions) + for (const auto &[first, second] : libraryFunctions) { ss << " runtime.registerFunction(0x" << std::hex << first << std::dec << ", " << second << ");\n"; @@ -2287,7 +2286,7 @@ namespace ps2recomp ss << "switch (ctx->r[" << indexReg << "]) {\n"; - for (const auto & [index, target] : entries) + for (const auto &[index, target] : entries) { ss << " case " << index << ": {\n"; diff --git a/ps2xRecomp/src/lib/ps2_recompiler.cpp b/ps2xRecomp/src/lib/ps2_recompiler.cpp index 9b65eeb..d9fe7f9 100644 --- a/ps2xRecomp/src/lib/ps2_recompiler.cpp +++ b/ps2xRecomp/src/lib/ps2_recompiler.cpp @@ -281,7 +281,7 @@ namespace ps2recomp { if (function.isStub) { - std::string generatedName = m_codeGenerator->getGeneratedFunctionName(function); + std::string generatedName = m_codeGenerator->getFunctionName(function.start); std::stringstream stub; stub << "void " << generatedName << "(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime) { "; @@ -483,8 +483,7 @@ namespace ps2recomp continue; } - std::string finalName = sanitizeFunctionName(function.name); - finalName = m_codeGenerator->getGeneratedFunctionName(function); + std::string finalName = m_codeGenerator->getFunctionName(function.start); ss << "void " << finalName << "(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime);\n"; } diff --git a/ps2xRecomp/src/lib/r5900_decoder.cpp b/ps2xRecomp/src/lib/r5900_decoder.cpp index dcafb3a..f68b576 100644 --- a/ps2xRecomp/src/lib/r5900_decoder.cpp +++ b/ps2xRecomp/src/lib/r5900_decoder.cpp @@ -1,4 +1,5 @@ #include "ps2recomp/r5900_decoder.h" +#include "rabbitizer.h" #include namespace ps2recomp @@ -59,194 +60,114 @@ namespace ps2recomp inst.modificationInfo.modifiesMemory = false; inst.modificationInfo.modifiesControl = false; - switch (inst.opcode) - { - case OPCODE_SPECIAL: - decodeSpecial(inst); - break; + RabbitizerInstruction rabbitizerInst; + RabbitizerInstructionR5900_init(&rabbitizerInst, rawInstruction, address); + RabbitizerInstructionR5900_processUniqueId(&rabbitizerInst); - case OPCODE_REGIMM: - decodeRegimm(inst); - break; + const RabbitizerInstrDescriptor *desc = rabbitizerInst.descriptor; - case OPCODE_J: - decodeJType(inst); - break; + inst.isBranch = RabbitizerInstrDescriptor_isBranch(desc); + inst.isJump = RabbitizerInstrDescriptor_isJump(desc); + inst.isCall = RabbitizerInstruction_isFunctionCall(&rabbitizerInst); + inst.isReturn = RabbitizerInstruction_isReturn(&rabbitizerInst) || + rabbitizerInst.uniqueId == RABBITIZER_INSTR_ID_cpu_eret; + inst.hasDelaySlot = RabbitizerInstruction_hasDelaySlot(&rabbitizerInst); - case OPCODE_JAL: - decodeJType(inst); - break; + inst.isLoad = RabbitizerInstrDescriptor_doesLoad(desc); + inst.isStore = RabbitizerInstrDescriptor_doesStore(desc); - case OPCODE_BEQ: - case OPCODE_BNE: - case OPCODE_BLEZ: - case OPCODE_BGTZ: - case OPCODE_BEQL: - case OPCODE_BNEL: - case OPCODE_BLEZL: - case OPCODE_BGTZL: - decodeIType(inst); - inst.isBranch = true; - inst.hasDelaySlot = true; - inst.modificationInfo.modifiesControl = true; // PC potentially - break; + inst.isMMI = inst.opcode == OPCODE_MMI; + inst.isVU = inst.opcode == OPCODE_COP2 || + inst.opcode == OPCODE_LWC2 || + inst.opcode == OPCODE_LDC2 || + inst.opcode == OPCODE_SWC2 || + inst.opcode == OPCODE_SDC2; - case OPCODE_DADDI: - case OPCODE_DADDIU: - decodeIType(inst); - if (inst.rt != 0) - inst.modificationInfo.modifiesGPR = true; - break; + bool modifiesGpr = false; + if (RabbitizerInstrDescriptor_modifiesRs(desc) && inst.rs != 0) + { + modifiesGpr = true; + } + if (RabbitizerInstrDescriptor_modifiesRt(desc) && inst.rt != 0) + { + modifiesGpr = true; + } + if (RabbitizerInstrDescriptor_modifiesRd(desc) && inst.rd != 0) + { + modifiesGpr = true; + } + if (RabbitizerInstrDescriptor_doesLink(desc)) + { + modifiesGpr = true; + } + inst.modificationInfo.modifiesGPR = modifiesGpr; - case OPCODE_MMI: - decodeMMI(inst); - break; + inst.modificationInfo.modifiesFPR = RabbitizerInstrDescriptor_modifiesFs(desc) || + RabbitizerInstrDescriptor_modifiesFt(desc) || + RabbitizerInstrDescriptor_modifiesFd(desc); - case OPCODE_LQ: - decodeIType(inst); - inst.isLoad = true; - inst.isMultimedia = true; // 128-bit load - break; + inst.modificationInfo.modifiesMemory = RabbitizerInstrDescriptor_doesStore(desc); + inst.modificationInfo.modifiesControl = RabbitizerInstrDescriptor_isBranch(desc) || + RabbitizerInstrDescriptor_isJump(desc) || + RabbitizerInstrDescriptor_isTrap(desc) || + RabbitizerInstrDescriptor_modifiesHI(desc) || + RabbitizerInstrDescriptor_modifiesLO(desc); - case OPCODE_SQ: - decodeIType(inst); - inst.isStore = true; - inst.isMultimedia = true; // 128-bit store - inst.modificationInfo.modifiesMemory = true; - break; - - case OPCODE_LB: - case OPCODE_LH: - case OPCODE_LW: - case OPCODE_LBU: - case OPCODE_LHU: - case OPCODE_LWU: - case OPCODE_LD: - inst.isLoad = true; - if (inst.rt != 0) - inst.modificationInfo.modifiesGPR = true; - break; + if (rabbitizerInst.uniqueId == RABBITIZER_INSTR_ID_cpu_eret) + { + inst.isReturn = true; + inst.hasDelaySlot = false; + inst.modificationInfo.modifiesControl = true; + } - case OPCODE_LWL: - case OPCODE_LWR: - case OPCODE_LDL: - case OPCODE_LDR: + if (inst.opcode == OPCODE_LL || inst.opcode == OPCODE_LLD) + { inst.isLoad = true; + inst.modificationInfo.modifiesControl = true; if (inst.rt != 0) + { inst.modificationInfo.modifiesGPR = true; - inst.modificationInfo.modifiesMemory = true; - break; + } + } - case OPCODE_LL: - case OPCODE_LLD: - decodeIType(inst); - inst.isLoad = true; + if (inst.opcode == OPCODE_SC || inst.opcode == OPCODE_SCD) + { + inst.isStore = true; + inst.modificationInfo.modifiesControl = true; if (inst.rt != 0) { - inst.modificationInfo.modifiesGPR = true; + inst.modificationInfo.modifiesGPR = true; // success flag to rt } - // LL/LLD manipulate the load-linked bit in COP0 status - inst.modificationInfo.modifiesControl = true; - break; - - case OPCODE_LWC1: - inst.isLoad = true; - inst.modificationInfo.modifiesFPR = true; - break; + } - case OPCODE_LDC1: // Not present/used on EE FPU - case OPCODE_LWC2: // Maybe unused - case OPCODE_LDC2: // VU Load - inst.isLoad = true; + if (inst.opcode == OPCODE_COP2) + { + decodeCOP2(inst); + } + else if (inst.opcode == OPCODE_LWC2 || inst.opcode == OPCODE_LDC2) + { inst.isVU = true; inst.modificationInfo.modifiesVFR = true; - break; - - case OPCODE_SB: - case OPCODE_SH: - case OPCODE_SW: - case OPCODE_SD: - inst.isStore = true; - inst.modificationInfo.modifiesMemory = true; - break; - - case OPCODE_SWL: - case OPCODE_SWR: - case OPCODE_SDL: - case OPCODE_SDR: - inst.isStore = true; - inst.modificationInfo.modifiesMemory = true; - break; - - case OPCODE_SWC1: - inst.isStore = true; - inst.modificationInfo.modifiesMemory = true; - break; - - case OPCODE_SDC1: // Not present/used on EE FPU - case OPCODE_SWC2: // Potentially unused - case OPCODE_SDC2: - inst.isStore = true; + } + else if (inst.opcode == OPCODE_SWC2 || inst.opcode == OPCODE_SDC2) + { inst.isVU = true; inst.modificationInfo.modifiesMemory = true; - break; - - case OPCODE_SC: - case OPCODE_SCD: - inst.isStore = true; - inst.modificationInfo.modifiesMemory = true; - if (inst.rt != 0) - inst.modificationInfo.modifiesGPR = true; // Writes success/fail to rt - inst.modificationInfo.modifiesControl = true; // Reads/Clears LLBit - break; - - case OPCODE_ADDI: - case OPCODE_ADDIU: - case OPCODE_SLTI: - case OPCODE_SLTIU: - case OPCODE_ANDI: - case OPCODE_ORI: - case OPCODE_XORI: - case OPCODE_LUI: - decodeIType(inst); - if (inst.rt != 0) - inst.modificationInfo.modifiesGPR = true; - break; - - case OPCODE_CACHE: - decodeIType(inst); - inst.modificationInfo.modifiesControl = true; // Cache state - break; - - case OPCODE_PREF: - decodeIType(inst); - break; - - case OPCODE_COP0: - decodeCOP0(inst); - break; - - case OPCODE_COP1: - decodeCOP1(inst); - break; - - case OPCODE_COP2: - decodeCOP2(inst); - break; + } - default: - // Default to I-type for most other instructions - decodeIType(inst); - break; + if (inst.opcode == OPCODE_LQ || inst.opcode == OPCODE_SQ) + { + inst.isMultimedia = true; } - // Generic multimedia flag if MMI or VU if (inst.isMMI || inst.isVU) { inst.isMultimedia = true; inst.vectorInfo.isVector = inst.isVU; // Only VU ops are truly vector } + RabbitizerInstructionR5900_destroy(&rabbitizerInst); + return inst; }