Followed the writer, not the reader. [phase+10160]'s only writer in the image is one site in the mission frame loop sub_821AA1B0: it does obj->get() on an object fetched from a registry by id 0x20FFFF02, stores it to the phase, then clears the object -- read, publish, clear, every frame. The id namespace has exactly three members (0x20FFFF00/01/02), each built at exactly 4 sites, and two of those are in sub_821D5178, which gets 0x20FFFF01 and 0x20FFFF02 and logs both: GamePart_ReadyRoom::Impl::OnCommand - Wait() command is requested. Check flow control valiables. WAIT_MODE : %d, REQUEST_NEXT : %d Argument order gives 0x20FFFF01 = WAIT_MODE, 0x20FFFF02 = REQUEST_NEXT. PrepareScript corroborates: it sets WAIT_MODE=1, REQUEST_NEXT=0 before an ISL script runs. So the six tutorial stages' lone dominating condition request_next() != 1 is the script waiting on the game part's proceed flag. 6 artefact lines changed, all 6 pair exactly.
153 lines
6.4 KiB
Markdown
153 lines
6.4 KiB
Markdown
# 🟡 The three built-ins that appear *inside* clear conditions — read, not named
|
||
|
||
[`isl-phase-guards`](isl-phase-guards.md) produced the per-phase clear
|
||
conditions, but three of the built-ins in them were unread, so the conditions
|
||
were only half-readable. All three are read now. **None of them is named** — the
|
||
corpus has withdrawn two names taken from usage shape, and what I read is not
|
||
enough to name these.
|
||
|
||
Implementations via the vtable at `0x820A84BC` (control: built-in 69
|
||
`unit_state` → slot 184 → `0x8226ADF0`, as recorded).
|
||
|
||
| built-in | slot | implementation |
|
||
|---|---|---|
|
||
| **104** | 288 | `0x8226BFE0` |
|
||
| **141** | 428 | `0x8226C9B8` |
|
||
| **7** | 40 | `0x82266C68` |
|
||
|
||
## ✅ `builtin104` is a pure getter — and it is the whole tutorial clear condition
|
||
|
||
```
|
||
8226BFE0 lwz r11, 10160(r3) ; r3 = the ScriptPhase
|
||
8226BFE4 stw r11, 164(r3) ; -> special[0]
|
||
8226BFE8 blr
|
||
```
|
||
|
||
Three instructions. It returns **`[phase+10160]`** and nothing else. So all six
|
||
tutorial stages (S18–S23) end on the value of a **single engine-written word** —
|
||
which is exactly why their exits have one dominating condition each and why
|
||
`isl-builtins.md` saw built-in 104 only ever inside a poll loop.
|
||
|
||
**That word has exactly one writer** in the whole image:
|
||
|
||
```
|
||
821AAD74 lwz r11, 104(r30)
|
||
821AAD84 lwz r11, 12(r11)
|
||
821AAD88 cmpi cr6, 0, r11, 16 ; skip if == 16
|
||
821AAD90 cmpi cr6, 0, r11, 32 ; skip if > 32
|
||
821AAD98 lwz r11, 4(r10)
|
||
821AAD9C stw r29, 10160(r11) ; <- the only write
|
||
```
|
||
|
||
inside `sub_821AA1B0`, gated on a type/kind field being in `(16, 32]`. `r29`
|
||
there comes from `or r29, r3, r3` — the return of a preceding call.
|
||
|
||
## ✅ (2026-08-27) NAMED — built-in 104 is `request_next`, and the game says so itself
|
||
|
||
`r29` is the return of a **getter on an object fetched from a registry by id**:
|
||
|
||
```
|
||
821AAD1C lwz r4, 2424(r30) ; the registry
|
||
821AAD20 addis r5, r0, 0x20FF
|
||
821AAD28 ori r5, r5, 0xFF02 ; <- the id
|
||
821AAD38 bcctrl ; registry->slot1(&out, 0x20FFFF02)
|
||
821AAD40 lwz r11, 0(r3) ; lwz r11, 8(r11)
|
||
821AAD4C bcctrl ; r29 = obj->slot2() -- GET
|
||
821AAD9C stw r29, 10160(r11) ; -> [phase+10160]
|
||
821AADA0..DD4 ; then the SAME id again, obj->slot1(0) -- CLEAR
|
||
```
|
||
|
||
So the frame loop **reads the value, publishes it to the script, and clears it**.
|
||
|
||
**The id namespace has exactly three members** — `0x20FFFF00`, `0x20FFFF01`,
|
||
`0x20FFFF02` — each built at exactly 4 sites image-wide. Two of those sites are
|
||
in `sub_821D5178`, which fetches `0x20FFFF01` and `0x20FFFF02`, calls the same
|
||
`slot2()` getter on each, and passes both to a log call whose format string is
|
||
at `0x820A4968`:
|
||
|
||
> `silph::GamePart_ReadyRoom::Impl::OnCommand - Wait() command is requested.`
|
||
> `Check flow control valiables. WAIT_MODE : %d, REQUEST_NEXT : %d`
|
||
|
||
Argument order settles which is which: `r4` (first `%d`, **WAIT_MODE**) is
|
||
`0x20FFFF01`'s value, `r5` (second, **REQUEST_NEXT**) is `0x20FFFF02`'s.
|
||
|
||
> ⇒ **`[phase+10160]` is `REQUEST_NEXT`, and built-in 104 returns it.**
|
||
|
||
That is the game's own name for the variable, not a shape-based guess.
|
||
Corroboration: `GamePart_ReadyRoom::Impl::PrepareScript` sets `WAIT_MODE = 1`
|
||
and `REQUEST_NEXT = 0` before an ISL script runs, and `sub_821AA1B0` clears both
|
||
each frame after reading. `[phase+10160]` has **exactly one writer and one
|
||
reader** in the whole image (the two above); the three other hits on offset
|
||
10160 are `lfs` on unrelated objects.
|
||
|
||
So the six tutorial stages' exit condition `request_next() != 1` is the script
|
||
waiting on the surrounding game part's *proceed* flag — which is why S18–S23
|
||
each have exactly one dominating condition.
|
||
|
||
🟡 Still unread: what the `(16, 32]` gate on `[r30+104]`'s `+12` selects, and
|
||
`0x20FFFF00`, the third id (used by the `GRAPH_PATH` / `EX_FONT` / `SYSTEM`
|
||
code in `sub_821D6350` and `sub_821D6A40`).
|
||
|
||
🟡 Its neighbours belong to the same cluster: `builtin103` reads
|
||
`[phase+10156]` and `[phase+10152]` (9 and 7 writers), and a sibling vtable stub
|
||
clears `[phase+10152]`. The shape is an engine→script status trio, but that is a
|
||
description, not a name.
|
||
|
||
## ✅ `builtin7`'s mechanism — and an independent confirmation
|
||
|
||
```
|
||
82266C84 lwz r11, 324(r25) ; the unit array
|
||
82266C88 lwz r10, 4(r26) ; local[4]
|
||
82266C94 lwzx r11, r10, r11 ; -> the record
|
||
82266C98 lwz r10, 4(r11) ; the handle … (alive?)
|
||
82266CA4 lwz r11, 16(r11) ; rec+16 = the unit STATE
|
||
82266CA8..CBC ; bail if state == 1, 3 or 4
|
||
82266CC0 lwz r10, 12(r26) ; local[12]
|
||
82266CC8 lwz r11, 244(r25) ; [phase+244] = SYMBOL TABLE 1
|
||
82266CD8 lwzx r10, r10, r11 ; -> resolve local[12] as a symtab-1 index
|
||
82266CD0 lfd f31, 25600(r9) ; a double constant
|
||
82266CD4 lwz r9, 16(r26) ; local[16]
|
||
```
|
||
|
||
🔑 **`isl.py`'s `SYM1_SLOTS` already lists slot 12 for built-in 7**, derived
|
||
purely from operand ranges. Reading the implementation shows the *mechanism* —
|
||
`local[12]` is indexed into `[phase+244]`, which
|
||
[`isl-bytecode.md`](isl-bytecode.md) documents as symbol table 1 (routes,
|
||
messages, objectives). **Two independent methods, same conclusion.**
|
||
|
||
That makes the Stage 02 phase-2 condition
|
||
|
||
```
|
||
builtin7(TCT206, 1, Route_TCT206_p2S, 4294967295, 500) == 1
|
||
```
|
||
|
||
structurally coherent — a unit, a **route symbol**, and two numbers — but *what*
|
||
it asks about the route is not read, so it stays `builtin7`.
|
||
|
||
## 🟡 `builtin141` — only the entry read
|
||
|
||
```
|
||
8226C9D4 lwz r11, 324(r29) ; unit array
|
||
8226C9D8 lwz r10, 4(r31) ; local[4]
|
||
8226C9E8 lwz r8, 4(r8) ; handle
|
||
8226C9F0 bc … 0x8226CA0C ; if alive, continue
|
||
8226C9F4 addi r11, r0, 0
|
||
8226C9F8 stw r11, 164(r29) ; dead -> special[0] = 0
|
||
```
|
||
|
||
The same unit-array opening as every unit predicate, returning 0 when the unit is
|
||
gone. Everything past `0x8226CA0C` is unread. Stage 16 calls it twice with
|
||
arguments differing in one position (`0` vs `-4000`), which *looks* like a
|
||
coordinate — and looking like one is precisely the evidence this corpus does not
|
||
accept.
|
||
|
||
## 🟡 Not settled
|
||
|
||
* **No name for any of the three.** For 104 that needs the meaning of `r29` at
|
||
`0x821AAD9C` and of the `(16, 32]` gate; for 7 and 141, their bodies past the
|
||
entry checks.
|
||
* `builtin103`'s fields `[phase+10152]` / `[phase+10156]` have 9 and 7 writers,
|
||
none read.
|
||
* This does not change any artefact — the conditions already printed
|
||
`builtin104`, `builtin7`, `builtin141`, and still do.
|