re: timer_set's second argument is a SECOND countdown, not a warning threshold

mission-phase-timers.md left 180 open as "a limit and a warning
threshold is the obvious reading, but not established".  Reading
sub_822639B8 -- ScriptPhase::Update 0x82263528, same dt as the
stopwatch bank -- settles it the other way:

    if running: [+304] += dt
    if armed:   [+308] -= dt   while [+308] > 0
                else [+312] -= dt, clamped at 0

[+312] is never compared with [+308]; it is decremented, and only in
the A<=0 arm.  Two sequential countdowns.  Disc-wide the second
argument is 180 in all 29 timer_set sites while the first varies
(600 x19, 1200 x8, 900, 1800).

Built-ins 123-127 are vtable slots 90-94 on five scalars at
[phase+304..320].  125 and 126 have ZERO call sites in all 28 scripts:
the script arms, starts and stops this clock but never reads it.

Corrects mission-phase-timers.md, which merged this clock with
stopwatch 0 -- timer_resume starts [+304], set_flag(0) one instruction
later starts the stopwatch the timeline's kind=0 reads.

Docs only; all seven ISL artefacts regenerate byte-identical.
This commit is contained in:
Sylpheed RE agent
2026-08-27 09:03:18 +00:00
parent 3cad5d063f
commit d79f59d24e
6 changed files with 192 additions and 7 deletions

View File

@@ -293,6 +293,49 @@ unknown, what evidence exists, and what the first step would be. Move an item in
no writer searched for — a game-speed setter would be one; and 31 `read_freg`
sites index by a computed value rather than an immediate.
* ✅🔴 **(2026-08-27) `timer_set`'s SECOND ARGUMENT IS A SECOND COUNTDOWN — the
"warning threshold" reading is REFUTED. [structures/isl-mission-timer](structures/isl-mission-timer.md).**
`mission-phase-timers.md` left it open as "a limit and a warning threshold is
the obvious reading, but it is **not** established". Reading the updater
settles it the other way. The **mission timer** is five scalars on the
ScriptPhase — `[+304]` elapsed (up), `[+308]` countdown A, `[+312]` countdown
B, `[+316]` running, `[+320]` armed — zeroed together by `sub_8225FEF8`, and
driven by **`sub_822639B8`, called from `ScriptPhase::Update` at `0x82263528`
with `fmr f1, f30` — the SAME `dt` the stopwatch bank gets**, so it counts the
same seconds. Its body: `if running: [+304] += dt`, and `if armed:` **`[+308]
-= dt` while `[+308] > 0`, ELSE `[+312] -= dt` clamped at 0.** 🔑 A threshold
would be *compared*; `[+312]` is never compared with `[+308]` anywhere — it is
decremented, and the `bc 12, gt` at `0x822639FC` jumps **away** to the A arm,
so B only runs once A is spent. **Two sequential countdowns.** Disc-wide, 29
`timer_set` sites: first argument 600 ×19 / 1200 ×8 / 900 / 1800, **second
argument 180 in all 29** — a per-mission limit then a fixed 3-minute stage.
(Stated as consistent-with, not as the proof; the disassembly is the proof.)
**The five built-ins are vtable slots 9094** (`0x8226C690`): 127
`timer_set(a,b)` arms and loads A/B (as **doubles** from `local[0]`/`local[1]`),
123 `timer_resume` starts the elapsed counter, 124 `timer_stop` stops it and
disarms, 125 `timer_reset` zeroes all three floats, 126 `timer_elapsed` returns
`[+304]`. 🔑 **125 and 126 have ZERO call sites in all 28 scripts** — the script
arms, starts and stops this clock but **never reads it**. Its only consumer read
here is a 32-byte message (`0xAB03E5BA` → the bus at `[0x828F35DC]`) carrying
elapsed, B, running, and `expired = armed && A <= 0`.
🔴 **CORRECTION to `mission-phase-timers.md`:** "timer 0 is started by
`timer_set(1200,180)` then `timer_resume`, with a 1200-second limit" **merges
two clocks**. `timer_set`/`timer_resume` arm and start THIS object;
`set_flag(0)`, one instruction later, starts **stopwatch 0**, which is what the
timeline's `kind = 0` reads. They start together and so read almost the same
value — which is why the merge was invisible. The 1200 s is the mission timer's,
not stopwatch 0's.
✅ This also unblocks the naming collision noted in
[isl-timers](structures/isl-timers.md): 123127 act on five *scalars*, 8/9/93
on a 32-entry *bank*. Not renamed here — that is its own change and its own
artefact diff; everything regenerates byte-identical as it stands.
🟡 Not settled: **who subscribes to `0xAB03E5BA`** — the literal appears at
exactly one site, the producer, but `0xAB03` is the high half of many message
tags here and dispatch is not by literal compare, so one occurrence is **not**
evidence of no consumer. So whether running out of time ends the mission is
unread, as is what B reaching zero does, and whether all 170 `timer_stop` sites
are phase teardown.
## ✅✅ SOLVED — the mission freeze was a modal sign-in dialog (2026-08-26)
`XamShowSigninUI` opens a modal dialog and `xeXamDispatchDialog` blocks the