fix(core): seed the IPL2 exit state so CIC-6105 titles boot (R-23) - #175
Conversation
Ocarina of Time, Majora's Mask, Banjo-Tooie and Donkey Kong 64 now boot through hle_boot. Their IPL3 is a different program: it self-descrambles with an XOR loop that reads 0x44(t3) - DMEM + 0x40, its own image - and hle_boot never seeded the IPL2 exit state IPL3 inherits, so t3 was 0 and the descramble read low RDRAM. Measured, not invented. The registers were captured at IPL3's entry (0xA4000040) by running the console's real IPL1/IPL2 out of a PIF ROM dump through real_pif_boot, then keeping only the values IDENTICAL across ROMs of different CIC variants - Banjo-Tooie (6105) compared against Super Mario 64 (6102): at=1, a2=0xA4001F0C, a3=0xA4001F08, t0=0xC0, t2=0x40, t3=0xA4000000, s4=1, ra=0xA4001550. v0, v1, a0, a1 and t4-t9 are deliberately excluded and asserted absent: they carry IPL2's running checksum of that particular cartridge's IPL3 and differ per ROM, so freezing them would fabricate a value the boot computes. s6 is the CIC seed, already set per-CIC. Corroborated independently rather than by inspection: Banjo-Tooie under HLE now halts at pc=0x800329a8, the exact PC real_pif_boot reaches on the same ROM, with retired counts within 0.3%. Mutation-checked on t3 alone - remove it and Banjo-Tooie returns to a NOP sled with RDRAM entirely empty. Consequences beyond the four titles. The commercial capstone's CIC-6105 skip is deleted, since there is nothing left to skip. The T-71-003 microcode witness set doubled from 4 titles to 8: Ocarina of Time executes 733 distinct RSP instructions and submits 17,900 RDP commands, Majora's Mask 732 and 2,310, Conker's Bad Fur Day 414, Jet Force Gemini 343. Jet Force Gemini scans out 69,479 lit pixels. n64-systemtest is unchanged at 90 suite-wide with Phase 1 categories Failed: 0. Still open in R-18: Banjo-Kazooie, 1080 Snowboarding and F-Zero X (CIC-6103/6106) fill IMEM but never start the RSP, and Blast Corps and Bomberman 64 never load microcode at all. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 4 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthrough
ChangesCIC-6105 HLE boot support
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant CommercialBoot
participant hle_boot
participant CPU
CommercialBoot->>hle_boot: boot selected commercial ROM
hle_boot->>CPU: seed ROM-independent IPL2 exit registers
CPU-->>CommercialBoot: execute IPL3 and report boot progress
Possibly related PRs
🚥 Pre-merge checks | ✅ 9 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (9 passed)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@crates/rustyn64-core/src/boot.rs`:
- Around line 313-321: Extend the excluded checksum-register table in the boot
test to include registers 13, 14, 15, and 24 for t5 through t8, alongside the
existing t4 and t9 entries. Ensure every documented t4–t9 register is asserted
as unset while preserving the current v0, v1, a0, and a1 checks.
In `@crates/rustyn64-test-harness/tests/commercial_boot.rs`:
- Around line 130-133: Restrict the boot witness around roms.remove(0) to
CIC-6105: deterministically select a ROM identified as CIC-6105, or classify the
selected ROM and assert that classification before running it. Ensure the test
proves a CIC-6105 ROM was exercised rather than an arbitrary folder entry.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 55068cd8-f199-4674-ba8c-98c2f5b9c23a
📒 Files selected for processing (4)
CHANGELOG.mdcrates/rustyn64-core/src/boot.rscrates/rustyn64-test-harness/tests/commercial_boot.rsdocs/accuracy-ledger.md
…ices Review follow-up on R-23. The exclusion table asserted only t4 and t9 while the documentation claims t4-t9, so a seed slipping into t5, t6, t7 or t8 would have passed. All six are now asserted. Listing only the endpoints of a documented range is the same shape of gap as a comment that states a rule the code does not implement. Register indices are now named constants (GPR_AT, GPR_A2, GPR_A3, GPR_T0, GPR_T2, GPR_T3, GPR_S4, GPR_RA) matching the existing GPR_SP, so the seeds read as intent rather than as bare numbers. Corrects an overstatement I introduced. The comment said "every CIC variant is HLE-bootable since R-23 closed", which is wrong: R-18 still has open 6102/6103/ 6106 cases. The capstone's actual claim is narrower - whichever title is selected reaches its own code in RDRAM - and the comment now says that. Rather than scope the capstone to CIC-6105 as suggested, which would narrow a general capstone to prove one ticket, it now classifies the selected ROM and REPORTS whether a 6105 title was exercised. Not an assertion, because a corpus may legitimately contain none - but said out loud, so "R-23 still holds" is never inferred from a run that never tested it. Against the staged corpus it prints "including a CIC-6105 title, so R-23 is exercised". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Antigravity — magic register indices. ADOPTED. |
docs/cart.md described hle_boot as seeding "the post-IPL3 CPU/COP0/PI-DOM1 state", which no longer covers what it does or why it matters. It now spells out both register groups: sp = 0xA4001FF0 (IPL3's stack, cited to the IPL1 listing, whose absence caused R-18's NOP sled) and the ROM-independent IPL2 exit state measured from a real PIF ROM boot, with t3 called out as the register CIC-6105's self-descrambling IPL3 reads through. It also records what is deliberately NOT seeded and why: v0, v1, a0, a1 and t4-t9 carry IPL2's checksum of that cartridge's IPL3 and differ per ROM, so freezing them would fabricate a computed value. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Docs-As-Spec Sync — ADOPTED. It now documents both register groups explicitly:
And, equally importantly, what is deliberately not seeded: |
Antigravity review (Gemini via Ultra)This PR seeds the ROM-independent IPL2 exit state during Blocking issuesNone found. Suggestions
Nitpicks
Automated first-pass review by |
|
Note for the record before merging: the one remaining failed pre-merge check (Docs-As-Spec Sync) is stale, not unaddressed. Its stated premise is " Verified on the branch rather than asserted: Merging on that basis — the finding is addressed, only the verdict predates it. Everything else is green: CI 9/9, all review threads resolved, no outside-diff findings outstanding. |
|
Correction to my earlier reply on this PR. I said Antigravity's magic-number suggestion was adopted. It was not — the change never landed. The script that applied it hit an assertion on a later, unrelated substitution and exited before writing the file, so none of its earlier edits were saved either. A separate hand-edit fixed the exclusion table, which made the resulting diff look like the whole batch had applied. Caught by grepping Fixed in #176. No behaviour change — identical indices, and the R-23 seed test asserts the register values, which is also why nothing failed: nothing was wrong, only unclear. Flagging it here rather than quietly landing #176, since the false "adopted" is in this thread. |
* refactor(core): name the IPL2-exit GPR indices (missed in #175) The named-constant change I reported as adopted on #175 never landed. The script that applied it hit an assertion on a later, unrelated substitution and exited before writing the file, so none of its earlier edits were saved either. A separate edit fixed the exclusion table by hand, which made the diff look like the whole batch had applied. The seeds now use GPR_AT, GPR_A2, GPR_A3, GPR_T0, GPR_T2, GPR_T3, GPR_S4 and GPR_RA alongside the existing GPR_SP. GPR_T3 carries its own doc comment naming what it is for, since it is the register that decides whether CIC-6105 boots. No behaviour change: the indices are identical, and the R-23 seed test asserts the resulting register values either way. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor(cpu): move the GPR name constants into rustyn64-cpu::regs::gpr Review follow-up on #176. The constants were module-private to boot.rs, which invites a second private copy elsewhere - and a second copy is how $t3 and $t4 eventually swap in one of them. They now live in rustyn64_cpu::regs::gpr, beside the register file they index. Naming the existing s3-s7 block through the same vocabulary exposed a real duplicate: my S4 seed wrote r20 = 1, and the s3-s7 block three lines later wrote r20 = 1 again as tv_type. Same register, same value, two writes, no second source of truth. The duplicate is removed and the reason it is absent is stated, so it does not get "helpfully" restored. Oracle: n64-systemtest unchanged at 90 suite-wide, Phase 1 categories Failed: 0 - measured, not assumed, though this change emits identical register writes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ocarina of Time boots, and runs its own microcode
Closes ledger R-23. CIC-6105 titles — Ocarina of Time, Majora's Mask,
Banjo-Tooie, Donkey Kong 64, Conker, Jet Force Gemini — now boot through
hle_boot.Their IPL3 is a different program: it opens with a self-descrambling XOR loop
reading
0x44(t3)— DMEM + 0x40, its own image.hle_bootnever seeded the IPL2exit state IPL3 inherits, so
t3was0and the descramble read low RDRAM.Measured, not invented
The registers were captured at IPL3's entry (
0xA400_0040) by running theconsole's real IPL1/IPL2 out of a PIF ROM dump via
real_pif_boot, then keepingonly values identical across ROMs of different CIC variants (Banjo-Tooie/6105
vs Super Mario 64/6102):
at=1,a2=0xA400_1F0C,a3=0xA400_1F08,t0=0xC0,t2=0x40,t3=0xA400_0000,s4=1,ra=0xA400_1550v0/v1/a0/a1andt4–t9are excluded, and asserted absent — theycarry IPL2's running checksum of that cartridge's IPL3 and differ per ROM, so
freezing them would fabricate a value the boot computes.
s6is the CIC seed,already set per-CIC.
Corroboration and mutation check
pc=0x800329a8— the exact PCreal_pif_bootreaches on the same ROM, retired counts within 0.3%. Twoindependent boot paths converging on the same address is stronger evidence than
either alone.
t3alone: remove that one register and Banjo-Tooiereturns to a NOP sled with RDRAM entirely empty. It is the decisive seed,
exactly as predicted from reading the 6105 IPL3.
Consequences beyond those titles
T-71-003witness set doubled, 4 titles → 8:Ocarina of Time submitting 17,900 RDP commands is the real F3DEX pipeline running
end to end. Jet Force Gemini scans out 69,479 lit pixels.
n64-systemtest unchanged at 90 suite-wide, Phase 1 categories
Failed: 0.Still open (R-18 remainder)
4094 bytes but never start the RSP.
Not claimed as fixed — a different failure, upstream of the RSP seam.
Gates
cargo fmt --all --check,cargo clippy --workspace --all-targets -- -D warnings,cargo test --workspace,RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps,pre-commit run markdownlint --all-files— each run separately withits exit status checked, no pipes. Plus
#[ignore]d n64-systemtest, bothcommercial capstones, and the microcode witness.
🤖 Generated with Claude Code