feat: added missing v0 instructions

feat: added threads inplementation on ps2 syscalls
feat: patch entrypoint on recompiler
feat: ps2 macros now is part of the runtime
feat: use thread to hold game loop
feat: sanitize functions like point out by chrisking1981
fix: fix old instructions cast
fix: fix missing patch for opcode j
This commit is contained in:
Ran-j
2025-12-27 11:33:24 -03:00
parent 409570c709
commit 2d1f7ec07f
12 changed files with 777 additions and 57 deletions
@@ -10,6 +10,7 @@
namespace ps2recomp
{
extern const std::unordered_set<std::string> kKeywords;
class CodeGenerator
{
@@ -17,6 +18,15 @@ namespace ps2recomp
CodeGenerator(const std::vector<Symbol> &symbols);
~CodeGenerator();
struct BootstrapInfo
{
bool valid = false;
uint32_t entry = 0;
uint32_t bssStart = 0;
uint32_t bssEnd = 0;
uint32_t gp = 0;
};
std::string generateFunction(const Function &function, const std::vector<Instruction> &instructions, const bool &useHeaders);
std::string generateFunctionRegistration(const std::vector<Function> &functions, const std::map<uint32_t, std::string> &stubs);
std::string generateMacroHeader();
@@ -24,12 +34,14 @@ namespace ps2recomp
const Function &function, const std::unordered_set<uint32_t> &internalTargets);
void setRenamedFunctions(const std::unordered_map<uint32_t, std::string> &renames);
void setBootstrapInfo(const BootstrapInfo &info);
std::unordered_set<uint32_t> collectInternalBranchTargets(const Function &function,
const std::vector<Instruction> &instructions);
public:
std::vector<Symbol> m_symbols;
std::unordered_map<uint32_t, std::string> m_renamedFunctions;
BootstrapInfo m_bootstrapInfo;
std::string translateInstruction(const Instruction &inst);
std::string translateMMIInstruction(const Instruction &inst);
@@ -104,9 +116,11 @@ namespace ps2recomp
// Jump Table Generation
std::string generateJumpTableSwitch(const Instruction &inst, uint32_t tableAddress,
const std::vector<JumpTableEntry> &entries);
std::string generateBootstrapFunction() const;
Symbol *findSymbolByAddress(uint32_t address);
std::string getFunctionName(uint32_t address);
std::string getGeneratedFunctionName(const Function &function);
};
}
@@ -29,6 +29,7 @@ namespace ps2recomp
uint8_t *getSectionData(const std::string &sectionName);
uint32_t getSectionAddress(const std::string &sectionName);
uint32_t getSectionSize(const std::string &sectionName);
uint32_t getEntryPoint() const;
private:
std::string m_filePath;
@@ -39,6 +39,8 @@ namespace ps2recomp
std::unordered_map<uint32_t, std::vector<Instruction>> m_decodedFunctions;
std::unordered_map<std::string, bool> m_skipFunctions;
std::map<uint32_t, std::string> m_generatedStubs;
std::unordered_map<uint32_t, std::string> m_functionRenames;
CodeGenerator::BootstrapInfo m_bootstrapInfo;
bool decodeFunction(Function &function);
void discoverAdditionalEntryPoints();
+137 -7
View File
@@ -8,6 +8,23 @@
#include <iostream>
#include <cctype>
namespace ps2recomp
{
static const std::unordered_set<std::string> kKeywords = {
"alignas", "alignof", "and", "and_eq", "asm", "auto", "bitand", "bitor", "bool",
"break", "case", "catch", "char", "char8_t", "char16_t", "char32_t", "class",
"compl", "concept", "const", "consteval", "constexpr", "constinit", "const_cast",
"continue", "co_await", "co_return", "co_yield", "decltype", "default", "delete",
"do", "double", "dynamic_cast", "else", "enum", "explicit", "export", "extern",
"false", "float", "for", "friend", "goto", "if", "inline", "int", "long", "mutable",
"namespace", "new", "noexcept", "not", "not_eq", "nullptr", "operator", "or", "or_eq",
"private", "protected", "public", "register", "reinterpret_cast", "requires", "return",
"short", "signed", "sizeof", "static", "static_assert", "static_cast", "struct",
"switch", "template", "this", "thread_local", "throw", "true", "try", "typedef",
"typeid", "typename", "union", "unsigned", "using", "virtual", "void", "volatile",
"wchar_t", "while", "xor", "xor_eq", "std"};
}
namespace ps2recomp
{
CodeGenerator::CodeGenerator(const std::vector<Symbol> &symbols)
@@ -20,6 +37,11 @@ namespace ps2recomp
m_renamedFunctions = renames;
}
void CodeGenerator::setBootstrapInfo(const BootstrapInfo &info)
{
m_bootstrapInfo = info;
}
std::string CodeGenerator::getFunctionName(uint32_t address)
{
auto it = m_renamedFunctions.find(address);
@@ -46,13 +68,35 @@ namespace ps2recomp
return false;
}
static bool isReservedCxxKeyword(const std::string &name)
{
return kKeywords.find(name) != kKeywords.end();
}
static std::string sanitizeFunctionName(const std::string &name)
{
// ugly but will do for now
if (name == "main")
return "ps2_main";
if (isReservedCxxKeyword(name))
return "ps2_" + name;
if (!isReservedCxxIdentifier(name))
return name;
return "ps2_" + name;
}
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)
{
@@ -78,7 +122,11 @@ namespace ps2recomp
std::string funcName = getFunctionName(target);
if (!funcName.empty())
{
ss << " " << funcName << "(rdram, ctx, runtime); return;\n";
ss << " " << funcName << "(rdram, ctx, runtime);\n";
if (branchInst.opcode == OPCODE_J)
{
ss << " return;\n";
}
}
else
{
@@ -268,6 +316,18 @@ namespace ps2recomp
ss << "#define PS2_RUNTIME_MACROS_H\n\n";
ss << "#include <cstdint>\n";
ss << "#include <immintrin.h> // For SSE/AVX intrinsics\n\n";
ss << "#include <intrin.h>\n\n";
ss << "inline uint32_t ps2_clz32(uint32_t val) {\n";
ss << "#if defined(_MSC_VER)\n";
ss << " unsigned long idx;\n";
ss << " if (_BitScanReverse(&idx, val)) {\n";
ss << " return 31u - idx;\n";
ss << " }\n";
ss << " return 32u;\n";
ss << "#else\n";
ss << " return val == 0 ? 32u : (uint32_t)__builtin_clz(val);\n";
ss << "#endif\n";
ss << "}\n\n";
ss << "// Basic MIPS arithmetic operations\n";
ss << "#define ADD32(a, b) ((uint32_t)((a) + (b)))\n";
@@ -553,6 +613,36 @@ namespace ps2recomp
{
std::stringstream ss;
static const std::unordered_set<std::string> systemCallNames = {
"FlushCache", "ResetEE", "SetMemoryMode",
"CreateThread", "DeleteThread", "StartThread", "ExitThread", "ExitDeleteThread",
"TerminateThread", "SuspendThread", "ResumeThread", "GetThreadId", "ReferThreadStatus",
"SleepThread", "WakeupThread", "iWakeupThread", "ChangeThreadPriority",
"RotateThreadReadyQueue", "ReleaseWaitThread", "iReleaseWaitThread",
"CreateSema", "DeleteSema", "SignalSema", "iSignalSema", "WaitSema", "PollSema",
"iPollSema", "ReferSemaStatus", "iReferSemaStatus", "CreateEventFlag",
"DeleteEventFlag", "SetEventFlag", "iSetEventFlag", "ClearEventFlag",
"iClearEventFlag", "WaitEventFlag", "PollEventFlag", "iPollEventFlag",
"ReferEventFlagStatus", "iReferEventFlagStatus", "SetAlarm", "iSetAlarm",
"CancelAlarm", "iCancelAlarm", "EnableIntc", "DisableIntc", "EnableDmac",
"DisableDmac", "SifStopModule", "SifLoadModule", "SifInitRpc", "SifBindRpc",
"SifCallRpc", "SifRegisterRpc", "SifCheckStatRpc", "SifSetRpcQueue",
"SifRemoveRpcQueue", "SifRemoveRpc", "fioOpen", "fioClose", "fioRead", "fioWrite",
"fioLseek", "fioMkdir", "fioChdir", "fioRmdir", "fioGetstat", "fioRemove",
"GsSetCrt", "GsGetIMR", "GsPutIMR", "GsSetVideoMode", "GetOsdConfigParam",
"SetOsdConfigParam", "GetRomName", "sceSifLoadModule",
"SifSetDChain"};
if (systemCallNames.find(function.name) != systemCallNames.end())
{
std::string sanitizedName = sanitizeFunctionName(function.name);
ss << "// System call wrapper for " << function.name << "\n";
ss << "void " << sanitizedName << "(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime) {\n";
ss << " ps2_syscalls::" << function.name << "(rdram, ctx, runtime);\n";
ss << "}\n";
return ss.str();
}
if (useHeaders)
{
ss << "#include \"ps2_runtime_macros.h\"\n";
@@ -565,7 +655,7 @@ namespace ps2recomp
ss << "// Function: " << function.name << "\n";
ss << "// Address: 0x" << std::hex << function.start << " - 0x" << function.end << std::dec << "\n";
std::string sanitizedName = sanitizeFunctionName(function.name);
std::string sanitizedName = getGeneratedFunctionName(function);
ss << "void " << sanitizedName << "(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime) {\n\n";
for (size_t i = 0; i < instructions.size(); ++i)
@@ -1290,7 +1380,7 @@ namespace ps2recomp
case MMI_MADDU1:
return fmt::format("{{ uint64_t acc = ((uint64_t)ctx->hi1 << 32) | ctx->lo1; uint64_t prod = (uint64_t)GPR_U32(ctx, {}) * (uint64_t)GPR_U32(ctx, {}); uint64_t result = acc + prod; ctx->lo1 = (uint32_t)result; ctx->hi1 = (uint32_t)(result >> 32); }}", rs, rt);
case MMI_PLZCW:
return fmt::format("{{ uint32_t val = GPR_U32(ctx, {}); SET_GPR_U32(ctx, {}, val == 0 ? 32 : __builtin_clz(val)); }}", rs, rd);
return fmt::format("{{ uint32_t val = GPR_U32(ctx, {}); SET_GPR_U32(ctx, {}, ps2_clz32(val)); }}", rs, rd);
case MMI_PSLLH:
return fmt::format("SET_GPR_VEC(ctx, {}, _mm_slli_epi16(GPR_VEC(ctx, {}), {}));", rd, rt, sa);
case MMI_PSRLH:
@@ -1652,7 +1742,7 @@ namespace ps2recomp
case VU0_CR_R:
return fmt::format("ctx->vu0_r = _mm_castsi128_ps(GPR_VEC(ctx, {}));", rt);
case VU0_CR_I:
return fmt::format("ctx->vu0_i = *(float*)&GPR_U32(ctx, {});", rt);
return fmt::format("{{ uint32_t tmp = GPR_U32(ctx, {}); ctx->vu0_i = *reinterpret_cast<float*>(&tmp); }}", rt);
case VU0_CR_TPC:
return fmt::format("ctx->vu0_tpc = GPR_U32(ctx, {});", rt);
case VU0_CR_CMSAR0:
@@ -1686,7 +1776,7 @@ namespace ps2recomp
case VU0_CR_CLIP2:
return fmt::format("ctx->vu0_clip_flags2 = GPR_U32(ctx, {});", rt);
case VU0_CR_P:
return fmt::format("ctx->vu0_p = *(float*)&GPR_U32(ctx, {});", rt);
return fmt::format("{{ uint32_t tmp = GPR_U32(ctx, {}); ctx->vu0_p = *reinterpret_cast<float*>(&tmp); }}", rt);
case VU0_CR_XITOP:
return fmt::format("ctx->vu0_xitop = GPR_U32(ctx, {}) & 0x3FF;", rt);
case VU0_CR_ITOP:
@@ -2558,16 +2648,25 @@ namespace ps2recomp
continue;
}
std::string generatedName = getGeneratedFunctionName(function);
if (function.isStub)
{
stubFunctions.push_back({function.start, sanitizeFunctionName(function.name)});
stubFunctions.push_back({function.start, generatedName});
}
else
{
normalFunctions.push_back({function.start, sanitizeFunctionName(function.name)});
normalFunctions.push_back({function.start, generatedName});
}
}
if (m_bootstrapInfo.valid)
{
ss << " // Register ELF entry bootstrap\n";
ss << " runtime.registerFunction(0x" << std::hex << m_bootstrapInfo.entry << std::dec
<< ", entry_" << std::hex << m_bootstrapInfo.entry << std::dec << ");\n\n";
}
ss << " // Register recompiled functions\n";
for (const auto &function : normalFunctions)
{
@@ -2648,4 +2747,35 @@ namespace ps2recomp
return nullptr;
}
std::string CodeGenerator::generateBootstrapFunction() const
{
if (!m_bootstrapInfo.valid)
return {};
std::stringstream ss;
ss << "// Auto-generated bootstrap for ELF entry point\n";
ss << "void entry_" << std::hex << m_bootstrapInfo.entry << std::dec
<< "(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime) {\n";
if (m_bootstrapInfo.bssEnd > m_bootstrapInfo.bssStart)
{
ss << " const uint32_t bss_start = 0x" << std::hex << m_bootstrapInfo.bssStart << ";\n";
ss << " const uint32_t bss_end = 0x" << std::hex << m_bootstrapInfo.bssEnd << ";\n";
ss << " __m128i zero = _mm_setzero_si128();\n";
ss << " for (uint32_t addr = bss_start; addr < bss_end; addr += 16) {\n";
ss << " WRITE128(addr, zero);\n";
ss << " }\n\n";
}
if (m_bootstrapInfo.gp != 0)
{
ss << " SET_GPR_U32(ctx, 28, 0x" << std::hex << m_bootstrapInfo.gp << ");\n";
}
if (m_bootstrapInfo.bssEnd > m_bootstrapInfo.bssStart)
{
ss << " SET_GPR_U32(ctx, 29, bss_end);\n";
}
ss << " InitThread(rdram, ctx, runtime);\n";
ss << "}\n";
return ss.str();
}
};
+7 -2
View File
@@ -131,6 +131,11 @@ namespace ps2recomp
return 0;
}
uint32_t ElfParser::getEntryPoint() const
{
return static_cast<uint32_t>(m_elf->get_entry());
}
ElfParser::~ElfParser() = default;
bool ElfParser::parse()
@@ -204,7 +209,7 @@ namespace ps2recomp
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;
@@ -214,7 +219,7 @@ namespace ps2recomp
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
+129 -5
View File
@@ -9,6 +9,7 @@
#include <cctype>
#include <unordered_set>
#include <optional>
#include <limits>
namespace fs = std::filesystem;
@@ -43,6 +44,65 @@ namespace ps2recomp
m_sections = m_elfParser->getSections();
m_relocations = m_elfParser->getRelocations();
if (m_functions.empty())
{
std::cerr << "No functions found in ELF file." << std::endl;
return false;
}
{
m_bootstrapInfo = {};
uint32_t entry = m_elfParser->getEntryPoint();
std::cout << "ELF entry point: 0x" << std::hex << entry << std::dec << std::endl;
uint32_t bssStart = std::numeric_limits<uint32_t>::max();
uint32_t bssEnd = 0;
for (const auto &sec : m_sections)
{
if (sec.isBSS && sec.size > 0)
{
bssStart = std::min(bssStart, sec.address);
bssEnd = std::max(bssEnd, sec.address + sec.size);
}
}
uint32_t gp = 0;
for (const auto &sym : m_symbols)
{
if (sym.name == "_gp")
{
gp = sym.address;
break;
}
}
if (bssStart != std::numeric_limits<uint32_t>::max())
{
std::cout << "BSS range: 0x" << std::hex << bssStart << " - 0x" << bssEnd
<< " (size 0x" << (bssEnd - bssStart) << "), gp=0x" << gp << std::dec << std::endl;
}
else
{
std::cout << "No BSS found, gp=0x" << std::hex << gp << std::dec << std::endl;
}
if (entry != 0)
{
m_bootstrapInfo.valid = true;
m_bootstrapInfo.entry = entry;
if (bssStart != std::numeric_limits<uint32_t>::max() && bssEnd > bssStart)
{
m_bootstrapInfo.bssStart = bssStart;
m_bootstrapInfo.bssEnd = bssEnd;
}
else
{
m_bootstrapInfo.bssStart = 0;
m_bootstrapInfo.bssEnd = 0;
}
m_bootstrapInfo.gp = gp;
}
}
std::cout << "Extracted " << m_functions.size() << " functions, "
<< m_symbols.size() << " symbols, "
<< m_sections.size() << " sections, "
@@ -50,6 +110,7 @@ namespace ps2recomp
m_decoder = std::make_unique<R5900Decoder>();
m_codeGenerator = std::make_unique<CodeGenerator>(m_symbols);
m_codeGenerator->setBootstrapInfo(m_bootstrapInfo);
fs::create_directories(m_config.outputPath);
@@ -116,20 +177,43 @@ namespace ps2recomp
{
try
{
std::unordered_map<uint32_t, std::string> renamed;
m_functionRenames.clear();
std::unordered_map<std::string, int> nameCounts;
for (const auto &function : m_functions)
{
if (!function.isRecompiled)
continue;
std::string sanitized = sanitizeFunctionName(function.name);
if (sanitized != function.name)
nameCounts[sanitized]++;
}
for (const auto &function : m_functions)
{
if (!function.isRecompiled)
continue;
std::string sanitized = sanitizeFunctionName(function.name);
bool isDuplicate = nameCounts[sanitized] > 1;
if (isDuplicate || sanitized != function.name)
{
renamed[function.start] = sanitized;
std::stringstream ss;
if (isDuplicate)
{
ss << sanitized << "_0x" << std::hex << function.start;
}
else
{
ss << sanitized;
}
m_functionRenames[function.start] = ss.str();
}
}
if (m_codeGenerator)
{
m_codeGenerator->setRenamedFunctions(renamed);
m_codeGenerator->setRenamedFunctions(m_functionRenames);
}
generateFunctionHeader();
@@ -142,6 +226,12 @@ namespace ps2recomp
combinedOutput << "#include \"ps2_runtime_macros.h\"\n";
combinedOutput << "#include \"ps2_runtime.h\"\n";
combinedOutput << "#include \"ps2_recompiled_stubs.h\"\n";
combinedOutput << "#include \"ps2_syscalls.h\"\n";
if (m_bootstrapInfo.valid)
{
combinedOutput << "\n"
<< m_codeGenerator->generateBootstrapFunction() << "\n\n";
}
for (const auto &function : m_functions)
{
@@ -179,6 +269,17 @@ namespace ps2recomp
}
else
{
if (m_bootstrapInfo.valid)
{
std::stringstream boot;
boot << "#include \"ps2_recompiled_functions.h\"\n\n";
boot << "#include \"ps2_runtime_macros.h\"\n";
boot << "#include \"ps2_runtime.h\"\n\n";
boot << m_codeGenerator->generateBootstrapFunction() << "\n";
fs::path bootPath = fs::path(m_config.outputPath) / "ps2_entry_bootstrap.cpp";
writeToFile(bootPath.string(), boot.str());
}
for (const auto &function : m_functions)
{
if (!function.isRecompiled || function.isStub)
@@ -282,10 +383,23 @@ namespace ps2recomp
{
if (function.isRecompiled)
{
ss << "void " << sanitizeFunctionName(function.name) << "(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime);\n";
std::string finalName = sanitizeFunctionName(function.name);
auto renameIt = m_functionRenames.find(function.start);
if (renameIt != m_functionRenames.end())
{
finalName = renameIt->second;
}
ss << "void " << finalName << "(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime);\n";
}
}
if (m_bootstrapInfo.valid)
{
ss << "void entry_" << std::hex << m_bootstrapInfo.entry << std::dec
<< "(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime);\n";
}
ss << "\n#endif // PS2_RECOMPILED_FUNCTIONS_H\n";
fs::path headerPath = fs::path(m_config.outputPath) / "ps2_recompiled_functions.h";
@@ -524,6 +638,16 @@ namespace ps2recomp
std::string PS2Recompiler::sanitizeFunctionName(const std::string &name) const
{
if (name == "main")
{
return "ps2_main";
}
if (ps2recomp::kKeywords.find(name) != ps2recomp::kKeywords.end())
{
return "ps2_" + name;
}
if (name.size() >= 2 && name[0] == '_' && (name[1] == '_' || std::isupper(static_cast<unsigned char>(name[1]))))
{
return "ps2_" + name;
+4
View File
@@ -33,6 +33,10 @@ add_executable(ps2EntryRunner
${RUNNER_SRC_FILES}
)
if (MSVC)
target_compile_options(ps2EntryRunner PRIVATE /FS /Z7)
endif()
target_include_directories(ps2_runtime PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
)
+14 -10
View File
@@ -96,6 +96,8 @@ struct alignas(16) R5900Context
uint32_t vu0_fbrst4; // FBRST4
uint32_t vu0_itop;
uint32_t vu0_info;
uint32_t vu0_xitop; // VU0 XITOP - input ITOP for VIF/VU sync
uint32_t vu0_pc;
float vu0_cf[4]; // VU0 FMAC control floating-point registers
@@ -168,6 +170,18 @@ struct alignas(16) R5900Context
vu0_fbrst2 = 0;
vu0_fbrst3 = 0;
vu0_fbrst4 = 0;
vu0_xitop = 0;
vu0_pc = 0;
vu0_tpc = 0;
vu0_vpu_stat2 = 0;
vu0_tpc2 = 0;
vu0_cmsar1 = 0;
vu0_vpu_stat3 = 0;
vu0_cmsar2 = 0;
vu0_vpu_stat4 = 0;
vu0_itop = 0;
vu0_info = 0;
// Reset COP0 registers
cop0_index = 0;
@@ -193,16 +207,6 @@ struct alignas(16) R5900Context
cop0_taghi = 0;
cop0_errorepc = 0;
vu0_tpc = 0;
vu0_vpu_stat2 = 0;
vu0_tpc2 = 0;
vu0_cmsar1 = 0;
vu0_vpu_stat3 = 0;
vu0_cmsar2 = 0;
vu0_vpu_stat4 = 0;
vu0_itop = 0;
vu0_info = 0;
// Reset COP1 state
fcr31 = 0;
}
+4
View File
@@ -3,6 +3,10 @@
#include "ps2_runtime.h"
#include <mutex>
#include <atomic>
// Number of active host threads spawned for PS2 thread emulation
extern std::atomic<int> g_activeThreads;
static std::mutex g_sys_fd_mutex;
+215
View File
@@ -0,0 +1,215 @@
#ifndef PS2_RUNTIME_MACROS_H
#define PS2_RUNTIME_MACROS_H
#include <cstdint>
#include <immintrin.h> // For SSE/AVX intrinsics
#include <intrin.h>
inline uint32_t ps2_clz32(uint32_t val) {
#if defined(_MSC_VER)
unsigned long idx;
if (_BitScanReverse(&idx, val)) {
return 31u - idx;
}
return 32u;
#else
return val == 0 ? 32u : (uint32_t)__builtin_clz(val);
#endif
}
// Basic MIPS arithmetic operations
#define ADD32(a, b) ((uint32_t)((a) + (b)))
#define ADD32_OV(rs, rt, result32, overflow) do { int32_t _a = (int32_t)(rs); int32_t _b = (int32_t)(rt); int32_t _r = _a + _b; overflow = (((_a ^ _b) >= 0) && ((_a ^ _r) < 0)); result32 = (uint32_t)_r; } while (0);
#define SUB32(a, b) ((uint32_t)((a) - (b)))
#define SUB32_OV(rs, rt, result32, overflow) do { int32_t _a = (int32_t)(rs); int32_t _b = (int32_t)(rt); int32_t _r = _a - _b; overflow = (((_a ^ _b) < 0) && ((_a ^ _r) < 0)); result32 = (uint32_t)_r; } while (0);
#define MUL32(a, b) ((uint32_t)((a) * (b)))
#define DIV32(a, b) ((uint32_t)((a) / (b)))
#define AND32(a, b) ((uint32_t)((a) & (b)))
#define OR32(a, b) ((uint32_t)((a) | (b)))
#define XOR32(a, b) ((uint32_t)((a) ^ (b)))
#define NOR32(a, b) ((uint32_t)(~((a) | (b))))
#define SLL32(a, b) ((uint32_t)((a) << (b)))
#define SRL32(a, b) ((uint32_t)((a) >> (b)))
#define SRA32(a, b) ((uint32_t)((int32_t)(a) >> (b)))
#define SLT32(a, b) ((uint32_t)((int32_t)(a) < (int32_t)(b) ? 1 : 0))
#define SLTU32(a, b) ((uint32_t)((a) < (b) ? 1 : 0))
// PS2-specific 128-bit MMI operations
#define PS2_PEXTLW(a, b) _mm_unpacklo_epi32((__m128i)(b), (__m128i)(a))
#define PS2_PEXTUW(a, b) _mm_unpackhi_epi32((__m128i)(b), (__m128i)(a))
#define PS2_PEXTLH(a, b) _mm_unpacklo_epi16((__m128i)(b), (__m128i)(a))
#define PS2_PEXTUH(a, b) _mm_unpackhi_epi16((__m128i)(b), (__m128i)(a))
#define PS2_PEXTLB(a, b) _mm_unpacklo_epi8((__m128i)(b), (__m128i)(a))
#define PS2_PEXTUB(a, b) _mm_unpackhi_epi8((__m128i)(b), (__m128i)(a))
#define PS2_PADDW(a, b) _mm_add_epi32((__m128i)(a), (__m128i)(b))
#define PS2_PSUBW(a, b) _mm_sub_epi32((__m128i)(a), (__m128i)(b))
#define PS2_PMAXW(a, b) _mm_max_epi32((__m128i)(a), (__m128i)(b))
#define PS2_PMINW(a, b) _mm_min_epi32((__m128i)(a), (__m128i)(b))
#define PS2_PADDH(a, b) _mm_add_epi16((__m128i)(a), (__m128i)(b))
#define PS2_PSUBH(a, b) _mm_sub_epi16((__m128i)(a), (__m128i)(b))
#define PS2_PMAXH(a, b) _mm_max_epi16((__m128i)(a), (__m128i)(b))
#define PS2_PMINH(a, b) _mm_min_epi16((__m128i)(a), (__m128i)(b))
#define PS2_PADDB(a, b) _mm_add_epi8((__m128i)(a), (__m128i)(b))
#define PS2_PSUBB(a, b) _mm_sub_epi8((__m128i)(a), (__m128i)(b))
#define PS2_PAND(a, b) _mm_and_si128((__m128i)(a), (__m128i)(b))
#define PS2_POR(a, b) _mm_or_si128((__m128i)(a), (__m128i)(b))
#define PS2_PXOR(a, b) _mm_xor_si128((__m128i)(a), (__m128i)(b))
#define PS2_PNOR(a, b) _mm_xor_si128(_mm_or_si128((__m128i)(a), (__m128i)(b)), _mm_set1_epi32(0xFFFFFFFF))
// PS2 VU (Vector Unit) operations
#define PS2_VADD(a, b) _mm_add_ps((__m128)(a), (__m128)(b))
#define PS2_VSUB(a, b) _mm_sub_ps((__m128)(a), (__m128)(b))
#define PS2_VMUL(a, b) _mm_mul_ps((__m128)(a), (__m128)(b))
#define PS2_VDIV(a, b) _mm_div_ps((__m128)(a), (__m128)(b))
#define PS2_VMULQ(a, q) _mm_mul_ps((__m128)(a), _mm_set1_ps(q))
// Memory access helpers
#define READ8(addr) (*(uint8_t*)((rdram) + ((addr) & PS2_RAM_MASK)))
#define READ16(addr) (*(uint16_t*)((rdram) + ((addr) & PS2_RAM_MASK)))
#define READ32(addr) (*(uint32_t*)((rdram) + ((addr) & PS2_RAM_MASK)))
#define READ64(addr) (*(uint64_t*)((rdram) + ((addr) & PS2_RAM_MASK)))
#define READ128(addr) (*((__m128i*)((rdram) + ((addr) & PS2_RAM_MASK))))
#define WRITE8(addr, val) (*(uint8_t*)((rdram) + ((addr) & PS2_RAM_MASK)) = (val))
#define WRITE16(addr, val) (*(uint16_t*)((rdram) + ((addr) & PS2_RAM_MASK)) = (val))
#define WRITE32(addr, val) (*(uint32_t*)((rdram) + ((addr) & PS2_RAM_MASK)) = (val))
#define WRITE64(addr, val) (*(uint64_t*)((rdram) + ((addr) & PS2_RAM_MASK)) = (val))
#define WRITE128(addr, val) (*((__m128i*)((rdram) + ((addr) & PS2_RAM_MASK))) = (val))
#define PS2_PCGTW(a, b) _mm_cmpgt_epi32((__m128i)(a), (__m128i)(b))
#define PS2_PCGTH(a, b) _mm_cmpgt_epi16((__m128i)(a), (__m128i)(b))
#define PS2_PCGTB(a, b) _mm_cmpgt_epi8((__m128i)(a), (__m128i)(b))
#define PS2_PCEQW(a, b) _mm_cmpeq_epi32((__m128i)(a), (__m128i)(b))
#define PS2_PCEQH(a, b) _mm_cmpeq_epi16((__m128i)(a), (__m128i)(b))
#define PS2_PCEQB(a, b) _mm_cmpeq_epi8((__m128i)(a), (__m128i)(b))
#define PS2_PABSW(a) _mm_abs_epi32((__m128i)(a))
#define PS2_PABSH(a) _mm_abs_epi16((__m128i)(a))
#define PS2_PABSB(a) _mm_abs_epi8((__m128i)(a))
#define PS2_PPACW(a, b) _mm_packs_epi32((__m128i)(b), (__m128i)(a))
#define PS2_PPACH(a, b) _mm_packs_epi16((__m128i)(b), (__m128i)(a))
#define PS2_PPACB(a, b) _mm_packus_epi16(_mm_packs_epi32((__m128i)(b), (__m128i)(a)), _mm_setzero_si128())
#define PS2_PINTH(a, b) _mm_unpacklo_epi16(_mm_shuffle_epi32((__m128i)(b), _MM_SHUFFLE(3,2,1,0)), _mm_shuffle_epi32((__m128i)(a), _MM_SHUFFLE(3,2,1,0)))
#define PS2_PINTEH(a, b) _mm_unpackhi_epi16(_mm_shuffle_epi32((__m128i)(b), _MM_SHUFFLE(3,2,1,0)), _mm_shuffle_epi32((__m128i)(a), _MM_SHUFFLE(3,2,1,0)))
#define PS2_PMADDW(a, b) _mm_add_epi32(_mm_mullo_epi32(_mm_shuffle_epi32((__m128i)(a), _MM_SHUFFLE(1,0,3,2)), _mm_shuffle_epi32((__m128i)(b), _MM_SHUFFLE(1,0,3,2))), _mm_mullo_epi32(_mm_shuffle_epi32((__m128i)(a), _MM_SHUFFLE(3,2,1,0)), _mm_shuffle_epi32((__m128i)(b), _MM_SHUFFLE(3,2,1,0))))
#define PS2_PSLLVW(a, b) _mm_custom_sllv_epi32((__m128i)(a), (__m128i)(b))
#define PS2_PSRLVW(a, b) _mm_custom_srlv_epi32((__m128i)(a), (__m128i)(b))
#define PS2_PSRAVW(a, b) _mm_custom_srav_epi32((__m128i)(a), (__m128i)(b))
inline __m128i _mm_custom_sllv_epi32(__m128i a, __m128i count) {
int32_t a_arr[4], count_arr[4], result[4];
_mm_storeu_si128((__m128i*)a_arr, a);
_mm_storeu_si128((__m128i*)count_arr, count);
for (int i = 0; i < 4; i++) {
result[i] = a_arr[i] << (count_arr[i] & 0x1F);
}
return _mm_loadu_si128((__m128i*)result);
}
inline __m128i _mm_custom_srlv_epi32(__m128i a, __m128i count) {
int32_t a_arr[4], count_arr[4], result[4];
_mm_storeu_si128((__m128i*)a_arr, a);
_mm_storeu_si128((__m128i*)count_arr, count);
for (int i = 0; i < 4; i++) {
result[i] = (uint32_t)a_arr[i] >> (count_arr[i] & 0x1F);
}
return _mm_loadu_si128((__m128i*)result);
}
inline __m128i _mm_custom_srav_epi32(__m128i a, __m128i count) {
int32_t a_arr[4], count_arr[4], result[4];
_mm_storeu_si128((__m128i*)a_arr, a);
_mm_storeu_si128((__m128i*)count_arr, count);
for (int i = 0; i < 4; i++) {
result[i] = a_arr[i] >> (count_arr[i] & 0x1F);
}
return _mm_loadu_si128((__m128i*)result);
}
#define PS2_PMFHL_LW(hi, lo) _mm_unpacklo_epi64(lo, hi)
#define PS2_PMFHL_UW(hi, lo) _mm_unpackhi_epi64(lo, hi)
#define PS2_PMFHL_SLW(hi, lo) _mm_packs_epi32(lo, hi)
#define PS2_PMFHL_LH(hi, lo) _mm_shuffle_epi32(_mm_packs_epi32(lo, hi), _MM_SHUFFLE(3,1,2,0))
#define PS2_PMFHL_SH(hi, lo) _mm_shufflehi_epi16(_mm_shufflelo_epi16(_mm_packs_epi32(lo, hi), _MM_SHUFFLE(3,1,2,0)), _MM_SHUFFLE(3,1,2,0))
// FPU (COP1) operations
#define FPU_ADD_S(a, b) ((float)(a) + (float)(b))
#define FPU_SUB_S(a, b) ((float)(a) - (float)(b))
#define FPU_MUL_S(a, b) ((float)(a) * (float)(b))
#define FPU_DIV_S(a, b) ((float)(a) / (float)(b))
#define FPU_SQRT_S(a) sqrtf((float)(a))
#define FPU_ABS_S(a) fabsf((float)(a))
#define FPU_MOV_S(a) ((float)(a))
#define FPU_NEG_S(a) (-(float)(a))
#define FPU_ROUND_L_S(a) ((int64_t)roundf((float)(a)))
#define FPU_TRUNC_L_S(a) ((int64_t)(float)(a))
#define FPU_CEIL_L_S(a) ((int64_t)ceilf((float)(a)))
#define FPU_FLOOR_L_S(a) ((int64_t)floorf((float)(a)))
#define FPU_ROUND_W_S(a) ((int32_t)roundf((float)(a)))
#define FPU_TRUNC_W_S(a) ((int32_t)(float)(a))
#define FPU_CEIL_W_S(a) ((int32_t)ceilf((float)(a)))
#define FPU_FLOOR_W_S(a) ((int32_t)floorf((float)(a)))
#define FPU_CVT_S_W(a) ((float)(int32_t)(a))
#define FPU_CVT_S_L(a) ((float)(int64_t)(a))
#define FPU_CVT_W_S(a) ((int32_t)(float)(a))
#define FPU_CVT_L_S(a) ((int64_t)(float)(a))
#define FPU_C_F_S(a, b) (0)
#define FPU_C_UN_S(a, b) (isnan((float)(a)) || isnan((float)(b)))
#define FPU_C_EQ_S(a, b) ((float)(a) == (float)(b))
#define FPU_C_UEQ_S(a, b) ((float)(a) == (float)(b) || isnan((float)(a)) || isnan((float)(b)))
#define FPU_C_OLT_S(a, b) ((float)(a) < (float)(b))
#define FPU_C_ULT_S(a, b) ((float)(a) < (float)(b) || isnan((float)(a)) || isnan((float)(b)))
#define FPU_C_OLE_S(a, b) ((float)(a) <= (float)(b))
#define FPU_C_ULE_S(a, b) ((float)(a) <= (float)(b) || isnan((float)(a)) || isnan((float)(b)))
#define FPU_C_SF_S(a, b) (0)
#define FPU_C_NGLE_S(a, b) (isnan((float)(a)) || isnan((float)(b)))
#define FPU_C_SEQ_S(a, b) ((float)(a) == (float)(b))
#define FPU_C_NGL_S(a, b) ((float)(a) == (float)(b) || isnan((float)(a)) || isnan((float)(b)))
#define FPU_C_LT_S(a, b) ((float)(a) < (float)(b))
#define FPU_C_NGE_S(a, b) ((float)(a) < (float)(b) || isnan((float)(a)) || isnan((float)(b)))
#define FPU_C_LE_S(a, b) ((float)(a) <= (float)(b))
#define FPU_C_NGT_S(a, b) ((float)(a) <= (float)(b) || isnan((float)(a)) || isnan((float)(b)))
#define PS2_QFSRV(rs, rt, sa) _mm_or_si128(_mm_srl_epi32(rt, _mm_cvtsi32_si128(sa)), _mm_sll_epi32(rs, _mm_cvtsi32_si128(32 - sa)))
#define PS2_PCPYLD(rs, rt) _mm_unpacklo_epi64(rt, rs)
#define PS2_PEXEH(rs) _mm_shufflelo_epi16(_mm_shufflehi_epi16(rs, _MM_SHUFFLE(2, 3, 0, 1)), _MM_SHUFFLE(2, 3, 0, 1))
#define PS2_PEXEW(rs) _mm_shuffle_epi32(rs, _MM_SHUFFLE(2, 3, 0, 1))
#define PS2_PROT3W(rs) _mm_shuffle_epi32(rs, _MM_SHUFFLE(0, 3, 2, 1))
// Additional VU0 operations
#define PS2_VSQRT(x) sqrtf(x)
#define PS2_VRSQRT(x) (1.0f / sqrtf(x))
#define PS2_VCALLMS(addr) // VU0 microprogram calls not supported directly
#define PS2_VCALLMSR(reg) // VU0 microprogram calls not supported directly
#define GPR_U32(ctx_ptr, reg_idx) ((reg_idx == 0) ? 0U : ctx_ptr->r[reg_idx].m128i_u32[0])
#define GPR_S32(ctx_ptr, reg_idx) ((reg_idx == 0) ? 0 : ctx_ptr->r[reg_idx].m128i_i32[0])
#define GPR_U64(ctx_ptr, reg_idx) ((reg_idx == 0) ? 0ULL : ctx_ptr->r[reg_idx].m128i_u64[0])
#define GPR_S64(ctx_ptr, reg_idx) ((reg_idx == 0) ? 0LL : ctx_ptr->r[reg_idx].m128i_i64[0])
#define GPR_VEC(ctx_ptr, reg_idx) ((reg_idx == 0) ? _mm_setzero_si128() : ctx_ptr->r[reg_idx])
#define SET_GPR_U32(ctx_ptr, reg_idx, val) \
do \
{ \
if (reg_idx != 0) \
ctx_ptr->r[reg_idx] = _mm_set_epi32(0, 0, 0, (val)); \
} while (0)
#define SET_GPR_S32(ctx_ptr, reg_idx, val) \
do \
{ \
if (reg_idx != 0) \
ctx_ptr->r[reg_idx] = _mm_set_epi32(0, 0, 0, (val)); \
} while (0)
#define SET_GPR_U64(ctx_ptr, reg_idx, val) \
do \
{ \
if (reg_idx != 0) \
ctx_ptr->r[reg_idx] = _mm_set_epi64x(0, (val)); \
} while (0)
#define SET_GPR_S64(ctx_ptr, reg_idx, val) \
do \
{ \
if (reg_idx != 0) \
ctx_ptr->r[reg_idx] = _mm_set_epi64x(0, (val)); \
} while (0)
#define SET_GPR_VEC(ctx_ptr, reg_idx, val) \
do \
{ \
if (reg_idx != 0) \
ctx_ptr->r[reg_idx] = (val); \
} while (0)
#endif // PS2_RUNTIME_MACROS_H
+29 -7
View File
@@ -1,4 +1,5 @@
#include "ps2_runtime.h"
#include "ps2_syscalls.h"
#include <iostream>
#include <fstream>
#include <algorithm>
@@ -49,7 +50,7 @@ struct ProgramHeader
};
#define PT_LOAD 1 // Loadable segment
static constexpr int FB_WIDTH = 640;
static constexpr int FB_HEIGHT = 448;
static constexpr uint32_t DEFAULT_FB_ADDR = 0x00100000; // location in RDRAM the guest will draw to
@@ -175,6 +176,12 @@ bool PS2Runtime::loadELF(const std::string &elfPath)
m_loadedModules.push_back(module);
// Debug: peek at some early globals to verify init state
uint32_t dbg_addr = 0x00300000 + 11240;
uint8_t *dbg_base = m_memory.getRDRAM();
uint32_t dbg_val = *reinterpret_cast<uint32_t *>(dbg_base + (dbg_addr & PS2_RAM_MASK));
std::cout << "Debug: [0x" << std::hex << dbg_addr << "] = 0x" << dbg_val << std::dec << std::endl;
std::cout << "ELF file loaded successfully. Entry point: 0x" << std::hex << m_cpuContext.pc << std::dec << std::endl;
return true;
}
@@ -298,31 +305,44 @@ void PS2Runtime::run()
Texture2D frameTex = LoadTextureFromImage(blank);
UnloadImage(blank);
std::atomic<bool> running{true};
g_activeThreads.store(1, std::memory_order_relaxed);
std::thread gameThread([&, entryPoint]() {
std::thread gameThread([&, entryPoint]()
{
try
{
entryPoint(m_memory.getRDRAM(), &m_cpuContext, this);
std::cout << "Game thread returned. PC=0x" << std::hex << m_cpuContext.pc
<< " RA=0x" << m_cpuContext.r[31].m128i_u32[0] << std::dec << std::endl;
}
catch (const std::exception &e)
{
std::cerr << "Error during program execution: " << e.what() << std::endl;
}
running = false;
});
g_activeThreads.fetch_sub(1, std::memory_order_relaxed); });
while (running && !WindowShouldClose())
uint64_t tick = 0;
while (g_activeThreads.load(std::memory_order_relaxed) > 0)
{
if ((tick++ % 120) == 0)
{
std::cout << "[run] activeThreads=" << g_activeThreads.load(std::memory_order_relaxed) << std::endl;
}
UploadFrame(frameTex, this);
BeginDrawing();
ClearBackground(BLACK);
DrawTexture(frameTex, 0, 0, WHITE);
EndDrawing();
if (WindowShouldClose())
{
std::cout << "[run] window close requested, breaking out of loop" << std::endl;
break;
}
}
if (!running)
if (g_activeThreads.load(std::memory_order_relaxed) == 0)
{
// Game thread finished on its own
if (gameThread.joinable())
@@ -341,4 +361,6 @@ void PS2Runtime::run()
UnloadTexture(frameTex);
CloseWindow();
std::cout << "[run] exiting loop, activeThreads=" << g_activeThreads.load(std::memory_order_relaxed) << std::endl;
}
+221 -26
View File
@@ -1,6 +1,7 @@
#include "ps2_syscalls.h"
#include "ps2_runtime.h"
#include "ps2_runtime_macros.h"
#include "ps2_stubs.h"
#include <iostream>
#include <cstring>
@@ -9,11 +10,43 @@
#include <cmath>
#include <vector>
#include <unordered_map>
#include <thread>
#include <condition_variable>
#include <atomic>
#include <filesystem>
std::unordered_map<int, FILE *> g_fileDescriptors;
int g_nextFd = 3; // Start after stdin, stdout, stderr
struct ThreadInfo
{
uint32_t entry = 0;
uint32_t stack = 0;
uint32_t stackSize = 0;
uint32_t gp = 0;
uint32_t priority = 0;
uint32_t attr = 0;
uint32_t option = 0;
uint32_t arg = 0;
bool started = false;
};
struct SemaInfo
{
int count = 0;
int maxCount = 0;
std::mutex m;
std::condition_variable cv;
};
static std::unordered_map<int, ThreadInfo> g_threads;
static int g_nextThreadId = 2; // Reserve 1 for the main thread
static thread_local int g_currentThreadId = 1;
static std::unordered_map<int, std::shared_ptr<SemaInfo>> g_semas;
static int g_nextSemaId = 1;
std::atomic<int> g_activeThreads{0};
int allocatePs2Fd(FILE *file)
{
if (!file)
@@ -118,133 +151,295 @@ namespace ps2_syscalls
void CreateThread(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)
{
// TODO
uint32_t paramAddr = getRegU32(ctx, 4); // $a0 points to ThreadParam
const uint32_t *param = reinterpret_cast<const uint32_t *>(getConstMemPtr(rdram, paramAddr));
if (!param)
{
std::cerr << "CreateThread error: invalid ThreadParam address 0x" << std::hex << paramAddr << std::dec << std::endl;
setReturnS32(ctx, -1);
return;
}
ThreadInfo info{};
info.attr = param[0];
info.entry = param[1];
info.stack = param[2];
info.stackSize = param[3];
info.gp = param[5]; // Often gp is at offset 20
info.priority = param[4]; // Commonly priority/init attr slot
info.option = param[6];
int id = g_nextThreadId++;
g_threads[id] = info;
std::cout << "[CreateThread] id=" << id
<< " entry=0x" << std::hex << info.entry
<< " stack=0x" << info.stack
<< " size=0x" << info.stackSize
<< " gp=0x" << info.gp
<< " prio=" << std::dec << info.priority << std::endl;
setReturnS32(ctx, id);
}
void DeleteThread(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)
{
// TODO
int tid = static_cast<int>(getRegU32(ctx, 4)); // $a0
g_threads.erase(tid);
setReturnS32(ctx, 0);
}
void StartThread(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)
{
// TODO
int tid = static_cast<int>(getRegU32(ctx, 4)); // $a0 = thread id
uint32_t arg = getRegU32(ctx, 5); // $a1 = user arg
auto it = g_threads.find(tid);
if (it == g_threads.end())
{
std::cerr << "StartThread error: unknown thread id " << tid << std::endl;
setReturnS32(ctx, -1);
return;
}
ThreadInfo &info = it->second;
if (info.started)
{
setReturnS32(ctx, 0);
return;
}
info.started = true;
info.arg = arg;
// Spawn a host thread to simulate PS2 thread execution.
g_activeThreads.fetch_add(1, std::memory_order_relaxed);
std::thread([=]() mutable {
R5900Context threadCtxCopy = *ctx; // Copy current CPU state to simulate a new thread context
R5900Context *threadCtx = &threadCtxCopy;
if (info.stack && info.stackSize)
{
SET_GPR_U32(threadCtx, 29, info.stack + info.stackSize); // SP at top of stack
}
if (info.gp)
{
SET_GPR_U32(threadCtx, 28, info.gp);
}
SET_GPR_U32(threadCtx, 4, info.arg);
threadCtx->pc = info.entry;
PS2Runtime::RecompiledFunction func = runtime->lookupFunction(info.entry);
g_currentThreadId = tid;
std::cout << "[StartThread] id=" << tid
<< " entry=0x" << std::hex << info.entry
<< " sp=0x" << GPR_U32(threadCtx, 29)
<< " gp=0x" << GPR_U32(threadCtx, 28)
<< " arg=0x" << info.arg << std::dec << std::endl;
try
{
func(rdram, threadCtx, runtime);
}
catch (const std::exception &e)
{
std::cerr << "[StartThread] id=" << tid << " exception: " << e.what() << std::endl;
}
std::cout << "[StartThread] id=" << tid << " returned (pc=0x"
<< std::hex << threadCtx->pc << std::dec << ")" << std::endl;
g_activeThreads.fetch_sub(1, std::memory_order_relaxed);
}).detach();
// for now report success to the caller.
setReturnS32(ctx, 0);
}
void ExitThread(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)
{
// TODO
std::cout << "PS2 ExitThread: Thread is exiting (PC=0x" << std::hex << ctx->pc << std::dec << ")" << std::endl;
setReturnS32(ctx, 0);
}
void ExitDeleteThread(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)
{
// TODO
int tid = static_cast<int>(getRegU32(ctx, 4));
g_threads.erase(tid);
setReturnS32(ctx, 0);
}
void TerminateThread(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)
{
// TODO
int tid = static_cast<int>(getRegU32(ctx, 4));
g_threads.erase(tid);
setReturnS32(ctx, 0);
}
void SuspendThread(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)
{
// TODO
setReturnS32(ctx, 0);
}
void ResumeThread(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)
{
// TODO
setReturnS32(ctx, 0);
}
void GetThreadId(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)
{
// TODO
setReturnS32(ctx, g_currentThreadId);
}
void ReferThreadStatus(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)
{
// TODO
setReturnS32(ctx, 0);
}
void SleepThread(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)
{
// TODO
setReturnS32(ctx, 0);
}
void WakeupThread(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)
{
// TODO
setReturnS32(ctx, 0);
}
void iWakeupThread(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)
{
// TODO
setReturnS32(ctx, 0);
}
void ChangeThreadPriority(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)
{
// TODO
int tid = static_cast<int>(getRegU32(ctx, 4));
int newPrio = static_cast<int>(getRegU32(ctx, 5));
auto it = g_threads.find(tid);
if (it != g_threads.end())
{
it->second.priority = newPrio;
}
setReturnS32(ctx, 0);
}
void RotateThreadReadyQueue(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)
{
// TODO
setReturnS32(ctx, 0);
}
void ReleaseWaitThread(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)
{
// TODO
setReturnS32(ctx, 0);
}
void iReleaseWaitThread(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)
{
// TODO
setReturnS32(ctx, 0);
}
void CreateSema(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)
{
// TODO
uint32_t paramAddr = getRegU32(ctx, 4); // $a0
const uint32_t *param = reinterpret_cast<const uint32_t *>(getConstMemPtr(rdram, paramAddr));
int init = 0;
int max = 1;
if (param)
{
// sceSemaParam layout commonly: attr(0), option(1), initCount(2), maxCount(3)
init = static_cast<int>(param[2]);
max = static_cast<int>(param[3]);
}
int id = g_nextSemaId++;
auto info = std::make_shared<SemaInfo>();
info->count = init;
info->maxCount = max;
g_semas.emplace(id, info);
std::cout << "[CreateSema] id=" << id << " init=" << init << " max=" << max << std::endl;
setReturnS32(ctx, id);
}
void DeleteSema(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)
{
// TODO
int sid = static_cast<int>(getRegU32(ctx, 4));
g_semas.erase(sid);
setReturnS32(ctx, 0);
}
void SignalSema(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)
{
// TODO
int sid = static_cast<int>(getRegU32(ctx, 4));
auto it = g_semas.find(sid);
if (it != g_semas.end())
{
auto sema = it->second;
std::lock_guard<std::mutex> lock(sema->m);
if (sema->count < sema->maxCount)
{
sema->count++;
}
sema->cv.notify_one();
}
setReturnS32(ctx, 0);
}
void iSignalSema(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)
{
// TODO
SignalSema(rdram, ctx, runtime);
}
void WaitSema(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)
{
// TODO
// For now, never block; treat as immediately acquired.
int sid = static_cast<int>(getRegU32(ctx, 4));
auto it = g_semas.find(sid);
if (it != g_semas.end())
{
auto sema = it->second;
std::lock_guard<std::mutex> lock(sema->m);
if (sema->count > 0)
{
sema->count--;
}
}
setReturnS32(ctx, 0);
}
void PollSema(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)
{
// TODO
int sid = static_cast<int>(getRegU32(ctx, 4));
auto it = g_semas.find(sid);
if (it != g_semas.end())
{
auto sema = it->second;
std::lock_guard<std::mutex> lock(sema->m);
if (sema->count > 0)
{
sema->count--;
setReturnS32(ctx, 0);
return;
}
}
setReturnS32(ctx, 0);
}
void iPollSema(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)
{
// TODO
PollSema(rdram, ctx, runtime);
}
void ReferSemaStatus(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)
{
// TODO
setReturnS32(ctx, 0);
}
void iReferSemaStatus(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)
{
// TODO
ReferSemaStatus(rdram, ctx, runtime);
}
void CreateEventFlag(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime)