refactor: refactor runtime and recompile for better workflow and correct generating code and analyze output

This commit is contained in:
Ran-j
2026-01-04 22:21:50 -03:00
parent 2ca84c131a
commit 2e08d5e87b
9 changed files with 1535 additions and 546 deletions
+71 -192
View File
@@ -13,6 +13,9 @@ namespace fs = std::filesystem;
namespace ps2recomp
{
static bool hasPs2ApiPrefix(const std::string &name);
static bool isDoNotSkipOrStub(const std::string &name);
ElfAnalyzer::ElfAnalyzer(const std::string &elfPath)
: m_elfPath(elfPath)
{
@@ -189,13 +192,14 @@ namespace ps2recomp
const std::vector<std::string> stdLibFuncs = {
// I/O functions
"printf", "sprintf", "snprintf", "fprintf", "vprintf", "vfprintf", "vsprintf", "vsnprintf",
"puts", "putchar", "getchar", "gets", "fgets", "fputs", "scanf", "fscanf", "sscanf",
"puts", "putchar", "getchar", "gets", "fgets", "fputs", "scanf", "fscanf", "sscanf",
"sprint", "sbprintf",
// Memory management
"malloc", "free", "calloc", "realloc", "aligned_alloc", "posix_memalign",
// Memory manipulation
"memcpy", "memset", "memmove", "memcmp", "memchr", "bcopy", "bzero",
"memcpy", "memset", "memmove", "memcmp", "memcpy2", "memchr", "bcopy", "bzero",
// String manipulation
"strcpy", "strncpy", "strcat", "strncat", "strcmp", "strncmp", "strlen", "strstr",
@@ -230,134 +234,7 @@ namespace ps2recomp
// Extra string helpers
"strnlen", "strspn", "strcspn", "strcasecmp", "strncasecmp"};
// PS2-specific system functions
const std::vector<std::string> ps2SysFuncs = {
// EE Kernel
"FlushCache", "EI", "DI", "SYNC", "ExitThread", "SleepThread", "WakeupThread",
"syscall", "ResetEE", "SetGsCrt", "Exit", "LoadExecPS2", "ExecPS2", "GetThreadId",
"RFU009", "InitRCnt", "GetOsTick", "ResetRCnt", "ChangeThreadPriority",
"DisableFPUExceptions", "EnableFPUExceptions", "GetEEStatus", "SetEEStatus",
"GetCop0", "SetCop0", "GetCop1", "SetCop1", "Exception",
"CreateThread", "DeleteThread", "StartThread", "SuspendThread", "ResumeThread",
"GetThreadStatus", "ReferThreadStatus", "iWakeupThread", "iResumeThread",
"TerminateThread", "EnableIntc", "DisableIntc", "EnableDmac", "DisableDmac",
"ExitDeleteThread", "ExitHandler", "ExecOSD", "ExecPS2Patch", "EnableCache",
"EndOfHeap", "ExpandScratchPad",
// SIF
"SifInitRpc", "SifExitRpc", "SifBindRpc", "SifCallRpc", "SifRegisterRpc",
"SifCheckStatRpc", "SifSetRpcQueue", "SifRpcLoop", "SifGetOtherData",
"sceSifAddCmdHandler", "sceSifRemoveCmdHandler", "sceSifSendCmd",
"sceSifInitCmd", "sceSifExitCmd", "sceSifSetCmdBuffer", "SifDmaInit",
"SifSetDma", "SifSetDChain", "iSifSetDChain", "SifSetOneDma",
"sceSifDmaStat", "sceSifSetDmaIntr", "sceSifResetDmaIntr",
"sceSifWriteBackDCache",
// IOP
"PollSema", "WaitSema", "SignalSema", "iSignalSema", "CreateSema",
"DeleteSema", "iWaitSema", "PollEventFlag", "WaitEventFlag", "SignalEventFlag",
"iSignalEventFlag", "CreateEventFlag", "DeleteEventFlag",
// Timer
"CreateAlarm", "iSetAlarm", "SetAlarm", "iReleaseAlarm", "ReleaseAlarm",
"USec2SysClock", "GetSystemTime", "SetSystemTime", "SysClock2USec",
// CD/DVD driver helpers
"cd_callback", "cmd_sem_init", "ncmd_prechk", "scmd_prechk",
"cdvd_exit", "fileXioInit", "fileXioExit", "fileXioOpen",
"fileXioClose", "fileXioRead", "fileXioWrite", "fileXioLseek",
"fileXioGetStat", "fileXioSetBlockMode",
// Interrupt / DMA handlers
"AddIntcHandler", "AddIntcHandler2", "RemoveIntcHandler", "RemoveIntcHandler2",
"AddDmacHandler", "AddDmacHandler2", "RemoveDmacHandler", "RemoveDmacHandler2",
"AddSbusIntcHandler", "RemoveSbusIntcHandler", "EnableIntcHandler", "EnableDmacHandler"
};
// PS2-specific library functions
const std::vector<std::string> ps2LibFuncs = {
// GS
"GsSetCrt", "GsGetIMR", "GsPutIMR", "GsSetIMR", "GsInit", "GsSyncV",
"GsGetVideoMode", "GsSetVideoMode", "GsDefDispBuffer", "GsResetGraph",
"GsPutDrawEnv", "GsSetClip", "GsSetScissor", "GsInitialCursor", "GsDrawCursor",
"GsSetVmode", "GsSetXYOffset", "GsSetClear", "GsTest", "GsTexure", "GsDisplay",
"GsDrawPixel", "GsDrawLine", "GsDrawBox", "GsDrawTriangle", "GsDrawRect",
"GsDrawSprite", "GsPrimTriangle", "GsSwapFrame", "GsLoadImage", "GsPutImage",
"GsMakeIndex", "GsSetCombineMode", "GsSetVertexColor", "GsSetOrigin",
// Pad
"PadInit", "PadPortOpen", "PadGetState", "PadRead", "PadSetMainMode",
"PadSetActDirect", "PadSetActAlign", "PadGetReqState", "PadInfoMode",
"PadInfoAct", "PadInfoComb", "PadSetActLED", "PadPortClose", "PadStateIntToStr",
"PadGetReqState", "PadInfoPressMode", "PadEnterPressMode", "PadExitPressMode",
// IPU
"IPU_FDEC", "IPU_FRST", "IPU_SETIQ", "IPU_IDEC", "IPU_CSC", "IPU_PACK",
"IPU_VDEC", "IPU_FDTV", "IPU_SETTH", "IPU_Disable", "IPU_Reset",
// DMA
"DmaGetChcr", "DmaGetMadr", "DmaGetTadr", "DmaGetQwc", "DmaGetRemaining",
"DmaSetChcr", "DmaSetMadr", "DmaSetTadr", "DmaSetQwc", "DmaStartTransfer",
"DmaEnableDma", "DmaDisableDma", "DmaTransferMem", "DmaWaitForTransfer",
// CDVD
"CdInit", "CdDiskReady", "CdGetError", "CdGetToc", "CdReadSector",
"CdGetDiscType", "CdDiskReady", "CdTrayReq", "CdSync", "CdRead",
"CdStop", "CdSetmode", "CdSearchFile", "CdReadChain", "CdReadILINK",
// Other libraries
"audsrv_init", "audsrv_adpcm_init", "audsrv_set_volume", "audsrv_play_adpcm",
"loadModules", "fioInit", "mcInit", "mtapInit", "padInit", "sioInit",
"ethPutIFAddr", "ethGetNetEther", "ethPutNetIFaddr", "ethGetHWaddr",
"ethUsrPkt_input", "ethIntrEnable", "ethSetupIF", "ethPutArpReq",
"ethPktToIF", "ethGetArpEntry", "ethAllocTxPacket", "ethFreeTxPacket",
"sio_puts", "sio_printf", "sio_getc",
// Extra audsrv helpers
"audsrv_quit", "audsrv_play_audio", "audsrv_stop_audio", "audsrv_wait_audio",
"audsrv_set_format"};
// Add new PS2-specific functions for more complete coverage
const std::vector<std::string> additionalPs2Funcs = {
// VIF and FIFO functions
"VIF0_STAT", "VIF0_FBRST", "VIF0_ERR", "VIF0_MARK", "VIF0_CYCLE", "VIF0_MODE",
"VIF0_NUM", "VIF0_MASK", "VIF0_CODE", "VIF0_ITOPS", "VIF0_ITOP", "VIF0_R0",
"VIF0_R1", "VIF0_R2", "VIF0_R3", "VIF0_C0", "VIF0_C1", "VIF0_C2", "VIF0_C3",
"VIF1_STAT", "VIF1_FBRST", "VIF1_ERR", "VIF1_MARK", "VIF1_CYCLE", "VIF1_MODE",
"VIF1_NUM", "VIF1_MASK", "VIF1_CODE", "VIF1_ITOPS", "VIF1_BASE", "VIF1_OFST",
"VIF1_TOPS", "VIF1_ITOP", "VIF1_TOP", "VIF1_R0", "VIF1_R1", "VIF1_R2", "VIF1_R3",
"VIF1_C0", "VIF1_C1", "VIF1_C2", "VIF1_C3",
// Graphics Synthesis functions
"GsGetGParam", "GsSetGParam", "GsGParam", "GsSetCBM", "GsCBM", "GsAddFB",
"GsAddFT", "GsFreeMem", "GsGetFBMem", "GsGetFTMem", "GsGetFT", "GsGetFB",
"GsSetRefView", "GsSetView", "GsGetActiveFrame", "GsSetDrawFrameBuffer",
"GsSetDisplayFrameBuffer", "GsSetZBufferAddress", "GsSetCLUT", "GsSetPaintMethod",
"GsCleanZBuffer", "GsSwapDispBuffer", "GsDrawSync", "GsVSync",
// Audio functions
"SdInit", "SdSetParam", "SdGetParam", "SdSetSwitch", "SdGetSwitch", "SdSetAddr",
"SdGetAddr", "SdSetCoreAttr", "SdGetCoreAttr", "SdNote2Pitch", "SdPitch2Note",
"SdProcBatch", "SdProcBatchEx", "SdVoiceTrans", "SdBlockTrans", "SdVoiceTransStatus",
"SdBlockTransStatus", "iSdVoiceTrans", "iSdBlockTrans", "SdSetTransCallback",
"SdSetIRQCallback", "SdSetEffectAttr", "SdGetEffectAttr", "SdClearEffectWorkArea",
// SPU2 functions
"sceSPU2Init", "sceSPU2Reset", "sceSPU2SetVolume", "sceSPU2GetVolume",
"sceSPU2SetReverb", "sceSPU2GetReverb", "sceSPU2SetTransferMode",
"sceSPU2GetTransferMode", "sceSPU2Write", "sceSPU2Read", "sceSPU2ReadDMA",
"sceSPU2WriteDMA", "sceSPU2SetVoiceAttributes", "sceSPU2GetVoiceAttributes",
// Libmath
"sinf", "cosf", "tanf", "asinf", "acosf", "atanf", "atan2f", "sinhf", "coshf", "tanhf",
"sinl", "cosl", "tanl", "asinl", "acosl", "atanl", "atan2l", "sinhl", "coshl", "tanhl",
"sqrtf", "powf", "expf", "logf", "log10f"};
// Combine all library functions
m_libFunctions.insert(stdLibFuncs.begin(), stdLibFuncs.end());
m_libFunctions.insert(ps2SysFuncs.begin(), ps2SysFuncs.end());
m_libFunctions.insert(ps2LibFuncs.begin(), ps2LibFuncs.end());
m_libFunctions.insert(additionalPs2Funcs.begin(), additionalPs2Funcs.end());
}
void ElfAnalyzer::analyzeEntryPoint()
@@ -387,8 +264,7 @@ namespace ps2recomp
std::cout << "Found initialization call to: " << func.name << " at 0x"
<< std::hex << inst.address << std::dec << std::endl;
if (func.name.find("init") != std::string::npos ||
func.name.find("Init") != std::string::npos)
if (!isDoNotSkipOrStub(func.name) && (func.name.find("init") != std::string::npos || func.name.find("Init") != std::string::npos))
{
m_skipFunctions.insert(func.name);
}
@@ -425,43 +301,12 @@ namespace ps2recomp
{
m_libFunctions.insert(symbol.name);
}
if (isSystemFunction(symbol.name))
else if (isSystemFunction(symbol.name))
{
m_skipFunctions.insert(symbol.name);
}
}
}
for (const auto &func : m_functions)
{
if (m_libFunctions.find(func.name) != m_libFunctions.end() ||
m_skipFunctions.find(func.name) != m_skipFunctions.end())
{
continue;
}
if (identifyMemcpyPattern(func))
{
std::cout << "Identified function " << func.name << " as memcpy-like implementation" << std::endl;
m_libFunctions.insert(func.name);
}
else if (identifyMemsetPattern(func))
{
std::cout << "Identified function " << func.name << " as memset-like implementation" << std::endl;
m_libFunctions.insert(func.name);
}
else if (identifyStringOperationPattern(func))
{
std::cout << "Identified function " << func.name << " as string operation implementation" << std::endl;
m_libFunctions.insert(func.name);
}
else if (identifyMathPattern(func))
{
std::cout << "Identified function " << func.name << " as math implementation" << std::endl;
m_libFunctions.insert(func.name);
}
}
}
void ElfAnalyzer::analyzeDataUsage()
@@ -1274,7 +1119,9 @@ namespace ps2recomp
// If too many patches in one function, maybe better to skip it
if (patchAddrs.size() > 5 &&
static_cast<double>(patchAddrs.size()) / ((func.end - func.start) / 4) > 0.2)
static_cast<double>(patchAddrs.size()) / ((func.end - func.start) / 4) > 0.2 &&
!isLibraryFunction(func.name) &&
!isDoNotSkipOrStub(func.name))
{
std::cout << " - Adding " << func.name << " to skip list due to high patch density" << std::endl;
m_skipFunctions.insert(func.name);
@@ -1639,6 +1486,50 @@ namespace ps2recomp
return result;
}
static bool hasPs2ApiPrefix(const std::string &name)
{
if (name.empty())
return false;
const std::vector<std::string> libraryPrefixes = {
"sce", "Sce", "SCE", // Sony prefixes
"sif", "Sif", "SIF", // SIF functions
"pad", "Pad", "PAD", // Pad functions
"gs", "Gs", "GS", // Graphics Synthesizer
"dma", "Dma", "DMA", // DMA functions
"iop", "Iop", "IOP", // IOP functions
"vif", "Vif", "VIF", // VIF functions
"spu", "Spu", "SPU", // SPU functions
"mc", "Mc", "MC", // Memory Card functions
"libc", "Libc", "LIBC" // C library functions
};
std::string base = name;
if (base[0] == '_' && base.size() > 1)
{
base = base.substr(1);
}
for (const auto &prefix : libraryPrefixes)
{
if (base.rfind(prefix, 0) == 0)
{
return true;
}
}
return false;
}
static bool isDoNotSkipOrStub(const std::string &name)
{
static const std::unordered_set<std::string> kDoNotSkipOrStub = {
"topThread",
"cmd_sem_init"};
return kDoNotSkipOrStub.find(name) != kDoNotSkipOrStub.end();
}
bool ElfAnalyzer::isSystemFunction(const std::string &name) const
{
static const std::unordered_set<std::string> systemFuncs = {
@@ -1663,34 +1554,11 @@ namespace ps2recomp
if (name.empty())
return false;
if (name[0] == '_' && name.size() > 1 && std::isalpha(name[1]))
{
return true; // Many library functions start with underscore
}
const std::vector<std::string> libraryPrefixes = {
"sce", "Sce", "SCE", // Sony prefixes
"sif", "Sif", "SIF", // SIF functions
"pad", "Pad", "PAD", // Pad functions
"gs", "Gs", "GS", // Graphics Synthesizer
"dma", "Dma", "DMA", // DMA functions
"iop", "Iop", "IOP", // IOP functions
"vif", "Vif", "VIF", // VIF functions
"spu", "Spu", "SPU", // SPU functions
"mc", "Mc", "MC", // Memory Card functions
"libc", "Libc", "LIBC" // C library functions
};
for (const auto &prefix : libraryPrefixes)
{
if (name.rfind(prefix, 0) == 0)
{
return true;
}
}
if (hasPs2ApiPrefix(name))
return true;
// Check for common C/C++ library function names
static const std::regex cLibPattern("^(mem|str|time|f?printf|f?scanf|malloc|free|calloc|realloc|atoi|itoa|rand|srand|abort|exit|atexit|getenv|system|bsearch|qsort|abs|labs|div|ldiv|mblen|mbtowc|wctomb|mbstowcs|wcstombs).*");
static const std::regex cLibPattern("^_*(mem|str|time|f?printf|f?scanf|malloc|free|calloc|realloc|atoi|itoa|rand|srand|abort|exit|atexit|getenv|system|bsearch|qsort|abs|labs|div|ldiv|mblen|mbtowc|wctomb|mbstowcs|wcstombs).*");
if (std::regex_match(name, cLibPattern))
{
return true;
@@ -1771,6 +1639,14 @@ namespace ps2recomp
{
return false;
}
if (isDoNotSkipOrStub(function.name))
{
return false;
}
if (hasPs2ApiPrefix(function.name))
{
return false;
}
std::vector<Instruction> instructions = decodeFunction(function);
@@ -1826,7 +1702,10 @@ namespace ps2recomp
if (isSelfModifyingCode(function))
{
std::cout << "Function " << function.name << " contains self-modifying code" << std::endl;
m_skipFunctions.insert(function.name);
if (!isLibraryFunction(function.name) && !isDoNotSkipOrStub(function.name))
{
m_skipFunctions.insert(function.name);
}
}
if (isLoopHeavyFunction(function))
@@ -1918,4 +1797,4 @@ namespace ps2recomp
return currentAddr + 4;
}
}
}
@@ -9,6 +9,7 @@
#include <string>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <filesystem>
namespace ps2recomp
@@ -38,6 +39,7 @@ namespace ps2recomp
std::unordered_map<uint32_t, std::vector<Instruction>> m_decodedFunctions;
std::unordered_map<std::string, bool> m_skipFunctions;
std::unordered_set<std::string> m_stubFunctions;
std::map<uint32_t, std::string> m_generatedStubs;
std::unordered_map<uint32_t, std::string> m_functionRenames;
CodeGenerator::BootstrapInfo m_bootstrapInfo;
@@ -45,6 +47,7 @@ namespace ps2recomp
bool decodeFunction(Function &function);
void discoverAdditionalEntryPoints();
bool shouldSkipFunction(const std::string &name) const;
bool isStubFunction(const std::string &name) const;
std::string generateRuntimeHeader();
bool generateFunctionHeader();
bool generateStubHeader();
+2 -171
View File
@@ -2452,159 +2452,6 @@ namespace ps2recomp
std::string CodeGenerator::generateFunctionRegistration(const std::vector<Function> &functions,
const std::map<uint32_t, std::string> &stubs)
{
static const std::unordered_map<std::string, std::pair<uint32_t, std::string>> systemCalls = {
// Memory management
{"FlushCache", {0x0040, "ps2_syscalls::FlushCache"}},
{"ResetEE", {0x0042, "ps2_syscalls::ResetEE"}},
{"SetMemoryMode", {0x0043, "ps2_syscalls::SetMemoryMode"}},
// Thread management
{"CreateThread", {0x0055, "ps2_syscalls::CreateThread"}},
{"DeleteThread", {0x0056, "ps2_syscalls::DeleteThread"}},
{"StartThread", {0x0057, "ps2_syscalls::StartThread"}},
{"ExitThread", {0x003C, "ps2_syscalls::ExitThread"}},
{"ExitDeleteThread", {0x003D, "ps2_syscalls::ExitDeleteThread"}},
{"TerminateThread", {0x0058, "ps2_syscalls::TerminateThread"}},
{"SuspendThread", {0x0059, "ps2_syscalls::SuspendThread"}},
{"ResumeThread", {0x005A, "ps2_syscalls::ResumeThread"}},
{"GetThreadId", {0x0047, "ps2_syscalls::GetThreadId"}},
{"ReferThreadStatus", {0x005B, "ps2_syscalls::ReferThreadStatus"}},
{"SleepThread", {0x005C, "ps2_syscalls::SleepThread"}},
{"WakeupThread", {0x005D, "ps2_syscalls::WakeupThread"}},
{"iWakeupThread", {0x005E, "ps2_syscalls::iWakeupThread"}},
{"ChangeThreadPriority", {0x005F, "ps2_syscalls::ChangeThreadPriority"}},
{"RotateThreadReadyQueue", {0x0060, "ps2_syscalls::RotateThreadReadyQueue"}},
{"ReleaseWaitThread", {0x0061, "ps2_syscalls::ReleaseWaitThread"}},
{"iReleaseWaitThread", {0x0062, "ps2_syscalls::iReleaseWaitThread"}},
// Semaphores
{"CreateSema", {0x0064, "ps2_syscalls::CreateSema"}},
{"DeleteSema", {0x0065, "ps2_syscalls::DeleteSema"}},
{"SignalSema", {0x0066, "ps2_syscalls::SignalSema"}},
{"iSignalSema", {0x0067, "ps2_syscalls::iSignalSema"}},
{"WaitSema", {0x0068, "ps2_syscalls::WaitSema"}},
{"PollSema", {0x0069, "ps2_syscalls::PollSema"}},
{"iPollSema", {0x006A, "ps2_syscalls::iPollSema"}},
{"ReferSemaStatus", {0x006B, "ps2_syscalls::ReferSemaStatus"}},
{"iReferSemaStatus", {0x006C, "ps2_syscalls::iReferSemaStatus"}},
// Event flags
{"CreateEventFlag", {0x006D, "ps2_syscalls::CreateEventFlag"}},
{"DeleteEventFlag", {0x006E, "ps2_syscalls::DeleteEventFlag"}},
{"SetEventFlag", {0x006F, "ps2_syscalls::SetEventFlag"}},
{"iSetEventFlag", {0x0070, "ps2_syscalls::iSetEventFlag"}},
{"ClearEventFlag", {0x0071, "ps2_syscalls::ClearEventFlag"}},
{"iClearEventFlag", {0x0072, "ps2_syscalls::iClearEventFlag"}},
{"WaitEventFlag", {0x0073, "ps2_syscalls::WaitEventFlag"}},
{"PollEventFlag", {0x0074, "ps2_syscalls::PollEventFlag"}},
{"iPollEventFlag", {0x0075, "ps2_syscalls::iPollEventFlag"}},
{"ReferEventFlagStatus", {0x0076, "ps2_syscalls::ReferEventFlagStatus"}},
{"iReferEventFlagStatus", {0x0077, "ps2_syscalls::iReferEventFlagStatus"}},
// Alarm
{"SetAlarm", {0x0078, "ps2_syscalls::SetAlarm"}},
{"iSetAlarm", {0x0079, "ps2_syscalls::iSetAlarm"}},
{"CancelAlarm", {0x007A, "ps2_syscalls::CancelAlarm"}},
{"iCancelAlarm", {0x007B, "ps2_syscalls::iCancelAlarm"}},
// Intr handlers
{"EnableIntc", {0x0080, "ps2_syscalls::EnableIntc"}},
{"DisableIntc", {0x0081, "ps2_syscalls::DisableIntc"}},
{"EnableDmac", {0x0082, "ps2_syscalls::EnableDmac"}},
{"DisableDmac", {0x0083, "ps2_syscalls::DisableDmac"}},
// RPC and IOP
{"SifStopModule", {0x0085, "ps2_syscalls::SifStopModule"}},
{"SifLoadModule", {0x0086, "ps2_syscalls::SifLoadModule"}},
{"SifInitRpc", {0x00A5, "ps2_syscalls::SifInitRpc"}},
{"SifBindRpc", {0x00A6, "ps2_syscalls::SifBindRpc"}},
{"SifCallRpc", {0x00A7, "ps2_syscalls::SifCallRpc"}},
{"SifRegisterRpc", {0x00A8, "ps2_syscalls::SifRegisterRpc"}},
{"SifCheckStatRpc", {0x00A9, "ps2_syscalls::SifCheckStatRpc"}},
{"SifSetRpcQueue", {0x00AA, "ps2_syscalls::SifSetRpcQueue"}},
{"SifRemoveRpcQueue", {0x00AB, "ps2_syscalls::SifRemoveRpcQueue"}},
{"SifRemoveRpc", {0x00AC, "ps2_syscalls::SifRemoveRpc"}},
// IO system calls
{"fioOpen", {0x00B0, "ps2_syscalls::fioOpen"}},
{"fioClose", {0x00B1, "ps2_syscalls::fioClose"}},
{"fioRead", {0x00B2, "ps2_syscalls::fioRead"}},
{"fioWrite", {0x00B3, "ps2_syscalls::fioWrite"}},
{"fioLseek", {0x00B4, "ps2_syscalls::fioLseek"}},
{"fioMkdir", {0x00B5, "ps2_syscalls::fioMkdir"}},
{"fioChdir", {0x00B6, "ps2_syscalls::fioChdir"}},
{"fioRmdir", {0x00B7, "ps2_syscalls::fioRmdir"}},
{"fioGetstat", {0x00B8, "ps2_syscalls::fioGetstat"}},
{"fioRemove", {0x00B9, "ps2_syscalls::fioRemove"}},
// Graphics
{"GsSetCrt", {0x00C0, "ps2_syscalls::GsSetCrt"}},
{"GsGetIMR", {0x00C1, "ps2_syscalls::GsGetIMR"}},
{"GsPutIMR", {0x00C2, "ps2_syscalls::GsPutIMR"}},
{"GsSetVideoMode", {0x00C3, "ps2_syscalls::GsSetVideoMode"}},
// Miscellaneous
{"GetOsdConfigParam", {0x00F0, "ps2_syscalls::GetOsdConfigParam"}},
{"SetOsdConfigParam", {0x00F1, "ps2_syscalls::SetOsdConfigParam"}},
{"GetRomName", {0x00F2, "ps2_syscalls::GetRomName"}},
{"SifLoadElfPart", {0x00F6, "ps2_syscalls::SifLoadElfPart"}},
{"sceSifLoadModule", {0x0122, "ps2_syscalls::sceSifLoadModule"}},
{"TODO", {0x0000, "ps2_syscalls::TODO"}}};
static const std::unordered_map<std::string, std::string> libraryStubs = {
// Memory operations
{"malloc", "ps2_stubs::malloc"},
{"free", "ps2_stubs::free"},
{"calloc", "ps2_stubs::calloc"},
{"realloc", "ps2_stubs::realloc"},
{"memcpy", "ps2_stubs::memcpy"},
{"memset", "ps2_stubs::memset"},
{"memmove", "ps2_stubs::memmove"},
{"memcmp", "ps2_stubs::memcmp"},
// String operations
{"strcpy", "ps2_stubs::strcpy"},
{"strncpy", "ps2_stubs::strncpy"},
{"strlen", "ps2_stubs::strlen"},
{"strcmp", "ps2_stubs::strcmp"},
{"strncmp", "ps2_stubs::strncmp"},
{"strcat", "ps2_stubs::strcat"},
{"strncat", "ps2_stubs::strncat"},
{"strchr", "ps2_stubs::strchr"},
{"strrchr", "ps2_stubs::strrchr"},
{"strstr", "ps2_stubs::strstr"},
// I/O operations
{"printf", "ps2_stubs::printf"},
{"sprintf", "ps2_stubs::sprintf"},
{"snprintf", "ps2_stubs::snprintf"},
{"puts", "ps2_stubs::puts"},
{"fopen", "ps2_stubs::fopen"},
{"fclose", "ps2_stubs::fclose"},
{"fread", "ps2_stubs::fread"},
{"fwrite", "ps2_stubs::fwrite"},
{"fprintf", "ps2_stubs::fprintf"},
{"fseek", "ps2_stubs::fseek"},
{"ftell", "ps2_stubs::ftell"},
{"fflush", "ps2_stubs::fflush"},
// Math functions
{"sqrt", "ps2_stubs::sqrt"},
{"sin", "ps2_stubs::sin"},
{"cos", "ps2_stubs::cos"},
{"tan", "ps2_stubs::tan"},
{"atan2", "ps2_stubs::atan2"},
{"pow", "ps2_stubs::pow"},
{"exp", "ps2_stubs::exp"},
{"log", "ps2_stubs::log"},
{"log10", "ps2_stubs::log10"},
{"ceil", "ps2_stubs::ceil"},
{"floor", "ps2_stubs::floor"},
{"fabs", "ps2_stubs::fabs"},
{"TODO", "ps2_stubs::TODO"}};
std::stringstream ss;
std::unordered_set<uint32_t> registeredAddresses;
@@ -2613,6 +2460,7 @@ namespace ps2recomp
ss << "#include \"ps2_runtime.h\"\n";
ss << "#include \"ps2_recompiled_functions.h\"\n";
ss << "#include \"ps2_stubs.h\"\n";
ss << "#include \"ps2_recompiled_stubs.h\"//this will give duplicated erros because runtime maybe has it define already, just delete the TODOS ones\n";
ss << "#include \"ps2_syscalls.h\"\n\n";
// Registration function
@@ -2628,26 +2476,9 @@ namespace ps2recomp
for (const auto &function : functions)
{
if (!function.isRecompiled)
if (!function.isRecompiled && !function.isStub)
continue;
bool isSystemCall = systemCalls.find(function.name) != systemCalls.end();
bool isLibCall = libraryStubs.find(function.name) != libraryStubs.end();
if (isSystemCall)
{
const auto &syscallInfo = systemCalls.at(function.name);
systemCallFunctions.push_back({syscallInfo.first, syscallInfo.second});
continue;
}
if (isLibCall)
{
uint32_t libAddr = libBaseAddr + (libOffset++ * 4);
libraryFunctions.push_back({libAddr, libraryStubs.at(function.name)});
continue;
}
std::string generatedName = getGeneratedFunctionName(function);
if (function.isStub)
+39 -8
View File
@@ -31,6 +31,10 @@ namespace ps2recomp
{
m_skipFunctions[name] = true;
}
for (const auto &name : m_config.stubImplementations)
{
m_stubFunctions.insert(name);
}
m_elfParser = std::make_unique<ElfParser>(m_config.inputPath);
if (!m_elfParser->parse())
@@ -139,6 +143,12 @@ namespace ps2recomp
{
std::cout << "processing function: " << function.name << std::endl;
if (isStubFunction(function.name))
{
function.isStub = true;
continue;
}
if (shouldSkipFunction(function.name))
{
std::cout << "Skipping function: " << function.name << std::endl;
@@ -182,7 +192,7 @@ namespace ps2recomp
std::unordered_map<std::string, int> nameCounts;
for (const auto &function : m_functions)
{
if (!function.isRecompiled)
if (!function.isRecompiled && !function.isStub)
continue;
std::string sanitized = sanitizeFunctionName(function.name);
nameCounts[sanitized]++;
@@ -190,7 +200,7 @@ namespace ps2recomp
for (const auto &function : m_functions)
{
if (!function.isRecompiled)
if (!function.isRecompiled && !function.isStub)
continue;
std::string sanitized = sanitizeFunctionName(function.name);
@@ -216,6 +226,19 @@ namespace ps2recomp
m_codeGenerator->setRenamedFunctions(m_functionRenames);
}
m_generatedStubs.clear();
for (const auto &function : m_functions)
{
if (function.isStub)
{
std::string generatedName = m_codeGenerator->getGeneratedFunctionName(function);
std::stringstream stub;
stub << "void " << generatedName
<< "(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime) { ps2_syscalls::TODO(rdram, ctx, runtime); }";
m_generatedStubs[function.start] = stub.str();
}
}
generateFunctionHeader();
if (m_config.singleFileOutput)
@@ -235,7 +258,7 @@ namespace ps2recomp
for (const auto &function : m_functions)
{
if (!function.isRecompiled)
if (!function.isRecompiled && !function.isStub)
{
continue;
}
@@ -282,7 +305,7 @@ namespace ps2recomp
for (const auto &function : m_functions)
{
if (!function.isRecompiled || function.isStub)
if (!function.isRecompiled && !function.isStub)
{
continue;
}
@@ -317,8 +340,7 @@ namespace ps2recomp
std::cout << "Wrote individual function files to: " << m_config.outputPath << std::endl;
}
std::string registerFunctions = m_codeGenerator->generateFunctionRegistration(
m_functions, m_generatedStubs);
std::string registerFunctions = m_codeGenerator->generateFunctionRegistration(m_functions, m_generatedStubs);
fs::path registerPath = fs::path(m_config.outputPath) / "register_functions.cpp";
writeToFile(registerPath.string(), registerFunctions);
@@ -345,9 +367,13 @@ namespace ps2recomp
// ss << "namespace ps2recomp {\n";
// ss << "namespace stubs {\n\n";
for (const auto &funcName : m_config.skipFunctions)
std::unordered_set<std::string> stubNames;
stubNames.insert(m_config.skipFunctions.begin(), m_config.skipFunctions.end());
stubNames.insert(m_config.stubImplementations.begin(), m_config.stubImplementations.end());
for (const auto &funcName : stubNames)
{
ss << "void " << funcName << "(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) { ps2_syscalls::TODO(rdram, ctx, runtime); }\n";
ss << "void " << funcName << "(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime);\n";
}
// ss << "\n} // namespace stubs\n";
@@ -594,6 +620,11 @@ namespace ps2recomp
return m_skipFunctions.find(name) != m_skipFunctions.end();
}
bool PS2Recompiler::isStubFunction(const std::string &name) const
{
return m_stubFunctions.find(name) != m_stubFunctions.end();
}
std::string PS2Recompiler::generateRuntimeHeader()
{
return m_codeGenerator->generateMacroHeader();
+19 -1
View File
@@ -7,6 +7,7 @@
#include <string>
#include <functional>
#include <immintrin.h> // For SSE/AVX instructions
#include <atomic>
#include <filesystem>
#include <iostream>
@@ -34,6 +35,7 @@ constexpr uint32_t PS2_VU1_DATA_BASE = 0x1100C000;
constexpr uint32_t PS2_GS_BASE = 0x12000000;
constexpr uint32_t PS2_GS_PRIV_REG_BASE = 0x12000000; // GS Privileged Registers
constexpr uint32_t PS2_GS_PRIV_REG_SIZE = 0x2000;
constexpr size_t PS2_GS_VRAM_SIZE = 4 * 1024 * 1024; // 4MB GS VRAM
#define PS2_FIO_O_RDONLY 0x0001
#define PS2_FIO_O_WRONLY 0x0002
@@ -351,6 +353,10 @@ public:
uint8_t *getRDRAM() { return m_rdram; }
uint8_t *getScratchpad() { return m_scratchpad; }
uint8_t *getIOPRAM() { return iop_ram; }
uint64_t dmaStartCount() const { return m_dmaStartCount.load(std::memory_order_relaxed); }
uint64_t gifCopyCount() const { return m_gifCopyCount.load(std::memory_order_relaxed); }
uint64_t gsWriteCount() const { return m_gsWriteCount.load(std::memory_order_relaxed); }
uint64_t vifWriteCount() const { return m_vifWriteCount.load(std::memory_order_relaxed); }
// Read/write memory
uint8_t read8(uint32_t address);
@@ -377,7 +383,12 @@ public:
bool isCodeModified(uint32_t address, uint32_t size);
void clearModifiedFlag(uint32_t address, uint32_t size);
private:
// GS register accessors
GSRegisters &gs() { return gs_regs; }
const GSRegisters &gs() const { return gs_regs; }
uint8_t *getGSVRAM() { return m_gsVRAM; }
const uint8_t *getGSVRAM() const { return m_gsVRAM; }
bool hasSeenGifCopy() const { return m_seenGifCopy; }
// Main RAM (32MB)
uint8_t *m_rdram;
@@ -387,11 +398,17 @@ private:
// IOP RAM (2MB)
uint8_t *iop_ram;
bool m_seenGifCopy;
std::atomic<uint64_t> m_dmaStartCount{0};
std::atomic<uint64_t> m_gifCopyCount{0};
std::atomic<uint64_t> m_gsWriteCount{0};
std::atomic<uint64_t> m_vifWriteCount{0};
// I/O registers
std::unordered_map<uint32_t, uint32_t> m_ioRegisters;
// Registers
GSRegisters gs_regs;
uint8_t *m_gsVRAM;
VIFRegisters vif0_regs;
VIFRegisters vif1_regs;
DMARegisters dma_regs[10]; // 10 DMA channels
@@ -434,6 +451,7 @@ public:
void registerFunction(uint32_t address, RecompiledFunction func);
RecompiledFunction lookupFunction(uint32_t address);
bool hasFunction(uint32_t address) const;
void SignalException(R5900Context *ctx, PS2Exception exception);
+4 -1
View File
@@ -90,6 +90,9 @@ namespace ps2_syscalls
void SifSetRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void SifRemoveRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void SifRemoveRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifCallRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifSendCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void _sceRpcGetPacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void fioOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void fioClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
@@ -116,4 +119,4 @@ namespace ps2_syscalls
void TODO(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
}
#endif // PS2_SYSCALLS_H
#endif // PS2_SYSCALLS_H
+308 -43
View File
@@ -2,9 +2,104 @@
#include <iostream>
#include <cstring>
#include <stdexcept>
#include <unordered_map>
namespace
{
inline bool isGsPrivReg(uint32_t addr)
{
return addr >= PS2_GS_PRIV_REG_BASE && addr < PS2_GS_PRIV_REG_BASE + PS2_GS_PRIV_REG_SIZE;
}
inline uint64_t *gsRegPtr(GSRegisters &gs, uint32_t addr)
{
uint32_t off = addr - PS2_GS_PRIV_REG_BASE;
switch (off)
{
case 0x0000:
return &gs.pmode;
case 0x0010:
return &gs.smode1;
case 0x0020:
return &gs.smode2;
case 0x0030:
return &gs.srfsh;
case 0x0040:
return &gs.synch1;
case 0x0050:
return &gs.synch2;
case 0x0060:
return &gs.syncv;
case 0x0070:
return &gs.dispfb1;
case 0x0080:
return &gs.display1;
case 0x0090:
return &gs.dispfb2;
case 0x00A0:
return &gs.display2;
case 0x00B0:
return &gs.extbuf;
case 0x00C0:
return &gs.extdata;
case 0x00D0:
return &gs.extwrite;
case 0x00E0:
return &gs.bgcolor;
case 0x1000:
return &gs.csr;
case 0x1010:
return &gs.imr;
case 0x1040:
return &gs.busdir;
case 0x1080:
return &gs.siglblid;
default:
return nullptr;
}
}
inline void logGsWrite(uint32_t addr, uint64_t value)
{
static std::unordered_map<uint32_t, int> logCount;
int &count = logCount[addr];
if (count < 10)
{
std::cout << "[GS] write 0x" << std::hex << addr << " = 0x" << value << std::dec << std::endl;
}
++count;
}
constexpr uint32_t kSchedulerBase = 0x00363a10;
constexpr uint32_t kSchedulerSpan = 0x00000420;
static int g_schedWriteLogCount = 0;
inline void logSchedulerWrite(uint32_t physAddr, uint32_t size, uint64_t value)
{
if (physAddr < kSchedulerBase || physAddr >= kSchedulerBase + kSchedulerSpan)
{
return;
}
if (g_schedWriteLogCount >= 64)
{
return;
}
std::cout << "[sched write" << size << "] addr=0x" << std::hex << physAddr
<< " val=0x" << value << std::dec << std::endl;
++g_schedWriteLogCount;
}
}
// Helpers for GS VRAM addressing (PSMCT32 only in this minimal path).
static inline uint32_t gs_vram_offset(uint32_t basePage, uint32_t x, uint32_t y, uint32_t fbw)
{
// basePage is in 2048-byte units; fbw is in blocks of 64 pixels.
uint32_t strideBytes = fbw * 64 * 4;
return basePage * 2048 + y * strideBytes + x * 4;
}
PS2Memory::PS2Memory()
: m_rdram(nullptr), m_scratchpad(nullptr)
: m_rdram(nullptr), m_scratchpad(nullptr), m_gsVRAM(nullptr), m_seenGifCopy(false)
{
}
@@ -21,6 +116,12 @@ PS2Memory::~PS2Memory()
delete[] m_scratchpad;
m_scratchpad = nullptr;
}
if (m_gsVRAM)
{
delete[] m_gsVRAM;
m_gsVRAM = nullptr;
}
}
bool PS2Memory::initialize(size_t ramSize)
@@ -70,6 +171,20 @@ bool PS2Memory::initialize(size_t ramSize)
// Initialize GS registers
memset(&gs_regs, 0, sizeof(gs_regs));
// Allocate GS VRAM (4MB)
m_gsVRAM = new uint8_t[PS2_GS_VRAM_SIZE];
if (!m_gsVRAM)
{
delete[] m_rdram;
delete[] m_scratchpad;
delete[] iop_ram;
m_rdram = nullptr;
m_scratchpad = nullptr;
iop_ram = nullptr;
return false;
}
std::memset(m_gsVRAM, 0, PS2_GS_VRAM_SIZE);
// Initialize VIF registers
memset(&vif0_regs, 0, sizeof(vif0_regs));
memset(&vif1_regs, 0, sizeof(vif1_regs));
@@ -94,22 +209,17 @@ bool PS2Memory::isScratchpad(uint32_t address) const
uint32_t PS2Memory::translateAddress(uint32_t virtualAddress)
{
// Handle special memory regions
if (isScratchpad(virtualAddress))
{
// Scratchpad is directly mapped
return virtualAddress - PS2_SCRATCHPAD_BASE;
}
// For RDRAM, mask the address to get the physical address
if (virtualAddress < PS2_RAM_SIZE ||
(virtualAddress >= 0x80000000 && virtualAddress < 0x80000000 + PS2_RAM_SIZE))
{
// KSEG0 is directly mapped, just mask out the high bits
return virtualAddress & 0x1FFFFFFF;
}
// For addresses that need TLB lookup
if (virtualAddress >= 0xC0000000)
{
for (const auto &entry : m_tlbEntries)
@@ -128,11 +238,9 @@ uint32_t PS2Memory::translateAddress(uint32_t virtualAddress)
}
}
}
// TLB miss
throw std::runtime_error("TLB miss for address: 0x" + std::to_string(virtualAddress));
}
// Default to simple masking for other addresses
return virtualAddress & 0x1FFFFFFF;
}
@@ -151,24 +259,22 @@ uint8_t PS2Memory::read8(uint32_t address)
}
else if (physAddr >= PS2_IO_BASE && physAddr < PS2_IO_BASE + PS2_IO_SIZE)
{
// IO registers - often not handled byte by byte
uint32_t regAddr = physAddr & ~0x3; // Align to word boundary
uint32_t regAddr = physAddr & ~0x3;
if (m_ioRegisters.find(regAddr) != m_ioRegisters.end())
{
uint32_t value = m_ioRegisters[regAddr];
uint32_t shift = (physAddr & 3) * 8;
return (value >> shift) & 0xFF;
}
return 0; // Unimplemented IO register
return 0;
}
// Handle other memory regions ,for now return 0 for unimplemented regions
// TODO: Handle other memory regions
return 0;
}
uint16_t PS2Memory::read16(uint32_t address)
{
// Check alignment
if (address & 1)
{
throw std::runtime_error("Unaligned 16-bit read at address: 0x" + std::to_string(address));
@@ -187,7 +293,6 @@ uint16_t PS2Memory::read16(uint32_t address)
}
else if (physAddr >= PS2_IO_BASE && physAddr < PS2_IO_BASE + PS2_IO_SIZE)
{
// IO registers - align to word boundary and extract relevant bits
uint32_t regAddr = physAddr & ~0x3;
if (m_ioRegisters.find(regAddr) != m_ioRegisters.end())
{
@@ -195,7 +300,7 @@ uint16_t PS2Memory::read16(uint32_t address)
uint32_t shift = (physAddr & 2) * 8;
return (value >> shift) & 0xFFFF;
}
return 0; // Unimplemented IO register
return 0;
}
return 0;
@@ -203,12 +308,19 @@ uint16_t PS2Memory::read16(uint32_t address)
uint32_t PS2Memory::read32(uint32_t address)
{
// Check alignment
if (address & 3)
{
throw std::runtime_error("Unaligned 32-bit read at address: 0x" + std::to_string(address));
}
if (isGsPrivReg(address))
{
uint64_t *reg = gsRegPtr(gs_regs, address);
uint32_t off = address & 7;
uint64_t val = reg ? *reg : 0;
return (uint32_t)(val >> (off * 8));
}
const bool scratch = isScratchpad(address);
uint32_t physAddr = translateAddress(address);
@@ -222,12 +334,11 @@ uint32_t PS2Memory::read32(uint32_t address)
}
else if (physAddr >= PS2_IO_BASE && physAddr < PS2_IO_BASE + PS2_IO_SIZE)
{
// IO registers
if (m_ioRegisters.find(physAddr) != m_ioRegisters.end())
{
return m_ioRegisters[physAddr];
}
return 0; // Unimplemented IO register
return 0;
}
return 0;
@@ -235,12 +346,17 @@ uint32_t PS2Memory::read32(uint32_t address)
uint64_t PS2Memory::read64(uint32_t address)
{
// Check alignment
if (address & 7)
{
throw std::runtime_error("Unaligned 64-bit read at address: 0x" + std::to_string(address));
}
if (isGsPrivReg(address))
{
uint64_t *reg = gsRegPtr(gs_regs, address);
return reg ? *reg : 0;
}
const bool scratch = isScratchpad(address);
uint32_t physAddr = translateAddress(address);
@@ -259,7 +375,6 @@ uint64_t PS2Memory::read64(uint32_t address)
__m128i PS2Memory::read128(uint32_t address)
{
// Check alignment
if (address & 15)
{
throw std::runtime_error("Unaligned 128-bit read at address: 0x" + std::to_string(address));
@@ -294,6 +409,7 @@ void PS2Memory::write8(uint32_t address, uint8_t value)
else if (physAddr < PS2_RAM_SIZE)
{
m_rdram[physAddr] = value;
logSchedulerWrite(physAddr, 8, value);
}
else if (physAddr >= PS2_IO_BASE && physAddr < PS2_IO_BASE + PS2_IO_SIZE)
{
@@ -304,13 +420,12 @@ void PS2Memory::write8(uint32_t address, uint8_t value)
uint32_t newValue = (m_ioRegisters[regAddr] & mask) | ((uint32_t)value << shift);
m_ioRegisters[regAddr] = newValue;
// Handle potential side effects of IO register writes
// TODO: Handle potential side effects of IO register writes
}
}
void PS2Memory::write16(uint32_t address, uint16_t value)
{
// Check alignment
if (address & 1)
{
throw std::runtime_error("Unaligned 16-bit write at address: 0x" + std::to_string(address));
@@ -326,28 +441,41 @@ void PS2Memory::write16(uint32_t address, uint16_t value)
else if (physAddr < PS2_RAM_SIZE)
{
*reinterpret_cast<uint16_t *>(&m_rdram[physAddr]) = value;
logSchedulerWrite(physAddr, 16, value);
}
else if (physAddr >= PS2_IO_BASE && physAddr < PS2_IO_BASE + PS2_IO_SIZE)
{
// IO registers - handle halfword writes
uint32_t regAddr = physAddr & ~0x3;
uint32_t shift = (physAddr & 2) * 8;
uint32_t mask = ~(0xFFFF << shift);
uint32_t newValue = (m_ioRegisters[regAddr] & mask) | ((uint32_t)value << shift);
m_ioRegisters[regAddr] = newValue;
// Handle potential side effects of IO register writes
// TODO: Handle potential side effects of IO register writes
}
}
void PS2Memory::write32(uint32_t address, uint32_t value)
{
// Check alignment
if (address & 3)
{
throw std::runtime_error("Unaligned 32-bit write at address: 0x" + std::to_string(address));
}
if (isGsPrivReg(address))
{
uint64_t *reg = gsRegPtr(gs_regs, address);
if (reg)
{
uint32_t off = address & 7;
uint64_t mask = 0xFFFFFFFFULL << (off * 8);
uint64_t newVal = (*reg & ~mask) | ((uint64_t)value << (off * 8));
*reg = newVal;
logGsWrite(address, newVal);
}
return;
}
const bool scratch = isScratchpad(address);
uint32_t physAddr = translateAddress(address);
@@ -361,9 +489,16 @@ void PS2Memory::write32(uint32_t address, uint32_t value)
markModified(address, 4);
*reinterpret_cast<uint32_t *>(&m_rdram[physAddr]) = value;
logSchedulerWrite(physAddr, 32, value);
}
else if (physAddr >= PS2_IO_BASE && physAddr < PS2_IO_BASE + PS2_IO_SIZE)
{
static int ioLogCount = 0;
if (ioLogCount < 64)
{
std::cout << "[IO write32] addr=0x" << std::hex << physAddr << " val=0x" << value << std::dec << std::endl;
++ioLogCount;
}
// Handle IO register writes with potential side effects
writeIORegister(physAddr, value);
}
@@ -371,12 +506,22 @@ void PS2Memory::write32(uint32_t address, uint32_t value)
void PS2Memory::write64(uint32_t address, uint64_t value)
{
// Check alignment
if (address & 7)
{
throw std::runtime_error("Unaligned 64-bit write at address: 0x" + std::to_string(address));
}
if (isGsPrivReg(address))
{
uint64_t *reg = gsRegPtr(gs_regs, address);
if (reg)
{
*reg = value;
logGsWrite(address, value);
}
return;
}
const bool scratch = isScratchpad(address);
uint32_t physAddr = translateAddress(address);
@@ -387,10 +532,10 @@ void PS2Memory::write64(uint32_t address, uint64_t value)
else if (physAddr < PS2_RAM_SIZE)
{
*reinterpret_cast<uint64_t *>(&m_rdram[physAddr]) = value;
logSchedulerWrite(physAddr, 64, value);
}
else
{
// Split into two 32-bit writes for other memory regions
write32(address, (uint32_t)value);
write32(address + 4, (uint32_t)(value >> 32));
}
@@ -398,7 +543,6 @@ void PS2Memory::write64(uint32_t address, uint64_t value)
void PS2Memory::write128(uint32_t address, __m128i value)
{
// Check alignment
if (address & 15)
{
throw std::runtime_error("Unaligned 128-bit write at address: 0x" + std::to_string(address));
@@ -415,10 +559,12 @@ void PS2Memory::write128(uint32_t address, __m128i value)
{
_mm_storeu_si128(reinterpret_cast<__m128i *>(&m_rdram[physAddr]), value);
}
else if (physAddr < PS2_GS_VRAM_SIZE)
{
_mm_storeu_si128(reinterpret_cast<__m128i *>(&m_gsVRAM[physAddr]), value);
}
else
{
// Split into smaller writes for other memory regions
// Extract the data using SSE intrinsics
uint64_t lo = _mm_extract_epi64(value, 0);
uint64_t hi = _mm_extract_epi64(value, 1);
@@ -429,9 +575,30 @@ void PS2Memory::write128(uint32_t address, __m128i value)
bool PS2Memory::writeIORegister(uint32_t address, uint32_t value)
{
if (address >= 0x10008000 && address < 0x1000F000)
{
static int dmaLogCount = 0;
if (dmaLogCount < 100)
{
uint32_t channelBase = address & 0xFFFFFF00;
uint32_t offset = address & 0xFF;
std::cout << "[DMA reg] ch=0x" << std::hex << channelBase
<< " off=0x" << offset << " = 0x" << value << std::dec << std::endl;
dmaLogCount++;
if (offset == 0x00 && (value & 0x100))
{
uint32_t madr = m_ioRegisters[channelBase + 0x10];
uint32_t qwc = m_ioRegisters[channelBase + 0x20];
uint32_t tadr = m_ioRegisters[channelBase + 0x30];
std::cout << "[DMA start] ch=0x" << std::hex << channelBase
<< " madr=0x" << madr << " qwc=0x" << qwc
<< " tadr=0x" << tadr << std::dec << std::endl;
m_dmaStartCount.fetch_add(1, std::memory_order_relaxed);
}
}
}
m_ioRegisters[address] = value;
// Now check if this is a special hardware register
if (address >= 0x10000000 && address < 0x10010000)
{
// Timer/counter registers
@@ -441,12 +608,53 @@ bool PS2Memory::writeIORegister(uint32_t address, uint32_t value)
return true;
}
// VIF0/VIF1 registers
if (address >= 0x10003800 && address < 0x10003A00)
{
static int vif0Log = 0;
if (vif0Log < 50)
{
std::cout << "[VIF0] write 0x" << std::hex << address << " = 0x" << value << std::dec << std::endl;
++vif0Log;
}
m_vifWriteCount.fetch_add(1, std::memory_order_relaxed);
}
if (address >= 0x10003C00 && address < 0x10003E00)
{
static int vif1Log = 0;
if (vif1Log < 50)
{
std::cout << "[VIF1] write 0x" << std::hex << address << " = 0x" << value << std::dec << std::endl;
++vif1Log;
}
m_vifWriteCount.fetch_add(1, std::memory_order_relaxed);
}
// DMA registers
if (address >= 0x10008000 && address < 0x1000F000)
{
std::cout << "DMA register write: " << std::hex << address << " = " << value << std::dec << std::endl;
// Check if we need to start a DMA transfer
// Dump current DMA regs for all channels
static bool dumpedDma = false;
if (!dumpedDma)
{
for (int ch = 0; ch < 10; ++ch)
{
uint32_t base = 0x10008000 + ch * 0x100;
uint32_t chcr_v = m_ioRegisters[base + 0x00];
uint32_t madr_v = m_ioRegisters[base + 0x10];
uint32_t qwc_v = m_ioRegisters[base + 0x20];
uint32_t tadr_v = m_ioRegisters[base + 0x30];
std::cout << "[DMA dump] ch" << ch
<< " chcr=0x" << std::hex << chcr_v
<< " madr=0x" << madr_v
<< " qwc=0x" << qwc_v
<< " tadr=0x" << tadr_v << std::dec << std::endl;
}
dumpedDma = true;
}
if ((address & 0xFF) == 0x00)
{ // CHCR registers
if (value & 0x100)
@@ -459,17 +667,81 @@ bool PS2Memory::writeIORegister(uint32_t address, uint32_t value)
<< ", MADR: " << std::hex << madr
<< ", QWC: " << qwc << std::dec << std::endl;
// Would actually start DMA here
// Minimal GIF (channel 2) and VIF1 (channel 1) image transfer: copy from EE memory to GS VRAM.
// Only handles simple linear IMAGE transfers; treats destination as current DISPFBUF1 FBP.
if ((channelBase == 0x1000A000 || channelBase == 0x10009000) && m_gsVRAM)
{
auto doCopy = [&](uint32_t srcAddr, uint32_t qwCount)
{
uint32_t bytes = qwCount * 16;
uint32_t src = translateAddress(srcAddr);
uint32_t basePage = static_cast<uint32_t>(gs_regs.dispfb1 & 0x1FF);
uint32_t dest = basePage * 2048;
std::cout << "[GIF] ch=" << ((channelBase == 0x1000A000) ? 2 : 1)
<< " IMAGE copy bytes=" << bytes
<< " src=0x" << std::hex << srcAddr
<< " (phys 0x" << src << ")"
<< " dest=0x" << dest << std::dec << std::endl;
if (dest + bytes > PS2_GS_VRAM_SIZE)
{
bytes = std::min<uint32_t>(bytes, PS2_GS_VRAM_SIZE - dest);
}
if (src + bytes > PS2_RAM_SIZE)
{
bytes = std::min<uint32_t>(bytes, PS2_RAM_SIZE - src);
}
std::memcpy(m_gsVRAM + dest, m_rdram + src, bytes);
m_seenGifCopy = true;
m_gifCopyCount.fetch_add(1, std::memory_order_relaxed);
};
// Dump GIF tag/header
uint32_t phys = translateAddress(madr);
if (phys + 16 <= PS2_RAM_SIZE)
{
const uint8_t *p = m_rdram + phys;
uint64_t tag0 = *reinterpret_cast<const uint64_t *>(p + 0);
uint64_t tag1 = *reinterpret_cast<const uint64_t *>(p + 8);
std::cout << "[GIF] tag0=0x" << std::hex << tag0 << " tag1=0x" << tag1 << std::dec << std::endl;
}
if (qwc > 0)
{
doCopy(madr, qwc);
}
else
{
// Simple DMA chain walker for one tag from TADR (REF/NEXT).
uint32_t tadr = m_ioRegisters[channelBase + 0x30];
uint32_t physTag = translateAddress(tadr);
if (physTag + 16 <= PS2_RAM_SIZE)
{
const uint8_t *tp = m_rdram + physTag;
uint64_t tag = *reinterpret_cast<const uint64_t *>(tp);
uint16_t tagQwc = static_cast<uint16_t>(tag & 0xFFFF);
uint32_t id = static_cast<uint32_t>((tag >> 28) & 0x7);
uint32_t addr = static_cast<uint32_t>((tag >> 32) & 0x7FFFFFF);
std::cout << "[DMA chain] ch=" << ((channelBase == 0x1000A000) ? 2 : 1)
<< " tag id=0x" << std::hex << id
<< " qwc=" << tagQwc
<< " addr=0x" << addr
<< " raw=0x" << tag << std::dec << std::endl;
if (id == 0 || id == 1 || id == 2)
{
doCopy(addr, tagQwc);
}
}
}
m_ioRegisters[address] &= ~0x100;
}
}
}
return true;
}
// Interrupt control registers
if (address >= 0x10000200 && address < 0x10000300)
{
std::cout << "Interrupt register write: " << std::hex << address << " = " << value << std::dec << std::endl;
// Handle interrupt register side effects
return true;
}
}
@@ -477,7 +749,7 @@ bool PS2Memory::writeIORegister(uint32_t address, uint32_t value)
{
// GS registers
std::cout << "GS register write: " << std::hex << address << " = " << value << std::dec << std::endl;
// Handle GS register side effects
m_gsWriteCount.fetch_add(1, std::memory_order_relaxed);
return true;
}
@@ -492,7 +764,6 @@ uint32_t PS2Memory::readIORegister(uint32_t address)
return it->second;
}
// Special cases for reads from hardware registers that have side effects
if (address >= 0x10000000 && address < 0x10010000)
{
// Timer registers
@@ -535,7 +806,6 @@ void PS2Memory::registerCodeRegion(uint32_t start, uint32_t end)
region.start = start;
region.end = end;
// Initialize the modified bitmap (one bit per 4-byte word)
size_t sizeInWords = (end - start) / 4;
region.modified.resize(sizeInWords, false);
@@ -560,7 +830,6 @@ void PS2Memory::markModified(uint32_t address, uint32_t size)
uint32_t overlapStart = std::max(address, region.start);
uint32_t overlapEnd = std::min(address + size, region.end);
// Mark each 4-byte word in the overlap as modified
for (uint32_t addr = overlapStart; addr < overlapEnd; addr += 4)
{
size_t bitIndex = (addr - region.start) / 4;
@@ -582,11 +851,9 @@ bool PS2Memory::isCodeModified(uint32_t address, uint32_t size)
continue;
}
// Calculate overlap
uint32_t overlapStart = std::max(address, region.start);
uint32_t overlapEnd = std::min(address + size, region.end);
// Check each 4-byte word in the overlap
for (uint32_t addr = overlapStart; addr < overlapEnd; addr += 4)
{
size_t bitIndex = (addr - region.start) / 4;
@@ -609,11 +876,9 @@ void PS2Memory::clearModifiedFlag(uint32_t address, uint32_t size)
continue;
}
// Calculate overlap
uint32_t overlapStart = std::max(address, region.start);
uint32_t overlapEnd = std::min(address + size, region.end);
// Clear flags for each 4-byte word in the overlap
for (uint32_t addr = overlapStart; addr < overlapEnd; addr += 4)
{
size_t bitIndex = (addr - region.start) / 4;
+514 -13
View File
@@ -1,13 +1,18 @@
#include "ps2_runtime.h"
#include "ps2_syscalls.h"
#include "ps2_runtime_macros.h"
#include <iostream>
#include <fstream>
#include <algorithm>
#include <cstring>
#include <atomic>
#include <thread>
#include <unordered_map>
#include "raylib.h"
// From ps2_syscalls.cpp to help keep the scheduler semaphore sane.
extern std::atomic<int> g_schedulerSemaId;
#define ELF_MAGIC 0x464C457F // "\x7FELF" in little endian
#define ET_EXEC 2 // Executable file
@@ -54,11 +59,190 @@ struct ProgramHeader
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
static constexpr uint32_t DEFAULT_FB_SIZE = FB_WIDTH * FB_HEIGHT * 4;
static void UploadFrame(Texture2D &tex, PS2Runtime *rt)
{
uint8_t *src = rt->memory().getRDRAM() + (DEFAULT_FB_ADDR & 0x1FFFFFFF);
UpdateTexture(tex, src);
// Try to use GS dispfb/display registers to locate the visible buffer.
const GSRegisters &gs = rt->memory().gs();
// DISPFBUF1 fields: FBP (bits 0-8) * 2048 bytes, FBW (bits 10-15) blocks of 64 pixels, PSM (bits 16-20)
uint32_t dispfb = static_cast<uint32_t>(gs.dispfb1 & 0xFFFFFFFFULL);
uint32_t fbp = dispfb & 0x1FF;
uint32_t fbw = (dispfb >> 10) & 0x3F;
uint32_t psm = (dispfb >> 16) & 0x1F;
// DISPLAY1 fields: DX,DY not used here; DW,DH are width/height minus 1 (11 bits each)
uint64_t display64 = gs.display1;
uint32_t dw = static_cast<uint32_t>((display64 >> 23) & 0x7FF);
uint32_t dh = static_cast<uint32_t>((display64 >> 34) & 0x7FF);
// Default to 640x448 if regs look strange.
uint32_t width = (dw + 1);
uint32_t height = (dh + 1);
if (dw == 0)
width = FB_WIDTH;
if (dh == 0)
height = FB_HEIGHT;
if (width > FB_WIDTH)
width = FB_WIDTH;
if (height > FB_HEIGHT)
height = FB_HEIGHT;
static uint64_t prev_dispfb = ~0ull;
static uint64_t prev_display = ~0ull;
static bool vramLogged = false;
if (gs.dispfb1 != prev_dispfb || gs.display1 != prev_display)
{
std::cout << "[GS] dispfb1=0x" << std::hex << gs.dispfb1
<< " display1=0x" << gs.display1 << std::dec << std::endl;
prev_dispfb = gs.dispfb1;
prev_display = gs.display1;
// Allow VRAM peek to re-log when the buffer changes.
vramLogged = false;
}
// Only handle PSMCT32 (0) in this minimal blitter.
if (psm != 0)
{
uint8_t *src = rt->memory().getRDRAM() + (DEFAULT_FB_ADDR & 0x1FFFFFFF);
UpdateTexture(tex, src);
return;
}
constexpr uint32_t DEFAULT_FB_ADDR = 0x00100000;
uint32_t baseBytes = fbp * 2048;
if (fbp == 0)
{
baseBytes = DEFAULT_FB_ADDR;
}
uint32_t strideBytes = (fbw ? fbw : (FB_WIDTH / 64)) * 64 * 4;
uint8_t *rdram = rt->memory().getRDRAM();
uint8_t *gsvram = rt->memory().getGSVRAM();
std::vector<uint8_t> scratch(FB_WIDTH * FB_HEIGHT * 4, 0);
for (uint32_t y = 0; y < height; ++y)
{
uint32_t srcOff = baseBytes + y * strideBytes;
uint32_t dstOff = y * FB_WIDTH * 4;
uint32_t copyW = width * 4;
uint32_t srcIdx = srcOff;
if (!vramLogged)
{
uint32_t sum = 0;
for (int i = 0; i < 32 && (srcIdx + i) < PS2_GS_VRAM_SIZE; ++i)
{
sum += gsvram[srcIdx + i];
}
std::cout << "[VRAM peek] sum first32=0x" << std::hex << sum << std::dec << std::endl;
vramLogged = true;
}
if (srcIdx + copyW <= PS2_GS_VRAM_SIZE && gsvram)
{
std::memcpy(&scratch[dstOff], gsvram + srcIdx, copyW);
}
else
{
uint32_t rdramIdx = srcOff & PS2_RAM_MASK;
if (rdramIdx + copyW > PS2_RAM_SIZE)
copyW = PS2_RAM_SIZE - rdramIdx;
std::memcpy(&scratch[dstOff], rdram + rdramIdx, copyW);
}
}
// Peek first few bytes to see if anything is drawn.
uint32_t peekOff = 0;
uint32_t sum = 0;
for (int i = 0; i < 32; ++i)
{
sum += scratch[peekOff + i];
}
static int peekCount = 0;
if (peekCount < 4)
{
std::cout << "[FB peek] sum first32=0x" << std::hex << sum << std::dec
<< " w=" << width << " h=" << height << std::endl;
++peekCount;
}
UpdateTexture(tex, scratch.data());
}
static void DumpFramebufferSample(PS2Memory &mem)
{
uint32_t base = DEFAULT_FB_ADDR & 0x1FFFFFFF;
uint8_t *ptr = mem.getRDRAM() + base;
uint32_t sum = 0;
for (int i = 0; i < 32; ++i)
{
sum += ptr[i];
}
std::cout << "[FB] addr=0x" << std::hex << DEFAULT_FB_ADDR << " first32 sum=0x" << sum << std::dec
<< " bytes:";
for (int i = 0; i < 16; ++i)
{
std::cout << " " << (int)ptr[i];
}
std::cout << std::dec << std::endl;
// Also dump the thread param block used in InitThread (0x363610 area)
uint32_t tparam = 0x363600 & PS2_RAM_MASK;
uint32_t *tp = reinterpret_cast<uint32_t *>(mem.getRDRAM() + tparam);
std::cout << "[InitThread params] @0x363600: "
<< std::hex << tp[0] << " " << tp[1] << " " << tp[2] << " " << tp[3]
<< " " << tp[4] << " " << tp[5] << " " << tp[6] << std::dec << std::endl;
}
// Trace hook for a suspected thread entry to see what it does.
static PS2Runtime::RecompiledFunction g_entry_10c920 = nullptr;
static void entry_10c920_traced(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
std::cout << "[trace] enter 0x10c920 sp=0x" << std::hex << ctx->r[29].m128i_u32[0]
<< " gp=0x" << ctx->r[28].m128i_u32[0]
<< " ra=0x" << ctx->r[31].m128i_u32[0] << std::dec << std::endl;
if (g_entry_10c920)
{
g_entry_10c920(rdram, ctx, runtime);
}
std::cout << "[trace] exit 0x10c920 pc=0x" << std::hex << ctx->pc
<< " ra=0x" << ctx->r[31].m128i_u32[0] << std::dec << std::endl;
}
// Hook for sceGsPutDispEnv to capture display buffer setup.
static PS2Runtime::RecompiledFunction g_putDispEnv = nullptr;
static void sceGsPutDispEnv_hook(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t envPtr = GPR_U32(ctx, 4);
const uint8_t *base = runtime->memory().getRDRAM();
constexpr uint32_t MASK = PS2_RAM_SIZE - 1;
const uint64_t *env = reinterpret_cast<const uint64_t *>(base + (envPtr & MASK));
if (env)
{
auto &gs = runtime->memory().gs();
gs.dispfb1 = env[0];
gs.display1 = env[1];
std::cout << "[hook] sceGsPutDispEnv @0x" << std::hex << envPtr
<< " dispfb1=0x" << env[0] << " display1=0x" << env[1] << std::dec << std::endl;
}
if (g_putDispEnv)
{
g_putDispEnv(rdram, ctx, runtime);
}
}
// Hook for sceSifCallRpc to keep IOP RPC loops from stalling the main thread.
static PS2Runtime::RecompiledFunction g_sceSifCallRpc = nullptr;
static void sceSifCallRpc_stub(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
static int logCount = 0;
if (logCount++ < 5)
{
std::cout << "[stub] sceSifCallRpc fno=0x" << std::hex << GPR_U32(ctx, 5)
<< " mode=0x" << GPR_U32(ctx, 6)
<< " send=0x" << GPR_U32(ctx, 7)
<< " recv=0x" << GPR_U32(ctx, 8) << std::dec << std::endl;
}
SET_GPR_S32(ctx, 2, 0);
}
PS2Runtime::PS2Runtime()
@@ -176,6 +360,24 @@ bool PS2Runtime::loadELF(const std::string &elfPath)
m_loadedModules.push_back(module);
// acccordind to GPT some titles expect cmd_sem_init to see -1 sentinels before creating semaphores.
const uint32_t semaInitAddrs[] = {0x00302c90u, 0x00302c94u, 0x00302c98u, 0x00302c9cu};
bool seeded = false;
for (uint32_t addr : semaInitAddrs)
{
uint32_t physAddr = m_memory.translateAddress(addr);
uint32_t *p = reinterpret_cast<uint32_t *>(m_memory.getRDRAM() + physAddr);
if (*p == 0)
{
*p = 0xFFFFFFFFu;
seeded = true;
}
}
if (seeded)
{
std::cout << "[init] Seeded cmd_sem_init sema IDs to -1" << std::endl;
}
// Debug: peek at some early globals to verify init state
uint32_t dbg_addr = 0x00300000 + 11240;
uint8_t *dbg_base = m_memory.getRDRAM();
@@ -191,6 +393,11 @@ void PS2Runtime::registerFunction(uint32_t address, RecompiledFunction func)
m_functionTable[address] = func;
}
bool PS2Runtime::hasFunction(uint32_t address) const
{
return m_functionTable.find(address) != m_functionTable.end();
}
PS2Runtime::RecompiledFunction PS2Runtime::lookupFunction(uint32_t address)
{
auto it = m_functionTable.find(address);
@@ -218,21 +425,81 @@ void PS2Runtime::SignalException(R5900Context *ctx, PS2Exception exception)
}
}
static PS2Runtime::RecompiledFunction g_entry_10cb00 = nullptr;
static void entry_10cb00_hook(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
static int logCount = 0;
if (logCount < 16)
{
std::cout << "[cmdq] enqueue cmd=0 tid=" << GPR_U32(ctx, 16)
<< " a0=0x" << std::hex << GPR_U32(ctx, 4) << std::dec << std::endl;
++logCount;
}
if (g_entry_10cb00)
{
g_entry_10cb00(rdram, ctx, runtime);
}
}
static PS2Runtime::RecompiledFunction g_entry_10cb98 = nullptr;
static void entry_10cb98_hook(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
static int logCount = 0;
if (logCount < 16)
{
std::cout << "[cmdq] enqueue cmd=1 tid=" << GPR_U32(ctx, 16)
<< " a0=0x" << std::hex << GPR_U32(ctx, 4) << std::dec << std::endl;
++logCount;
}
if (g_entry_10cb98)
{
g_entry_10cb98(rdram, ctx, runtime);
}
}
static PS2Runtime::RecompiledFunction g_entry_10cc34 = nullptr;
static void entry_10cc34_hook(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
static int logCount = 0;
if (logCount < 16)
{
std::cout << "[cmdq] enqueue cmd=2 tid=" << GPR_U32(ctx, 16)
<< " a0=0x" << std::hex << GPR_U32(ctx, 4) << std::dec << std::endl;
++logCount;
}
if (g_entry_10cc34)
{
g_entry_10cc34(rdram, ctx, runtime);
}
}
void PS2Runtime::executeVU0Microprogram(uint8_t *rdram, R5900Context *ctx, uint32_t address)
{
std::cout << "VU0 microprogram call to address 0x" << std::hex << address
<< " - not implemented" << std::dec << std::endl;
static std::unordered_map<uint32_t, int> seen;
int &count = seen[address];
if (count < 3)
{
std::cout << "[VU0] microprogram @0x" << std::hex << address
<< " pc=0x" << ctx->pc
<< " ra=0x" << ctx->r[31].m128i_u32[0]
<< std::dec << std::endl;
}
++count;
// mayve implement like this or a vu0_interpreter
// Placeholder for VU0 microprogram execution
// auto microprog = findCompiledMicroprogram(address);
// if (microprog) microprog(rdram, ctx);
// Clear/seed status so dependent code sees "success".
ctx->vu0_clip_flags = 0;
ctx->vu0_clip_flags2 = 0;
ctx->vu0_mac_flags = 0;
ctx->vu0_status = 0;
ctx->vu0_q = 1.0f;
// TODO: Implement a real interpreter. For now, no register mutations beyond defaults.
}
void PS2Runtime::vu0StartMicroProgram(uint8_t *rdram, R5900Context *ctx, uint32_t address)
{
std::cout << "VU0 microprogram call to address 0x" << std::hex << address
<< " - not implemented" << std::dec << std::endl;
// VCALLMS/VCALLMSR paths both end up here; reuse the same minimal stub.
executeVU0Microprogram(rdram, ctx, address);
}
void PS2Runtime::handleSyscall(uint8_t *rdram, R5900Context *ctx)
@@ -305,8 +572,118 @@ void PS2Runtime::run()
Texture2D frameTex = LoadTextureFromImage(blank);
UnloadImage(blank);
if (hasFunction(0x10c920))
{
g_entry_10c920 = lookupFunction(0x10c920);
registerFunction(0x10c920, entry_10c920_traced);
std::cout << "[trace] hooked entry 0x10c920 for logging" << std::endl;
}
else
{
std::cout << "[trace] entry 0x10c920 not registered" << std::endl;
}
if (hasFunction(0x1004b8))
{
g_putDispEnv = lookupFunction(0x1004b8);
registerFunction(0x1004b8, sceGsPutDispEnv_hook);
std::cout << "[hook] wrapped sceGsPutDispEnv at 0x1004b8" << std::endl;
}
if (hasFunction(0x10ed80))
{
g_sceSifCallRpc = lookupFunction(0x10ed80);
registerFunction(0x10ed80, sceSifCallRpc_stub);
std::cout << "[hook] wrapped sceSifCallRpc at 0x10ed80" << std::endl;
}
if (hasFunction(0x10cb00))
{
g_entry_10cb00 = lookupFunction(0x10cb00);
registerFunction(0x10cb00, entry_10cb00_hook);
std::cout << "[hook] wrapped cmd queue (cmd=0) at 0x10cb00" << std::endl;
}
if (hasFunction(0x10cb98))
{
g_entry_10cb98 = lookupFunction(0x10cb98);
registerFunction(0x10cb98, entry_10cb98_hook);
std::cout << "[hook] wrapped cmd queue (cmd=1) at 0x10cb98" << std::endl;
}
if (hasFunction(0x10cc34))
{
g_entry_10cc34 = lookupFunction(0x10cc34);
registerFunction(0x10cc34, entry_10cc34_hook);
std::cout << "[hook] wrapped cmd queue (cmd=2) at 0x10cc34" << std::endl;
}
g_activeThreads.store(1, std::memory_order_relaxed);
// for now if the scheduler sema hasn't been created yet, force InitThread to set it up.
if (hasFunction(0x10c9f8))
{
uint32_t *sched = reinterpret_cast<uint32_t *>(m_memory.getRDRAM() + (0x363a10 & PS2_RAM_MASK));
if (!sched || sched[0] == 0)
{
RecompiledFunction initThread = lookupFunction(0x10c9f8);
R5900Context initCtx{};
std::memset(&initCtx, 0, sizeof(initCtx));
initCtx.r[0] = _mm_set1_epi32(0);
initCtx.r[29] = _mm_set1_epi32(0x02000000);
initCtx.r[28] = _mm_set1_epi32(0x36a7f0);
initCtx.pc = 0x10c9f8;
std::cout << "[autorun] running InitThread pc=0x10c9f8" << std::endl;
initThread(m_memory.getRDRAM(), &initCtx, this);
}
}
// Fallback: if the game's main entry (ps2_main at 0x12b0a0) is registered, start it on a separate host thread.
// The normal bootstrap thread seems to stall before spawning it, so we kick it off manually.
if (hasFunction(0x12b0a0))
{
RecompiledFunction ps2Main = lookupFunction(0x12b0a0);
g_activeThreads.fetch_add(1, std::memory_order_relaxed);
std::thread([=]() mutable
{
R5900Context localCtx{};
std::memset(&localCtx, 0, sizeof(localCtx));
// Set baseline registers similar to the primary thread.
localCtx.r[0] = _mm_set1_epi32(0);
localCtx.r[29] = _mm_set1_epi32(0x02000000); // SP top of RAM
localCtx.r[28] = _mm_set1_epi32(0x36a7f0); // GP from ELF bootstrap
localCtx.pc = 0x12b0a0;
std::cout << "[autorun] starting ps2_main fallback pc=0x12b0a0 sp=0x02000000 gp=0x36a7f0" << std::endl;
try
{
ps2Main(m_memory.getRDRAM(), &localCtx, this);
std::cout << "[autorun] ps2_main returned pc=0x" << std::hex << localCtx.pc
<< " ra=0x" << localCtx.r[31].m128i_u32[0] << std::dec << std::endl;
}
catch (const std::exception &e)
{
std::cerr << "[autorun] ps2_main exception: " << e.what() << std::endl;
}
g_activeThreads.fetch_sub(1, std::memory_order_relaxed); })
.detach();
}
else
{
std::cout << "[autorun] ps2_main not registered; skipping fallback launch" << std::endl;
}
// Dump a small sample
{
uint32_t base = DEFAULT_FB_ADDR & 0x1FFFFFFF;
uint8_t *ptr = m_memory.getRDRAM() + base;
uint32_t sum = 0;
for (int i = 0; i < 32; ++i)
sum += ptr[i];
std::cout << "[FB] addr=0x" << std::hex << DEFAULT_FB_ADDR
<< " first32 sum=0x" << sum << " bytes:";
for (int i = 0; i < 16; ++i)
{
std::cout << " " << (int)ptr[i];
}
std::cout << std::dec << std::endl;
}
std::thread gameThread([&, entryPoint]()
{
try
@@ -321,12 +698,137 @@ void PS2Runtime::run()
}
g_activeThreads.fetch_sub(1, std::memory_order_relaxed); });
static uint32_t lastSchedId = 0;
uint64_t tick = 0;
while (g_activeThreads.load(std::memory_order_relaxed) > 0)
{
{
uint32_t *sched = reinterpret_cast<uint32_t *>(m_memory.getRDRAM() + (0x363a10 & PS2_RAM_MASK));
int known = g_schedulerSemaId.load(std::memory_order_relaxed);
if (sched)
{
if ((sched[0] == 0 || sched[0] > 1000) && known > 0)
sched[0] = static_cast<uint32_t>(known);
// head/tail indices
if (sched[2] > 511)
sched[2] = 0;
if (sched[3] > 511)
sched[3] = 0;
}
}
{
constexpr uint32_t kSchedSpan = 0x420;
static std::vector<uint8_t> schedSnapshot;
static int schedDeltaLogs = 0;
uint8_t *rdram = m_memory.getRDRAM();
uint32_t base = 0x363a10 & PS2_RAM_MASK;
if (schedSnapshot.empty())
{
schedSnapshot.resize(kSchedSpan);
std::memcpy(schedSnapshot.data(), rdram + base, kSchedSpan);
}
else
{
int diffCount = 0;
int detailCount = 0;
for (uint32_t i = 0; i < kSchedSpan; ++i)
{
uint8_t cur = rdram[(base + i) & PS2_RAM_MASK];
uint8_t prev = schedSnapshot[i];
if (cur != prev)
{
schedSnapshot[i] = cur;
++diffCount;
if (schedDeltaLogs < 32 && detailCount < 8)
{
std::cout << "[sched delta] off=0x" << std::hex << i
<< " " << (int)prev << "->" << (int)cur << std::dec << std::endl;
++detailCount;
}
}
}
if (diffCount > 0 && schedDeltaLogs < 32)
{
std::cout << "[sched delta] changed=" << diffCount << std::endl;
++schedDeltaLogs;
}
}
}
if ((tick++ % 120) == 0)
{
std::cout << "[run] activeThreads=" << g_activeThreads.load(std::memory_order_relaxed) << std::endl;
std::cout << "[run] activeThreads=" << g_activeThreads.load(std::memory_order_relaxed);
std::cout << " pc=0x" << std::hex << m_cpuContext.pc
<< " ra=0x" << m_cpuContext.r[31].m128i_u32[0]
<< " sp=0x" << m_cpuContext.r[29].m128i_u32[0]
<< " gp=0x" << m_cpuContext.r[28].m128i_u32[0] << std::dec << std::endl;
}
if ((tick % 600) == 0)
{
static int schedLog = 0;
if (schedLog < 5)
{
uint8_t *rdram = m_memory.getRDRAM();
uint32_t base = 0x363a10 & PS2_RAM_MASK;
uint32_t *p = reinterpret_cast<uint32_t *>(rdram + base);
uint32_t argPtr = 0x363a18 & PS2_RAM_MASK;
uint32_t idx = *reinterpret_cast<uint32_t *>(rdram + argPtr) & 0x1FF;
uint32_t cmdBase = (argPtr + 8) & PS2_RAM_MASK;
uint32_t tidBase = (argPtr + 9) & PS2_RAM_MASK;
uint8_t cmd0 = rdram[cmdBase & PS2_RAM_MASK];
uint8_t tid0 = rdram[tidBase & PS2_RAM_MASK];
uint8_t cmdIdx = rdram[(cmdBase + (idx << 1)) & PS2_RAM_MASK];
uint8_t tidIdx = rdram[(tidBase + (idx << 1)) & PS2_RAM_MASK];
std::cout << "[sched] sema=" << p[0] << " q1=" << p[1] << " head=" << p[2] << " tail=" << p[3]
<< " cmd0=" << (int)cmd0 << "/" << (int)tid0
<< " cmd[idx=" << idx << "]=" << (int)cmdIdx << "/" << (int)tidIdx
<< " lastId=" << lastSchedId << std::endl;
++schedLog;
}
static uint64_t lastDma = 0, lastGif = 0, lastGs = 0, lastVif = 0;
uint64_t curDma = m_memory.dmaStartCount();
uint64_t curGif = m_memory.gifCopyCount();
uint64_t curGs = m_memory.gsWriteCount();
uint64_t curVif = m_memory.vifWriteCount();
if (curDma != lastDma || curGif != lastGif || curGs != lastGs || curVif != lastVif)
{
std::cout << "[hw] dma_starts=" << curDma
<< " gif_copies=" << curGif
<< " gs_writes=" << curGs
<< " vif_writes=" << curVif << std::endl;
lastDma = curDma;
lastGif = curGif;
lastGs = curGs;
lastVif = curVif;
}
}
// Kick the scheduler semaphore (stored at 0x363a10) to simulate VBlank-style ticks.
{
uint32_t schedId = *reinterpret_cast<uint32_t *>(m_memory.getRDRAM() + (0x363a10 & PS2_RAM_MASK));
if (schedId && schedId < 0x1000)
{
lastSchedId = schedId;
}
if (schedId == 0 && lastSchedId != 0)
{
schedId = lastSchedId; // fall back to the last seen non-zero id
}
else if (schedId >= 0x1000 && lastSchedId != 0)
{
// Ignore obviously bogus ids that are likely other data scribbling over the struct.
schedId = lastSchedId;
}
if (schedId)
{
R5900Context semaCtx{};
R5900Context *semaCtxPtr = &semaCtx;
SET_GPR_U32(semaCtxPtr, 4, schedId);
ps2_syscalls::SignalSema(m_memory.getRDRAM(), semaCtxPtr, this);
}
}
UploadFrame(frameTex, this);
@@ -344,7 +846,6 @@ void PS2Runtime::run()
if (g_activeThreads.load(std::memory_order_relaxed) == 0)
{
// Game thread finished on its own
if (gameThread.joinable())
{
gameThread.join();
@@ -352,7 +853,7 @@ void PS2Runtime::run()
}
else
{
// Window was closed while the game thread is still running
if (gameThread.joinable())
{
gameThread.detach();
File diff suppressed because it is too large Load Diff