From 63d3ce089fdede4069ece443d2a5a03fadd0be5d Mon Sep 17 00:00:00 2001 From: Sylpheed RE agent Date: Tue, 25 Aug 2026 17:54:34 +0000 Subject: [PATCH] re: trigger queue layout at phase+272, and a readable pending count Chasing what makes the phase-1 condition re-evaluate, since the polls do not run continuously. Two method corrections: searching the VM range for '272(rN)' mostly returns VTABLE slot offsets -- 0x82273174 lwz r11,272(r11) is followed by mtctr/bctrl, a virtual call through slot 68, not an access to the phase field. And [phase+272] is not a pointer to a queue but an EMBEDDED container: vt2 (sub_82265DD0) is 'addi r3,r3,272 ; b 0x8226E3B8', passing phase+272 as this. Layout from the push/pop pair (sub_8226E3B8 from built-in 100, sub_8226E220 called every frame from sub_8226D740): +12 list head, +16 current node, +20 element count (zero = empty, tested first by the pop), +24 scratch. The pop returns the record through out-parameters read from node+8: three u32s, a double at +16, another u32 at +24 -- matching the six pointers sub_8226D740 passes in. The actionable part is [phase+272+20], a live pending-trigger count readable from /dev/shm. Watching it alongside [ScriptMission+40] should show when the engine hands the script an event, which is when condition coroutines start -- the thing every phase experiment so far has been blind to. Layout is from disassembly only; not yet verified live. --- docs/re/structures/isl-builtins.md | 41 ++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/docs/re/structures/isl-builtins.md b/docs/re/structures/isl-builtins.md index 33dc3076..80eeff92 100644 --- a/docs/re/structures/isl-builtins.md +++ b/docs/re/structures/isl-builtins.md @@ -230,3 +230,44 @@ facts — the *identifications* (which built-in does what) have held up well. slots, by simulating the constant/stack dataflow of `sub_822FE040`. * Names here are from handler behaviour, not from symbols; `isl.py` prints a bare `builtinN` for anything unread rather than guessing. + +## ✅ The trigger queue at `phase+272` — layout, and a readable pending count + +Chasing what makes the phase-1 condition re-evaluate (the polls do **not** run +continuously — see [script-runtime-probe](../script-runtime-probe.md)). + +**Two method corrections first**, because both nearly sent me the wrong way: + +* Searching the VM's address range for `272(rN)` returns mostly **vtable slot + offsets**, not accesses to the phase field. `0x82273174 lwz r11,272(r11)` is + followed by `mtctr; bctrl` — it is a virtual call through slot 68, nothing to + do with `[phase+272]`. +* `[phase+272]` is **not a pointer to a queue** — it is an **embedded container**. + `vt2` (`sub_82265DD0`) is literally `addi r3,r3,272 ; b 0x8226E3B8`, i.e. it + passes `phase+272` as `this` into the push. + +### Container layout, from the push/pop pair + +`sub_8226E3B8` (push, reached from built-in **100**) and `sub_8226E220` (pop, +called every frame from `sub_8226D740`): + +| offset in the container | meaning | +|---|---| +| `+12` | list head/sentinel (`addi r31, r30, 12`) | +| `+16` | current node pointer | +| `+20` | **element count — zero means empty**; the pop tests it first and returns 0 | +| `+24` | scratch: the popped node is stashed here | + +The pop hands the record out through **out-parameters**, reading from +`node+8`: `+0`, `+4`, `+8` as `u32`s, `+16` as a `double`, `+24` as another +`u32` — which matches `sub_8226D740` passing six pointers into local slots. + +### 🎯 `[phase + 272 + 20]` is a live "pending triggers" counter + +That is the useful part: a single `u32` that says how many triggers are queued, +readable from `/dev/shm` with no debugger. Watching it alongside +`[ScriptMission+40]` should show **when** the engine hands the script an event — +which is exactly the moment the condition coroutines get started, and the thing +every phase experiment so far has been blind to. + +**Not yet verified live.** The layout above is read off the disassembly only.