mirror of
https://github.com/ran-j/PS2Recomp.git
synced 2026-10-03 02:56:41 -04:00
migrate from private cloud
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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 §ion : 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 §ion : 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 §ionName)
|
||||
{
|
||||
for (const auto §ion : m_sections)
|
||||
{
|
||||
if (section.name == sectionName)
|
||||
{
|
||||
return section.data;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t ElfParser::getSectionAddress(const std::string §ionName)
|
||||
{
|
||||
for (const auto §ion : m_sections)
|
||||
{
|
||||
if (section.name == sectionName)
|
||||
{
|
||||
return section.address;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t ElfParser::getSectionSize(const std::string §ionName)
|
||||
{
|
||||
for (const auto §ion : 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user