From 9ee32f794023d3b223398d949e3de24656483cd0 Mon Sep 17 00:00:00 2001 From: Sylpheed RE agent Date: Thu, 27 Aug 2026 07:30:41 +0000 Subject: [PATCH] re: decode the trigger node -- and withdraw two of my own claims about it The reader is sub_8226E220, called by the per-frame drain sub_8226D740 as (phase+272, six out-params). It pops one node and copies seven payload fields out. Built-in 25, the appender, writes exactly those seven offsets: +0 local[4] -- the unit +24 the constant 1 +4 local[12] +28 local[12] again +8 computed +32 -- +16 a DOUBLE from local[16] Neither side was derived from the other, so the agreement is the check. Container: +16 head, +20 pending count -- which matches the count isl-builtins.md watched live at phase+272+20 from a completely different direction -- and +24 a cursor. WITHDRAWN, from the previous commit: "the drain spawns from [node+112]". r31 = r1 - 256, the stack frame, so +112 is an output slot and not a node field. REFUTED, the follow-up hypothesis that the trigger carries the routine offset and so names the code nothing else starts: payload+28 is local[12], and across all 25 call sites disc-wide those values are the small integers 1 through 12 -- 1 of 25 (4.0%) land on the instruction stream against a 16.0% control, and none are unreached run-starts. Below chance. My first version of that test used n=2, Stage 02 only; it happened to agree, but two samples could not have supported it either way. One correction in the other direction, to the corpus: isl-builtins.md withdrew sub_8226E458's link to the trigger queue on the grounds that "the argument is lwz r4, 324(r29), the unit array, not the trigger container". That is the SECOND argument; the first is r26 = phase + 272, set twelve instructions earlier. The drain does operate on the container. What sub_8226E458 does to it remains unread, so only the argument is corrected, not the conclusion. All artefacts regenerate byte-identical; documentation only. Still open: what local[12] indexes; what the drain actually spawns, since [stack+112] is filled from payload+28 yet the spawner wants a code offset, so some step in that chain is not what I read; and what starts the ~15% of unreached code. --- docs/re/BACKLOG.md | 21 ++++++ docs/re/isl-coroutine-spawner.md | 10 +-- docs/re/structures/isl-trigger-node.md | 97 ++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 5 deletions(-) create mode 100644 docs/re/structures/isl-trigger-node.md diff --git a/docs/re/BACKLOG.md b/docs/re/BACKLOG.md index eb61394..d9e7144 100644 --- a/docs/re/BACKLOG.md +++ b/docs/re/BACKLOG.md @@ -98,6 +98,27 @@ unknown, what evidence exists, and what the first step would be. Move an item in the trigger queue at `phase+272`; that is the exact over-reach the corpus already withdrew once, so it is not asserted. +* βœ…πŸ”΄ **(2026-08-27) THE TRIGGER NODE IS DECODED β€” and two of my own claims are + withdrawn. [structures/isl-trigger-node](structures/isl-trigger-node.md).** + The reader is `sub_8226E220`, called by the drain `sub_8226D740` as + `(phase+272, …6 out-params)`. **Writer (built-in 25) and reader touch the same + seven payload offsets** β€” +0 `local[4]` (the unit), +4 and +28 `local[12]`, +8 + computed, +16 a **double** from `local[16]`, +24 the constant 1, +32 β€” neither + derived from the other. Container: +16 head, **+20 pending count** (matching the + count `isl-builtins.md` watched live at `phase+272+20`), +24 cursor. + πŸ”΄ **WITHDRAWN: "the drain spawns from `[node+112]`"** β€” `r31 = r1 βˆ’ 256`, the + STACK FRAME; `+112` is an output slot. + πŸ”΄ **REFUTED: the trigger carries a routine offset.** `payload+28` = `local[12]`, + and across **all 25 sites disc-wide** those are the small integers **1…12**: + **1/25 = 4.0 % land on the instruction stream vs a 16.0 % control**, 0 are + unreached run-starts. ⚠️ My first version of that test was **n=2** (Stage 02 + only) β€” same answer, but unsupportable either way. + βœ… **And one correction TO the corpus:** `isl-builtins.md` withdrew + `sub_8226E458`'s link to the trigger queue by reading its SECOND argument + (`[phase+324]`); the FIRST is `r26 = phase + 272`, set twelve instructions + earlier. The drain does operate on the container β€” what `sub_8226E458` does to + it stays unread. 🟑 What starts the ~15 % of unreached code: still open, not this. + ## βœ…βœ… 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-coroutine-spawner.md b/docs/re/isl-coroutine-spawner.md index 8239f4d..a44f5be 100644 --- a/docs/re/isl-coroutine-spawner.md +++ b/docs/re/isl-coroutine-spawner.md @@ -35,7 +35,7 @@ So the record reads `0x1883, base, MAIN_ENTRY, 0, code_end, force_end_handler`. among the CFG's entry points by another route β€” so this corrects the *meaning* of a field without moving any coverage number. -## πŸ”‘ The drain spawns from `[node+112]` +## πŸ”΄ WITHDRAWN: "the drain spawns from `[node+112]`" The two calls inside `sub_8226D740` both do @@ -45,10 +45,10 @@ lwz r4, 232(r29) ; the phase's code base bl 0x822737C8 ``` -`isl-builtins.md` describes `sub_8226D740` as the per-frame engineβ†’script drain, -and here it is spawning routines from a **code offset carried in the drained -node at `+112`**. That is the first field of a trigger-style node to be located, -and it is the most promising remaining lead on the routines nothing else starts. +**`r31 = r1 - 256`** β€” it is the **stack frame**, so `+112` is an output slot, +not a node field. See [structures/isl-trigger-node](structures/isl-trigger-node.md), +which reads the node properly and also refutes the follow-up hypothesis that the +trigger carries a routine offset. ## 🟑 Not settled diff --git a/docs/re/structures/isl-trigger-node.md b/docs/re/structures/isl-trigger-node.md new file mode 100644 index 0000000..9bcd523 --- /dev/null +++ b/docs/re/structures/isl-trigger-node.md @@ -0,0 +1,97 @@ +# βœ… The trigger node's layout β€” from a matched writer/reader pair + +[isl-trigger-queue](../isl-trigger-queue.md) located the *appender* (built-in 25). +The node's contents are now read, because the **reader** was found too and the two +agree field for field. + +## The reader: `sub_8226E220` + +`sub_8226D740` β€” the per-frame engineβ†’script drain β€” calls it as + +``` +8226D758 addi r26, r29, 272 ; r29 = the phase, so r26 = the TRIGGER CONTAINER +8226D79C or r3, r26, r26 +8226D7A0 bl 0x8226E220 ; (container, &out88, &out84, &out80, + ; &out120, &out92, &out112) +``` + +and it pops one node: + +``` +8226E228 lwz r10, 20(r11) ; the PENDING COUNT -- return 0 if empty +8226E244 lwz r10, 16(r11) ; the list head +8226E250 stw r10, 24(r11) ; a cursor +8226E254 lwz r11, 8(r10) ; -> the node PAYLOAD +``` + +## βœ… The layout, confirmed from both ends + +| payload | written by built-in 25 | read by `sub_8226E220` | source | +|---|---|---|---| +| **+0** | `stw r26, 0(r30)` | β†’ out88 | `local[4]` β€” the unit | +| **+4** | `stw r28, 4(r30)` | β†’ out84 | `local[12]` | +| **+8** | `stw r27, 8(r30)` | β†’ out80 | computed | +| **+16** | `stfd f31, 16(r30)` | β†’ out120 (a **double**) | `local[16]` | +| **+24** | `stw r25, 24(r30)` | β†’ out92 | the constant 1 | +| **+28** | `stw r28, 28(r30)` | β†’ out112 | `local[12]` again | +| **+32** | `stw r11, 32(r30)` | β†’ out116 | β€” | +| +36 | `stw r23, 36(r30)` | *not read here* | β€” | + +Container: **+16** head, **+20** pending count, **+24** cursor. The `+20` matches +the count `isl-builtins.md` watched live at `phase+272+20`, from a completely +different direction. + +That the writer and the reader touch the same seven offsets, in the same order, +is the check β€” neither was derived from the other. + +`payload+0` being `local[4]` also agrees with `UNIT_ARG`, which lists built-in 25 +and was derived from operand ranges. + +## πŸ”΄ Two corrections, both mine + +### `[node+112]` does not exist + +[isl-coroutine-spawner](../isl-coroutine-spawner.md) recorded the drain's spawns +as taking their offset from `[node+112]`. **`r31 = r1 - 256`** β€” it is the +**stack frame**, so `+112` is an output slot, not a node field. Withdrawn. + +### And the trigger does NOT carry a routine offset + +The follow-up hypothesis β€” that `payload+28` reaches the spawner and so names the +routines nothing else starts β€” does not survive. `payload+28` is `local[12]`, and +across **all 25 call sites disc-wide** those values are the small integers +**1 … 12**: + +| | | +|---|---| +| land on the instruction stream | **1 / 25 = 4.0 %** | +| control, random 4-aligned offsets | 16.0 % | +| are unreached run-starts | **0** | + +Below chance. They are indices of some kind, not code offsets. + +⚠️ My first version of this test used **n = 2** β€” Stage 02 only. It happened to +give the same answer, but two samples could not have supported it either way. + +## βœ… One correction to the corpus, in the other direction + +`isl-builtins.md` withdrew the idea that `sub_8226E458` touches the trigger +queue, reasoning: *"At `0x8226D780` the argument is `lwz r4, 324(r29)` β€” +`[ScriptPhase+324]`, the unit array, not the trigger container."* + +The call is `sub_8226E458(r3 = r26, r4 = [phase+324])`, and **`r26` was set to +`phase + 272` twelve instructions earlier**. The withdrawal read the *second* +argument. `sub_8226D740` does operate on the trigger container; what +`sub_8226E458` does *to* it is still unread, so only the argument is corrected +here, not the conclusion. + +## 🟑 Not settled + +* **What `local[12]` indexes.** Values 1–12, and `SYM1_SLOTS` lists slot 12 for + built-in 25 (symbol table 1 β€” routes, messages, objectives), which is + consistent with a small index but was not verified here. +* **What the drain actually spawns.** `[stack+112]` is filled from `payload+28`, + yet the spawner takes a code offset β€” so some step in that chain is not what I + read. Left open rather than guessed. +* **`sub_8226E458`'s effect** on the container. +* **What starts the ~15 % of unreached code** β€” still open, and this was not it.