diff --git a/docs/re/BACKLOG.md b/docs/re/BACKLOG.md index 986bc180..8f48420d 100644 --- a/docs/re/BACKLOG.md +++ b/docs/re/BACKLOG.md @@ -6,6 +6,20 @@ unknown, what evidence exists, and what the first step would be. Move an item in --- +* ✅ **(2026-08-27) The trigger-queue appender is IDENTIFIED — [isl-trigger-queue](isl-trigger-queue.md).** + `isl-builtins.md` left "what actually appends a node is still unidentified" and + proposed a gdb watchpoint; it is static after all. The container embeds its inner + list at `+12`, so the appender must pass `container+12` to the generic helper + `sub_8226EAB8` — only 2 of its 16 callers do, and one of them sets + `addi r29, r24, 272` first. That is **`sub_8226A0D8` = ScriptPhase vtable slot 28 + = BUILT-IN 25**, with **ZERO real `bl`s** in the image, so it is reached only as a + built-in: **the SCRIPT appends triggers, not the engine.** 🔴 **REFUTED in the same + pass:** built-in 25 does NOT carry the coroutine entry a trigger fires — 0 % of its + staged operands land on an instruction boundary against a **38.7 %** chance control, + and it has only **2 call sites in Stage 02**, far too few for ~15 % of the code. So + what starts the 389 unreachable routines is **still open**. 🟡 A trigger node's + layout and its consumer are unread; built-in 25 stays unnamed. + ## ✅✅ SOLVED — the mission freeze was a modal sign-in dialog (2026-08-26) `XamShowSigninUI` opens a modal dialog and `xeXamDispatchDialog` blocks the diff --git a/docs/re/isl-trigger-queue.md b/docs/re/isl-trigger-queue.md new file mode 100644 index 00000000..0dde8bca --- /dev/null +++ b/docs/re/isl-trigger-queue.md @@ -0,0 +1,81 @@ +# ✅ What appends to the trigger queue — built-in 25, not the engine + +[`structures/isl-builtins.md`](structures/isl-builtins.md) took the trigger queue +at `phase+272` a long way — its layout, its live count at `+272+20`, and the +generic appender `sub_8226EAB8` — and stopped at: *"What actually appends a node +is still unidentified."* Its suggested next step was a **gdb watchpoint** on a +running emulator. + +It is a static question after all. + +## The search that works + +`sub_8226EAB8` is a shared list helper with 16 callers, so it cannot be +identified by name. But the corpus records that the trigger container **embeds +its inner list at `+12`**, so the caller must hand it `container + 12`. Two of +the 16 do: + +``` +0x8226E208 addi r3, r30, 12 +0x8226A2B4 addi r3, r29, 12 +``` + +and in the second, `r29` is set 34 instructions earlier: + +``` +8226A22C addi r29, r24, 272 ; r24 = the ScriptPhase +8226A2B4 addi r3, r29, 12 ; -> phase + 272 + 12 + bl 0x8226EAB8 ; the generic append +``` + +`phase + 272 + 12` is exactly the inner list whose count the corpus watched at +`phase + 272 + 20`. **`sub_8226A0D8` is the appender.** + +## 🔑 And it is a BUILT-IN, so the script appends its own triggers + +`sub_8226A0D8` is **ScriptPhase vtable slot 28**, and slot 28 is what +**built-in 25**'s stub tail-calls. Its entry is the standard built-in shape — +`r3` = the phase, `r4` = the `local[]` base, `[phase+324]` indexed by `local[4]`. + +**There are ZERO real `bl 0x8226A0D8` instructions in the image.** It is reached +only through the vtable, i.e. only as a built-in. So the queue is populated by +the **script**, not by the engine — which retires the residual "the engine moves +records into the phase's queue each frame" reading that `isl-builtins.md` already +flagged as unsupported by its call site. + +Built-in 25's argument shape agrees with the corpus's independently measured +tables: it is in `UNIT_ARG` (a unit at `local[4]`) and in `SYM1_SLOTS[12]` (a +symbol-table-1 name — a route, message or objective — at `local[12]`). + +## 🔴 REFUTED: this does not explain the unreachable routines + +The attractive next step was that built-in 25 carries the coroutine entry a +trigger fires, which would have seeded the 389 routines reachable from no static +entry. It does not. + +At Stage 02's built-in 25 sites, taking each staged operand as a code offset from +the phase base: + +| staged slot | values | landing on an instruction boundary | +|---|---|---| +| 0 | 2 | **0 — 0 %** | +| 4 | 2 | **0 — 0 %** | +| 8 | 2 | **0 — 0 %** | +| 12 | 2 | **0 — 0 %** | +| *control:* an arbitrary 4-aligned offset | — | **38.7 %** | + +Nothing scores even at chance. And the scale is wrong anyway: built-in 25 has +**only 2 call sites in Stage 02** — far too few to account for ~15 % of the code +being unreachable. + +So the trigger queue is now identified, but **what starts the unreachable +routines remains open**, and it is not this. + +## 🟡 Not settled + +* **What a trigger node contains**, and what consumes it. The append is located; + the node's own layout is not read. +* **The other `+12` caller**, `0x8226E208` in `sub_8226E160`, appends to some + other embedded list — not shown to be the trigger queue. +* **Built-in 25 is still unnamed.** "Appends to the trigger container" is what it + does mechanically; what the trigger *means* needs the consumer. diff --git a/docs/re/structures/isl-builtins.md b/docs/re/structures/isl-builtins.md index f3d8ee70..973716d2 100644 --- a/docs/re/structures/isl-builtins.md +++ b/docs/re/structures/isl-builtins.md @@ -897,7 +897,10 @@ So built-in 100 *clears* the trigger queue and then rebuilds the thread list via ("push the argument record ... then drain/rebuild"), and the "push" label was mine, not the disassembly's. `xrefs` gives it two callers: `0x82265DD4` (vt2, the script side) and **`0x8226D420`, an engine site** — so the engine clears it -too. What actually *appends* a node is still unidentified. +too. ~~What actually *appends* a node is still unidentified.~~ +✅ **(2026-08-27) IDENTIFIED — see [isl-trigger-queue](../isl-trigger-queue.md): +it is `sub_8226A0D8` = ScriptPhase vtable slot 28 = BUILT-IN 25.** The *script* +appends triggers; the engine does not. ## ✅ FOUND: the appender is `sub_8226EAB8`, and the count lives at `inner+8`