re: ISL operand kinds decoded; arguments are staged through local[]

Resolver table 0x82271D74 gives four kinds: 0 global[i], 1 immediate,
2 special[i] ([phase+164]/[phase+168]), 3 local[i] ([phase+20+i]). Byte[0] is
the rvalue kind, byte[1] the lvalue kind, so the recurring instruction pair is
argument staging -- values land in local[] at offsets 0,4,8,0xC and the next
call consumes them. A built-in's arguments are not in its own instruction.

Fixed a decode that would have been believed: immediates in set.f are DOUBLES
carried as two words (op 1 stores with stfd). Reading the high word as a float
gives 2.125 where the script means 3.0.

isl.py now tracks staging and prints call arguments, so the run-up to the first
END PHASE in Stage02 reads as builtin=64(0x42,2,1,9,1,-1) / 120 / 59(3) / 85(3)
/ 4(3) / 6. Three built-ins taking 3 just before the phase ends look like a
wait-seconds family -- flagged as unconfirmed until the built-in table is read.
This commit is contained in:
Sylpheed RE agent
2026-08-25 12:28:04 +00:00
parent 7b445916bc
commit 3f13a8ceef
3 changed files with 119 additions and 7 deletions

View File

@@ -34,6 +34,22 @@ and by every routine ending on a `ret`:
byte[3] opcode | byte[2] length | byte[1],byte[0] operand kinds
following words: operands (12 bytes is the common `call` form)
**Operand kinds** (resolver table `0x82271D74`, 4 entries):
0 global[i] lis 0x828E / bl 82454A40 / lwzx -- indexed global array
1 immediate mr r3,r31 -- the operand word itself
2 special[i] [phase+164] if i==0 else [phase+168]
3 local[i] addi r3,r3,20 / lwzx -- [phase+20 + i]
so the recurring pair
set.i k=01,02 <A> <V> special[A] = V (immediate -> special)
set.i k=02,03 <B> <0> local[B] = special[0]
is **argument staging**: values land in `local[]` slots 0,4,8,0xC… and the next
`call` consumes them. That is why a built-in's arguments are not in its own
instruction.
A `call` carries the built-in id in word@+4 and a monotonically increasing
STATEMENT ID in word@+8 (0x245, 0x248, 0x24A, ... across a routine) -- the value
`sub_82272220` stores to `[phase+200]`, i.e. a source-position counter.
@@ -49,6 +65,8 @@ import sys
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'}
OPS = {
0: 'set.i', 1: 'set.f',
2: 'cmp.a', 4: 'cmp.a', 6: 'cmp.a', 8: 'cmp.a',
@@ -63,8 +81,10 @@ def load(path):
return open(path, 'rb').read()
def dis(b, off, count=40, code_base=0x24):
def dis(b, off, count=40, code_base=0x24, args=True):
out = []
staged = {} # local[] slot -> last value staged into it
pending = None # value most recently put in special[0]
for _ in range(count):
if off + 4 > len(b):
break
@@ -80,8 +100,41 @@ def dis(b, off, count=40, code_base=0x24):
if off + i + 4 <= len(b):
words.append(struct.unpack_from('>I', b, off + i)[0])
extra = ''
if op in (0, 1) and len(words) >= 2:
# op 0/1: lvalue = (kind byte[1], word@+4); rvalue = (kind byte[0], word@+8)
rv = words[1]
extra = ' %s[%d] = %s%s' % (
KIND.get(k0, '?%d' % k0), words[0],
KIND.get(k1, '?%d' % k1),
('' if k1 == 1 else '[%s]' % rv) if True else '')
if k1 == 1:
if op == 1:
lo = words[2] if len(words) > 2 else 0
extra += ' %.6g' % struct.unpack(
'>d', struct.pack('>II', words[1], lo))[0]
else:
extra += ' 0x%X' % words[1]
# track the staging pattern so a call can show its arguments
if op in (0, 1) and len(words) >= 2:
if k0 == 2 and k1 == 1:
if op == 1:
# op 1 stores with stfd, so an immediate float operand is a
# DOUBLE carried as two words -- reading only the high word
# as a float gives 2.125 where the script means 3.0.
lo = words[2] if len(words) > 2 else 0
pending = '%.6g' % struct.unpack(
'>d', struct.pack('>II', words[1], lo))[0]
else:
pending = words[1]
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]
if args and staged:
extra += '(' + ', '.join(
'%s' % (('0x%X' % v) if isinstance(v, int) else v)
for _, v in sorted(staged.items())) + ')'
staged = {}
elif op == 12 and words:
extra = ' -> code+0x%X (file 0x%X)' % (words[0], code_base + words[0])
out.append('%06X: %08X %-6s len=%-3d k=%02x,%02x %s%s' % (