diff --git a/docs/re/BACKLOG.md b/docs/re/BACKLOG.md index 727834a..e38dd65 100644 --- a/docs/re/BACKLOG.md +++ b/docs/re/BACKLOG.md @@ -65,9 +65,20 @@ unknown, what evidence exists, and what the first step would be. Move an item in each appear TWICE** — but the times are round numbers, so presence alone is weak; the doubling is the sharper detail. Artefacts `data/isl-stage02-schedule.txt` and `data/isl-schedule-all.txt`; the four earlier artefacts regenerate byte-identical. - 🟡 **Still NOT what starts the unreachable code** — 0 of the 675 targets are - unreached run-starts, so the ~15 % gap stands. The consumer is unread, and whether - the clock is per-phase or per-mission is an inference from layout, not a read. + ✅ **(2026-08-27) THE CONSUMER IS FOUND — `sub_822748D0`, called from + `ScriptPhase::Update` (`sub_82263408`).** The phase initialiser stores + `[phase+240] = code_base + entry_a` (`add r10,r26,r21; stw r10,240(r30)`), and + `+240` has exactly two readers in the ISL region. The walker confirms EVERY field + independently of my structural inference: count at `table+4`, `kind` at + `group+20`, float `t` at `group+12`, offset at `group+4`, **stride 24**. + 🔑 **`kind` is a CLOCK INDEX, not a flag** — `rlwinm kind,2,0,29` + `lfsx` into + float arrays at **`[phase+104]`** and **`[phase+88]`**; an entry fires when its + `t` lies between the two readings (the "which events did this frame cross" test). + ✅ Both arrays are phase fields, so **the clock is PER PHASE** — previously an + inference from layout, now a read. 🟡 Still open: what the six clocks are (only + indices 0 and 5 are used); `sub_822737C8`, which actually starts the routine, is + unread. 🟡 **Still NOT what starts the unreachable code** — 0 of the 675 targets + are unreached run-starts, so the ~15 % gap stands. ## ✅✅ SOLVED — the mission freeze was a modal sign-in dialog (2026-08-26) diff --git a/docs/re/structures/isl-schedule.md b/docs/re/structures/isl-schedule.md index c56e8a1..b2a9adb 100644 --- a/docs/re/structures/isl-schedule.md +++ b/docs/re/structures/isl-schedule.md @@ -64,13 +64,69 @@ sharper detail, and it was not predicted in advance. 675 entries), generator `isl_report.py schedule`. The four earlier artefacts regenerate byte-identical. -## 🟡 Not settled +## ✅ The CONSUMER — `sub_822748D0`, called from `ScriptPhase::Update` -* **`kind` (0 or 5) is not identified.** Two behaviours, no reading. +Found statically. The phase initialiser `sub_82270DF8` takes the `0x1883` +record's six words as arguments and stores + +``` +82270FC8 stw r26, 232(r30) ; [phase+232] = code base (already documented) +82270FDC add r10, r26, r21 ; base + entry_a +82270FEC stw r10, 240(r30) ; [phase+240] = THE TABLE POINTER +``` + +`[phase+240]` has exactly **two** readers in the ISL region, and one of them, +`sub_822748D0`, is called from **`sub_82263408` = `ScriptPhase::Update`**. It is +the timeline walker, and it confirms every field of the decode independently: + +``` +822748E0 lwz r11, 240(r30) ; the table +822748E4 lwz r10, 4(r11) ; N -- the count, at table+4 ✓ +822748E8 addi r11, r11, 8 ; skip the count record ✓ +822748F4 addi r31, r11, 20 ; -> group+20 +822748FC lwz r11, 0(r31) ; kind at group+20 ✓ +82274900 lfs f0, -8(r31) ; t at group+12, as a FLOAT ✓ +82274904 lwz r10, 104(r30) ; clock array A +82274908 rlwinm r11, r11, 2,0,29 ; kind * 4 +8227490C lfsx f13, r10, r11 ; A[kind] +82274910 fcmpu cr6, f13, f0 +82274914 bc ... skip +82274918 lwz r10, 88(r30) ; clock array B +8227491C lfsx f13, r10, r11 ; B[kind] +82274920 fcmpu cr6, f0, f13 +82274924 bc ... skip +8227492C lwz r5, -16(r31) ; offset at group+4 ✓ +82274930 lwz r4, 232(r30) ; the code base +82274934 bl 0x822737C8 ; start the routine +8227493C addi r31, r31, 24 ; STRIDE 24 = three 8-byte records ✓ +``` + +Count position, float position, offset position and the 24-byte stride are all +confirmed by the engine's own reader rather than by my structural inference. + +### ✅ `kind` is a CLOCK INDEX, not a flag + +`rlwinm r11, r11, 2, 0, 29` then `lfsx` — `kind` is multiplied by 4 and used to +index **float arrays at `[phase+104]` and `[phase+88]`**. So `kind` selects +*which timer* the entry is scheduled against, and 0 / 5 are two of at least six. + +The entry fires only when its `t` lies between the two clock readings — the +classic "which events did this frame cross" test, with one array holding the +previous value and the other the current. 🟡 Which array is which follows from +the comparison directions (`A[kind] > t` skips; `t >= B[kind]` skips) and is +stated as a reading of those two branches, not measured at runtime. + +### ✅ And the clock is PER PHASE + +Both arrays are fields of the phase object, so each phase has its own timers — +which is why every phase's table restarts at t = 0. That was previously an +inference from the layout; it is now a read. + +## 🟡 Not settled * **This is not what starts the unreachable code.** All 675 targets are already reachable — **0 are unreached run-starts** — so the ~15 % gap stands. -* **Whether the schedule is per-phase-clock or mission-clock** is not - established; each phase's table restarts at t=0, which suggests per-phase, but - that is an inference from the layout, not a read of the consumer. -* **The consumer is unread.** Nothing here shows the engine walking this table; - the decode rests on the structural checks above. +* **What the six clocks are.** `kind` indexes arrays at `[phase+88]` and + `[phase+104]`; only indices 0 and 5 are ever used by the data, and neither + array's contents were traced to a source. +* **`sub_822737C8`**, which actually starts the routine, is not read — it is + presumably the same spawner `start_coroutine` uses, but that is unchecked.