Start the main thread with COP0 Status.IE set - #214
Merged
Conversation
Sinan-Karakaya
force-pushed
the
fix/cop0-status-init
branch
from
August 18, 2026 13:39
627ffca to
fb50773
Compare
ran-j
requested changes
Aug 18, 2026
ran-j
left a comment
Owner
There was a problem hiding this comment.
Nice catch, almost ready to merge
| // 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, |
Owner
There was a problem hiding this comment.
Can you reduce this long comment please
| #endif | ||
|
|
||
| std::memset(&m_cpuContext, 0, sizeof(m_cpuContext)); | ||
| // Assign a default-constructed context rather than memset-ing this one. |
Guest code reads COP0 Status to decide whether interrupts are enabled,
and two different bits are involved:
IE (bit 0) the architectural MIPS interrupt enable, set once by the
kernel during boot and normally left set.
EIE (bit 16) the EE-specific enable that `ei` and `di` toggle.
We never execute the boot ROM, so nothing was setting either one, and
R5900Context started with Status at zero.
That is not cosmetic. libkernel's StartThread opens with
`mfc0 Status; xori 1; andi 1` and bails out with -1 when IE is clear --
its "you must call iStartThread from an interrupt handler" guard. With
Status at zero that guard fired every time, so every StartThread failed.
Dragon Quest VIII hits this during boot: it creates its CD streaming
thread, gets -1, prints "Can't start thread for streaming." and then
deadlocks with every thread blocked and none runnable. Nothing in the
runtime logs anything, because from its point of view the guest simply
asked a question and got an answer.
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", so the re-enable never happens.
Two changes, both needed:
- R5900Context's constructor now sets Status to EIE | IE rather than 0.
BEV is deliberately left clear -- that selects the boot exception
vectors, which is the pre-handoff state, not this one.
- PS2Runtime's constructor no longer memsets m_cpuContext. R5900Context
already zeroes itself before applying its reset values, so the memset
only threw those values away. Threads created later were unaffected
because EeScheduler::startThread assigns `R5900Context{}`, which is
why this presented as "the main thread cannot start threads" rather
than something more obviously global.
(cherry picked from commit 2ac2ce632082ff8b7683f0380361ddcbc410bdbc)
Sinan-Karakaya
force-pushed
the
fix/cop0-status-init
branch
from
August 18, 2026 20:26
fb50773 to
c41bb72
Compare
ran-j
approved these changes
Aug 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Guest code reads COP0
Statusto decide whether interrupts are enabled, and two different bits are involved:IEEIEei/ditoggleWe never execute the boot ROM, so nothing set either one, and
R5900Contextstarted withStatusat 0.That is not cosmetic. libkernel's
StartThreadopens withand returns -1 when that is true: its "you must call
iStartThreadfrom an interrupt handler" guard. WithStatusat zero the guard fires every time, so everyStartThreadfails.Dragon Quest VIII (SLUS-21207) hits this during boot. It creates its CD streaming thread, gets -1, prints
Can't start thread for streaming.and then deadlocks every guest thread, none runnable,EeScheduler::waitForEventin 10 of 10 stack samples. Nothing in the runtime logs anything, because from its point of view the guest asked a question and got an answer.EIEmatters for the matching reason:DIntrreports whether it was set so the caller knows whether to pair it with anEIntr. Starting at zero makesDIntralways answer "already disabled", so the re-enable never happens.Two changes, both needed:
R5900Context's constructor setsStatustoEIE | IEinstead of 0.BEVis deliberately left clear — that selects the boot exception vectors, which is the pre-handoff state, not this one.PS2Runtime's constructor no longermemsetsm_cpuContext.R5900Contextalready zeroes itself before applying its reset values, so the memset only threw them away. Threads created later were unaffected, becauseEeScheduler::startThreadassignsR5900Context{}, which is why this presented as "the main thread cannot start threads" rather than something more obviously global.Verified on a fully recompiled retail binary: before, the streaming thread never starts; after, it does and boot proceeds to the next stage.