migrate from private cloud

This commit is contained in:
Ran-j
2025-04-12 03:49:35 -03:00
commit 6e9049be40
29 changed files with 5353 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+126
View File
@@ -0,0 +1,126 @@
#include "ps2recomp/config_manager.h"
#include <toml.hpp>
#include <fstream>
#include <iostream>
#include <stdexcept>
namespace ps2recomp
{
ConfigManager::ConfigManager(const std::string &configPath)
: m_configPath(configPath)
{
}
ConfigManager::~ConfigManager() = default;
RecompilerConfig ConfigManager::loadConfig()
{
RecompilerConfig config;
try
{
auto data = toml::parse(m_configPath);
config.inputPath = toml::find<std::string>(data, "general", "input");
config.outputPath = toml::find<std::string>(data, "general", "output");
config.singleFileOutput = toml::find<bool>(data, "general", "single_file_output");
config.stubFunctions = toml::find<std::vector<std::string>>(data, "general", "stubs");
config.skipFunctions = toml::find<std::vector<std::string>>(data, "general", "skip");
if (data.contains("patches") && data.at("patches").is_table())
{
const auto &patches = toml::find(data, "patches");
if (patches.contains("instructions") && patches.at("instructions").is_array())
{
const auto &instPatches = toml::find(patches, "instructions").as_array();
for (const auto &patch : instPatches)
{
if (patch.contains("address") && patch.contains("value"))
{
uint32_t address = std::stoul(toml::find<std::string>(patch, "address"), nullptr, 0);
std::string value = toml::find<std::string>(patch, "value");
config.patches[address] = value;
}
}
}
}
if (data.contains("stub_implementations") && data.at("stub_implementations").is_table())
{
const auto &stubImpls = toml::find(data, "stub_implementations");
for (const auto &item : stubImpls.as_table())
{
const std::string &funcName = item.first;
const std::string &implementation = toml::find<std::string>(stubImpls, funcName);
config.stubImplementations[funcName] = implementation;
}
}
}
catch (const std::exception &e)
{
std::cerr << "Error parsing configuration file: " << e.what() << std::endl;
throw;
}
return config;
}
void ConfigManager::saveConfig(const RecompilerConfig &config)
{
toml::value data;
toml::table general;
general["input"] = config.inputPath;
general["output"] = config.outputPath;
general["single_file_output"] = config.singleFileOutput;
data["general"] = general;
toml::array stubs;
for (const auto &stub : config.stubFunctions)
{
stubs.push_back(stub);
}
data["stubs"] = stubs;
toml::array skips;
for (const auto &skip : config.skipFunctions)
{
skips.push_back(skip);
}
data["skip"] = skips;
toml::table patches;
toml::array instPatches;
for (const auto &patch : config.patches)
{
toml::table p;
p["address"] = "0x" + std::to_string(patch.first);
p["value"] = patch.second;
instPatches.push_back(p);
}
patches["instructions"] = instPatches;
data["patches"] = patches;
if (!config.stubImplementations.empty())
{
toml::table stubImpls;
for (const auto &impl : config.stubImplementations)
{
stubImpls[impl.first] = impl.second;
}
data["stub_implementations"] = stubImpls;
}
std::ofstream file(m_configPath);
if (!file)
{
throw std::runtime_error("Failed to open file for writing: " + m_configPath);
}
file << data;
}
} // namespace ps2recomp
+291
View File
@@ -0,0 +1,291 @@
#include "ps2recomp/elf_parser.h"
#include <iostream>
#include <stdexcept>
namespace ps2recomp
{
ElfParser::ElfParser(const std::string &filePath)
: m_filePath(filePath), m_elf(new ELFIO::elfio())
{
}
bool ElfParser::isExecutableSection(const ELFIO::section *section) const
{
return (section->get_flags() & ELFIO::SHF_EXECINSTR) != 0;
}
bool ElfParser::isDataSection(const ELFIO::section *section) const
{
return (section->get_flags() & ELFIO::SHF_ALLOC) != 0 &&
!(section->get_flags() & ELFIO::SHF_EXECINSTR);
}
std::vector<Function> ElfParser::extractFunctions()
{
std::vector<Function> functions;
for (const auto &symbol : m_symbols)
{
if (symbol.isFunction && symbol.size > 0)
{
Function func;
func.name = symbol.name;
func.start = symbol.address;
func.end = symbol.address + symbol.size;
func.isRecompiled = false;
func.isStub = false;
functions.push_back(func);
}
}
std::sort(functions.begin(), functions.end(),
[](const Function &a, const Function &b)
{ return a.start < b.start; });
return functions;
}
std::vector<Symbol> ElfParser::extractSymbols()
{
return m_symbols;
}
std::vector<Section> ElfParser::getSections()
{
return m_sections;
}
std::vector<Relocation> ElfParser::getRelocations()
{
return m_relocations;
}
bool ElfParser::isValidAddress(uint32_t address) const
{
for (const auto &section : m_sections)
{
if (address >= section.address && address < (section.address + section.size))
{
return true;
}
}
return false;
}
uint32_t ElfParser::readWord(uint32_t address) const
{
for (const auto &section : m_sections)
{
if (address >= section.address && address < (section.address + section.size))
{
if (section.data)
{
uint32_t offset = address - section.address;
return *reinterpret_cast<uint32_t *>(section.data + offset);
}
}
}
throw std::runtime_error("Invalid address for readWord: " + std::to_string(address));
}
uint8_t *ElfParser::getSectionData(const std::string &sectionName)
{
for (const auto &section : m_sections)
{
if (section.name == sectionName)
{
return section.data;
}
}
return nullptr;
}
uint32_t ElfParser::getSectionAddress(const std::string &sectionName)
{
for (const auto &section : m_sections)
{
if (section.name == sectionName)
{
return section.address;
}
}
return 0;
}
uint32_t ElfParser::getSectionSize(const std::string &sectionName)
{
for (const auto &section : m_sections)
{
if (section.name == sectionName)
{
return section.size;
}
}
return 0;
}
ElfParser::~ElfParser() = default;
bool ElfParser::parse()
{
if (!m_elf->load(m_filePath))
{
std::cerr << "Error: Could not load ELF file: " << m_filePath << std::endl;
return false;
}
// Check if this is a PS2 ELF (MIPS R5900)
if (m_elf->get_machine() != ELFIO::EM_MIPS)
{
std::cerr << "Error: Not a MIPS ELF file" << std::endl;
return false;
}
loadSections();
loadSymbols();
loadRelocations();
return true;
}
void ElfParser::loadSections()
{
m_sections.clear();
ELFIO::Elf_Half sec_num = m_elf->sections.size();
for (ELFIO::Elf_Half i = 0; i < sec_num; ++i)
{
ELFIO::section *psec = m_elf->sections[i];
Section section;
section.name = psec->get_name();
section.address = psec->get_address();
section.size = psec->get_size();
section.offset = psec->get_offset();
section.isCode = isExecutableSection(psec);
section.isData = isDataSection(psec);
section.isBSS = (psec->get_type() == ELFIO::SHT_NOBITS);
section.isReadOnly = !(psec->get_flags() & ELFIO::SHF_WRITE);
if (psec->get_size() > 0 && psec->get_type() != ELFIO::SHT_NOBITS)
{
section.data = (uint8_t *)psec->get_data();
}
else
{
section.data = nullptr;
}
m_sections.push_back(section);
}
}
void ElfParser::loadSymbols()
{
m_symbols.clear();
for (ELFIO::Elf_Half i = 0; i < m_elf->sections.size(); ++i)
{
ELFIO::section *psec = m_elf->sections[i];
if (psec->get_type() == ELFIO::SHT_SYMTAB || psec->get_type() == ELFIO::SHT_DYNSYM)
{
ELFIO::symbol_section_accessor symbols(*m_elf, psec);
ELFIO::Elf_Xword sym_num = symbols.get_symbols_num();
ELFIO::section *pstrSec = m_elf->sections[psec->get_link()];
ELFIO::string_section_accessor strings(pstrSec);
for (ELFIO::Elf_Xword j = 0; j < sym_num; ++j)
{
std::string name;
ELFIO::Elf64_Addr value;
ELFIO::Elf_Xword size;
unsigned char bind;
unsigned char type;
ELFIO::Elf_Half section_index;
unsigned char other;
symbols.get_symbol(j, name, value, size, bind, type, section_index, other);
// Skip empty symbols or those with invalid section index
if (name.empty() || section_index == ELFIO::SHN_UNDEF)
{
continue;
}
Symbol symbol;
symbol.name = name;
symbol.address = static_cast<uint32_t>(value);
symbol.size = static_cast<uint32_t>(size);
symbol.isFunction = (type == ELFIO::STT_FUNC);
symbol.isImported = (bind == ELFIO::STB_GLOBAL && section_index == ELFIO::SHN_UNDEF);
symbol.isExported = (bind == ELFIO::STB_GLOBAL && section_index != ELFIO::SHN_UNDEF);
m_symbols.push_back(symbol);
}
}
}
}
void ElfParser::loadRelocations()
{
m_relocations.clear();
for (ELFIO::Elf_Half i = 0; i < m_elf->sections.size(); ++i)
{
ELFIO::section *psec = m_elf->sections[i];
if (psec->get_type() == ELFIO::SHT_REL || psec->get_type() == ELFIO::SHT_RELA)
{
ELFIO::relocation_section_accessor relocs(*m_elf, psec);
ELFIO::section *symSec = m_elf->sections[psec->get_link()];
ELFIO::symbol_section_accessor symbols(*m_elf, symSec);
ELFIO::section *strSec = m_elf->sections[symSec->get_link()];
ELFIO::string_section_accessor strings(strSec);
for (ELFIO::Elf_Xword j = 0; j < relocs.get_entries_num(); ++j)
{
ELFIO::Elf64_Addr offset;
ELFIO::Elf_Word symbol;
ELFIO::Elf_Word type;
ELFIO::Elf_Sxword addend;
// Always use the 5-parameter version
if (psec->get_type() == ELFIO::SHT_REL)
{
// Pass addend even for REL sections
relocs.get_entry(j, offset, symbol, type, addend);
// Reset addend for REL sections since it's not part of the section
addend = 0;
}
else
{
relocs.get_entry(j, offset, symbol, type, addend);
}
Relocation reloc;
reloc.offset = static_cast<uint32_t>(offset);
reloc.info = (symbol << 8) | (type & 0xFF);
reloc.symbol = symbol;
reloc.type = type;
reloc.addend = static_cast<int32_t>(addend);
m_relocations.push_back(reloc);
}
}
}
}
}
+50
View File
@@ -0,0 +1,50 @@
#include "ps2recomp/ps2_recompiler.h"
#include <iostream>
#include <string>
using namespace ps2recomp;
void printUsage()
{
std::cout << "PS2Recomp - A static recompiler for PlayStation 2 ELF files\n";
std::cout << "Usage: ps2recomp <config.toml>\n";
std::cout << " config.toml: Configuration file for the recompiler\n";
}
int main(int argc, char *argv[])
{
if (argc < 2)
{
printUsage();
return 1;
}
std::string configPath = argv[1];
try
{
PS2Recompiler recompiler(configPath);
if (!recompiler.initialize())
{
std::cerr << "Failed to initialize recompiler\n";
return 1;
}
if (!recompiler.recompile())
{
std::cerr << "Recompilation failed\n";
return 1;
}
recompiler.generateOutput();
std::cout << "Recompilation completed successfully\n";
return 0;
}
catch (const std::exception &e)
{
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
}
+388
View File
@@ -0,0 +1,388 @@
#include "ps2recomp/ps2_recompiler.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <stdexcept>
#include <filesystem>
namespace fs = std::filesystem;
namespace ps2recomp
{
PS2Recompiler::PS2Recompiler(const std::string &configPath)
: m_configManager(configPath)
{
}
bool PS2Recompiler::initialize()
{
try
{
m_config = m_configManager.loadConfig();
for (const auto &name : m_config.stubFunctions)
{
m_stubFunctions[name] = true;
}
for (const auto &name : m_config.skipFunctions)
{
m_skipFunctions[name] = true;
}
m_elfParser = std::make_unique<ElfParser>(m_config.inputPath);
if (!m_elfParser->parse())
{
std::cerr << "Failed to parse ELF file: " << m_config.inputPath << std::endl;
return false;
}
m_functions = m_elfParser->extractFunctions();
m_symbols = m_elfParser->extractSymbols();
m_sections = m_elfParser->getSections();
m_relocations = m_elfParser->getRelocations();
m_decoder = std::make_unique<R5900Decoder>();
m_codeGenerator = std::make_unique<CodeGenerator>(m_symbols);
fs::create_directories(m_config.outputPath);
return true;
}
catch (const std::exception &e)
{
std::cerr << "Error during initialization: " << e.what() << std::endl;
return false;
}
}
bool PS2Recompiler::recompile()
{
try
{
std::cout << "Recompiling " << m_functions.size() << " functions..." << std::endl;
std::string runtimeHeader = generateRuntimeHeader();
fs::path runtimeHeaderPath = fs::path(m_config.outputPath) / "ps2_runtime_macros.h";
writeToFile(runtimeHeaderPath.string(), runtimeHeader);
size_t processedCount = 0;
for (auto &function : m_functions)
{
if (shouldSkipFunction(function.name))
{
std::cout << "Skipping function: " << function.name << std::endl;
continue;
}
if (shouldStubFunction(function.name))
{
std::cout << "Stubbing function: " << function.name << std::endl;
function.isStub = true;
// TODO: Generate stub implementation
continue;
}
if (shouldStubFunction(function.name))
{
std::cout << "Stubbing function: " << function.name << std::endl;
function.isStub = true;
function.isRecompiled = true; // we're generating code for it
// Generate stub implementation and store it
std::string stubCode = generateStubFunction(function);
m_generatedStubs[function.start] = stubCode;
continue;
}
if (!decodeFunction(function))
{
std::cerr << "Failed to decode function: " << function.name << std::endl;
return false;
}
function.isRecompiled = true;
#if _DEBUG
processedCount++;
if (processedCount % 100 == 0)
{
std::cout << "Processed " << processedCount << " functions." << std::endl;
}
#endif
}
std::cout << "Recompilation completed successfully." << std::endl;
return true;
}
catch (const std::exception &e)
{
std::cerr << "Error during recompilation: " << e.what() << std::endl;
return false;
}
}
void PS2Recompiler::generateOutput()
{
try
{
if (m_config.singleFileOutput)
{
std::stringstream combinedOutput;
combinedOutput << "#include \"ps2_runtime_macros.h\"\n";
combinedOutput << "#include \"ps2_runtime.h\"\n\n";
for (const auto &function : m_functions)
{
if (!function.isRecompiled)
{
continue;
}
if (function.isStub)
{
combinedOutput << m_generatedStubs[function.start] << "\n\n";
}
else
{
const auto &instructions = m_decodedFunctions[function.start];
std::string code = m_codeGenerator->generateFunction(function, instructions);
combinedOutput << code << "\n\n";
}
}
fs::path outputPath = fs::path(m_config.outputPath) / "recompiled.cpp";
writeToFile(outputPath.string(), combinedOutput.str());
std::cout << "Wrote combined output to: " << outputPath << std::endl;
}
else
{
for (const auto &function : m_functions)
{
if (!function.isRecompiled || function.isStub)
{
continue;
}
std::string code;
if (function.isStub)
{
code = m_generatedStubs[function.start];
}
else
{
const auto &instructions = m_decodedFunctions[function.start];
code = m_codeGenerator->generateFunction(function, instructions);
}
fs::path outputPath = getOutputPath(function);
fs::create_directories(outputPath.parent_path());
writeToFile(outputPath.string(), code);
}
std::cout << "Wrote individual function files to: " << m_config.outputPath << std::endl;
}
}
catch (const std::exception &e)
{
std::cerr << "Error during output generation: " << e.what() << std::endl;
}
}
bool PS2Recompiler::decodeFunction(Function &function)
{
std::vector<Instruction> instructions;
uint32_t start = function.start;
uint32_t end = function.end;
for (uint32_t address = start; address < end; address += 4)
{
try
{
if (!m_elfParser->isValidAddress(address))
{
std::cerr << "Invalid address: 0x" << std::hex << address << std::dec
<< " in function: " << function.name << std::endl;
return false;
}
uint32_t rawInstruction = m_elfParser->readWord(address);
auto patchIt = m_config.patches.find(address);
if (patchIt != m_config.patches.end())
{
rawInstruction = std::stoul(patchIt->second, nullptr, 0);
std::cout << "Applied patch at 0x" << std::hex << address << std::dec << std::endl;
}
Instruction inst = m_decoder->decodeInstruction(address, rawInstruction);
instructions.push_back(inst);
}
catch (const std::exception &e)
{
std::cerr << "Error decoding instruction at 0x" << std::hex << address << std::dec
<< " in function: " << function.name << ": " << e.what() << std::endl;
return false;
}
}
m_decodedFunctions[function.start] = instructions;
return true;
}
bool PS2Recompiler::shouldStubFunction(const std::string &name) const
{
return m_stubFunctions.find(name) != m_stubFunctions.end();
}
bool PS2Recompiler::shouldSkipFunction(const std::string &name) const
{
return m_skipFunctions.find(name) != m_skipFunctions.end();
}
std::string PS2Recompiler::generateRuntimeHeader()
{
return m_codeGenerator->generateMacroHeader();
}
std::string PS2Recompiler::generateStubFunction(const Function &function)
{
std::stringstream ss;
ss << "#include \"ps2_runtime_macros.h\"\n";
ss << "#include \"ps2_runtime.h\"\n\n";
ss << "// STUB FUNCTION: " << function.name << "\n";
ss << "// Address: 0x" << std::hex << function.start << " - 0x" << function.end << std::dec << "\n";
ss << "void " << function.name << "(uint8_t* rdram, R5900Context* ctx) {\n";
auto stubImpl = m_config.stubImplementations.find(function.name);
if (stubImpl != m_config.stubImplementations.end())
{
ss << " // Custom stub implementation\n";
ss << " " << stubImpl->second << "\n";
}
else
{
// Default stub implementation based on common functions
if (function.name == "printf" || function.name == "fprintf" ||
function.name == "sprintf" || function.name == "snprintf")
{
ss << " // Format string is in $a0 (r4), args start at $a1 (r5)\n";
ss << " #ifdef PS2_RECOMP_DEBUG\n";
ss << " printf(\"Stub called: " << function.name << " with format at 0x%08X\\n\", ctx->r[4]);\n";
ss << " #endif\n";
ss << " // Return success (number of characters, but we'll just say 1)\n";
ss << " ctx->r[2] = 1;\n";
}
else if (function.name == "malloc" || function.name == "calloc" ||
function.name == "realloc" || function.name == "memalign")
{
ss << " // Memory allocation - Would call the runtime's allocation system\n";
ss << " uint32_t size = ctx->r[4]; // Size is in $a0\n";
ss << " #ifdef PS2_RECOMP_DEBUG\n";
ss << " printf(\"Stub called: " << function.name << " size=%u\\n\", size);\n";
ss << " #endif\n";
ss << " // In a real implementation, call runtime->allocateMemory(size)\n";
ss << " ctx->r[2] = 0; // Return NULL for now - replace with actual allocation in real implementation\n";
}
else if (function.name == "free")
{
ss << " // Free memory - Would call the runtime's free system\n";
ss << " uint32_t ptr = ctx->r[4]; // Pointer is in $a0\n";
ss << " #ifdef PS2_RECOMP_DEBUG\n";
ss << " printf(\"Stub called: free(0x%08X)\\n\", ptr);\n";
ss << " #endif\n";
ss << " // In a real implementation, call runtime->freeMemory(ptr)\n";
}
else if (function.name == "memcpy" || function.name == "memmove")
{
ss << " // Memory copy\n";
ss << " uint32_t dst = ctx->r[4]; // Destination in $a0\n";
ss << " uint32_t src = ctx->r[5]; // Source in $a1\n";
ss << " uint32_t size = ctx->r[6]; // Size in $a2\n";
ss << " #ifdef PS2_RECOMP_DEBUG\n";
ss << " printf(\"Stub called: " << function.name << "(dst=0x%08X, src=0x%08X, size=%u)\\n\", dst, src, size);\n";
ss << " #endif\n";
ss << " // Only copy if within valid memory range\n";
ss << " if (dst < 0x2000000 && src < 0x2000000 && dst + size < 0x2000000 && src + size < 0x2000000) {\n";
ss << " memcpy(rdram + dst, rdram + src, size);\n";
ss << " }\n";
ss << " ctx->r[2] = dst; // Return destination pointer\n";
}
else if (function.name == "memset")
{
ss << " // Memory set\n";
ss << " uint32_t dst = ctx->r[4]; // Destination in $a0\n";
ss << " uint8_t value = (uint8_t)ctx->r[5]; // Value in $a1\n";
ss << " uint32_t size = ctx->r[6]; // Size in $a2\n";
ss << " #ifdef PS2_RECOMP_DEBUG\n";
ss << " printf(\"Stub called: memset(dst=0x%08X, value=%u, size=%u)\\n\", dst, value, size);\n";
ss << " #endif\n";
ss << " // Only set if within valid memory range\n";
ss << " if (dst < 0x2000000 && dst + size < 0x2000000) {\n";
ss << " memset(rdram + dst, value, size);\n";
ss << " }\n";
ss << " ctx->r[2] = dst; // Return destination pointer\n";
}
else
{
// Generic stub for unknown functions
ss << " // Default stub implementation\n";
ss << " #ifdef PS2_RECOMP_DEBUG\n";
ss << " printf(\"Stub function called: " << function.name << " at PC=0x%08X\\n\", ctx->pc);\n";
ss << " #endif\n";
ss << " // Default return value (0)\n";
ss << " ctx->r[2] = 0;\n";
}
}
ss << "}\n";
return ss.str();
}
bool PS2Recompiler::writeToFile(const std::string &path, const std::string &content)
{
std::ofstream file(path);
if (!file)
{
std::cerr << "Failed to open file for writing: " << path << std::endl;
return false;
}
file << content;
file.close();
return true;
}
std::filesystem::path PS2Recompiler::getOutputPath(const Function &function) const
{
std::string safeName = function.name;
std::replace_if(safeName.begin(), safeName.end(), [](char c)
{ return c == '/' || c == '\\' || c == ':' || c == '*' ||
c == '?' || c == '"' || c == '<' || c == '>' ||
c == '|' || c == '$'; }, '_');
if (safeName.empty())
{
std::stringstream ss;
ss << "func_" << std::hex << function.start;
safeName = ss.str();
}
std::filesystem::path outputPath = m_config.outputPath;
outputPath /= safeName + ".cpp";
return outputPath;
}
}
+636
View File
@@ -0,0 +1,636 @@
#include "ps2recomp/r5900_decoder.h"
namespace ps2recomp
{
R5900Decoder::R5900Decoder()
{
}
R5900Decoder::~R5900Decoder()
{
}
Instruction R5900Decoder::decodeInstruction(uint32_t address, uint32_t rawInstruction)
{
Instruction inst;
inst.address = address;
inst.raw = rawInstruction;
inst.opcode = OPCODE(rawInstruction);
inst.rs = RS(rawInstruction);
inst.rt = RT(rawInstruction);
inst.rd = RD(rawInstruction);
inst.sa = SA(rawInstruction);
inst.function = FUNCTION(rawInstruction);
inst.immediate = IMMEDIATE(rawInstruction);
inst.target = TARGET(rawInstruction);
inst.isMMI = false;
inst.isVU = false;
inst.isBranch = false;
inst.isJump = false;
inst.isCall = false;
inst.isReturn = false;
inst.hasDelaySlot = false;
inst.isMultimedia = false;
switch (inst.opcode)
{
case OPCODE_SPECIAL:
decodeSpecial(inst);
break;
case OPCODE_REGIMM:
decodeRegimm(inst);
break;
case OPCODE_J:
decodeJType(inst);
inst.isJump = true;
inst.hasDelaySlot = true;
break;
case OPCODE_JAL:
decodeJType(inst);
inst.isJump = true;
inst.isCall = true;
inst.hasDelaySlot = true;
break;
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;
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);
break;
case OPCODE_MMI:
decodeMMI(inst);
inst.isMMI = true;
inst.isMultimedia = true;
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
break;
case OPCODE_LB:
case OPCODE_LH:
case OPCODE_LWL:
case OPCODE_LW:
case OPCODE_LBU:
case OPCODE_LHU:
case OPCODE_LWR:
case OPCODE_LWU:
case OPCODE_LD:
case OPCODE_LDL:
case OPCODE_LDR:
case OPCODE_LL:
case OPCODE_LWC1:
case OPCODE_LDC1:
case OPCODE_LWC2:
case OPCODE_LDC2:
decodeIType(inst);
inst.isLoad = true;
break;
case OPCODE_SB:
case OPCODE_SH:
case OPCODE_SWL:
case OPCODE_SW:
case OPCODE_SWR:
case OPCODE_SD:
case OPCODE_SDL:
case OPCODE_SDR:
case OPCODE_SC:
case OPCODE_SWC1:
case OPCODE_SDC1:
case OPCODE_SWC2:
case OPCODE_SDC2:
case OPCODE_SCD:
decodeIType(inst);
inst.isStore = true;
break;
case OPCODE_COP0:
decodeCOP0(inst);
break;
case OPCODE_COP1:
decodeCOP1(inst);
break;
case OPCODE_COP2:
decodeCOP2(inst);
inst.isVU = true;
inst.isMultimedia = true;
break;
case OPCODE_PREF:
case OPCODE_CACHE:
// Prefetch and cache operations
decodeIType(inst);
break;
default:
// Default to I-type for most other instructions
decodeIType(inst);
break;
}
return inst;
}
void R5900Decoder::decodeRType(Instruction &inst) const
{
// R-type instructions already have all fields set correctly
}
void R5900Decoder::decodeIType(Instruction &inst) const
{
// I-type instructions already have all fields set correctly
}
void R5900Decoder::decodeJType(Instruction &inst) const
{
// J-type instructions already have all fields set correctly
}
void R5900Decoder::decodeSpecial(Instruction &inst) const
{
switch (inst.function)
{
case SPECIAL_JR:
inst.isJump = true;
inst.hasDelaySlot = true;
if (inst.rs == 31)
{
// jr $ra is typically a return
inst.isReturn = true;
}
break;
case SPECIAL_JALR:
inst.isJump = true;
inst.isCall = true;
inst.hasDelaySlot = true;
break;
case SPECIAL_SYSCALL:
case SPECIAL_BREAK:
// Special handling for syscall/break
break;
case SPECIAL_MFHI:
case SPECIAL_MTHI:
case SPECIAL_MFLO:
case SPECIAL_MTLO:
// HI/LO register operations
break;
case SPECIAL_MULT:
case SPECIAL_MULTU:
case SPECIAL_DIV:
case SPECIAL_DIVU:
// Multiplication and division operations
inst.isMultimedia = true;
break;
case SPECIAL_ADD:
case SPECIAL_ADDU:
case SPECIAL_SUB:
case SPECIAL_SUBU:
case SPECIAL_AND:
case SPECIAL_OR:
case SPECIAL_XOR:
case SPECIAL_NOR:
case SPECIAL_SLT:
case SPECIAL_SLTU:
// ALU operations
break;
case SPECIAL_SLL:
case SPECIAL_SRL:
case SPECIAL_SRA:
case SPECIAL_SLLV:
case SPECIAL_SRLV:
case SPECIAL_SRAV:
// Shift operations
break;
// 64-bit specific operations
case SPECIAL_DADD:
case SPECIAL_DADDU:
case SPECIAL_DSUB:
case SPECIAL_DSUBU:
case SPECIAL_DSLL:
case SPECIAL_DSRL:
case SPECIAL_DSRA:
case SPECIAL_DSLL32:
case SPECIAL_DSRL32:
case SPECIAL_DSRA32:
case SPECIAL_DSLLV:
case SPECIAL_DSRLV:
case SPECIAL_DSRAV:
// 64-bit operations
break;
default:
// Other R-type instructions
break;
}
}
void R5900Decoder::decodeRegimm(Instruction &inst) const
{
uint32_t rt = inst.rt;
switch (rt)
{
case REGIMM_BLTZ:
case REGIMM_BGEZ:
case REGIMM_BLTZL:
case REGIMM_BGEZL:
inst.isBranch = true;
inst.hasDelaySlot = true;
break;
case REGIMM_BLTZAL:
case REGIMM_BGEZAL:
case REGIMM_BLTZALL:
case REGIMM_BGEZALL:
inst.isBranch = true;
inst.isCall = true;
inst.hasDelaySlot = true;
break;
case REGIMM_MTSAB:
case REGIMM_MTSAH:
// PS2 specific MTSAB/MTSAH instructions (for QMFC2/QMTC2)
inst.isMultimedia = true;
break;
default:
// Other REGIMM instructions
break;
}
}
void R5900Decoder::decodeMMI(Instruction &inst) const
{
inst.isMMI = true;
inst.isMultimedia = true;
// The function field is actually determined by the lowest 6 bits (as in R-type)
uint32_t mmiFunction = inst.function;
// Categorize the MMI instruction type based on the rs field
uint32_t rs = inst.rs;
switch (mmiFunction)
{
case MMI_MADD:
case MMI_MADDU:
case MMI_MADD1:
case MMI_MADDU1:
// Multiply-add operations
break;
case MMI_PLZCW:
// Count leading zeros/ones
break;
case MMI_MFHI1:
case MMI_MTHI1:
case MMI_MFLO1:
case MMI_MTLO1:
// Secondary HI/LO register operations
break;
case MMI_MULT1:
case MMI_MULTU1:
case MMI_DIV1:
case MMI_DIVU1:
// Secondary multiply/divide operations
break;
case MMI_MMI0:
// First set of multimedia instructions
decodeMMI0(inst);
break;
case MMI_MMI1:
// Second set of multimedia instructions
decodeMMI1(inst);
break;
case MMI_MMI2:
// Third set of multimedia instructions
decodeMMI2(inst);
break;
case MMI_MMI3:
// Fourth set of multimedia instructions
decodeMMI3(inst);
break;
case MMI_PMFHL:
// PMFHL variations based on sa field
decodePMFHL(inst);
break;
case MMI_PMTHL:
// PMTHL operations
break;
case MMI_PSLLH:
case MMI_PSRLH:
case MMI_PSRAH:
case MMI_PSLLW:
case MMI_PSRLW:
case MMI_PSRAW:
// SIMD shift operations
break;
default:
// Unknown or unsupported MMI function
break;
}
}
void R5900Decoder::decodeCOP0(Instruction &inst) const
{
// COP0 (System Control) instructions
uint32_t rs = inst.rs; // Actually the cop0 format field
if (rs == COP0_MF)
{
// Move From COP0 register
}
else if (rs == COP0_MT)
{
// Move To COP0 register
}
else if (rs == COP0_CO)
{
// COProcessor operations
uint32_t function = inst.function;
if (function == COP0_CO_ERET)
{
inst.isReturn = true;
inst.hasDelaySlot = true;
}
else if (function == COP0_CO_TLBR ||
function == COP0_CO_TLBWI ||
function == COP0_CO_TLBWR ||
function == COP0_CO_TLBP)
{
// TLB operations
}
else if (function == COP0_CO_EI || function == COP0_CO_DI)
{
// Enable/Disable Interrupts
}
}
}
void R5900Decoder::decodeCOP1(Instruction &inst) const
{
// COP1 (FPU) instructions
uint32_t rs = inst.rs; // The FPU format field
if (rs == COP1_MF)
{
// Move From FPU register
}
else if (rs == COP1_CF)
{
// Move From FPU Control register
}
else if (rs == COP1_MT)
{
// Move To FPU register
}
else if (rs == COP1_CT)
{
// Move To FPU Control register
}
else if (rs == COP1_BC)
{
// FPU Branch on Condition
uint32_t rt = inst.rt; // The condition code
if (rt == COP1_BC_BCF || rt == COP1_BC_BCT)
{
inst.isBranch = true;
inst.hasDelaySlot = true;
}
}
else if (rs == COP1_S || rs == COP1_W)
{
// FPU operations (single precision or word)
uint32_t function = inst.function;
// Decode specific FPU operation based on function field
}
}
void R5900Decoder::decodeCOP2(Instruction &inst) const
{
// COP2 (VU0 macro mode) instructions
inst.isVU = true;
inst.isMultimedia = true;
uint32_t rs = inst.rs; // The VU0 format field
if (rs == COP2_MFC2)
{
// Move From COP2 register
}
else if (rs == COP2_CFC2)
{
// Move From COP2 Control register
}
else if (rs == COP2_MTC2)
{
// Move To COP2 register
}
else if (rs == COP2_CTC2)
{
// Move To COP2 Control register
}
else if (rs == COP2_BCF || rs == COP2_BCT)
{
// VU0 Branch on Condition
inst.isBranch = true;
inst.hasDelaySlot = true;
}
else
{
// VU0 vector operations
// These would need detailed decoding based on function field
}
}
void R5900Decoder::decodeMMI0(Instruction &inst) const
{
// Decode MMI0 subfunctions (based on function field)
uint32_t subFunction = inst.function & 0x3F;
// The implementation would set appropriate flags or properties based on the specific MMI0 operation
}
void R5900Decoder::decodeMMI1(Instruction &inst) const
{
// Decode MMI1 subfunctions (based on function field)
uint32_t subFunction = inst.function & 0x3F;
// The implementation would set appropriate flags or properties based on the specific MMI1 operation
}
void R5900Decoder::decodeMMI2(Instruction &inst) const
{
// Decode MMI2 subfunctions (based on function field)
uint32_t subFunction = inst.function & 0x3F;
// The implementation would set appropriate flags or properties based on the specific MMI2 operation
}
void R5900Decoder::decodeMMI3(Instruction &inst) const
{
// Decode MMI3 subfunctions (based on function field)
uint32_t subFunction = inst.function & 0x3F;
// The implementation would set appropriate flags or properties based on the specific MMI3 operation
}
void R5900Decoder::decodePMFHL(Instruction &inst) const
{
// PMFHL has different variations based on the sa field
uint32_t saField = inst.sa;
switch (saField)
{
case PMFHL_LW:
case PMFHL_UW:
case PMFHL_SLW:
case PMFHL_LH:
case PMFHL_SH:
// Set the appropriate flag for the PMFHL variation
inst.pmfhlVariation = saField;
break;
default:
// Unknown PMFHL variation
inst.pmfhlVariation = 0xFF;
break;
}
}
bool R5900Decoder::isBranchInstruction(const Instruction &inst) const
{
return inst.isBranch;
}
bool R5900Decoder::isJumpInstruction(const Instruction &inst) const
{
return inst.isJump;
}
bool R5900Decoder::isCallInstruction(const Instruction &inst) const
{
return inst.isCall;
}
bool R5900Decoder::isReturnInstruction(const Instruction &inst) const
{
return inst.isReturn;
}
bool R5900Decoder::isMMIInstruction(const Instruction &inst) const
{
return inst.isMMI;
}
bool R5900Decoder::isVUInstruction(const Instruction &inst) const
{
return inst.isVU;
}
bool R5900Decoder::isStore(const Instruction &inst) const
{
return inst.isStore;
}
bool R5900Decoder::isLoad(const Instruction &inst) const
{
return inst.isLoad;
}
bool R5900Decoder::hasDelaySlot(const Instruction &inst) const
{
return inst.hasDelaySlot;
}
uint32_t R5900Decoder::getBranchTarget(const Instruction &inst) const
{
if (!inst.isBranch)
{
return 0;
}
// Calculate branch target: PC + 4 + (sign-extended immediate << 2)
int32_t offset = static_cast<int16_t>(inst.immediate) << 2;
return inst.address + 4 + offset;
}
uint32_t R5900Decoder::getJumpTarget(const Instruction &inst) const
{
if (!inst.isJump)
{
return 0;
}
if (inst.opcode == OPCODE_J || inst.opcode == OPCODE_JAL)
{
// J/JAL: target is in the lower 26 bits, shifted left by 2
// and combined with the upper 4 bits of PC + 4
uint32_t pc_upper = (inst.address + 4) & 0xF0000000;
return pc_upper | (inst.target << 2);
}
else if (inst.opcode == OPCODE_SPECIAL &&
(inst.function == SPECIAL_JR || inst.function == SPECIAL_JALR))
{
// JR/JALR: target is in the rs register (can't be determined statically)
return 0;
}
return 0;
}
} // namespace ps2recomp