Files
Sylpheed/tools/re-capture/isl_report.py
Sylpheed RE agent 009bca880e re: an ISL symbol operand is a (tag, index) pair — and two more names withdrawn
Verified rather than adopted: a subagent proposed that every even operand
slot is a type tag. Measured, the strong form is false and a precise form is
true.

TRUE: a SYMBOL operand is two words, a tag holding the constant 1 followed
by the index. Slot 0 is the integer 1 in 19899/19899 calls whose slot 4 is a
unit; slot 8 is tag-shaped in 100% of calls for every built-in taking a
second unit; slot 16 is 1 in 152/152 for built-in 128, the only one taking a
third. The 24 built-ins whose slot 0 is NOT the constant are exactly those
taking no symbol there. This explains the unit slots 4/12/20 rather than
replacing them.

FALSE as stated: slot 8 is a bare double for built-ins 4, 20, 24, 26, 28,
29, 90, 106 and 127, and built-in 75 carries five bare indices at 0/4/8/12/16
with no tags at all. Each built-in has a fixed signature and is 100%
self-consistent; none of the 34 with >=20 sites mixes the two.

Symbol table 1 has three types -- 1 routes (1362), 6 messages (2247), 7
effects (81) -- and its operand slots are type-pure, measured the same way.
Resolving them makes listings say what the script means:
`request_script_message(MSG_VOICE_D_257, ...)`, a fourth independent
confirmation of that name. Slots 24@4, 46@12 and 114@4 resolve 100% but MIX
types 6 and 1, so they are left unresolved rather than guessed.

Two more names withdrawn, neither replaced:
* 88 `camera_at` -- ZERO call sites in all 28 stages; never testable.
* 90 `camera_at_route` -- 8 sites, all Stage 02 phase 3, first operand is
  symtab-1 type 7 `eff_n0071`, an EFFECT name, in 8/8, with a per-missile
  Route_ADT301..308_p3M at slot 20. Not aimed at a camera.

Left unnamed on purpose: replacing a guessed name with another guess is how
the three names corrected earlier today went wrong.

Also flagged: 115 `named_event`'s only symbol operand is an eff_* name in
84/84 sites, so that name is suspect too. Not renamed pending a handler read.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-25 22:51:07 +00:00

73 lines
2.6 KiB
Python

#!/usr/bin/env python3
"""Regenerate the committed ISL artefacts under `docs/re/data/`.
isl_report.py <StageNN.ssb> calls -> the call-site census + a listing
The artefact was produced by an uncommitted one-off, so it drifted out of date
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.
"""
import collections
import sys
import isl
def census(b):
cs = isl.call_sites(b)
return cs, collections.Counter(bid for _, bid, _ in cs)
def emit_calls(b, path):
cs, h = census(b)
s1, s2 = isl.symbols(b, 1), isl.symbols(b, 2)
print('# %s — ISL disassembly artefacts' % path)
print()
print('Generated by `tools/re-capture/isl_report.py calls`.')
print()
print('%d call sites, %d distinct built-ins' % (len(cs), len(h)))
for bid, n in h.most_common():
print(' builtin %-4d %5d site(s)' % (bid, n))
print()
print('## phase-control sites')
for bid, nm in ((6, 'END_PHASE'), (62, 'FORCE_END_PHASE'),
(39, 'MARK_LAST_PHASE'), (40, 'mark_not_last')):
offs = [off for off, b2, _ in cs if b2 == bid]
print('%-3d %-18s %2d: %s'
% (bid, nm, len(offs), ' '.join('0x%x' % o for o in offs)))
print()
print('## named built-ins used, by traffic')
for bid, n in h.most_common():
nm = isl.BUILTIN.get(bid)
if nm:
print(' %-3d %-24s %4d' % (bid, nm, n))
print()
print('## phase code bases')
print('Each phase has its OWN base; the file header offset is not it.')
print(' ' + ' '.join('0x%x' % x for x in isl.phase_bases(b)))
print()
print('## disassembly into the first END_PHASE')
target = [off for off, bid, _ in cs if bid == 6][0]
start = isl.resync(b, target)
print('resync from 0x%X' % start)
for line in isl.dis(b, start, 64, code_base=0x24, sym2=s2, sym1=s1):
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)
if __name__ == '__main__':
main()