diff --git a/CMakeLists.txt b/CMakeLists.txt index 058256da6c..e328369216 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ endif () set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) if (CMAKE_SYSTEM_NAME STREQUAL Linux) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unknown-pragmas -Wno-unused-variable -Wno-unused-parameter") @@ -26,6 +27,7 @@ elseif (MSVC) add_compile_options(/Zc:strictStrings-) add_compile_options(/MP) add_compile_options(/W0) + add_compile_options(/std:c++20) endif () if (NOT MSVC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error -Wno-c++11-narrowing") diff --git a/extern/aurora b/extern/aurora index 223bcf39b8..928834269d 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit 223bcf39b806f52021263d86ebe11eeffa7007f1 +Subproject commit 928834269d989109f2726fa61c66c07fc49e6475 diff --git a/include/JSystem/J3DGraphBase/J3DFifo.h b/include/JSystem/J3DGraphBase/J3DFifo.h index c93943864c..a1cd2ff5b8 100644 --- a/include/JSystem/J3DGraphBase/J3DFifo.h +++ b/include/JSystem/J3DGraphBase/J3DFifo.h @@ -16,15 +16,27 @@ inline void J3DFifoWriteXFCmdHdr(u16 addr, u8 len) { } inline void J3DFifoLoadIndx(u8 cmd, u16 indx, u16 addr) { +#ifdef TARGET_PC + GXCmd1u8(cmd); + GXCmd1u16(indx); + GXCmd1u16(addr); +#else GXWGFifo.u8 = cmd; GXWGFifo.u16 = indx; GXWGFifo.u16 = addr; +#endif } inline void J3DFifoWriteCPCmd(u8 cmd, u32 param) { +#ifdef TARGET_PC + GXCmd1u8(GX_LOAD_CP_REG); + GXCmd1u8(cmd); + GXCmd1u32(param); +#else GXWGFifo.u8 = GX_LOAD_CP_REG; GXWGFifo.u8 = cmd; GXWGFifo.u32 = param; +#endif } inline void J3DFifoLoadCPCmd(u8 reg, u32 value) { @@ -34,9 +46,15 @@ inline void J3DFifoLoadCPCmd(u8 reg, u32 value) { } inline void J3DFifoWriteXFCmd(u16 cmd, u16 len) { +#ifdef TARGET_PC + GXCmd1u8(GX_LOAD_XF_REG); + GXCmd1u16(len - 1); + GXCmd1u16(cmd); +#else GXWGFifo.u8 = GX_LOAD_XF_REG; GXWGFifo.u16 = (len - 1); GXWGFifo.u16 = cmd; +#endif } inline void J3DFifoLoadXFCmdHdr(u16 addr, u8 len) { diff --git a/include/JSystem/J3DU/J3DUClipper.h b/include/JSystem/J3DU/J3DUClipper.h index c8417b2a69..af5d655f12 100644 --- a/include/JSystem/J3DU/J3DUClipper.h +++ b/include/JSystem/J3DU/J3DUClipper.h @@ -18,8 +18,8 @@ public: void setFovy(f32 fovy) { mFovY = fovy; } void setAspect(f32 aspect) { mAspect = aspect; } - void setNear(f32 near) { mNear = near; } - void setFar(f32 far) { mFar = far; } + void setNear(f32 near_) { mNear = near_; } + void setFar(f32 far_) { mFar = far_; } f32 getFar() { return mFar; } diff --git a/include/dolphin/gx/GXStruct.h b/include/dolphin/gx/GXStruct.h index faba188d24..d460f76d55 100644 --- a/include/dolphin/gx/GXStruct.h +++ b/include/dolphin/gx/GXStruct.h @@ -36,7 +36,11 @@ typedef struct _GXColorS10 { } GXColorS10; typedef struct _GXTexObj { +#ifdef TARGET_PC + u32 dummy[22]; // Aurora's GXTexObj_ contains std::shared_ptr + many fields (~80 bytes) +#else u32 dummy[8]; +#endif } GXTexObj; typedef struct _GXLightObj { @@ -48,7 +52,11 @@ typedef struct _GXTexRegion { } GXTexRegion; typedef struct _GXTlutObj { +#ifdef TARGET_PC + u32 dummy[4]; // Aurora's GXTlutObj_ contains std::shared_ptr (8+ bytes) +#else u32 dummy[3]; +#endif } GXTlutObj; typedef struct _GXTlutRegion { diff --git a/include/dolphin/gx/GXVert.h b/include/dolphin/gx/GXVert.h index 374b87af6f..ddf01179bf 100644 --- a/include/dolphin/gx/GXVert.h +++ b/include/dolphin/gx/GXVert.h @@ -4,9 +4,45 @@ #ifdef __REVOLUTION_SDK__ #include #elif defined(TARGET_PC) -// On PC, use Aurora's GXVert declarations (extern functions implemented in -// GXVert.cpp, no hardware FIFO writes to 0xCC008000) +// On PC, include Aurora's GXVert for GXPosition/GXNormal/GXColor/GXTexCoord/GXEnd +// (extern functions implemented in Aurora's GXVert.cpp, stream-based vertex buffers) #include "../../../extern/aurora/include/dolphin/gx/GXVert.h" + +// Aurora's GXVert.h does not provide GXCmd, GXParam, GXMatrixIndex, or a valid +// GXWGFifo target. J3D code uses these for low-level display list writes. +// We declare them as extern (implemented in stubs.cpp) and provide a dummy +// GXWGFifo that writes into a throw-away buffer so direct FIFO writes don't crash. + +// Replace Aurora's GXWGFifo macro (pointing to 0xCC008000) with an extern variable +#undef GXWGFifo +#ifdef __cplusplus +extern "C" { +#endif + +// PPCWGPipe is already typedef'd by Aurora's GXVert.h above. +// Dummy FIFO sink: direct GXWGFifo writes in J3DFifo.h land here harmlessly. +extern volatile PPCWGPipe GXWGFifo; + +void GXCmd1u8(const u8 x); +void GXCmd1u16(const u16 x); +void GXCmd1u32(const u32 x); + +void GXParam1u8(const u8 x); +void GXParam1u16(const u16 x); +void GXParam1u32(const u32 x); +void GXParam1s8(const s8 x); +void GXParam1s16(const s16 x); +void GXParam1s32(const s32 x); +void GXParam1f32(const f32 x); +void GXParam3f32(const f32 x, const f32 y, const f32 z); +void GXParam4f32(const f32 x, const f32 y, const f32 z, const f32 w); + +void GXMatrixIndex1u8(const u8 x); + +#ifdef __cplusplus +} +#endif + #else #include #include diff --git a/src/DynamicLink.cpp b/src/DynamicLink.cpp index c05fa23458..83316581e5 100644 --- a/src/DynamicLink.cpp +++ b/src/DynamicLink.cpp @@ -184,8 +184,10 @@ static u32 calcSum2(u16 const* data, u32 size) { bool DynamicModuleControl::do_load() { if (mModule != NULL) { + printf("[DIAG] DynamicModuleControl::do_load(%s) already loaded\n", mName); fflush(stdout); return true; } + printf("[DIAG] DynamicModuleControl::do_load(%s) loading... sArchive=%p sFileCache=%p\n", mName, sArchive, sFileCache); fflush(stdout); JKRExpHeap* heap = mDoExt_getArchiveHeap(); s32 i = 0; while (true) { @@ -278,6 +280,7 @@ bool DynamicModuleControl::do_load() { break; } } + printf("[DIAG] DynamicModuleControl::do_load(%s) SUCCESS mModule=%p type=%d size=%d\n", mName, mModule, mResourceType, mSize); fflush(stdout); return true; } diff --git a/src/JSystem/JFramework/JFWDisplay.cpp b/src/JSystem/JFramework/JFWDisplay.cpp index eb40c484e4..7d129c4d45 100644 --- a/src/JSystem/JFramework/JFWDisplay.cpp +++ b/src/JSystem/JFramework/JFWDisplay.cpp @@ -520,8 +520,13 @@ static void JFWDrawDoneAlarm() { static void JFWGXAbortAlarmHandler(OSAlarm* param_0, OSContext* param_1) { diagnoseGpHang(); GXAbortFrame(); +#ifdef TARGET_PC + GXCmd1u8(0x61); + GXCmd1u32(0x5800000F); +#else GXWGFifo.u8 = 0x61; GXWGFifo.u32 = 0x5800000F; +#endif GXFifoObj* fifo = GXGetCPUFifo(); if (fifo != NULL) { diff --git a/src/c/c_dylink.cpp b/src/c/c_dylink.cpp index 27113d7a2a..37be56f20b 100644 --- a/src/c/c_dylink.cpp +++ b/src/c/c_dylink.cpp @@ -2,6 +2,8 @@ * c_dylink.cpp * REL to process name definitions and REL init functions */ +#include + #include "c/c_dylink.h" #include "DynamicLink.h" #include "JSystem/JKernel/JKRExpHeap.h" @@ -912,6 +914,7 @@ int cDyl_LinkASync(s16 i_ProfName) { JUT_ASSERT(266, DMC_initialized); if (!cDyl_Initialized) { + printf("[DIAG] cDyl_LinkASync: NOT initialized yet, profName=%d\n", i_ProfName); fflush(stdout); OS_REPORT_ERROR("初期化が終わってないのに呼んでもらっても困ります %d %s\n", i_ProfName, fpcDbSv_getNameString(i_ProfName)); return cPhs_INIT_e; } @@ -932,10 +935,12 @@ int cDyl_LinkASync(s16 i_ProfName) { return cPhs_COMPLEATE_e; } else { // "cDyl_LinkASync: Link failed. Returning\n" + printf("[DIAG] cDyl_LinkASync: link FAILED for profName=%d\n", i_ProfName); fflush(stdout); OSReport_Error("cDyl_LinkASync: リンクに失敗しました。諦めます\n"); return cPhs_ERROR_e; } } else { + printf("[DIAG] cDyl_LinkASync: load_async not ready for profName=%d\n", i_ProfName); fflush(stdout); return cPhs_INIT_e; } } @@ -944,16 +949,29 @@ int cDyl_LinkASync(s16 i_ProfName) { } static int cDyl_InitCallback(void* param_0) { + printf("[DIAG] cDyl_InitCallback: START\n"); fflush(stdout); JUT_ASSERT(335, !cDyl_Initialized); +#ifdef TARGET_PC + // On PC, the profile list is statically linked (g_fpcPf_ProfileList_p in f_pc_profile.cpp). + // Skip DVD-based REL loading and string table — OSLink/OSLinkFixed are stubs. + cDyl_Initialized = true; + fopScnM_CreateReq(PROC_LOGO_SCENE, 0x7FFF, 0, 0); + printf("[DIAG] cDyl_InitCallback: PROC_LOGO_SCENE created (PC path), DONE\n"); fflush(stdout); + return 1; +#else #if PLATFORM_GCN JKRHeap* parentHeap = mDoExt_getArchiveHeap(); #else JKRHeap* parentHeap = DynamicModuleControlBase::getHeap(); #endif + printf("[DIAG] cDyl_InitCallback: parentHeap=%p\n", parentHeap); fflush(stdout); JKRFileCache* loader = JKRMountDvdDrive("/", parentHeap, NULL); + printf("[DIAG] cDyl_InitCallback: JKRMountDvdDrive loader=%p\n", loader); fflush(stdout); + DynamicModuleControl::initialize(); + printf("[DIAG] cDyl_InitCallback: DynamicModuleControl::initialize done\n"); fflush(stdout); #if PLATFORM_GCN void* strTbl = JKRGetResource("/dvd/str/Final/Release/frameworkF.str"); @@ -962,17 +980,22 @@ static int cDyl_InitCallback(void* param_0) { #else void* strTbl = JKRGetResource("/dvd/str/Final/Release/frameworkF.str"); #endif + printf("[DIAG] cDyl_InitCallback: frameworkF.str=%p\n", strTbl); fflush(stdout); JKRDetachResource(strTbl, loader); JKRUnmountDvdDrive(loader); OSSetStringTable(strTbl); DynamicModuleControl dmc("f_pc_profile_lst"); + printf("[DIAG] cDyl_InitCallback: linking f_pc_profile_lst...\n"); fflush(stdout); dmc.link(); + printf("[DIAG] cDyl_InitCallback: link done\n"); fflush(stdout); cDyl_Initialized = true; fopScnM_CreateReq(PROC_LOGO_SCENE, 0x7FFF, 0, 0); + printf("[DIAG] cDyl_InitCallback: PROC_LOGO_SCENE created, DONE\n"); fflush(stdout); return 1; +#endif } static mDoDvdThd_callback_c* cDyl_DVD; diff --git a/src/d/d_s_logo.cpp b/src/d/d_s_logo.cpp index e905222717..46dbcc8a0f 100644 --- a/src/d/d_s_logo.cpp +++ b/src/d/d_s_logo.cpp @@ -3,6 +3,8 @@ * Game Boot Logo's Display */ +#include + #include "d/dolzel.h" // IWYU pragma: keep #include "d/d_s_logo.h" @@ -589,11 +591,21 @@ static int resLoad(request_of_phase_process_class* i_phase, dScnLogo_c* i_this) } int dScnLogo_c::create() { + static bool sDiagLogged = false; + if (!sDiagLogged) { + printf("[DIAG] dScnLogo_c::create START\n"); fflush(stdout); + } int phase_state = resLoad(&field_0x1c4, this); + if (!sDiagLogged) { + printf("[DIAG] dScnLogo_c::create resLoad=%d (need %d for complete)\n", phase_state, cPhs_COMPLEATE_e); fflush(stdout); + sDiagLogged = true; + } if (phase_state != cPhs_COMPLEATE_e) { return phase_state; } + printf("[DIAG] dScnLogo_c::create resLoad COMPLETE, continuing init...\n"); fflush(stdout); + #if PLATFORM_WII data_8053a730 = 1; #endif @@ -853,6 +865,7 @@ void dScnLogo_c::dvdDataLoad() { } static int dScnLogo_Create(scene_class* i_this) { + printf("[DIAG] dScnLogo_Create: entry i_this=%p\n", i_this); fflush(stdout); return (new (i_this) dScnLogo_c())->create(); } diff --git a/src/dolphin/gx/GXMisc.c b/src/dolphin/gx/GXMisc.c index 1200461d79..a3bd8c6cfb 100644 --- a/src/dolphin/gx/GXMisc.c +++ b/src/dolphin/gx/GXMisc.c @@ -135,10 +135,16 @@ void GXSetDrawDone(void) { CHECK_GXBEGIN(488, "GXSetDrawDone"); enabled = OSDisableInterrupts(); +#ifdef TARGET_PC + // On PC there is no GX GPU, so draw is always immediately done. + // Without the hardware finish interrupt, GXWaitDrawDone would deadlock. + DrawDone = 1; +#else reg = 0x45000002; GX_WRITE_RAS_REG(reg); GXFlush(); DrawDone = 0; +#endif OSRestoreInterrupts(enabled); } @@ -147,6 +153,12 @@ void GXWaitDrawDone(void) { CHECK_GXBEGIN(534, "GXWaitDrawDone"); +#ifdef TARGET_PC + // On PC there is no GX hardware — draw is always done immediately. + DrawDone = 1; + return; +#endif + enabled = OSDisableInterrupts(); while (!DrawDone) { OSSleepThread(&FinishQueue); @@ -156,6 +168,11 @@ void GXWaitDrawDone(void) { void GXDrawDone(void) { CHECK_GXBEGIN(566, "GXDrawDone"); +#ifdef TARGET_PC + // On PC, no GPU to wait for — return immediately. + DrawDone = 1; + return; +#endif GXSetDrawDone(); GXWaitDrawDone(); } diff --git a/src/dolphin/os/OSThread.cpp b/src/dolphin/os/OSThread.cpp index 74fffaa4e5..96ef2bcfff 100644 --- a/src/dolphin/os/OSThread.cpp +++ b/src/dolphin/os/OSThread.cpp @@ -313,7 +313,7 @@ int OSCreateThread(OSThread* thread, void* (*func)(void*), void* param, // ============================================================================ // Resume / Suspend // ============================================================================ - +/* s32 OSResumeThread(OSThread* thread) { if (!thread) return 0; @@ -365,6 +365,103 @@ s32 OSSuspendThread(OSThread* thread) { return prevSuspend; } +*/ + +// ============================================================================ +// Resume / Suspend +// ============================================================================ + +s32 OSResumeThread(OSThread* thread) { + if (!thread) + return 0; + + s32 prevSuspend = thread->suspend; + if (thread->suspend > 0) { + thread->suspend--; + } + + // Only wake up if suspend count drops to 0 + if (thread->suspend == 0) { + PCThreadData* data = nullptr; + + // Lock the global map to safely retrieve our thread data pointer + { + std::lock_guard mapLock(GetThreadDataMutex()); + auto it = GetThreadDataMap().find(thread); + if (it != GetThreadDataMap().end()) { + data = it->second.get(); + } + } + + if (data) { + // Lock the specific thread mutex to safely modify state and notify + std::unique_lock threadLock(data->mtx); + + if (!data->started) { + // First resume: launch the native thread + data->started = true; + data->suspended = false; + + // Unlock before launching to avoid potential deadlocks in thread initialization + threadLock.unlock(); + + data->nativeThread = std::thread(ThreadEntryWrapper, thread, data); + data->nativeThread.detach(); + OSReport("[PC-OSThread] Started thread %p\n", thread); + } else { + // Resume from suspension: signal the condition variable + // IMPORTANT: Set suspended to false BEFORE notifying to pass the wait predicate + data->suspended = false; + data->cv.notify_all(); + } + } + } + + return prevSuspend; +} + +s32 OSSuspendThread(OSThread* thread) { + if (!thread) + return 0; + + s32 prevSuspend = thread->suspend; + thread->suspend++; + + // If transitioning from running (0) to suspended (1) + if (prevSuspend == 0) { + PCThreadData* data = nullptr; + + // Lock the global map to find our thread data + { + std::lock_guard mapLock(GetThreadDataMutex()); + auto it = GetThreadDataMap().find(thread); + if (it != GetThreadDataMap().end()) { + data = it->second.get(); + } + } + + if (data && data->started) { + std::unique_lock threadLock(data->mtx); + data->suspended = true; + + // FIX: If the thread is suspending ITSELF, we must block execution here. + // This replicates the GameCube behavior where OSSuspendThread yields the CPU + // immediately. + if (thread == OSGetCurrentThread()) { + // Block until 'suspended' becomes false (set by OSResumeThread) + // The predicate protects against spurious wakeups. + data->cv.wait(threadLock, [data] { return !data->suspended; }); + } else { + // NOTE: Suspending *other* threads is difficult in C++ std::thread + // without cooperative checkpoints or platform-specific hacks. + // For now, we only set the flag. The target thread would need to check 'suspended' + // periodically. + } + } + } + + return prevSuspend; +} // ============================================================================ // Sleep / Wakeup (thread queue based) diff --git a/src/dusk/dvd_emu.cpp b/src/dusk/dvd_emu.cpp index 67a541c62d..6c886a4372 100644 --- a/src/dusk/dvd_emu.cpp +++ b/src/dusk/dvd_emu.cpp @@ -51,7 +51,7 @@ void setBasePath(const char* path) { g_basePath() = path; #endif - OSReport("[DvdEmu] Base path set to: %s\n", g_basePath().c_str()); + printf("[DvdEmu] Base path set to: %s\n", g_basePath().c_str()); fflush(stdout); } const char* getBasePath() { @@ -93,9 +93,9 @@ bool fileExists(const char* gcPath) { #endif if (exists) { - OSReport("[DvdEmu] FOUND: %s\n", gcPath); + printf("[DvdEmu] FOUND: %s\n", gcPath); fflush(stdout); } else { - OSReport("[DvdEmu] MISSING: %s\n", gcPath); + printf("[DvdEmu] MISSING: %s\n", gcPath); fflush(stdout); } return exists; @@ -116,11 +116,11 @@ u32 getFileSize(const char* gcPath) { void* loadFile(const char* gcPath, u32* outSize, void* heap) { std::string fullPath = convertPath(gcPath); - OSReport("[DvdEmu] Loading request: '%s'\n", gcPath); + printf("[DvdEmu] Loading request: '%s'\n", gcPath); fflush(stdout); FILE* f = fopen(fullPath.c_str(), "rb"); if (!f) { - OSReport("[DvdEmu] ERROR: Failed to open file at physical path: %s\n", fullPath.c_str()); + printf("[DvdEmu] ERROR: Failed to open file at physical path: %s\n", fullPath.c_str()); fflush(stdout); if (outSize) *outSize = 0; return nullptr; @@ -139,7 +139,7 @@ void* loadFile(const char* gcPath, u32* outSize, void* heap) { #endif if (!data) { - OSReport("[DvdEmu] FATAL: Failed to allocate %u bytes for %s\n", size, gcPath); + printf("[DvdEmu] FATAL: Failed to allocate %u bytes for %s\n", size, gcPath); fflush(stdout); fclose(f); if (outSize) *outSize = 0; @@ -150,14 +150,14 @@ void* loadFile(const char* gcPath, u32* outSize, void* heap) { fclose(f); if (bytesRead != size) { - OSReport("[DvdEmu] WARNING: Read error: expected %u, got %u for %s\n", size, bytesRead, - gcPath); + printf("[DvdEmu] WARNING: Read error: expected %u, got %u for %s\n", size, bytesRead, + gcPath); fflush(stdout); } if (outSize) *outSize = bytesRead; - OSReport("[DvdEmu] SUCCESS: Loaded %s (%u bytes)\n", gcPath, bytesRead); + printf("[DvdEmu] SUCCESS: Loaded %s (%u bytes)\n", gcPath, bytesRead); fflush(stdout); return data; } @@ -166,7 +166,7 @@ u32 loadFileToBuffer(const char* gcPath, void* buffer, u32 bufferSize, u32 offse FILE* f = fopen(fullPath.c_str(), "rb"); if (!f) { - OSReport("[DvdEmu] Failed to open file for buffer load: %s\n", fullPath.c_str()); + printf("[DvdEmu] Failed to open file for buffer load: %s\n", fullPath.c_str()); fflush(stdout); return 0; } @@ -186,7 +186,7 @@ u32 loadFileToBuffer(const char* gcPath, void* buffer, u32 bufferSize, u32 offse s32 DVDConvertPathToEntrynum_Emu(const char* path) { if (!DvdEmu::fileExists(path)) { - OSReport("[DVD] Error: File not found for entrynum conversion: %s\n", path); + printf("[DVD] Error: File not found for entrynum conversion: %s\n", path); fflush(stdout); return -1; } diff --git a/src/dusk/stubs.cpp b/src/dusk/stubs.cpp index 95df5128e5..722a005989 100644 --- a/src/dusk/stubs.cpp +++ b/src/dusk/stubs.cpp @@ -11,10 +11,60 @@ #include #include +/* +#ifndef _WIN32 +#include +#include +#include +#if __APPLE__ +#include +#endif +#endif + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include + +#undef IN +#undef OUT +#endif + +#if __APPLE__ +static u64 MachToDolphinNum; +static u64 MachToDolphinDenom; +#elif _WIN32 +static LARGE_INTEGER PerfFrequency; +#endif + +*/ + // ========================================================================== // General OS // ========================================================================== + +// Credits: Super Monkey Ball +/* +static u64 GetGCTicks() { +#if __APPLE__ + return mach_absolute_time() * MachToDolphinNum / MachToDolphinDenom; +#elif __linux__ || __FreeBSD__ + struct timespec tp; + clock_gettime(CLOCK_MONOTONIC, &tp); + + return ((tp.tv_sec * 1000000000ull) + tp.tv_nsec) * OS_CORE_CLOCK / 1000000000ull; +#elif _WIN32 + LARGE_INTEGER perf; + QueryPerformanceCounter(&perf); + perf.QuadPart *= OS_CORE_CLOCK; + perf.QuadPart /= PerfFrequency.QuadPart; + return perf.QuadPart; +#else + return 0; +#endif +} */ + u32 OSGetConsoleType() { return OS_CONSOLE_RETAIL1; } @@ -230,8 +280,15 @@ void OSTicksToCalendarTime(OSTime ticks, OSCalendarTime* td) { if (td) memset(td, 0, sizeof(OSCalendarTime)); } -OSTick OSGetTick(void) { return 0; } -OSTime OSGetTime(void) { return 0; } +OSTime OSGetTime(void) { + //return (OSTime)GetGCTicks(); + return 0; +} + +OSTick OSGetTick(void) { + //return (OSTick)GetGCTicks(); + return 0; +} u16 OSGetFontEncode() { return 0; } @@ -250,7 +307,13 @@ void OSSetStringTable(void* stringTable) {} BOOL OSUnlink(OSModuleInfo* oldModule) { return FALSE; } void OSSwitchFiberEx(__REGISTER u32 param_0, __REGISTER u32 param_1, __REGISTER u32 param_2, - __REGISTER u32 param_3, __REGISTER u32 code, __REGISTER u32 stack) {} + __REGISTER u32 param_3, __REGISTER u32 code, __REGISTER u32 stack) { + // On PC, call the function directly instead of switching stacks. + // The PPC version switches to 'stack' and calls code(param_0, param_1). + // Only caller is mDoPrintf_vprintf_Interrupt: OSSwitchFiberEx(fmt, args, 0, 0, vprintf, sp) + typedef void (*Func2)(u32, u32); + ((Func2)(uintptr_t)code)(param_0, param_1); +} u32 __OSGetDIConfig() { return 0; } u32 OSGetProgressiveMode(void) { return 0; } @@ -512,30 +575,35 @@ void LCEnable() { #pragma mark VI -static VIRetraceCallback sVIRetraceCallback = NULL; +// VI retrace emulation: on GameCube, the VI chip fires a hardware interrupt at +// every VSync (~60Hz). This triggers pre/post retrace callbacks, which in turn +// send messages to JUTVideo's message queue. waitForTick() blocks on that queue. +// On PC, we simulate this by calling VIWaitForRetrace() once per frame in the +// main loop, which increments the retrace counter and fires the callbacks. +static u32 sRetraceCount = 0; +static VIRetraceCallback sVIPreRetraceCallback = NULL; +static VIRetraceCallback sVIPostRetraceCallback = NULL; extern "C" { void VIConfigure(const GXRenderModeObj* rm) { - puts("VIConfigure is a stub"); + // puts("VIConfigure is a stub"); } void VIConfigurePan(u16 xOrg, u16 yOrg, u16 width, u16 height) { - puts("VIConfigurePan is a stub"); + // puts("VIConfigurePan is a stub"); } u32 VIGetRetraceCount() { - // puts("VIGetRetraceCount is a stub"); - return 0; // TODO this might be important + return sRetraceCount; } u32 VIGetNextField() { - puts("VIGetNextField is a stub"); return 0; } void VISetBlack(BOOL black) { - puts("VISetBlack is a stub"); + // puts("VISetBlack is a stub"); } void VISetNextFrameBuffer(void* fb) { @@ -543,34 +611,37 @@ void VISetNextFrameBuffer(void* fb) { } void VIWaitForRetrace() { - if (sVIRetraceCallback) { - sVIRetraceCallback(0); + sRetraceCount++; + if (sVIPreRetraceCallback) { + sVIPreRetraceCallback(sRetraceCount); + } + if (sVIPostRetraceCallback) { + sVIPostRetraceCallback(sRetraceCount); } } void* VIGetCurrentFrameBuffer(void) { - puts("VIGetCurrentFrameBuffer is a stub"); return NULL; } u32 VIGetDTVStatus(void) { - puts("VIGetDTVStatus is a stub"); return 0; } void* VIGetNextFrameBuffer(void) { - puts("VIGetNextFrameBuffer is a stub"); return NULL; } VIRetraceCallback VISetPostRetraceCallback(VIRetraceCallback callback) { - sVIRetraceCallback = callback; - return callback; + VIRetraceCallback old = sVIPostRetraceCallback; + sVIPostRetraceCallback = callback; + return old; } VIRetraceCallback VISetPreRetraceCallback(VIRetraceCallback cb) { - puts("VISetPreRetraceCallback is a stub"); - return cb; + VIRetraceCallback old = sVIPreRetraceCallback; + sVIPreRetraceCallback = cb; + return old; } } // extern "C" @@ -1396,19 +1467,58 @@ void GDSetVtxDescv(const GXVtxDescList* attrPtr) { #pragma mark GX #include +// Dummy FIFO sink for direct GXWGFifo writes in J3D code (e.g. J3DFifo.h). +// On GameCube these write to the GX command processor at 0xCC008000. +// On PC, writes land here harmlessly and are discarded. +volatile PPCWGPipe GXWGFifo; + +// GXCmd/GXParam/GXMatrixIndex: low-level command FIFO functions used by J3D. +// Route through Aurora's software FIFO so display list data is actually recorded. +// +// We forward-declare Aurora's FIFO functions with explicit stdint types instead of +// including fifo.hpp, because the game's u32 (unsigned long) differs from Aurora's +// u32 (uint32_t = unsigned int on MSVC). Including fifo.hpp would resolve its u32 +// parameter types to the game's unsigned long (since game headers are included first +// and set the include guards), causing MSVC name mangling mismatches at link time. +namespace aurora::gfx::fifo { + void write_u8(uint8_t val); + void write_u16(uint16_t val); + void write_u32(uint32_t val); + void write_f32(float val); +} + +// Cast to stdint types: game headers define u32=unsigned long, but Aurora uses +// uint32_t=unsigned int. Both are 32-bit on Win32 but have different MSVC name mangling. +void GXCmd1u8(const u8 x) { aurora::gfx::fifo::write_u8(static_cast(x)); } +void GXCmd1u16(const u16 x) { aurora::gfx::fifo::write_u16(static_cast(x)); } +void GXCmd1u32(const u32 x) { aurora::gfx::fifo::write_u32(static_cast(x)); } + +void GXParam1u8(const u8 x) { aurora::gfx::fifo::write_u8(static_cast(x)); } +void GXParam1u16(const u16 x) { aurora::gfx::fifo::write_u16(static_cast(x)); } +void GXParam1u32(const u32 x) { aurora::gfx::fifo::write_u32(static_cast(x)); } +void GXParam1s8(const s8 x) { aurora::gfx::fifo::write_u8(static_cast(x)); } +void GXParam1s16(const s16 x) { aurora::gfx::fifo::write_u16(static_cast(x)); } +void GXParam1s32(const s32 x) { aurora::gfx::fifo::write_u32(static_cast(x)); } +void GXParam1f32(const f32 x) { aurora::gfx::fifo::write_f32(x); } +void GXParam3f32(const f32 x, const f32 y, const f32 z) { + aurora::gfx::fifo::write_f32(x); + aurora::gfx::fifo::write_f32(y); + aurora::gfx::fifo::write_f32(z); +} +void GXParam4f32(const f32 x, const f32 y, const f32 z, const f32 w) { + aurora::gfx::fifo::write_f32(x); + aurora::gfx::fifo::write_f32(y); + aurora::gfx::fifo::write_f32(z); + aurora::gfx::fifo::write_f32(w); +} + +void GXMatrixIndex1u8(const u8 x) { aurora::gfx::fifo::write_u8(static_cast(x)); } + // Moved-in GX helpers and helpers for metrics/project void __GXSetSUTexSize() { puts("__GXSetSUTexSize is a stub"); } -void __GXSetVAT() { - puts("__GXSetVAT is a stub"); -} -void __GXSetVCD() { - puts("__GXSetVCD is a stub"); -} -void __GXUpdateBPMask() { - puts("__GXUpdateBPMask is a stub"); -} +// __GXSetVAT, __GXSetVCD, __GXUpdateBPMask: now provided by Aurora's GXManage.cpp (fifo branch) void GXSetGPMetric(GXPerf0 perf0, GXPerf1 perf1) { // puts("GXSetGPMetric is a stub"); @@ -1494,9 +1604,7 @@ void GXProject(f32 x, f32 y, f32 z, const f32 mtx[3][4], const f32* pm, const f3 void GXAbortFrame(void) { puts("GXAbortFrame is a stub"); } -void GXEnableTexOffsets(GXTexCoordID coord, u8 line_enable, u8 point_enable) { - puts("GXEnableTexOffsets is a stub"); -} +// GXEnableTexOffsets: now provided by Aurora's GXGeometry.cpp (fifo branch) OSThread* GXGetCurrentGXThread(void) { puts("GXGetCurrentGXThread is a stub"); return NULL; diff --git a/src/f_ap/f_ap_game.cpp b/src/f_ap/f_ap_game.cpp index b03bdc2561..2db1399d2b 100644 --- a/src/f_ap/f_ap_game.cpp +++ b/src/f_ap/f_ap_game.cpp @@ -720,11 +720,20 @@ void fapGm_After() { } void fapGm_Execute() { + static u32 sExecCount = 0; + if (sExecCount < 10 || (sExecCount % 300 == 0)) { + printf("[DIAG] fapGm_Execute frame=%d\n", sExecCount); + fflush(stdout); + } + sExecCount++; + #if DEBUG JUTDbPrint::getManager()->setCharColor(g_HIO.mColor); #endif - fpcM_Management(NULL, fapGm_After); + printf("[DIAG] fapGm_Execute: entering fpcM_Management...\n"); fflush(stdout); + fpcM_Management(NULL, fapGm_After); + printf("[DIAG] fapGm_Execute: fpcM_Management returned\n"); fflush(stdout); cCt_Counter(0); } diff --git a/src/f_op/f_op_scene_mng.cpp b/src/f_op/f_op_scene_mng.cpp index ba1f322139..7f40ccd845 100644 --- a/src/f_op/f_op_scene_mng.cpp +++ b/src/f_op/f_op_scene_mng.cpp @@ -7,6 +7,7 @@ #include "JSystem/JUtility/JUTAssert.h" #include "f_op/f_op_scene_iter.h" #include "f_op/f_op_scene_req.h" +#include scene_class* fopScnM_SearchByID(fpc_ProcID id) { return (scene_class*)fopScnIt_Judge((fop_ScnItFunc)fpcSch_JudgeByID, &id); @@ -29,7 +30,10 @@ fpc_ProcID fopScnM_DeleteReq(scene_class* i_scene) { } int fopScnM_CreateReq(s16 i_procName, s16 param_2, u16 param_3, u32 i_data) { - return fopScnRq_Request(0, 0, i_procName, (void*)i_data, param_2, param_3) != fpcM_ERROR_PROCESS_ID_e; + printf("[DIAG] fopScnM_CreateReq: procName=%d fade=%d\n", i_procName, param_2); fflush(stdout); + fpc_ProcID result = fopScnRq_Request(0, 0, i_procName, (void*)i_data, param_2, param_3); + printf("[DIAG] fopScnM_CreateReq: result=%d (error=%d)\n", result, fpcM_ERROR_PROCESS_ID_e); fflush(stdout); + return result != fpcM_ERROR_PROCESS_ID_e; } u32 fopScnM_ReRequest(s16 i_procName, u32 i_data) { diff --git a/src/f_op/f_op_scene_req.cpp b/src/f_op/f_op_scene_req.cpp index 17dd3c4059..05a44e10f1 100644 --- a/src/f_op/f_op_scene_req.cpp +++ b/src/f_op/f_op_scene_req.cpp @@ -9,6 +9,7 @@ #include "f_op/f_op_scene_pause.h" #include "f_pc/f_pc_executor.h" #include "f_pc/f_pc_manager.h" +#include static cPhs_Step fopScnRq_phase_ClearOverlap(scene_request_class* i_sceneReq) { return fopOvlpM_ClearOfReq() == 1 ? cPhs_NEXT_e : cPhs_INIT_e; @@ -16,7 +17,13 @@ static cPhs_Step fopScnRq_phase_ClearOverlap(scene_request_class* i_sceneReq) { } static cPhs_Step fopScnRq_phase_Execute(scene_request_class* i_sceneReq) { - return fpcNdRq_Execute(&i_sceneReq->create_request); + static int sExecLogCount = 0; + cPhs_Step ret = (cPhs_Step)fpcNdRq_Execute(&i_sceneReq->create_request); + if (sExecLogCount < 30) { + printf("[DIAG] fopScnRq_phase_Execute: ret=%d name=%d\n", ret, i_sceneReq->create_request.name); fflush(stdout); + sExecLogCount++; + } + return ret; } static cPhs_Step fopScnRq_phase_IsDoingOverlap(scene_request_class* i_sceneReq) { diff --git a/src/f_pc/f_pc_base.cpp b/src/f_pc/f_pc_base.cpp index 36af52f905..8e2e3f6a5e 100644 --- a/src/f_pc/f_pc_base.cpp +++ b/src/f_pc/f_pc_base.cpp @@ -14,6 +14,7 @@ #include "f_pc/f_pc_profile.h" #include "f_pc/f_pc_debug_sv.h" #include "Z2AudioLib/Z2AudioMgr.h" +#include BOOL fpcBs_Is_JustOfType(int i_typeA, int i_typeB) { if (i_typeB == i_typeA) { @@ -118,10 +119,13 @@ base_process_class* fpcBs_Create(s16 i_profname, fpc_ProcID i_procID, void* i_ap u32 size; pprofile = (process_profile_definition*)fpcPf_Get(i_profname); + printf("[DIAG] fpcBs_Create: profname=%d profile=%p procSize=%d unkSize=%d\n", + i_profname, pprofile, pprofile->process_size, pprofile->unk_size); fflush(stdout); size = pprofile->process_size + pprofile->unk_size; pprocess = (base_process_class*)cMl::memalignB(-4, size); if (pprocess == NULL) { + printf("[DIAG] fpcBs_Create: memalignB FAILED for size=%u\n", size); fflush(stdout); return NULL; } diff --git a/src/f_pc/f_pc_create_req.cpp b/src/f_pc/f_pc_create_req.cpp index 5e54f21ea4..3ea29645ae 100644 --- a/src/f_pc/f_pc_create_req.cpp +++ b/src/f_pc/f_pc_create_req.cpp @@ -11,6 +11,7 @@ #include "f_pc/f_pc_executor.h" #include "f_pc/f_pc_layer.h" #include "f_pc/f_pc_debug_sv.h" +#include BOOL fpcCtRq_isCreatingByID(create_tag* i_createTag, fpc_ProcID* i_id) { fpc_ProcID id = ((create_request*)i_createTag->base.mpTagData)->id; @@ -91,6 +92,12 @@ BOOL fpcCtRq_Do(create_request* i_request) { } } + static int sCtRqDoLogCount = 0; + if (sCtRqDoLogCount < 30) { + printf("[DIAG] fpcCtRq_Do: phase=%d process=%p\n", phase, i_request->process); fflush(stdout); + sCtRqDoLogCount++; + } + switch (phase) { case cPhs_COMPLEATE_e: { if (fpcEx_ToExecuteQ(i_request->process) == 0) diff --git a/src/f_pc/f_pc_draw.cpp b/src/f_pc/f_pc_draw.cpp index 5a5b220f82..0a3162793a 100644 --- a/src/f_pc/f_pc_draw.cpp +++ b/src/f_pc/f_pc_draw.cpp @@ -8,6 +8,7 @@ #include "f_pc/f_pc_leaf.h" #include "f_pc/f_pc_node.h" #include "f_pc/f_pc_pause.h" +#include int fpcDw_Execute(base_process_class* i_proc) { if (!fpcPause_IsEnable(i_proc, 2)) { @@ -32,9 +33,15 @@ int fpcDw_Execute(base_process_class* i_proc) { } int fpcDw_Handler(fpcDw_HandlerFuncFunc i_iterHandler, fpcDw_HandlerFunc i_func) { + static int sDwLogCount = 0; int ret; + if (sDwLogCount < 5) { printf("[DIAG] fpcDw_Handler: before BeforeOfDraw\n"); fflush(stdout); } cAPIGph_BeforeOfDraw(); + if (sDwLogCount < 5) { printf("[DIAG] fpcDw_Handler: before draw iteration\n"); fflush(stdout); } ret = i_iterHandler(i_func); + if (sDwLogCount < 5) { printf("[DIAG] fpcDw_Handler: before AfterOfDraw\n"); fflush(stdout); } cAPIGph_AfterOfDraw(); + if (sDwLogCount < 5) { printf("[DIAG] fpcDw_Handler: done\n"); fflush(stdout); } + sDwLogCount++; return ret; } diff --git a/src/f_pc/f_pc_manager.cpp b/src/f_pc/f_pc_manager.cpp index 612c4d9a6e..1ecb2b7473 100644 --- a/src/f_pc/f_pc_manager.cpp +++ b/src/f_pc/f_pc_manager.cpp @@ -20,6 +20,7 @@ #include "f_pc/f_pc_pause.h" #include "f_pc/f_pc_priority.h" #include "m_Do/m_Do_controller_pad.h" +#include void fpcM_Draw(void* i_proc) { fpcDw_Execute((base_process_class*)i_proc); @@ -42,16 +43,26 @@ BOOL fpcM_IsCreating(fpc_ProcID i_id) { } void fpcM_Management(fpcM_ManagementFunc i_preExecuteFn, fpcM_ManagementFunc i_postExecuteFn) { + static int sMgmtLogCount = 0; + MtxInit(); if (!fapGm_HIO_c::isCaptureScreen()) { dComIfGd_peekZdata(); } fapGm_HIO_c::executeCaptureScreen(); - if (!dShutdownErrorMsg_c::execute()) { + bool shutdownRet = dShutdownErrorMsg_c::execute(); + if (sMgmtLogCount < 10) { + printf("[DIAG] fpcM_Management: shutdown=%d\n", shutdownRet); fflush(stdout); + } + if (!shutdownRet) { static bool l_dvdError = false; - if (!dDvdErrorMsg_c::execute()) { + bool dvdErrRet = dDvdErrorMsg_c::execute(); + if (sMgmtLogCount < 10) { + printf("[DIAG] fpcM_Management: dvdError=%d\n", dvdErrRet); fflush(stdout); + } + if (!dvdErrRet) { if (l_dvdError) { dLib_time_c::startTime(); Z2GetSoundMgr()->pauseAllGameSound(false); @@ -60,36 +71,55 @@ void fpcM_Management(fpcM_ManagementFunc i_preExecuteFn, fpcM_ManagementFunc i_p cAPIGph_Painter(); + if (sMgmtLogCount < 10) { printf("[DIAG] fpcM_Management: after cAPIGph_Painter\n"); fflush(stdout); } + if (!dPa_control_c::isStatus(1)) { fpcDt_Handler(); } else { dPa_control_c::offStatus(1); } + if (sMgmtLogCount < 10) { printf("[DIAG] fpcM_Management: after fpcDt_Handler\n"); fflush(stdout); } + if (!fpcPi_Handler()) { JUT_ASSERT(353, FALSE); } + if (sMgmtLogCount < 10) { printf("[DIAG] fpcM_Management: after fpcPi_Handler\n"); fflush(stdout); } + if (!fpcCt_Handler()) { JUT_ASSERT(357, FALSE); } + if (sMgmtLogCount < 10) { printf("[DIAG] fpcM_Management: after fpcCt_Handler\n"); fflush(stdout); } + if (i_preExecuteFn != NULL) { i_preExecuteFn(); } + if (sMgmtLogCount < 10) { printf("[DIAG] fpcM_Management: after preExecute\n"); fflush(stdout); } + if (!fapGm_HIO_c::isCaptureScreen()) { fpcEx_Handler((fpcLnIt_QueueFunc)fpcM_Execute); } + + if (sMgmtLogCount < 10) { printf("[DIAG] fpcM_Management: after fpcEx_Handler\n"); fflush(stdout); } + if (!fapGm_HIO_c::isCaptureScreen() || fapGm_HIO_c::getCaptureScreenDivH() != 1) { fpcDw_Handler((fpcDw_HandlerFuncFunc)fpcM_DrawIterater, (fpcDw_HandlerFunc)fpcM_Draw); } + if (sMgmtLogCount < 10) { printf("[DIAG] fpcM_Management: after fpcDw_Handler\n"); fflush(stdout); } + if (i_postExecuteFn != NULL) { + if (sMgmtLogCount < 10) { + printf("[DIAG] fpcM_Management: calling postExecuteFn (fapGm_After)\n"); fflush(stdout); + } i_postExecuteFn(); } dComIfGp_drawSimpleModel(); + sMgmtLogCount++; } else if (!l_dvdError) { dLib_time_c::stopTime(); Z2GetSoundMgr()->pauseAllGameSound(true); diff --git a/src/f_pc/f_pc_node_req.cpp b/src/f_pc/f_pc_node_req.cpp index 34be4b85ff..7ecf1ea7e1 100644 --- a/src/f_pc/f_pc_node_req.cpp +++ b/src/f_pc/f_pc_node_req.cpp @@ -12,6 +12,7 @@ #include "f_pc/f_pc_stdcreate_req.h" #include "f_pc/f_pc_manager.h" #include "f_pc/f_pc_debug_sv.h" +#include void fpcNdRq_RequestQTo(node_create_request* i_request) { fpcLy_CreatedMesg(i_request->layer); @@ -47,10 +48,12 @@ int fpcNdRq_phase_IsCreated(node_create_request* i_request) { } int fpcNdRq_phase_Create(node_create_request* i_request) { + printf("[DIAG] fpcNdRq_phase_Create: name=%d layer=%p\n", i_request->name, i_request->layer); fflush(stdout); i_request->creating_id = fpcSCtRq_Request(i_request->layer, i_request->name, (stdCreateFunc)i_request->create_req_methods->post_method, i_request, i_request->data); + printf("[DIAG] fpcNdRq_phase_Create: creating_id=%d (error=%d)\n", i_request->creating_id, fpcM_ERROR_PROCESS_ID_e); fflush(stdout); if (i_request->creating_id == fpcM_ERROR_PROCESS_ID_e) { return cPhs_UNK3_e; } @@ -140,6 +143,12 @@ int fpcNdRq_Cancel(node_create_request* i_request) { int fpcNdRq_Handler() { node_class* node = l_fpcNdRq_Queue.mpHead; + static int sNdRqLogCount = 0; + if (l_fpcNdRq_Queue.mSize > 0 && sNdRqLogCount < 30) { + printf("[DIAG] fpcNdRq_Handler: queue size=%d\n", l_fpcNdRq_Queue.mSize); fflush(stdout); + sNdRqLogCount++; + } + #if DEBUG if (g_fpcDbSv_service[9] != NULL) { g_fpcDbSv_service[9](&l_fpcNdRq_Queue.mSize); diff --git a/src/f_pc/f_pc_stdcreate_req.cpp b/src/f_pc/f_pc_stdcreate_req.cpp index 9f56391dd2..4600ee2ba5 100644 --- a/src/f_pc/f_pc_stdcreate_req.cpp +++ b/src/f_pc/f_pc_stdcreate_req.cpp @@ -9,9 +9,11 @@ #include "f_pc/f_pc_manager.h" #include "f_pc/f_pc_debug_sv.h" #include +#include int fpcSCtRq_phase_Load(standard_create_request_class* i_request) { int ret = fpcLd_Load(i_request->process_name); + printf("[DIAG] fpcSCtRq_phase_Load: procName=%d ret=%d\n", i_request->process_name, ret); fflush(stdout); switch (ret) { case cPhs_INIT_e: @@ -26,15 +28,18 @@ int fpcSCtRq_phase_Load(standard_create_request_class* i_request) { } int fpcSCtRq_phase_CreateProcess(standard_create_request_class* i_request) { + printf("[DIAG] fpcSCtRq_phase_CreateProcess: procName=%d\n", i_request->process_name); fflush(stdout); fpcLy_SetCurrentLayer(i_request->base.layer); i_request->base.process = fpcBs_Create(i_request->process_name, i_request->base.id, i_request->process_append); if (i_request->base.process == NULL) { + printf("[DIAG] fpcSCtRq_phase_CreateProcess: fpcBs_Create FAILED for procName=%d\n", i_request->process_name); fflush(stdout); OS_REPORT("fpcSCtRq_phase_CreateProcess %d\n", i_request->process_name); fpcLd_Free(i_request->process_name); return cPhs_ERROR_e; } else { + printf("[DIAG] fpcSCtRq_phase_CreateProcess: fpcBs_Create OK proc=%p\n", i_request->base.process); fflush(stdout); i_request->base.process->create_req = &i_request->base; return cPhs_NEXT_e; } @@ -43,6 +48,11 @@ int fpcSCtRq_phase_CreateProcess(standard_create_request_class* i_request) { int fpcSCtRq_phase_SubCreateProcess(standard_create_request_class* i_request) { fpcLy_SetCurrentLayer(i_request->base.layer); int ret = fpcBs_SubCreate(i_request->base.process); + static int sSubCreateLogCount = 0; + if (sSubCreateLogCount < 20) { + printf("[DIAG] fpcSCtRq_phase_SubCreateProcess: procName=%d ret=%d\n", i_request->process_name, ret); fflush(stdout); + sSubCreateLogCount++; + } #if DEBUG if (ret == 0 && i_request->unk_0x60-- <= 0) { diff --git a/src/m_Do/m_Do_DVDError.cpp b/src/m_Do/m_Do_DVDError.cpp index d4884aeb1c..e5c6e524a1 100644 --- a/src/m_Do/m_Do_DVDError.cpp +++ b/src/m_Do/m_Do_DVDError.cpp @@ -4,11 +4,15 @@ */ #include "m_Do/m_Do_DVDError.h" -#include "JSystem/JKernel/JKRAssertHeap.h" #include +#include "JSystem/JKernel/JKRAssertHeap.h" +#include "m_Do/m_Do_Reset.h" #include "m_Do/m_Do_dvd_thread.h" #include "m_Do/m_Do_ext.h" -#include "m_Do/m_Do_Reset.h" + +// Added for the sleep workaround +#include +#include #if PLATFORM_GCN const int stack_size = 3072; @@ -16,7 +20,6 @@ const int stack_size = 3072; const int stack_size = 8192; #endif - bool mDoDvdErr_initialized; static OSThread DvdErr_thread; @@ -26,20 +29,24 @@ static OSThread DvdErr_thread; static u8 DvdErr_stack[stack_size] ATTRIBUTE_ALIGN(16); #pragma pop -static OSAlarm Alarm; +// Alarm is not needed for the PC workaround +// static OSAlarm Alarm; void mDoDvdErr_ThdInit() { if (mDoDvdErr_initialized) { return; } - OSTime time = OSGetTime(); + // OSTime time = OSGetTime(); // Unused in workaround - OSCreateThread(&DvdErr_thread, (void*(*)(void*))mDoDvdErr_Watch, NULL, DvdErr_stack + sizeof(DvdErr_stack), - sizeof(DvdErr_stack), OSGetThreadPriority(OSGetCurrentThread()) - 3, 1); + OSCreateThread(&DvdErr_thread, (void* (*)(void*))mDoDvdErr_Watch, NULL, + DvdErr_stack + sizeof(DvdErr_stack), sizeof(DvdErr_stack), + OSGetThreadPriority(OSGetCurrentThread()) - 3, 1); OSResumeThread(&DvdErr_thread); - OSCreateAlarm(&Alarm); - OSSetPeriodicAlarm(&Alarm, time, OS_BUS_CLOCK / 4, AlarmHandler); + + // PC Workaround: Disable Alarm logic. The thread will sleep itself. + // OSCreateAlarm(&Alarm); + // OSSetPeriodicAlarm(&Alarm, time, OS_BUS_CLOCK / 4, AlarmHandler); mDoDvdErr_initialized = true; } @@ -47,7 +54,7 @@ void mDoDvdErr_ThdInit() { void mDoDvdErr_ThdCleanup() { if (mDoDvdErr_initialized) { OSCancelThread(&DvdErr_thread); - OSCancelAlarm(&Alarm); + // OSCancelAlarm(&Alarm); // Disable Alarm cancel mDoDvdErr_initialized = false; } } @@ -66,10 +73,20 @@ static void mDoDvdErr_Watch(void*) { if (status == DVD_STATE_FATAL_ERROR) { mDoDvdThd::suspend(); } - OSSuspendThread(&DvdErr_thread); + + // PC Workaround: + // Instead of suspending and waiting for an Alarm (which might not be implemented), + // we simply sleep for a short duration. + // OS_BUS_CLOCK / 4 corresponds to roughly 1/4th of a second on GC. + // We use 250ms here to simulate the periodic check. + + // OSSuspendThread(&DvdErr_thread); // <-- Original causing deadlock without Alarm + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + } while (true); } static void AlarmHandler(OSAlarm*, OSContext*) { + // This handler is no longer called in the PC workaround OSResumeThread(&DvdErr_thread); -} +} \ No newline at end of file diff --git a/src/m_Do/m_Do_graphic.cpp b/src/m_Do/m_Do_graphic.cpp index 3cc285fd64..8eece7b783 100644 --- a/src/m_Do/m_Do_graphic.cpp +++ b/src/m_Do/m_Do_graphic.cpp @@ -3,6 +3,8 @@ * Graphics Management Functions */ +#include + #include "d/dolzel.h" // IWYU pragma: keep #include "JSystem/J2DGraph/J2DOrthoGraph.h" @@ -1511,6 +1513,14 @@ static void drawItem3D() { } int mDoGph_Painter() { + // Diagnostic: log windowNum to track game state machine progress + static bool sDiagLoggedWindow = false; + if (!sDiagLoggedWindow) { + int wn = dComIfGp_getWindowNum(); + printf("[DIAG] mDoGph_Painter: windowNum=%d\n", wn); fflush(stdout); + if (wn != 0) sDiagLoggedWindow = true; + } + #if DEBUG drawHeapMap(); #endif diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index 27e7e87026..bc13bacdae 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -5,6 +5,7 @@ */ #include "m_Do/m_Do_main.h" +#include #include #include "DynamicLink.h" #include "JSystem/JAudio2/JASAudioThread.h" @@ -178,11 +179,18 @@ void main01(void) { mDoCPd_c::read(); // Read Controller + // Simulate VI retrace interrupt — fires post-retrace callback which sends + // a message to JUTVideo's queue, unblocking waitForTick() in beginRender() + VIWaitForRetrace(); + // --- EXECUTE GAME LOGIC & RENDER --- + printf("[DIAG] main01: before fapGm_Execute (frame %d)\n", frame); fflush(stdout); fapGm_Execute(); + printf("[DIAG] main01: after fapGm_Execute\n"); fflush(stdout); // --- Frame End & Limiter --- aurora_end_frame(); + printf("[DIAG] main01: after aurora_end_frame\n"); fflush(stdout); std::this_thread::sleep_for(std::chrono::milliseconds(16)); // ~60 FPS Cap } while (true);