docs(ledger): localise Banjo-Tooie to a two-instruction loop (R-18) - #188
Conversation
Banjo-Tooie's CPU is executing -- retired advances by a perfectly constant 781,250 per frame -- while the fetch PC never leaves two instructions: 0x800329A8 (JAL 0x8002E9C0) and its delay slot. No exception vector appears at one-cycle sampling, so nothing is faulting. The first measurement of this was a sampling artefact and is recorded as such. Sampling every 400 master ticks (~100 instructions) reported ONE distinct PC over 60,000 samples, which reads as a deadlock; the period was commensurate with the loop. Re-sampled at coprime periods and at one CPU cycle, the answer is stable and different: exactly two PCs, 50/50. Evidence recorded without a cause attached: control never reaches the target, which does contain a valid function prologue; ra holds the JAL's own address rather than the architecturally required PC+8; the preceding instruction is ERET, whose successor should be unreachable; the RSP is halted throughout. Both obvious candidates were then weakened by checking rather than theorising. jal_links_past_the_delay_slot_and_jr_returns_there and eret_resumes_at_epc_and_has_no_delay_slot are already green, and n64-systemtest's CPU category passes at Failed: 0. So neither instruction is simply broken; the behaviour is conditional on state those tests do not set up, which is a different search than "fix JAL". Naming a cause before measuring its boundary has been wrong four times in this ledger, so this lands as a localisation with its caveats, not a diagnosis. Gates: fmt, cargo test --workspace, markdownlint -- green. Docs only. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 8 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 (1)
Comment |
… (R-18) Answers the open question this PR left, and retracts one of its own claims. Dumping all four inter-stage latches at one-cycle granularity: NOTHING retires at 0x800329A8. The JAL enters IC/RF and is discarded there, never reaching RF/EX. The only instruction to reach DC/WB in 400 cycles is the ERET at 0x800329A4, 200 times -- once every two cycles, which is exactly the constant 781,250/frame retirement rate. EPC = 0xFFFFFFFF800329A4, the ERET's own address. With Status = 0x0000FF01 (EXL=0, ERL=0), ERET correctly takes the ERL=0 path and sets PC <- EPC, landing back on itself. So ERET is behaving exactly to specification; the defect is that EPC points at the ERET. Cause.ExcCode is 0 and EXL is clear, so no exception is taken in the loop -- the corruption happens once at frame 12 and the machine then settles into a deterministic self-loop. Retracted: this PR recorded "ra holds the JAL's own address rather than the required PC+8" as a concrete architectural discrepancy. The JAL never executes, so ra was never its business and the observation carried no information. Struck rather than deleted, since the reasoning it invited is worth seeing. Eliminated: the I-cache hypothesis. The fetched words match RDRAM exactly. Remaining: what wrote that EPC. Two candidate sources -- libultra's dispatcher writes EPC via MTC0 before its ERET, or an exception was taken on the ERET itself, which sets EPC to the ERET's address. One probe distinguishes them. Gates: fmt, cargo test --workspace, markdownlint -- green. Docs only. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adjudication — Antigravity review, plus the open question is now answeredThe measurement this PR left openNothing retires at The
Retracted from this PR: I recorded " Suggestion: split the investigation log out of the ledger cell — REJECTED, with the reasonFair observation, and I agree the cell is long. Rejecting because it contradicts a deliberate structure here:
If the row later needs to be readable as a narrative I would rather extract all long residuals into Nitpick: dense paragraphs in a table cell hurt diffs — ACKNOWLEDGED, not acted onTrue, and it is the real cost of the append-only choice above. Mitigated in practice by each append being a single-line change to one row, so the diff is noisy in width but not in line count. Worth revisiting under the Gates re-run: fmt, |
Antigravity review (Gemini via Ultra)This documentation-only PR updates the entry for item R-18 in Blocking issuesNone found. Suggestions
Nitpicks
Automated first-pass review by |
Docs-only. Records where Banjo-Tooie actually stops, plus a sampling artefact caught on the way.
The finding
Banjo-Tooie's CPU is executing —
retiredadvances by a perfectly constant 781,250 per frame (exactly one instruction per 2 CPU cycles) — while the fetch PC never leaves two instructions:At
step = 2master ticks (one CPU cycle, the finest granularity there is) those are the only two PCs across 20,000 samples. No exception vector appears, so nothing is faulting — this is not an exception loop.A sampling artefact, recorded because the correction matters
The first measurement sampled every 400 master ticks (~100 instructions) and reported one distinct PC over 60,000 samples — which reads as a hard deadlock, and I reported it that way. The sample period was commensurate with the loop, so it landed on the same instruction every time.
Same class as "a test whose success and failure paths converge".
Evidence, stated without a cause attached
0x8002E9C0, which does contain a valid prologue (ADDIU sp,sp,-0x38/SW ra,0x24(sp)/ …) — the target is loaded; the transfer does not happen.ra = 0xFFFF_FFFF_8003_29A8— the JAL's own address. MIPS requiresr31 = PC + 8 = 0x8003_29B0. A concrete discrepancy against the architecture, not an interpretation.ERET, whose successor is normally unreachable — so arriving at0x8003_29A8is itself suspect.Caveat kept deliberately:
Cpu::pcis the fetch PC of a five-stage pipeline (ADR 0007), not the retiring position.Both obvious candidates were weakened by checking, not theorising
jal_links_past_the_delay_slot_and_jr_returns_there,a_jal_in_a_delay_slot_links_past_the_outer_target, anderet_resumes_at_epc_and_has_no_delay_slotare already green, and n64-systemtest's CPU category passes atFailed: 0.So neither
JALnorERETis simply broken — the behaviour is conditional on state those unit tests do not set up, which is a materially different search than "fix JAL". Next step is to measure which instruction actually retires at0x8003_29A8; the fetch PC cannot distinguish "the JAL does not transfer" from "something else is fetched there", and the I-cache is an untested intermediary (CACHEIndex/Hit is already a recorded instance of a comment disagreeing with its code).Naming a cause before measuring its boundary has been wrong four times in this ledger, so this lands as a localisation with its caveats, not a diagnosis.
Gates
cargo fmt --all --check·cargo test --workspace·pre-commit run markdownlint --all-files— green. Docs only, no behaviour change.