diff --git a/docs/re/BACKLOG.md b/docs/re/BACKLOG.md index 2823cc93..e9364512 100644 --- a/docs/re/BACKLOG.md +++ b/docs/re/BACKLOG.md @@ -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" diff --git a/docs/re/data/isl-stage02-phase-ends.txt b/docs/re/data/isl-stage02-phase-ends.txt new file mode 100644 index 00000000..9c6fc633 --- /dev/null +++ b/docs/re/data/isl-stage02-phase-ends.txt @@ -0,0 +1,368 @@ +# Stage02.ssb — where each phase ends + +Generated by `tools/re-capture/isl_report.py phase-ends`. +Decoded with `stop_at_ret=False`; a `ret` is a coroutine YIELD, so +the listing continues past it. + +phase code bases: 0xe4 0x14aa8 0x24b4c +15 phase-ending call(s): 12 END_PHASE, 3 FORCE_END_PHASE + +## phase 1 — builtin 6 (END_PHASE) at 0x51E4 + 005124: 01020C0A op10 len=12 k=01,02 00000000 00000001 + 005130: 0000080D op13 len=8 k=00,00 00004E2C + 005138: 01020C0A op10 len=12 k=01,02 00000000 00000002 + 005144: 0000080D op13 len=8 k=00,00 00004ED4 + 00514C: 01020C0A op10 len=12 k=01,02 00000000 00000003 + 005158: 0000080D op13 len=8 k=00,00 00004F7C + 005160: 00000C13 call len=12 k=00,00 00000078 00000248 wait_cmds_drained + 00516C: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 00517C: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 005188: 00000C13 call len=12 k=00,00 0000003B 0000024A fade_sound(3) + 005194: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 0051A4: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 0051B0: 00000C13 call len=12 k=00,00 00000055 0000024B builtin85(3) + 0051BC: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 0051CC: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 0051D8: 00000C13 call len=12 k=00,00 00000004 0000024C wait_s(3) + 0051E4: 00000C13 call len=12 k=00,00 00000006 0000024D END_PHASE + 0051F0: 00000C13 call len=12 k=00,00 0000000B 0000024E end_coroutine + 0051FC: 01030C00 set.i len=12 k=01,03 00000000 00000001 local[0] = imm 0x1 + 005208: 01030C00 set.i len=12 k=01,03 00000004 00000056 local[4] = imm 0x56 + 005214: 01021001 set.f len=16 k=01,02 00000000 00000000 00000000 special[0] = imm 0 + 005224: 02030C01 set.f len=12 k=02,03 00000008 00000000 local[8] = special[0] + +## phase 1 — builtin 6 (END_PHASE) at 0x5828 + 005768: 01020C0A op10 len=12 k=01,02 00000000 00000003 + 005774: 0000080D op13 len=8 k=00,00 00005448 + 00577C: 01020C0A op10 len=12 k=01,02 00000000 00000004 + 005788: 0000080D op13 len=8 k=00,00 000054F0 + 005790: 01020C0A op10 len=12 k=01,02 00000000 00000005 + 00579C: 0000080D op13 len=8 k=00,00 00005598 + 0057A4: 00000C13 call len=12 k=00,00 00000078 00000272 wait_cmds_drained + 0057B0: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 0057C0: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 0057CC: 00000C13 call len=12 k=00,00 0000003B 00000274 fade_sound(3) + 0057D8: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 0057E8: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 0057F4: 00000C13 call len=12 k=00,00 00000055 00000275 builtin85(3) + 005800: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 005810: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 00581C: 00000C13 call len=12 k=00,00 00000004 00000276 wait_s(3) + 005828: 00000C13 call len=12 k=00,00 00000006 00000277 END_PHASE + 005834: 00000C13 call len=12 k=00,00 0000000B 00000278 end_coroutine + 005840: 01021001 set.f len=16 k=01,02 00000000 406A4000 00000000 special[0] = imm 210 + 005850: 02020C01 set.f len=12 k=02,02 00000001 00000000 special[1] = special[0] + 00585C: 02000416 op22 len=4 k=02,00 + 005860: 01020C00 set.i len=12 k=01,02 00000000 00000000 special[0] = imm 0x0 + +## phase 1 — builtin 6 (END_PHASE) at 0x6010 + 005F40: 02030C00 set.i len=12 k=02,03 0000000C 00000000 local[12] = special[0] + 005F4C: 01020C00 set.i len=12 k=01,02 00000000 00000001 special[0] = imm 0x1 + 005F58: 02030C00 set.i len=12 k=02,03 00000010 00000000 local[16] = special[0] + 005F64: 01021001 set.f len=16 k=01,02 00000000 BFF00000 00000000 special[0] = imm -1 + 005F74: 02030C01 set.f len=12 k=02,03 00000018 00000000 local[24] = special[0] + 005F80: 00000C13 call len=12 k=00,00 00000040 0000029D request_script_message(0x1, -1) + 005F8C: 00000C13 call len=12 k=00,00 00000078 0000029E wait_cmds_drained + 005F98: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 005FA8: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 005FB4: 00000C13 call len=12 k=00,00 0000003B 000002A1 fade_sound(3) + 005FC0: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 005FD0: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 005FDC: 00000C13 call len=12 k=00,00 00000055 000002A2 builtin85(3) + 005FE8: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 005FF8: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 006004: 00000C13 call len=12 k=00,00 00000004 000002A3 wait_s(3) + 006010: 00000C13 call len=12 k=00,00 00000006 000002A4 END_PHASE + 00601C: 00000C13 call len=12 k=00,00 0000000B 000002A5 end_coroutine + 006028: 01021001 set.f len=16 k=01,02 00000000 4092C000 00000000 special[0] = imm 1200 + 006038: 02020C01 set.f len=12 k=02,02 00000001 00000000 special[1] = special[0] + 006044: 02000416 op22 len=4 k=02,00 + 006048: 01020C00 set.i len=12 k=01,02 00000000 00000000 special[0] = imm 0x0 + +## phase 1 — builtin 6 (END_PHASE) at 0x6260 + 006190: 02030C00 set.i len=12 k=02,03 0000000C 00000000 local[12] = special[0] + 00619C: 01020C00 set.i len=12 k=01,02 00000000 00000001 special[0] = imm 0x1 + 0061A8: 02030C00 set.i len=12 k=02,03 00000010 00000000 local[16] = special[0] + 0061B4: 01021001 set.f len=16 k=01,02 00000000 BFF00000 00000000 special[0] = imm -1 + 0061C4: 02030C01 set.f len=12 k=02,03 00000018 00000000 local[24] = special[0] + 0061D0: 00000C13 call len=12 k=00,00 00000040 000002B6 request_script_message(0x1, -1) + 0061DC: 00000C13 call len=12 k=00,00 00000078 000002B7 wait_cmds_drained + 0061E8: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 0061F8: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 006204: 00000C13 call len=12 k=00,00 0000003B 000002B9 fade_sound(3) + 006210: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 006220: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 00622C: 00000C13 call len=12 k=00,00 00000055 000002BA builtin85(3) + 006238: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 006248: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 006254: 00000C13 call len=12 k=00,00 00000004 000002BB wait_s(3) + 006260: 00000C13 call len=12 k=00,00 00000006 000002BC END_PHASE + 00626C: 00000C13 call len=12 k=00,00 0000000B 000002BD end_coroutine + 006278: 00000414 ret len=4 k=00,00 + 00627C: 01020C00 set.i len=12 k=01,02 00000000 00000002 special[0] = imm 0x2 + 006288: 02020C00 set.i len=12 k=02,02 00000001 00000000 special[1] = special[0] + 006294: 02000415 op21 len=4 k=02,00 + +## phase 1 — builtin 62 (FORCE_END_PHASE) at 0x1482C + 014770: 01020C00 set.i len=12 k=01,02 00000000 00000000 special[0] = imm 0x0 + 01477C: 02030C00 set.i len=12 k=02,03 00000004 00000000 local[4] = special[0] + 014788: 01020C00 set.i len=12 k=01,02 00000000 00000000 special[0] = imm 0x0 + 014794: 02030C00 set.i len=12 k=02,03 00000008 00000000 local[8] = special[0] + 0147A0: 01020C00 set.i len=12 k=01,02 00000000 00000008 special[0] = imm 0x8 + 0147AC: 02030C00 set.i len=12 k=02,03 0000000C 00000000 local[12] = special[0] + 0147B8: 01020C00 set.i len=12 k=01,02 00000000 00000001 special[0] = imm 0x1 + 0147C4: 02030C00 set.i len=12 k=02,03 00000010 00000000 local[16] = special[0] + 0147D0: 01021001 set.f len=16 k=01,02 00000000 40240000 00000000 special[0] = imm 10 + 0147E0: 02030C01 set.f len=12 k=02,03 00000018 00000000 local[24] = special[0] + 0147EC: 00000C13 call len=12 k=00,00 00000040 000008F8 request_script_message(0x0, 0x0, 0x8, 0x1, 10) + 0147F8: 01020C00 set.i len=12 k=01,02 00000000 000107F4 special[0] = imm 0x107F4 + 014804: 02030C00 set.i len=12 k=02,03 00000000 00000000 local[0] = special[0] + 014810: 00000C13 call len=12 k=00,00 00000001 000008F9 start_coroutine(0x107F4) + 01481C: 00000C13 call len=12 k=00,00 0000000B 000008FB end_coroutine + 014828: 00000414 ret len=4 k=00,00 + 01482C: 00000C13 call len=12 k=00,00 0000003E 00000000 FORCE_END_PHASE + 014838: 00000C13 call len=12 k=00,00 0000007C 00000903 timer_stop + 014844: 00000414 ret len=4 k=00,00 + 014848: 00000819 op25? len=8 k=00,00 00000019 + 014850: 00000819 op25? len=8 k=00,00 00002B20 + 014858: 0000081A op26? len=8 k=00,00 00000000 + +## phase 2 — builtin 6 (END_PHASE) at 0x19640 + 019580: 01020C0A op10 len=12 k=01,02 00000000 00000001 + 01958C: 0000080D op13 len=8 k=00,00 000048C4 + 019594: 01020C0A op10 len=12 k=01,02 00000000 00000002 + 0195A0: 0000080D op13 len=8 k=00,00 0000496C + 0195A8: 01020C0A op10 len=12 k=01,02 00000000 00000003 + 0195B4: 0000080D op13 len=8 k=00,00 00004A14 + 0195BC: 00000C13 call len=12 k=00,00 00000078 00000204 wait_cmds_drained + 0195C8: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 0195D8: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 0195E4: 00000C13 call len=12 k=00,00 0000003B 00000206 fade_sound(3) + 0195F0: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 019600: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 01960C: 00000C13 call len=12 k=00,00 00000055 00000207 builtin85(3) + 019618: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 019628: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 019634: 00000C13 call len=12 k=00,00 00000004 00000208 wait_s(3) + 019640: 00000C13 call len=12 k=00,00 00000006 00000209 END_PHASE + 01964C: 00000C13 call len=12 k=00,00 0000000B 0000020A end_coroutine + 019658: 01030C00 set.i len=12 k=01,03 00000000 00000001 local[0] = imm 0x1 + 019664: 01030C00 set.i len=12 k=01,03 00000004 00000075 local[4] = imm 0x75 + 019670: 01021001 set.f len=16 k=01,02 00000000 00000000 00000000 special[0] = imm 0 + 019680: 02030C01 set.f len=12 k=02,03 00000008 00000000 local[8] = special[0] + +## phase 2 — builtin 6 (END_PHASE) at 0x19934 + 019864: 02030C00 set.i len=12 k=02,03 0000000C 00000000 local[12] = special[0] + 019870: 01020C00 set.i len=12 k=01,02 00000000 00000001 special[0] = imm 0x1 + 01987C: 02030C00 set.i len=12 k=02,03 00000010 00000000 local[16] = special[0] + 019888: 01021001 set.f len=16 k=01,02 00000000 BFF00000 00000000 special[0] = imm -1 + 019898: 02030C01 set.f len=12 k=02,03 00000018 00000000 local[24] = special[0] + 0198A4: 00000C13 call len=12 k=00,00 00000040 0000021D request_script_message(0x1, -1) + 0198B0: 00000C13 call len=12 k=00,00 00000078 0000021E wait_cmds_drained + 0198BC: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 0198CC: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 0198D8: 00000C13 call len=12 k=00,00 0000003B 00000220 fade_sound(3) + 0198E4: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 0198F4: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 019900: 00000C13 call len=12 k=00,00 00000055 00000221 builtin85(3) + 01990C: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 01991C: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 019928: 00000C13 call len=12 k=00,00 00000004 00000222 wait_s(3) + 019934: 00000C13 call len=12 k=00,00 00000006 00000223 END_PHASE + 019940: 00000C13 call len=12 k=00,00 0000000B 00000224 end_coroutine + 01994C: 01000C00 set.i len=12 k=01,00 00000004 00000000 global[4] = imm 0x0 + 019958: 01030C00 set.i len=12 k=01,03 00000000 00000001 local[0] = imm 0x1 + 019964: 01030C00 set.i len=12 k=01,03 00000004 00000075 local[4] = imm 0x75 + 019970: 01030C00 set.i len=12 k=01,03 00000008 00000001 local[8] = imm 0x1 + +## phase 2 — builtin 6 (END_PHASE) at 0x1AC44 + 01AB74: 02030C00 set.i len=12 k=02,03 00000000 00000000 local[0] = special[0] + 01AB80: 00000C13 call len=12 k=00,00 00000009 000002A1 read_freg + 01AB8C: 02020C01 set.f len=12 k=02,02 00000001 00000000 special[1] = special[0] + 01AB98: 01021001 set.f len=16 k=01,02 00000000 40140000 00000000 special[0] = imm 5 + 01ABA8: 02020C05 cmp.b len=12 k=02,02 00000000 00000001 + 01ABB4: 02030C01 set.f len=12 k=02,03 00000008 00000000 local[8] = special[0] + 01ABC0: 00000C13 call len=12 k=00,00 00000004 000002A1 wait_s(5) + 01ABCC: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 01ABDC: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 01ABE8: 00000C13 call len=12 k=00,00 0000003B 000002A4 fade_sound(3) + 01ABF4: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 01AC04: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 01AC10: 00000C13 call len=12 k=00,00 00000055 000002A5 builtin85(3) + 01AC1C: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 01AC2C: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 01AC38: 00000C13 call len=12 k=00,00 00000004 000002A6 wait_s(3) + 01AC44: 00000C13 call len=12 k=00,00 00000006 000002A7 END_PHASE + 01AC50: 00000C13 call len=12 k=00,00 0000000B 000002A8 end_coroutine + 01AC5C: 00000414 ret len=4 k=00,00 + 01AC60: 00020C00 set.i len=12 k=00,02 00000000 0000004C special[0] = global[76] + 01AC6C: 0000080C jmp len=8 k=00,00 00006C18 -> code+0x6C18 (file 0x1B6C0) + 01AC74: 01030C00 set.i len=12 k=01,03 00000000 00000001 local[0] = imm 0x1 + +## phase 2 — builtin 62 (FORCE_END_PHASE) at 0x249F0 + 024938: 0000080E op14 len=8 k=00,00 0000FF44 + 024940: 01020C00 set.i len=12 k=01,02 00000000 000000F1 special[0] = imm 0xF1 + 02494C: 02030C00 set.i len=12 k=02,03 00000000 00000000 local[0] = special[0] + 024958: 01020C00 set.i len=12 k=01,02 00000000 00000000 special[0] = imm 0x0 + 024964: 02030C00 set.i len=12 k=02,03 00000004 00000000 local[4] = special[0] + 024970: 01020C00 set.i len=12 k=01,02 00000000 00000000 special[0] = imm 0x0 + 02497C: 02030C00 set.i len=12 k=02,03 00000008 00000000 local[8] = special[0] + 024988: 01020C00 set.i len=12 k=01,02 00000000 00000009 special[0] = imm 0x9 + 024994: 02030C00 set.i len=12 k=02,03 0000000C 00000000 local[12] = special[0] + 0249A0: 01020C00 set.i len=12 k=01,02 00000000 00000001 special[0] = imm 0x1 + 0249AC: 02030C00 set.i len=12 k=02,03 00000010 00000000 local[16] = special[0] + 0249B8: 01021001 set.f len=16 k=01,02 00000000 BFF00000 00000000 special[0] = imm -1 + 0249C8: 02030C01 set.f len=12 k=02,03 00000018 00000000 local[24] = special[0] + 0249D4: 00000C13 call len=12 k=00,00 00000040 00000773 request_script_message(MSG_VOICE_D_303, 0x0, 0x0, 0x9, 0x1, -1) + 0249E0: 00000C13 call len=12 k=00,00 0000000B 00000774 end_coroutine + 0249EC: 00000414 ret len=4 k=00,00 + 0249F0: 00000C13 call len=12 k=00,00 0000003E 00000000 FORCE_END_PHASE + 0249FC: 00000C13 call len=12 k=00,00 0000007C 0000077F timer_stop + 024A08: 00000414 ret len=4 k=00,00 + 024A0C: 00000819 op25? len=8 k=00,00 0000000D + 024A14: 00000819 op25? len=8 k=00,00 0000392C + 024A1C: 0000081A op26? len=8 k=00,00 00000000 + +## phase 3 — builtin 6 (END_PHASE) at 0x2B96C + 02B8AC: 01020C0A op10 len=12 k=01,02 00000000 00000001 + 02B8B8: 0000080D op13 len=8 k=00,00 00006B4C + 02B8C0: 01020C0A op10 len=12 k=01,02 00000000 00000002 + 02B8CC: 0000080D op13 len=8 k=00,00 00006BF4 + 02B8D4: 01020C0A op10 len=12 k=01,02 00000000 00000003 + 02B8E0: 0000080D op13 len=8 k=00,00 00006C9C + 02B8E8: 00000C13 call len=12 k=00,00 00000078 00000270 wait_cmds_drained + 02B8F4: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 02B904: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 02B910: 00000C13 call len=12 k=00,00 0000003B 00000272 fade_sound(3) + 02B91C: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 02B92C: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 02B938: 00000C13 call len=12 k=00,00 00000055 00000273 builtin85(3) + 02B944: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 02B954: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 02B960: 00000C13 call len=12 k=00,00 00000004 00000274 wait_s(3) + 02B96C: 00000C13 call len=12 k=00,00 00000006 00000275 END_PHASE + 02B978: 00000C13 call len=12 k=00,00 0000000B 00000276 end_coroutine + 02B984: 01030C00 set.i len=12 k=01,03 00000000 00000001 local[0] = imm 0x1 + 02B990: 01030C00 set.i len=12 k=01,03 00000004 00000056 local[4] = imm 0x56 + 02B99C: 01021001 set.f len=16 k=01,02 00000000 00000000 00000000 special[0] = imm 0 + 02B9AC: 02030C01 set.f len=12 k=02,03 00000008 00000000 local[8] = special[0] + +## phase 3 — builtin 6 (END_PHASE) at 0x2BFB0 + 02BEF0: 01020C0A op10 len=12 k=01,02 00000000 00000003 + 02BEFC: 0000080D op13 len=8 k=00,00 00007168 + 02BF04: 01020C0A op10 len=12 k=01,02 00000000 00000004 + 02BF10: 0000080D op13 len=8 k=00,00 00007210 + 02BF18: 01020C0A op10 len=12 k=01,02 00000000 00000005 + 02BF24: 0000080D op13 len=8 k=00,00 000072B8 + 02BF2C: 00000C13 call len=12 k=00,00 00000078 0000029A wait_cmds_drained + 02BF38: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 02BF48: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 02BF54: 00000C13 call len=12 k=00,00 0000003B 0000029C fade_sound(3) + 02BF60: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 02BF70: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 02BF7C: 00000C13 call len=12 k=00,00 00000055 0000029D builtin85(3) + 02BF88: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 02BF98: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 02BFA4: 00000C13 call len=12 k=00,00 00000004 0000029E wait_s(3) + 02BFB0: 00000C13 call len=12 k=00,00 00000006 0000029F END_PHASE + 02BFBC: 00000C13 call len=12 k=00,00 0000000B 000002A0 end_coroutine + 02BFC8: 01000C0A op10 len=12 k=01,00 00000070 00000004 + 02BFD4: 0000080F op15 len=8 k=00,00 000076AC + 02BFDC: 01020C00 set.i len=12 k=01,02 00000000 00000000 special[0] = imm 0x0 + 02BFE8: 02030C00 set.i len=12 k=02,03 00000000 00000000 local[0] = special[0] + +## phase 3 — builtin 6 (END_PHASE) at 0x2C1E0 + 02C110: 02030C00 set.i len=12 k=02,03 0000000C 00000000 local[12] = special[0] + 02C11C: 01020C00 set.i len=12 k=01,02 00000000 00000001 special[0] = imm 0x1 + 02C128: 02030C00 set.i len=12 k=02,03 00000010 00000000 local[16] = special[0] + 02C134: 01021001 set.f len=16 k=01,02 00000000 BFF00000 00000000 special[0] = imm -1 + 02C144: 02030C01 set.f len=12 k=02,03 00000018 00000000 local[24] = special[0] + 02C150: 00000C13 call len=12 k=00,00 00000040 000002B3 request_script_message(0x1, -1) + 02C15C: 00000C13 call len=12 k=00,00 00000078 000002B4 wait_cmds_drained + 02C168: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 02C178: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 02C184: 00000C13 call len=12 k=00,00 0000003B 000002B6 fade_sound(3) + 02C190: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 02C1A0: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 02C1AC: 00000C13 call len=12 k=00,00 00000055 000002B7 builtin85(3) + 02C1B8: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 02C1C8: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 02C1D4: 00000C13 call len=12 k=00,00 00000004 000002B8 wait_s(3) + 02C1E0: 00000C13 call len=12 k=00,00 00000006 000002B9 END_PHASE + 02C1EC: 00000C13 call len=12 k=00,00 0000000B 000002BA end_coroutine + 02C1F8: 01021001 set.f len=16 k=01,02 00000000 4072C000 00000000 special[0] = imm 300 + 02C208: 02020C01 set.f len=12 k=02,02 00000001 00000000 special[1] = special[0] + 02C214: 02000416 op22 len=4 k=02,00 + 02C218: 01020C00 set.i len=12 k=01,02 00000000 00000000 special[0] = imm 0x0 + +## phase 3 — builtin 6 (END_PHASE) at 0x2CF74 + 02CEBC: 02020C00 set.i len=12 k=02,02 00000001 00000000 special[1] = special[0] + 02CEC8: 02000415 op21 len=4 k=02,00 + 02CECC: 00000C13 call len=12 k=00,00 00000063 00000306 builtin99 + 02CED8: 00020417 op23 len=4 k=00,02 + 02CEDC: 02020C0A op10 len=12 k=02,02 00000000 00000001 + 02CEE8: 0000080D op13 len=8 k=00,00 000083B0 + 02CEF0: 01000C00 set.i len=12 k=01,00 00000074 00000002 global[116] = imm 0x2 + 02CEFC: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 02CF0C: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 02CF18: 00000C13 call len=12 k=00,00 0000003B 0000030A fade_sound(3) + 02CF24: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 02CF34: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 02CF40: 00000C13 call len=12 k=00,00 00000055 0000030B builtin85(3) + 02CF4C: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 02CF5C: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 02CF68: 00000C13 call len=12 k=00,00 00000004 0000030C wait_s(3) + 02CF74: 00000C13 call len=12 k=00,00 00000006 0000030D END_PHASE + 02CF80: 00000C13 call len=12 k=00,00 0000000B 0000030E end_coroutine + 02CF8C: 01021001 set.f len=16 k=01,02 00000000 4092C000 00000000 special[0] = imm 1200 + 02CF9C: 02020C01 set.f len=12 k=02,02 00000001 00000000 special[1] = special[0] + 02CFA8: 02000416 op22 len=4 k=02,00 + 02CFAC: 01020C00 set.i len=12 k=01,02 00000000 00000000 special[0] = imm 0x0 + +## phase 3 — builtin 6 (END_PHASE) at 0x2D1DC + 02D10C: 01020C00 set.i len=12 k=01,02 00000000 00000001 special[0] = imm 0x1 + 02D118: 02030C00 set.i len=12 k=02,03 00000010 00000000 local[16] = special[0] + 02D124: 01021001 set.f len=16 k=01,02 00000000 BFF00000 00000000 special[0] = imm -1 + 02D134: 02030C01 set.f len=12 k=02,03 00000018 00000000 local[24] = special[0] + 02D140: 00000C13 call len=12 k=00,00 00000040 00000320 request_script_message(0x1, -1) + 02D14C: 00000C13 call len=12 k=00,00 00000078 00000321 wait_cmds_drained + 02D158: 01000C00 set.i len=12 k=01,00 00000074 00000001 global[116] = imm 0x1 + 02D164: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 02D174: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 02D180: 00000C13 call len=12 k=00,00 0000003B 00000325 fade_sound(3) + 02D18C: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 02D19C: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 02D1A8: 00000C13 call len=12 k=00,00 00000055 00000326 builtin85(3) + 02D1B4: 01021001 set.f len=16 k=01,02 00000000 40080000 00000000 special[0] = imm 3 + 02D1C4: 02030C01 set.f len=12 k=02,03 00000000 00000000 local[0] = special[0] + 02D1D0: 00000C13 call len=12 k=00,00 00000004 00000327 wait_s(3) + 02D1DC: 00000C13 call len=12 k=00,00 00000006 00000328 END_PHASE + 02D1E8: 00000C13 call len=12 k=00,00 0000000B 00000329 end_coroutine + 02D1F4: 00000414 ret len=4 k=00,00 + 02D1F8: 01030C00 set.i len=12 k=01,03 00000000 00000001 local[0] = imm 0x1 + 02D204: 01030C00 set.i len=12 k=01,03 00000004 00000046 local[4] = imm 0x46 + 02D210: 01021001 set.f len=16 k=01,02 00000000 00000000 00000000 special[0] = imm 0 + +## phase 3 — builtin 62 (FORCE_END_PHASE) at 0x34A10 + 034974: 0000080D op13 len=8 k=00,00 0000FE3C + 03497C: 00000C13 call len=12 k=00,00 0000000B 00000728 end_coroutine + 034988: 01000C0A op10 len=12 k=01,00 00000014 00000007 + 034994: 0000080E op14 len=8 k=00,00 0000FEB4 + 03499C: 01020C00 set.i len=12 k=01,02 00000000 00000001 special[0] = imm 0x1 + 0349A8: 02020C00 set.i len=12 k=02,02 00000001 00000000 special[1] = special[0] + 0349B4: 02000415 op21 len=4 k=02,00 + 0349B8: 01030C00 set.i len=12 k=01,03 00000000 00000001 local[0] = imm 0x1 + 0349C4: 01030C00 set.i len=12 k=01,03 00000004 00000049 local[4] = imm 0x49 + 0349D0: 00000C13 call len=12 k=00,00 00000045 0000072C unit_state(ADT308) + 0349DC: 00020417 op23 len=4 k=00,02 + 0349E0: 02020C0A op10 len=12 k=02,02 00000000 00000001 + 0349EC: 0000080D op13 len=8 k=00,00 0000FEB4 + 0349F4: 00000C13 call len=12 k=00,00 0000000B 0000072D end_coroutine + 034A00: 00000C13 call len=12 k=00,00 0000000B 00000731 end_coroutine + 034A0C: 00000414 ret len=4 k=00,00 + 034A10: 00000C13 call len=12 k=00,00 0000003E 00000000 FORCE_END_PHASE + 034A1C: 00020C00 set.i len=12 k=00,02 00000000 00000074 special[0] = global[116] + 034A28: 0000080C jmp len=8 k=00,00 0000FF44 -> code+0xFF44 (file 0x34A90) + 034A30: 01020C00 set.i len=12 k=01,02 00000000 000000D0 special[0] = imm 0xD0 + 034A3C: 02030C00 set.i len=12 k=02,03 00000000 00000000 local[0] = special[0] + 034A48: 00000C13 call len=12 k=00,00 00000053 0000073A builtin83(0xD0) diff --git a/docs/re/isl-stream-is-flat.md b/docs/re/isl-stream-is-flat.md new file mode 100644 index 00000000..cfbda0eb --- /dev/null +++ b/docs/re/isl-stream-is-flat.md @@ -0,0 +1,106 @@ +# ✅ The ISL instruction stream is FLAT — the recorded blocker was my own decoder + +Two files recorded the same blocker on producing a faithful per-phase condition +listing — `tools/re-capture/isl_report.py`'s docstring and +[`structures/isl-builtins.md`](structures/isl-builtins.md): + +> *"neither `isl.resync` … nor a naive linear decode from the phase base reaches +> every call site, so producing that listing faithfully needs the **coroutine +> entry points**, which `start_coroutine`'s operand carries and this tool does +> not yet follow."* + +**Refuted.** The coroutine entry points were never the problem. + +## The measurement + +`isl.call_sites()` enumerates calls by **scanning the encoding**, not by +decoding, so it is an independent denominator — exactly the control this needed. +Against it, for `Stage02.ssb` (2 846 call sites): + +| decode strategy | call sites reached | +|---|---| +| linear + jumps, stopping at `ret` — *what the tool did* | 133 — **4.7 %** | +| linear + jumps, continuing past `ret` | 2 275 — 79.9 % | +| … + following `start_coroutine`'s target — *the recorded fix* | 2 355 — **82.7 %** | +| **plain linear decode, no control flow at all** | **2 846 — 100.0 %** | + +Following the coroutine entries — the thing both files said was required — buys +**2.8 points**. Ignoring control flow entirely gets everything. + +### And it holds across the disc + +Decoding linearly from the first phase base to `code_end`: + +**28 files, 25 705 / 25 705 call sites = 100.00 %, and 28 / 28 decode clean to +the end with no desync.** Not one stage has a gap or a bad length. + +So the stream is a flat, self-consistent instruction sequence. Reaching a call +site needs no control-flow reconstruction — only a correct length walk. + +## The actual bug: `ret` is a YIELD + +`isl.dis` ended its loop on `if op == 20: break`. Op 20 *is* `ret` — but this is +a **coroutine VM**: the handler "sets `r29=1` and takes the suspend path", the +thread parks, and it later **resumes at the following instruction**. Code +continues after a `ret`, so stopping there abandons the file at the first one. +For Stage 02 that is offset `0x1F0` of a `0x34C80` code region — 271 of 18 739 +instructions. + +This also silently truncated my own reading: the listing for the phase-3 +`FORCE_END_PHASE` at `0x34A10` stopped four instructions short of the site, +at the `ret` at `0x34A0C`. + +`dis` now takes `stop_at_ret` (default `True`, preserving the old output — the +committed `data/isl-stage02.txt` regenerates **byte-identical**), and +`isl.linear_offsets()` is the correct walk. + +## ✅ By-product: `start_coroutine`'s target is staged slot 0 + +Worth keeping even though it was not the blocker. Of 83 `start_coroutine` sites +in phase 1, the only staged slot is **0**, and `phase_base + slot0` lands on a +valid instruction in **73/83 = 88 %**. + +**Control:** an arbitrary 4-aligned offset in the code region looks like a valid +instruction **38.7 %** of the time (20 887 / 53 990). So 88 % is discriminating, +not the null result — the operand is a code offset relative to the phase base. + +## 🟡 Where a phase actually ends — and where the condition is NOT + +New artefact [`data/isl-stage02-phase-ends.txt`](data/isl-stage02-phase-ends.txt) +(generator: `isl_report.py phase-ends`). Stage 02 has **15** phase-ending calls: +12 `END_PHASE` (6) and 3 `FORCE_END_PHASE` (62). + +**Every one of the 12 `END_PHASE` sites sits in the same stereotyped outro:** + +``` +call wait_cmds_drained +call fade_sound(3) +call builtin85(3) +call wait_s(3) +call END_PHASE +call end_coroutine +``` + +That is the phase *teardown*, not its condition — so **`END_PHASE`'s call site +is the wrong place to look for the clear condition.** The decision is upstream, +in the `op10`/`op13` pair that precedes the outro, which +[`structures/isl-builtins.md`](structures/isl-builtins.md) already flags as an +unread compare/branch (handlers `0x82271598` and `0x82271830`). + +The phase-3 `FORCE_END_PHASE` region shows the shape a real condition takes — +a poll loop, not a straight line: + +``` +op10 global[20], 7 +op14 -> 0xFEB4 +call unit_state(ADT308) +op23 +op10 special, 1 +op13 -> 0xFEB4 ; branch back — poll until the test passes +call end_coroutine +``` + +🟡 **Not settled:** `op10`/`op13`/`op14`/`op21`/`op23` are still unread +handlers, so which way each branch goes is a guess, and I am not naming them +from a pattern — the corpus has paid for that twice already. Reading those five +handlers is what turns this listing into the per-phase clear condition. diff --git a/docs/re/structures/isl-builtins.md b/docs/re/structures/isl-builtins.md index 8f1f9fc0..71b3d02e 100644 --- a/docs/re/structures/isl-builtins.md +++ b/docs/re/structures/isl-builtins.md @@ -601,8 +601,12 @@ Counting call sites in `Stage02.ssb` (`data/isl-stage02.txt`, regenerated by ⚠️ The sibling artefact `data/isl-stage02-conditions.txt` **predates the name corrections above** — it still prints `yield`, `await_label` and `push_trigger`, and its operand rendering predates the staging fix. It has no committed -generator; reproducing it needs the coroutine entry points, which -`start_coroutine`'s operand carries and the tool does not yet follow. +generator. ~~reproducing it needs the coroutine entry points, which +`start_coroutine`'s operand carries and the tool does not yet follow.~~ +🔴 **That reason is REFUTED** — see [isl-stream-is-flat](../isl-stream-is-flat.md). +The stream is flat; a plain linear decode reaches **25705/25705** call sites +across all 28 stages. What broke the naive decode was `isl.dis` stopping at +op 20 (`ret`), which in a coroutine VM is a *yield*, not an end of code. | built-in | sites | |---|---| diff --git a/tools/re-capture/isl.py b/tools/re-capture/isl.py index 0b5e6421..33c00e74 100755 --- a/tools/re-capture/isl.py +++ b/tools/re-capture/isl.py @@ -213,7 +213,37 @@ SYM1_SLOTS = { # not yet established. -def dis(b, off, count=40, code_base=0x24, args=True, sym2=None, sym1=None): +def linear_offsets(b, start=None): + """Every instruction offset, decoding linearly from the first phase base. + + MEASURED over all 28 stages: this reaches 25705/25705 of the call sites + `call_sites()` finds by scanning the encoding, and every file decodes clean + to `code_end` with no desync. The instruction stream is therefore FLAT -- + reaching a call site needs no control-flow reconstruction at all. + """ + end = struct.unpack_from('>I', b, 0x0C)[0] # symtab1 = end of code + if start is None: + start = phase_bases(b)[0] + out = [] + off = start + while off + 4 <= end: + out.append(off) + ln = (struct.unpack_from('>I', b, off)[0] >> 8) & 0xFF + if ln == 0 or ln % 2: + break + off += ln + return out + + +def dis(b, off, count=40, code_base=0x24, args=True, sym2=None, sym1=None, + stop_at_ret=True): + """`stop_at_ret` preserves the ORIGINAL behaviour and is wrong for reading. + + op 20 is `ret`, but in a coroutine VM that is a YIELD: the thread suspends + and later resumes at the following instruction, so code continues after it. + Stopping there reaches only 133 of Stage 02's 2846 call sites (4.7%); + continuing reaches all 2846. Pass `stop_at_ret=False` to read a listing. + """ out = [] staged = {} # local[] slot -> last value staged into it pending = None # value most recently put in special[0] @@ -301,7 +331,7 @@ def dis(b, off, count=40, code_base=0x24, args=True, sym2=None, sym1=None): out.append(' (length 0 -- stopping)') break off += ln - if op == 20: + if op == 20 and stop_at_ret: break return out diff --git a/tools/re-capture/isl_report.py b/tools/re-capture/isl_report.py index b1c60c9e..0b1bb57e 100644 --- a/tools/re-capture/isl_report.py +++ b/tools/re-capture/isl_report.py @@ -8,12 +8,13 @@ twice: once when operand staging was fixed (calls printed with too few arguments) and once when three built-in names were corrected. Keeping the generator in the tree is the point of this file. -`data/isl-stage02-conditions.txt` still has no generator here. A first attempt -is not committed because it printed most sites as bare `builtinN`: neither -`isl.resync` (it gives up far from a valid start) nor a naive linear decode from -the phase base reaches every call site, so producing that listing faithfully -needs the coroutine entry points, which `start_coroutine`'s operand carries and -this tool does not yet follow. + isl_report.py phase-ends -> every END_PHASE with its context + +The "needs the coroutine entry points" blocker recorded here is REFUTED: the +instruction stream is FLAT and `isl.linear_offsets` reaches 25705/25705 call +sites across all 28 stages. What broke the naive linear decode was `isl.dis` +stopping at op 20 (`ret`), which in a coroutine VM is a yield, not an end of +code -- see `docs/re/isl-stream-is-flat.md`. """ import collections import sys @@ -61,11 +62,42 @@ def emit_calls(b, path): print(line) +def emit_phase_ends(b, path): + """Every phase-ending call with the instructions that lead into it.""" + offs = isl.linear_offsets(b) + idx = {o: k for k, o in enumerate(offs)} + bases = isl.phase_bases(b) + s1, s2 = isl.symbols(b, 1), isl.symbols(b, 2) + ends = [(o, bid) for o, bid, _ in isl.call_sites(b) if bid in (6, 62)] + print('# %s — where each phase ends' % path) + print() + print('Generated by `tools/re-capture/isl_report.py phase-ends`.') + print('Decoded with `stop_at_ret=False`; a `ret` is a coroutine YIELD, so') + print('the listing continues past it.') + print() + print('phase code bases: ' + ' '.join('0x%x' % x for x in bases)) + print('%d phase-ending call(s): %d END_PHASE, %d FORCE_END_PHASE' + % (len(ends), sum(1 for _, b2 in ends if b2 == 6), + sum(1 for _, b2 in ends if b2 == 62))) + for o, bid in ends: + ph = sum(1 for x in bases if x <= o) + k = idx.get(o) + if k is None: + print('\n## 0x%X builtin %d — NOT on the linear stream' % (o, bid)) + continue + print('\n## phase %d — builtin %d (%s) at 0x%X' + % (ph, bid, isl.BUILTIN.get(bid, '?'), o)) + for line in isl.dis(b, offs[max(0, k - 16)], 22, + code_base=bases[ph - 1], sym2=s2, sym1=s1, + stop_at_ret=False): + print(' ' + line) + + def main(): path = sys.argv[1] b = isl.load(path) name = path.replace('\\', '/').split('/')[-1] - {'calls': emit_calls}[sys.argv[2]](b, name) + {'calls': emit_calls, 'phase-ends': emit_phase_ends}[sys.argv[2]](b, name) if __name__ == '__main__':