This repository has been archived on 2026-09-16. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Syplheed-Reborn/docs/re/structures/isl-condition-builtins.md
Sylpheed RE agent af61c162f3 isl: built-in 104 is request_next -- named from the game's own log string
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.
2026-08-27 09:31:01 +00:00

6.4 KiB
Raw Blame History

🟡 The three built-ins that appear inside clear conditions — read, not named

isl-phase-guards 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 (S18S23) 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 members0x20FFFF00, 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 S18S23 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 mechanismlocal[12] is indexed into [phase+244], which 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.