# ✅ Each phase carries a TIMELINE — 675 scheduled routines across the disc The trailing data table found at the end of every phase region ([isl-stream-entry-points](../isl-stream-entry-points.md)) is decoded. It is the mission's **scripted event schedule**. ## Layout After the phase's code ends — at the first value of that phase's mission-level `0x1883` record — comes a run of 8-byte typed records, tag `0x19` = int, `0x1A` = IEEE float: ``` int N -- entry count N x [ int offset ; float t ; int kind ] ``` `1 + 3N` matches the record count in **every** phase measured: Stage 02's three phases hold 76, 40 and 55 records for N = 25, 13 and 18. ## ✅ The checks | | | |---|---| | schedule entries disc-wide | **675** | | `0x1A` float records disc-wide | **675** — the same number, independently counted | | offsets landing on the instruction stream | **675 / 675 = 100.0 %** | | control: random 4-aligned offsets | 33.3 % | The float count and the entry count are derived by different routes and agree exactly, and every single offset resolves. `kind` is **0** (556) or **5** (119). ## ✅ The floats are seconds The distribution is unmistakable: 0, 0.5, 1, 4, 5, 30, 50, 60, 90, 120, 150, 170, 180, 210, 240, 270, 300, 330, 360, 420, 570, 1020, 1080, 1140, 1170 … mission times, not fractions. And the targets are what a schedule would point at — small one-shot coroutines: ``` 002C04: set.i local[0] = 1 002C1C: set.i local[4] = 1 002C34: set.f local[8] = 0.4 002C50: call builtin106(0x1, 0x1, 0.4) 002C5C: call end_coroutine ``` ## 🟡 A runtime cross-check — consistent, and not proof The closed `REMAINING OB` work measured, on the emulator over n=5 runs, that Stage 02's squadrons arrive at **t = 0, 120 and 210 s**. All three times are in phase 1's static schedule, and **120 and 210 each appear twice**, which is what two squadrons arriving together would look like. ⚠️ Stated as consistency rather than confirmation: these are round numbers, and phase 1 has ~22 distinct times spread over 0–1170, so three specified round values all being present is not by itself unlikely. The **doubling** is the sharper detail, and it was not predicted in advance. ## Artefacts `data/isl-stage02-schedule.txt` and `data/isl-schedule-all.txt` (all 28 stages, 675 entries), generator `isl_report.py schedule`. The four earlier artefacts regenerate byte-identical. ## ✅ The CONSUMER — `sub_822748D0`, called from `ScriptPhase::Update` 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 🔑 **(2026-08-27) And the array it indexes is the one `read_freg` exposes.** Built-in 9's stub bounds-checks `0 <= local[0] < 32` and then ``` 822725E8 lwz r10, 88(r31) ; [phase+88] 822725EC rlwinm r11, r11, 2, 0, 29 ; i * 4 822725F0 lfsx f0, r11, r10 ; a FLOAT ``` — exactly the array and the indexing the timeline walker uses. So `kind` is not a selector over some small private set of clocks: **it is an index into the same 32-entry float register file the script reads with `read_freg(i)`.** A clear condition like `read_freg(0) < 1200` and a timeline entry with `kind = 0` read the same register. 🔴 The "six clocks" framing below was mine and is too narrow — the file has **32** slots; the timeline data merely never uses more than indices 0 and 5. `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. * ~~**What the six clocks are.**~~ ✅ **SETTLED — [isl-timers](isl-timers.md).** The two arrays are a bank of **32 stopwatches**: `[phase+88]` is the current value, `[phase+104]` last frame's, `[phase+120]` a *running* flag, and `sub_822710D0` — called from `ScriptPhase::Update` — does `prev[i] = cur[i]; if running[i] then cur[i] += dt` every frame. Built-in 8 starts a timer, 9 reads it, 93 stops it. So the walker's `prev <= t < cur` test is "did timer `kind` cross `t` this frame", and **`t` is in SECONDS** — `dt` is `frames * (1/60) * 10000` ticks scaled back by `1e-4`, clamped at 3200 ticks = 0.32 s. That the numbers in the table *looked* like mission times is no longer the evidence. **675 / 675 timeline entries name a timer their own phase starts** (control 11.2 %), which is why `kind` is only ever 0 or 5: every phase opens `set_flag(5); set_flag(0)`. * **`sub_822737C8`**, which actually starts the routine, is not read — it is presumably the same spawner `start_coroutine` uses, but that is unchecked.