mirror of
https://github.com/ran-j/PS2Recomp.git
synced 2026-09-26 08:51:05 -04:00
feat: better decoder (#40)
* feat: use rabbitizer for decode MIPs instructions * feat: cleanup code generator * feat: fix test and add a sefe check for generate functions
This commit is contained in:
@@ -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
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
#endif // PS2RECOMP_INSTRUCTIONS_H
|
||||
|
||||
@@ -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<unsigned char>(name[1])))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isReservedCxxKeyword(const std::string& name)
|
||||
{
|
||||
return kKeywords.contains(name);
|
||||
}
|
||||
|
||||
CodeGenerator::CodeGenerator(const std::vector<Symbol> &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<unsigned char>(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<uint32_t> &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";
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "ps2recomp/r5900_decoder.h"
|
||||
#include "rabbitizer.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace ps2recomp
|
||||
@@ -59,194 +60,114 @@ namespace ps2recomp
|
||||
inst.modificationInfo.modifiesMemory = false;
|
||||
inst.modificationInfo.modifiesControl = false;
|
||||
|
||||
switch (inst.opcode)
|
||||
RabbitizerInstruction rabbitizerInst;
|
||||
RabbitizerInstructionR5900_init(&rabbitizerInst, rawInstruction, address);
|
||||
RabbitizerInstructionR5900_processUniqueId(&rabbitizerInst);
|
||||
|
||||
const RabbitizerInstrDescriptor *desc = rabbitizerInst.descriptor;
|
||||
|
||||
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);
|
||||
|
||||
inst.isLoad = RabbitizerInstrDescriptor_doesLoad(desc);
|
||||
inst.isStore = RabbitizerInstrDescriptor_doesStore(desc);
|
||||
|
||||
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;
|
||||
|
||||
bool modifiesGpr = false;
|
||||
if (RabbitizerInstrDescriptor_modifiesRs(desc) && inst.rs != 0)
|
||||
{
|
||||
case OPCODE_SPECIAL:
|
||||
decodeSpecial(inst);
|
||||
break;
|
||||
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_REGIMM:
|
||||
decodeRegimm(inst);
|
||||
break;
|
||||
inst.modificationInfo.modifiesFPR = RabbitizerInstrDescriptor_modifiesFs(desc) ||
|
||||
RabbitizerInstrDescriptor_modifiesFt(desc) ||
|
||||
RabbitizerInstrDescriptor_modifiesFd(desc);
|
||||
|
||||
case OPCODE_J:
|
||||
decodeJType(inst);
|
||||
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_JAL:
|
||||
decodeJType(inst);
|
||||
break;
|
||||
if (rabbitizerInst.uniqueId == RABBITIZER_INSTR_ID_cpu_eret)
|
||||
{
|
||||
inst.isReturn = true;
|
||||
inst.hasDelaySlot = false;
|
||||
inst.modificationInfo.modifiesControl = true;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
case OPCODE_DADDI:
|
||||
case OPCODE_DADDIU:
|
||||
decodeIType(inst);
|
||||
if (inst.rt != 0)
|
||||
inst.modificationInfo.modifiesGPR = true;
|
||||
break;
|
||||
|
||||
case OPCODE_MMI:
|
||||
decodeMMI(inst);
|
||||
break;
|
||||
|
||||
case OPCODE_LQ:
|
||||
decodeIType(inst);
|
||||
inst.isLoad = true;
|
||||
inst.isMultimedia = true; // 128-bit load
|
||||
break;
|
||||
|
||||
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;
|
||||
|
||||
case OPCODE_LWL:
|
||||
case OPCODE_LWR:
|
||||
case OPCODE_LDL:
|
||||
case OPCODE_LDR:
|
||||
inst.isLoad = true;
|
||||
if (inst.rt != 0)
|
||||
inst.modificationInfo.modifiesGPR = true;
|
||||
inst.modificationInfo.modifiesMemory = true;
|
||||
break;
|
||||
|
||||
case OPCODE_LL:
|
||||
case OPCODE_LLD:
|
||||
decodeIType(inst);
|
||||
if (inst.opcode == OPCODE_LL || inst.opcode == OPCODE_LLD)
|
||||
{
|
||||
inst.isLoad = true;
|
||||
inst.modificationInfo.modifiesControl = true;
|
||||
if (inst.rt != 0)
|
||||
{
|
||||
inst.modificationInfo.modifiesGPR = true;
|
||||
}
|
||||
// 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;
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
// Generic multimedia flag if MMI or VU
|
||||
if (inst.opcode == OPCODE_SC || inst.opcode == OPCODE_SCD)
|
||||
{
|
||||
inst.isStore = true;
|
||||
inst.modificationInfo.modifiesControl = true;
|
||||
if (inst.rt != 0)
|
||||
{
|
||||
inst.modificationInfo.modifiesGPR = true; // success flag to rt
|
||||
}
|
||||
}
|
||||
|
||||
if (inst.opcode == OPCODE_COP2)
|
||||
{
|
||||
decodeCOP2(inst);
|
||||
}
|
||||
else if (inst.opcode == OPCODE_LWC2 || inst.opcode == OPCODE_LDC2)
|
||||
{
|
||||
inst.isVU = true;
|
||||
inst.modificationInfo.modifiesVFR = true;
|
||||
}
|
||||
else if (inst.opcode == OPCODE_SWC2 || inst.opcode == OPCODE_SDC2)
|
||||
{
|
||||
inst.isVU = true;
|
||||
inst.modificationInfo.modifiesMemory = true;
|
||||
}
|
||||
|
||||
if (inst.opcode == OPCODE_LQ || inst.opcode == OPCODE_SQ)
|
||||
{
|
||||
inst.isMultimedia = true;
|
||||
}
|
||||
|
||||
if (inst.isMMI || inst.isVU)
|
||||
{
|
||||
inst.isMultimedia = true;
|
||||
inst.vectorInfo.isVector = inst.isVU; // Only VU ops are truly vector
|
||||
}
|
||||
|
||||
RabbitizerInstructionR5900_destroy(&rabbitizerInst);
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user