`SYLPHEED_DB` is the documented contract for the static-analysis database —
`docker/decoder/sylph-decoder` sets it, and `docs/agents/CONTAINER-NOTES.md` and
`decoder-loop.md` list it — but no reader honoured it:
isl_cmdtab.py, name_block_bases.py hardcoded `/work/xenia-rs/sylpheed.db`, a
path that has not existed anywhere since
`/work` became a clone. Both failed on
every machine, before and after the
database moved.
zq.py used `$SYLPH_XEXDB`, a name invented in
#39 without grepping for the existing one.
All three now resolve `$SYLPHEED_DB`, then `$SYLPH_XEXDB` (kept as an alias so
nothing already written against it breaks), then `<repo root>/sylpheed.db`, and
refuse with exit 1 naming both variables and the build command otherwise.
`grab_tutorial.sh` hardcoded its helpers into the retired `sylpheed-reborn`
checkout. That copy's `skip_intro.sh` still calls the removed `vgamepad` and
exits 0 having pressed nothing — the failure this repository's own copy was
rewritten to make loud. So the script was already running a silently broken
helper; it now resolves its helpers from its own directory. Nothing exists only
in reborn's `tools/re-capture` (checked: 0 reborn-only files).
Verified against the same database, output compared byte for byte with the
ORIGINAL scripts (path-substituted copies, sibling imports resolvable):
resolution path isl_cmdtab name_block_bases
default (root) identical identical
$SYLPHEED_DB identical identical
$SYLPH_XEXDB identical identical
bad path exit 1 exit 1 (zq.py: exit 1 too)
and `isl_cmdtab`'s output is byte-identical to the body of the committed
`docs/re/data/isl-command-table.txt`.
⚠️ `name_block_bases`'s output does NOT reproduce the committed
`docs/re/data/name-block-bases.txt` (2,609 lines differ, `strings in the image:
7366` vs `7140`). That is the database, not this change: the artefact was
generated from the agent box's older 586 MB database, and this machine's is the
current generator's. Two databases are in circulation. Not regenerated here.
⚠️ Also not fixed, because it is a different bug: `name_block_bases.py` globs
`/work/sylph_extract/**/*.pak`, another path that exists nowhere now; that part
of its report is silently empty. It should read `$SYLPHEED_DISC`.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
78 lines
2.9 KiB
Python
78 lines
2.9 KiB
Python
"""Recover the ISL interpreter's 1023-slot command table from sub_822FE040.
|
|
|
|
The builder fills every slot with a default, then overwrites individual slots.
|
|
Each write is `stw rX, off(r1)` (a two-word stack pair) followed by
|
|
`ld r9, off(r1)` and `std r9, N(r31)`, where slot = (N - 32) / 8.
|
|
"""
|
|
import duckdb, re, sys
|
|
|
|
|
|
def _resolve_db():
|
|
"""`$SYLPHEED_DB`, else `$SYLPH_XEXDB`, else `<repo root>/sylpheed.db`.
|
|
|
|
`SYLPHEED_DB` is the documented contract -- `docker/decoder/sylph-decoder` sets
|
|
it and `docs/agents/CONTAINER-NOTES.md` lists it -- but no reader honoured it:
|
|
this script hardcoded `/work/xenia-rs/sylpheed.db`, a path that has not
|
|
existed anywhere since `/work` became a clone, so it failed on every machine.
|
|
"""
|
|
import os, pathlib
|
|
for var in ('SYLPHEED_DB', 'SYLPH_XEXDB'):
|
|
v = os.environ.get(var)
|
|
if v:
|
|
p = pathlib.Path(v).expanduser()
|
|
if not p.is_file():
|
|
sys.exit(f'${var} is set but is not a file: {p}')
|
|
return str(p)
|
|
p = pathlib.Path(__file__).resolve().parents[2] / 'sylpheed.db'
|
|
if p.is_file():
|
|
return str(p)
|
|
sys.exit(f'no database: set $SYLPHEED_DB, or build {p} with '
|
|
'sylph-xexdb dis <xex|iso> --db sylpheed.db --analyze sql --quiet')
|
|
|
|
|
|
con=duckdb.connect(_resolve_db(), read_only=True)
|
|
rows=con.execute("select address, coalesce(ext_disasm,disasm) from instructions "
|
|
"where address>=? and address<? order by address",
|
|
[0x822FE040,0x822FE618]).fetchall()
|
|
gpr={}; stack={}; slots={}
|
|
lis=re.compile(r"lis\s+r(\d+), 0x([0-9A-Fa-f]+)")
|
|
addi=re.compile(r"addi\s+r(\d+), r(\d+), (-?\d+)")
|
|
subi=re.compile(r"subi\s+r(\d+), r(\d+), (-?\d+)")
|
|
li=re.compile(r"li\s+r(\d+), (-?\d+)")
|
|
stw=re.compile(r"stw\s+r(\d+), (-?\d+)\(r1\)")
|
|
ld=re.compile(r"ld\s+r(\d+), (-?\d+)\(r1\)")
|
|
std=re.compile(r"std\s+r(\d+), (-?\d+)\(r31\)")
|
|
for a,d in rows:
|
|
m=lis.match(d)
|
|
if m: gpr[int(m.group(1))]=int(m.group(2),16)<<16; continue
|
|
m=addi.match(d)
|
|
if m:
|
|
b=gpr.get(int(m.group(2)))
|
|
gpr[int(m.group(1))]=None if b is None else (b+int(m.group(3)))&0xFFFFFFFF
|
|
continue
|
|
m=subi.match(d)
|
|
if m:
|
|
b=gpr.get(int(m.group(2)))
|
|
gpr[int(m.group(1))]=None if b is None else (b-int(m.group(3)))&0xFFFFFFFF
|
|
continue
|
|
m=li.match(d)
|
|
if m: gpr[int(m.group(1))]=int(m.group(2))&0xFFFFFFFF; continue
|
|
m=stw.match(d)
|
|
if m: stack[int(m.group(2))]=gpr.get(int(m.group(1))); continue
|
|
m=ld.match(d)
|
|
if m:
|
|
off=int(m.group(2))
|
|
gpr[int(m.group(1))]=(stack.get(off), stack.get(off+4))
|
|
continue
|
|
m=std.match(d)
|
|
if m:
|
|
v=gpr.get(int(m.group(1)))
|
|
n=int(m.group(2))
|
|
if (n-32)%8==0 and isinstance(v,tuple):
|
|
slots[(n-32)//8]=v
|
|
continue
|
|
print("populated slots: %d" % len(slots))
|
|
for s in sorted(slots):
|
|
hi,lo=slots[s]
|
|
print(" opcode %-5d -> %s %s" % (s, '%08X'%hi if hi else 'None', '%08X'%lo if lo else ''))
|