re: the ISL stream is flat -- refute the "needs coroutine entry points" blocker

Two files (isl_report.py's docstring and structures/isl-builtins.md) recorded the
same blocker on a faithful per-phase condition listing: that it needs the coroutine
entry points from start_coroutine's operand.  Measured against isl.call_sites(),
which enumerates by scanning the encoding rather than by decoding and so is an
independent denominator:

  linear + jumps, stopping at ret (what the tool did)   133 / 2846 =   4.7%
  linear + jumps, continuing past ret                  2275 / 2846 =  79.9%
  ... + following start_coroutine (the recorded fix)   2355 / 2846 =  82.7%
  plain linear decode, no control flow at all          2846 / 2846 = 100.0%

Following the coroutine entries buys 2.8 points.  Disc-wide, a plain linear decode
from the first phase base reaches 25705/25705 call sites over all 28 stages, and
28/28 decode clean to code_end with no desync.

The real bug was isl.dis ending on `if op == 20: break`.  Op 20 is `ret`, but this
is a coroutine VM -- the thread suspends and resumes at the FOLLOWING instruction,
so code continues past it.  dis() now takes stop_at_ret (default True, preserving
the old output: data/isl-stage02.txt regenerates byte-identical) and
isl.linear_offsets() is the correct walk.

By-product, kept with its control: start_coroutine's target is staged slot 0 --
73/83 phase-1 sites land on a valid instruction, against a 38.7% chance rate for an
arbitrary 4-aligned offset.

New artefact data/isl-stage02-phase-ends.txt with a committed generator
(isl_report.py phase-ends).  It shows END_PHASE's call site is the WRONG place to
read a clear condition: all 12 Stage-02 sites sit in one stereotyped outro.  Not
settled, and stated as such: op10/op13/op14/op21/op23 are unread handlers, so the
condition in the poll loop upstream cannot be named yet.
This commit is contained in:
Sylpheed RE agent
2026-08-27 05:00:49 +00:00
parent ef4e975a47
commit f41847701c
6 changed files with 567 additions and 11 deletions

View File

@@ -1376,6 +1376,22 @@ premise was wrong.**
mission-level stream at `+0x24` is partly read (3 groups, one per phase, each
ending in a pair of plausible ISL entry offsets) but `1883`'s operand is not
uniformly a pointer — two land on IEEE floats.
**(2026-08-27) The decode obstacle is GONE — [isl-stream-is-flat](isl-stream-is-flat.md).**
The stream is FLAT: a plain linear decode from the first phase base reaches
**25705/25705 call sites across all 28 stages**, 28/28 clean to `code_end`.
The recorded "needs the coroutine entry points" blocker is refuted (following
them buys 2.8 points; ignoring control flow buys 100 %). The real bug was
`isl.dis` stopping at op 20 (`ret`) — a coroutine **yield**, not an end of
code — which reached only 4.7 %. Fixed via `stop_at_ret`; the committed
`data/isl-stage02.txt` regenerates byte-identical.
🟡 **But `END_PHASE`'s call site is the WRONG place to read the condition:**
all 12 Stage-02 `END_PHASE` sites sit in one stereotyped outro
(`wait_cmds_drained → fade_sound(3) → builtin85(3) → wait_s(3) → END_PHASE →
end_coroutine`). ▶️ **Next, and it is now the only thing in the way: read the
five branch handlers** `op10` (`0x82271598`), `op13` (`0x82271830`), `op14`,
`op21`, `op23`. The condition lives in the `op10`/`op13` poll loop upstream of
the outro — e.g. phase 3 polls `unit_state(ADT308)` and branches back to
`0xFEB4` until it passes. Artefact: `data/isl-stage02-phase-ends.txt`.
* 🐛 **(2026-08-25) The nav fix is NOT fully reliable.** `dialog_up.py` works on
the saved frame (mean 34.3 vs threshold 45), and one boot went
readyroom-in-9 s — but a later boot logged "load dialog not up yet, retrying"