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
This commit is contained in:
@@ -88,7 +88,13 @@ BUILTIN = {
|
||||
48: 'squadron_escort', 52: 'play_stream', 53: 'sound_busy', 54: 'stop_sound',
|
||||
56: 'unit_relation', 59: 'fade_sound', 62: 'FORCE_END_PHASE', 64: 'request_script_message',
|
||||
69: 'unit_state', 70: 'unit_alive', 72: 'group_ratio_pct', 73: 'timer_start',
|
||||
74: 'timer_limit', 88: 'camera_at', 90: 'camera_at_route',
|
||||
74: 'timer_limit',
|
||||
# ❌ 88 'camera_at' and 90 'camera_at_route' WITHDRAWN. 88 has ZERO call
|
||||
# sites in all 28 stages, so its name was never testable. 90 has exactly 8,
|
||||
# all in Stage 02 phase 3 (the cruise-missile act), and its first operand
|
||||
# resolves to symbol-table-1 type 7 -- `eff_n0071`, an EFFECT name -- in
|
||||
# 8/8, with a per-missile `Route_ADT30N_p3M` at slot 20. Whatever it does,
|
||||
# it is not aimed at a camera. Left unnamed rather than renamed on a guess.
|
||||
93: 'clear_flag', 94: 'is_engaged', 95: 'unit_hp_pct', 100: 'reset_phase_threads',
|
||||
102: 'prompt_yes_no', 109: 'set_unit_flags', 115: 'named_event',
|
||||
120: 'wait_cmds_drained', 123: 'timer_resume', 124: 'timer_stop',
|
||||
@@ -154,6 +160,46 @@ UNIT_ARG2 = {2, 18, 47, 48, 56, 79, 95, 128} # a SECOND unit index at blob[12
|
||||
UNIT_ARG3 = {128} # and a third at blob[20]
|
||||
UNIT_SLOTS = {4: UNIT_ARG, 12: UNIT_ARG2, 20: UNIT_ARG3}
|
||||
|
||||
# WHY the unit indices sit at 4/12/20 and never at 0/8/16: a **symbol operand is
|
||||
# a two-word pair** -- a tag word holding the constant 1, then the index. The
|
||||
# tag is not data, so printing it puts a meaningless leading `0x1` in front of
|
||||
# every unit predicate.
|
||||
#
|
||||
# Measured over all 28 stages:
|
||||
# * 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, and slot 16 is the constant 1 in 152/152 for built-in 128, the only
|
||||
# one taking a third;
|
||||
# * 24 built-ins have a slot 0 that is NOT the constant -- and every one of
|
||||
# them takes no symbol at slot 4 (`start_coroutine` a code offset, `wait_s`
|
||||
# a double, `set_flag` an index).
|
||||
#
|
||||
# The tag does NOT generalise to "every even slot is a tag": 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 symbol indices at 0/4/8/12/16 with no tags at all. Each
|
||||
# built-in has a fixed signature and is 100% consistent with itself; none mixes.
|
||||
TAG_SLOTS = {slot - 4 for slot in UNIT_SLOTS}
|
||||
|
||||
# Symbol table 1 holds three types, and its slots were measured the same way as
|
||||
# the unit slots (every observed value resolves, >=5 distinct values, and the
|
||||
# resolved type is pure):
|
||||
# type 1 (1362 entries) `Route_*` names
|
||||
# type 6 (2247) message / objective names
|
||||
# type 7 (81) `eff_*` effect names
|
||||
SYM1_SLOTS = {
|
||||
0: {64, 75, 115}, # 64 & 75 type 6; 115 type 7
|
||||
4: {75, 136},
|
||||
8: {75},
|
||||
12: {2, 3, 7, 16, 19, 25, 75, 108, 143},
|
||||
16: {75},
|
||||
20: {90},
|
||||
24: {48},
|
||||
28: {128},
|
||||
}
|
||||
# Deliberately NOT listed: built-ins 24@4, 46@12 and 114@4 resolve 100% but mix
|
||||
# type 6 and type 1, so the slot's meaning is not one thing. Recorded rather
|
||||
# than guessed at.
|
||||
|
||||
# Symbol table 2 holds TWO entity types: type 2 (1160 entries disc-wide) and
|
||||
# type 8 (249). They are not interchangeable -- built-ins 95 and 128 take a
|
||||
# type-2 unit at slot 4 and, at slot 12, an operand that is type 8 in 100% of
|
||||
@@ -161,7 +207,7 @@ UNIT_SLOTS = {4: UNIT_ARG, 12: UNIT_ARG2, 20: UNIT_ARG3}
|
||||
# not yet established.
|
||||
|
||||
|
||||
def dis(b, off, count=40, code_base=0x24, args=True, sym2=None):
|
||||
def dis(b, off, count=40, code_base=0x24, args=True, sym2=None, sym1=None):
|
||||
out = []
|
||||
staged = {} # local[] slot -> last value staged into it
|
||||
pending = None # value most recently put in special[0]
|
||||
@@ -221,15 +267,22 @@ def dis(b, off, count=40, code_base=0x24, args=True, sym2=None):
|
||||
extra = ' %s' % BUILTIN.get(words[0], 'builtin%d' % words[0])
|
||||
if args and staged:
|
||||
parts = []
|
||||
# The tag word in front of a symbol operand is not an argument.
|
||||
tags = {slot - 4 for slot, ids in UNIT_SLOTS.items()
|
||||
if words[0] in ids and slot in staged}
|
||||
for slot, v in sorted(staged.items()):
|
||||
if slot in tags and v == 1:
|
||||
continue
|
||||
txt = ('0x%X' % v) if isinstance(v, int) else v
|
||||
# Resolve only a slot that is declared a unit index AND
|
||||
# whose value really is one -- a resolver that invents a
|
||||
# name for a non-index is worse than one that prints the
|
||||
# raw number.
|
||||
# Resolve only a slot that is declared an index AND whose
|
||||
# value really is one -- a resolver that invents a name for
|
||||
# a non-index is worse than one that prints the raw number.
|
||||
if (sym2 and words[0] in UNIT_SLOTS.get(slot, ())
|
||||
and isinstance(v, int) and v in sym2):
|
||||
txt = sym2[v][1]
|
||||
elif (sym1 and words[0] in SYM1_SLOTS.get(slot, ())
|
||||
and isinstance(v, int) and v in sym1):
|
||||
txt = sym1[v][1]
|
||||
parts.append(txt)
|
||||
extra += '(' + ', '.join(parts) + ')'
|
||||
staged = {}
|
||||
|
||||
@@ -27,7 +27,7 @@ def census(b):
|
||||
|
||||
def emit_calls(b, path):
|
||||
cs, h = census(b)
|
||||
s2 = isl.symbols(b, 2)
|
||||
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`.')
|
||||
@@ -57,7 +57,7 @@ def emit_calls(b, path):
|
||||
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):
|
||||
for line in isl.dis(b, start, 64, code_base=0x24, sym2=s2, sym1=s1):
|
||||
print(line)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user