re: the 147 ISL built-ins characterised; Stage 02 gates on units, not counts
Table at 0x8227226C is 147 big-endian absolute VAs (verified structurally: it ends exactly where the first handler begins, all targets inside sub_82272220). Arguments are not in the instruction -- every handler does c_str() on [phase+20], a packed blob, which is what the local[] staging fills. Return 2 = yield; five built-ins block by skipping the pc advance. Recovered the ScriptPhase state layout: 32-entry float and flag register files, int/double result registers, the timer block, and the runtime unit array at +324 indexed by symbol-table-2 index -- a direct hook from bytecode call sites to the two .ssb symbol tables. Spot-checked two claims against the disassembly rather than trusting them: id 4 loads a DOUBLE into the thread countdown and returns 2 (wait_s), and id 24 reads current/initial squadron member counts (squad_survival_pct). Both exact. Counting Stage02.ssb: unit_state 255, hp_pct_test 167, dist_lt 92, unit_alive 71, unit_relation 52 -- and squad_survival_pct, group_ratio_pct and the two global counters are NOT called at all. So Stage 02's phases are gated on named units (destroyed / HP / proximity), never on an aggregate count, even though the kill-counter primitives exist in the VM. That answers the standing 'next wave after N kills or after an event?' question for this stage: specific units, not a number. isl.py now names the built-ins, so the run-up to the first END_PHASE reads wait_cmds_drained / fade_sound(3) / builtin85(3) / wait_s(3) / END_PHASE. Not settled: 3 handlers unresolved (55, 75, 105); the 1024-slot interpreter command table is only partly recovered.
This commit is contained in:
@@ -67,6 +67,30 @@ CODE_BASE_FIELD = 0x08 # .ssb header: code offset (0x24 in every file)
|
||||
# opcode -> (mnemonic, handler VA) from the jump table
|
||||
KIND = {0: 'global', 1: 'imm', 2: 'special', 3: 'local'}
|
||||
|
||||
# Built-in names, from the 147-entry table at 0x8227226C. Only the ones whose
|
||||
# handler was actually read are named; the rest print as a bare id rather than a
|
||||
# guess. See docs/re/structures/isl-builtins.md.
|
||||
BUILTIN = {
|
||||
1: 'start_coroutine', 2: 'deploy_squadron', 3: 'move_order', 4: 'wait_s',
|
||||
5: 'await_label', 6: 'END_PHASE', 8: 'set_flag', 9: 'read_freg',
|
||||
10: 'random', 11: 'yield', 13: 'play_se', 14: 'play_bgm',
|
||||
17: 'wait_frames', 18: 'dist_lt', 20: 'hp_pct_test', 24: 'squad_survival_pct',
|
||||
26: 'damage_unit', 30: 'objective_marker', 31: 'objective_marker_at_route',
|
||||
33: 'global_counter0', 34: 'global_counter1', 36: 'screen_fade',
|
||||
39: 'MARK_LAST_PHASE', 40: 'mark_not_last', 43: 'play_voice',
|
||||
45: 'play_voice_vol', 52: 'play_stream', 53: 'sound_busy', 54: 'stop_sound',
|
||||
56: 'unit_relation', 59: 'fade_sound', 62: 'FORCE_END_PHASE',
|
||||
69: 'unit_state', 70: 'unit_alive', 72: 'group_ratio_pct', 73: 'timer_start',
|
||||
74: 'timer_limit', 88: 'camera_at', 90: 'camera_at_route',
|
||||
93: 'clear_flag', 94: 'is_engaged', 95: 'unit_hp_pct', 100: 'push_trigger',
|
||||
102: 'prompt_yes_no', 109: 'set_unit_flags', 115: 'named_event',
|
||||
120: 'wait_cmds_drained', 123: 'timer_resume', 124: 'timer_stop',
|
||||
125: 'timer_reset', 126: 'timer_elapsed', 127: 'timer_set',
|
||||
132: 'player_gauge0_test', 133: 'player_gauge1_test', 134: 'player_byte',
|
||||
137: 'wait_units_ready', 139: 'fade_to_black_end', 142: 'deploy_and_wait',
|
||||
143: 'deploy_and_wait2', 145: 'random_rand',
|
||||
}
|
||||
|
||||
OPS = {
|
||||
0: 'set.i', 1: 'set.f',
|
||||
2: 'cmp.a', 4: 'cmp.a', 6: 'cmp.a', 8: 'cmp.a',
|
||||
@@ -129,7 +153,7 @@ def dis(b, off, count=40, code_base=0x24, args=True):
|
||||
elif k0 == 3 and k1 == 2 and pending is not None:
|
||||
staged[words[0]] = pending
|
||||
if op == 19 and words:
|
||||
extra = ' builtin=%d' % words[0]
|
||||
extra = ' %s' % BUILTIN.get(words[0], 'builtin%d' % words[0])
|
||||
if args and staged:
|
||||
extra += '(' + ', '.join(
|
||||
'%s' % (('0x%X' % v) if isinstance(v, int) else v)
|
||||
|
||||
Reference in New Issue
Block a user