From da6eb236efc7f21c04c1add9fb4d628749fd87d7 Mon Sep 17 00:00:00 2001 From: Sylpheed RE agent Date: Thu, 27 Aug 2026 06:42:47 +0000 Subject: [PATCH] re: the trigger-queue appender is built-in 25 -- the script appends, not the engine isl-builtins.md carried the trigger queue at phase+272 a long way -- layout, live count at +272+20, the generic appender sub_8226EAB8 -- and stopped at "what actually appends a node is still unidentified", proposing a gdb watchpoint on a running emulator. It is a static question after all. sub_8226EAB8 is a shared 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 appender must hand it container+12. Only two of the sixteen callers do, and one sets its base 34 instructions earlier: 8226A22C addi r29, r24, 272 ; r24 = the ScriptPhase 8226A2B4 addi r3, r29, 12 ; -> phase + 272 + 12 bl 0x8226EAB8 phase+272+12 is exactly the inner list whose count the corpus watched at +272+20. sub_8226A0D8 is ScriptPhase vtable SLOT 28, which is what BUILT-IN 25's stub tail-calls, and there are ZERO real `bl 0x8226A0D8` instructions in the image -- it is reached only through the vtable. So the queue is populated by the SCRIPT, retiring the residual "the engine moves records into the phase's queue each frame" reading that isl-builtins.md had already flagged as unsupported by its own call site. Built-in 25's arguments agree with two independently measured tables: UNIT_ARG (a unit at local[4]) and SYM1_SLOTS[12] (a symtab-1 name at local[12]). REFUTED in the same pass, with a control: built-in 25 does NOT carry the coroutine entry that a trigger fires, which would have seeded the 389 routines reachable from no static entry. Taking each staged operand as a code offset from the phase base, 0 of 8 land on an instruction boundary, against 38.7% for an arbitrary 4-aligned offset -- below chance, not above it. And the scale is wrong regardless: built-in 25 has only 2 call sites in Stage 02. So what starts the unreachable routines is still open, and it is not this. A trigger node's own layout and its consumer are unread, and built-in 25 stays unnamed. --- docs/re/BACKLOG.md | 14 ++++++ docs/re/isl-trigger-queue.md | 81 ++++++++++++++++++++++++++++++ docs/re/structures/isl-builtins.md | 5 +- 3 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 docs/re/isl-trigger-queue.md diff --git a/docs/re/BACKLOG.md b/docs/re/BACKLOG.md index 986bc18..8f48420 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 0000000..0dde8bc --- /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 f3d8ee7..973716d 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`