diff --git a/docs/HANDOFF-LOCAL-AGENT.md b/docs/HANDOFF-LOCAL-AGENT.md index 4da0ca0f..b6ab939f 100644 --- a/docs/HANDOFF-LOCAL-AGENT.md +++ b/docs/HANDOFF-LOCAL-AGENT.md @@ -1,74 +1,116 @@ -# Local-agent handoff tasks (Qwen 27B) +# Local-agent handoff tasks (Qwen 27B) — M-20 -Two self-contained, low-risk tasks that are **disjoint from the active Jungle -`-level_37` G_MTX investigation** (that work is in `port/fast3d/gfx_pc.cpp` -around `gfx_sp_matrix` / `seg_addr` / `gfx_run_dl` — do NOT touch that file). +The two M-19 tasks (D63 scaffolding removal in `libultra.c`; PORT-LEARNINGS +§E entries) are **done and committed** (`1fc3cff6`). Below are three fresh +tasks. -Source: `docs/BACKLOG.md` "Technical cleanup" section. +**All three are pure static analysis** — read code, produce a table, write +a doc section. **No builds. No running the game. No `.c` edits.** Deliverable +is a markdown write-up appended to `docs/PCPortResearch.md` §F (add the new +label to the §F index at the top of the file) plus, where noted, one line in +`docs/PORT-LEARNINGS.md`. + +Disjoint from the active session work (intro-render investigation in +`port/fast3d/`, propDef converter audit in `tools_pc/d88_propdefs.py`). **Do +NOT touch `port/fast3d/*`, `tools_pc/d88*`, or any `.c`/`.cpp` file.** + +READ FIRST for all tasks: `docs/PORT-LEARNINGS.md` (whole file — it is one +screen), then the specific §F entries named in each task (jump via the §F +index; do NOT linear-read `PCPortResearch.md`). --- -## Task A — doc only, zero code risk (do this first) +## Task A — `osVirtualToPhysical()` latent-truncation site audit (D131 corollary) -**Goal:** add two entries to `docs/PORT-LEARNINGS.md`. +**Background:** `docs/PORT-LEARNINGS.md` §A "D131 corollary". The port shim +`osVirtualToPhysical(va)` returns `(u32)(uintptr_t)va`, which silently drops +the high 32 bits of a **compiled module symbol** pointer (`.bss` / `.rodata` +address, image-based at `0x140000000`). D131 fixed the one live instance +(`explosionRenderPropSmoke`) centrally in `seg_addr()` in +`port/fast3d/gfx_pc.cpp`, which now restores the module high word for a +fallthrough w1 in `[0x40000000, 0x70000000)`. -1. New bug class **"D24-implications — host-scheduling nondeterminism / fake - priority semantics"**: the pthread kernel does not enforce N64's 0–31 - thread priorities (`osYieldThread` == `Sleep(0)`), so thread interleavings - impossible on real hardware can occur, and frame timing carries host - jitter. **How to apply:** when a flaky timing bug appears (especially - Phase 3 audio underruns), reach first for host thread priorities - (`SetThreadPriority`: scheduler thread time-critical, tick thread normal) - and the deferred `GE_DETERM` fixed-tick mode (§F D117) — NOT for kernel - changes. Link `[[d117]]`-style to the D117 finding. -2. Watch item under the same class: **`osYieldThread` == `Sleep(0)`** is the - one place the port fakes cooperative scheduling. If a level-sweep hot loop - misbehaves under host load, inspect this shim first. Keep as-is until then. +**Task:** grep every call site of `osVirtualToPhysical(` across `src/` +(`grep -rn 'osVirtualToPhysical(' src/`). For each, produce a table row: -Put both in section **E. Process / method notes** (or a new subsection if -that reads better). Match the file's existing terse style. No code changes. +| file:line | argument expression | arg is: runtime-DRAM ptr / compiled symbol / segmented / unknown | reaches a GBI w1 (`gSPMatrix`/`gSPVertex`/`gSPDisplayList`/`gDma*`)? | covered by D131 `seg_addr` fix? | residual risk | -**Verify:** file still renders as markdown; `git diff` is docs-only. +Classify "compiled symbol" when the argument is `&someGlobal`, a `.bss`/ +`.rodata` array/struct address, or a function-local `static`. Classify +"runtime-DRAM ptr" when it is the result of a heap/bank allocation +(`mempAlloc*`, `malloc`, a `->` deref of loaded data). Note which are armed +only when a specific effect first draws (explosion / glass / blood / +bondview2 — the §A note lists these). -**On expiry:** commit whatever is written; partial is fine. +**Deliverable:** the table + a short "residual risk" summary (are there sites +that reach fast3d NOT through `seg_addr`, e.g. a raw `memcpy` or a +non-w1 use?) → `docs/PCPortResearch.md` §F, new subsection +**"D131 corollary — osVirtualToPhysical site audit (M-20)"**, label added to +the §F index. + +**On expiry:** commit the partial table. --- -## Task B — mechanical dead-code removal, single file +## Task B — in-place ROM-struct relayout aliasing audit (D130 corollary) -**Goal:** remove the closed-out **D63** debug scaffolding from -**`port/src/libultra.c` ONLY** (leave `port/fast3d/gfx_pc.cpp`'s D63 code -alone — that file is in use by the Jungle work; a separate cleanup later). +**Background:** `docs/PORT-LEARNINGS.md` §A "D130 corollary". `romdataFixupFont` +(`port/src/romdata.c`) re-lays-out a ROM struct array (`fontchar` 24→32B) *in +place*: `dst = base + PCstride*i`, `src = base + N64stride*i`, and for small +`i` the two overlap by less than the per-element read span, so a +field-by-field copy loop clobbers a not-yet-read source field. D130 fixed +that one loop by staging all fields into locals first. -**FILES YOU MAY TOUCH:** `port/src/libultra.c` only. +**Task:** find every other in-place N64→PC struct-array relayout. Search: +`grep -rn 'romdataFixup\|Fixup\|relayout\|in.place\|24.*32\|->32B' port/src/` +and read `port/src/romdata.c` fully (it is the main offender file). For each +loop that (a) reads struct fields from a source offset and writes to a +different-stride destination **in the same buffer**, tabulate: -D63 is a closed finding; this code is dead weight in the hottest function in -the port. Remove, in `port/src/libultra.c`: +| file:line (fn) | struct | N64 stride | PC stride | loop direction | dst/src overlap for low i? | reads all fields before writing? | verdict (SAFE / ALIASING BUG / needs staging) | -- the helper block around lines ~490–610: `d63WatchGunbarrelSlot`, - `d63CheckSlot` (the "log only when the slot value changes" fn), the - `D63Act` struct + `s_d63act` ring buffer + `D63_ACT_N`, `d63On`, `d63Tid`, - `d63Act`, the watchdog thread fn + `d63WatchdogStart`, and the - `D63 clobber-watch` block. -- every call site marked `/* TEMP D63 */`: `d63WatchdogStart()` (~261), - `d63Act("send")` + `d63WatchGunbarrelSlot()` (~617–618), - `d63Act("recv")` + `d63WatchGunbarrelSlot()` (~658–659), - `d63Act(...)` (~1110), and the two `getenv("GE_D63")` gfx-task logging - blocks (~1117–1147). +**Deliverable:** the table → `docs/PCPortResearch.md` §F, new subsection +**"D130 corollary — in-place relayout audit (M-20)"**, label in the §F index. +If you find a concrete ALIASING BUG, describe the fix (stage fields into +locals, as D130 did) in prose in the write-up — **do not edit the `.c` +file**; flag it for the integrator. -Grep `D63` and `d63` in the file afterwards — should be zero hits. +**On expiry:** commit the partial table. -**CONSTRAINTS:** no behavior change; deletion only; do not touch any other -file; do not remove non-D63 code even if it looks unused. +--- -**BUDGET:** <= 3 build cycles. `export PATH="/c/msys64/mingw64/bin:$PATH" && -./build-pc.sh ntsc-final` must stay green (240/240 link). +## Task C — negative / OOB glyph-index audit (PORT-LEARNINGS §A last bullet) -**Verify:** build green; `grep -in d63 port/src/libultra.c` empty; -`./build-pc/ge007.x86_64.exe -level_09` still boots to frames (no crash in -first ~15 s). +**Background:** `docs/PORT-LEARNINGS.md` §A final bullet: `chars[*text - 0x21]` +with a control byte (`*text < 0x21`) indexes before `chars[0]`. Benign on +N64 (hits the adjacent 4-byte kerning table → garbage-but-valid glyph); +**fatal on PC** — the widened `fontchar` has an 8-byte `pixeldata` at a +higher offset, so a negative index reads a wild pointer → fast3d AV. No +level is known to feed a control byte to HUD text yet, so this is latent. -**On expiry:** `git checkout port/src/libultra.c` (revert) and write up which -symbols were still entangled. +**Task:** in `src/game/textrelated.c` (and any other file that indexes a +`fontchar`/`chars` array — grep `chars[` and `- 0x21` / `- 33` across +`src/game/`), enumerate every glyph-index expression on the `textRender*` / +`textMeasure*` ASCII paths. For each, tabulate: -**REPORT:** list of removed symbols + line count delta + build result. +| file:line (fn) | index expression | can the byte be `< 0x21` or `>= last glyph`? (what feeds it — dialogue? HUD? menu? filename?) | guarded already? | consequence on PC if OOB | + +**Deliverable:** the table + a recommendation: where exactly a +`GLYPH_IDX` clamp macro (`clamp to [0, nglyphs-1]`, or skip the glyph) would +go, expressed as a prose description of the one-line `#ifdef PORT` guard per +site — **do not edit `.c`**. → `docs/PCPortResearch.md` §F, new subsection +**"Glyph-index OOB audit (M-20)"**, label in the §F index. Add one terse +line to `docs/PORT-LEARNINGS.md` §A noting the audit is done and where the +clamp sites are. + +**On expiry:** commit the partial table. + +--- + +## Verify (all tasks) + +`git diff --stat` shows **only** `docs/PCPortResearch.md` and (Task C) +`docs/PORT-LEARNINGS.md` changed. No other files. Markdown still renders. +`git status` shows no deleted files. Commit message per task, e.g. +`docs: M-20 Qwen — D131 corollary osVirtualToPhysical site audit`. +**No `Co-Authored-By` / `Claude-Session` trailers** (repo policy). diff --git a/docs/LEVEL-STATUS.md b/docs/LEVEL-STATUS.md index 6808a8df..78ae146b 100644 --- a/docs/LEVEL-STATUS.md +++ b/docs/LEVEL-STATUS.md @@ -12,7 +12,7 @@ Build = `f2beae4b` + `tools_pc/d88_propdefs.py` (D123) + `port/src/gimgfixup.c` | Level | # | Status | Crash site | Class | |---|---|---|---|---| -| Bunker1 | 09 | **CRASH (regressed since M-18)** | boot crash `frames=0`, PC `0x1400066fc`, fault `0x00a2fc68` (stack ptr, top nibble masked). Reproduces on clean master `0a4b3bae` → pre-existing, not D131. Bisect D125→HEAD. | **new** | +| Bunker1 | 09 | **PASS** (M-20: re-verified — 1800 frames crash-free, no `ge007.crash.log`, direct `-level_09` boot at ~90us/frame). The M-19 "boot crash `frames=0` PC `0x1400066fc`" did **not** reproduce on `1fc3cff6` in three separate runs → it was sweep flakiness / D117 nondeterminism under machine load, not a real regression. | — | | Silo | 20 | **PASS** 91.7% | — | — | | Archives | 24 | **PASS** 91.1% | — | — | | Train | 25 | **PASS** 87.0% | — | — |