diff --git a/ps2xRuntime/include/ps2_runtime.h b/ps2xRuntime/include/ps2_runtime.h index a899408..961e78e 100644 --- a/ps2xRuntime/include/ps2_runtime.h +++ b/ps2xRuntime/include/ps2_runtime.h @@ -150,10 +150,33 @@ struct alignas(16) R5900Context // Reset COP0 registers cop0_random = 47; // Start at maximum value - // cop0_status = 0x400000; // BEV set, ERL clear, kernel mode - // 0x00400000 = BEV (Boot Exception Vectors). - // 0x00000000 = Normal mode (after BIOS handoff). - cop0_status = 0x00000000; + // Status as the EE kernel leaves it when it hands control to the game, + // which is the state recompiled code starts in -- we never execute the + // boot ROM that would otherwise set this up. + // + // Both interrupt-enable bits matter, and they are not the same bit: + // + // IE (bit 0) the architectural MIPS interrupt enable. The kernel + // sets it once during boot and it normally stays set. + // EIE (bit 16) the EE-specific enable that the `ei` and `di` + // instructions toggle. + // + // Interrupts are only really on when both are set, and guest code reads + // them separately. libkernel's StartThread, for instance, opens with + // `mfc0 Status; xori 1; andi 1` and refuses to run when IE is clear -- + // that is its "you must call iStartThread from an interrupt handler" + // guard. Leaving Status at zero made that guard fire forever, so every + // StartThread returned -1 and any game that creates a thread stalled + // with no diagnostic. + // + // EIE matters for the matching reason: DIntr reports whether it was set + // so the caller knows whether to pair it with an EIntr. Starting at zero + // makes DIntr always answer "already disabled" and the re-enable never + // happens. + // + // BEV (0x00400000) is deliberately not set: that selects the boot + // exception vectors, which is the pre-handoff state, not this one. + cop0_status = 0x00010001; // EIE | IE cop0_prid = 0x00002e20; // CPU ID for R5900 in_delay_slot = false; diff --git a/ps2xRuntime/src/lib/ps2_runtime.cpp b/ps2xRuntime/src/lib/ps2_runtime.cpp index ddacb0c..e5d23d9 100644 --- a/ps2xRuntime/src/lib/ps2_runtime.cpp +++ b/ps2xRuntime/src/lib/ps2_runtime.cpp @@ -491,7 +491,15 @@ PS2Runtime::PS2Runtime() } #endif - std::memset(&m_cpuContext, 0, sizeof(m_cpuContext)); + // Assign a default-constructed context rather than memset-ing this one. + // R5900Context's constructor already zeroes itself and then applies the + // architectural reset values on top -- COP0 Status, PRId, Random. A raw + // memset here silently threw those away, leaving Status at 0 for the main + // thread while every thread created later, which goes through + // `target->context = R5900Context{}` in EeScheduler::startThread, got the + // correct values. Guest code reads Status.IE to decide whether interrupts + // are enabled, so the main thread believed they were permanently off. + m_cpuContext = R5900Context{}; // R0 is always zero in MIPS m_cpuContext.r[0] = _mm_set1_epi32(0);