re: read the ISL branch handlers -- it is a condition-code machine

Closes the backlog item that was the last thing between the flat decode and a
per-phase clear condition, and closes isl-builtins.md's standing "op10 + op13 look
like a switch -- NOT confirmed".

op10 resolves two operands, issues a SIGNED cmp, and writes three condition bits to
a bitset at phase+24: bit 0 = EQ, bit 1 = GT, bit 2 = LT.  op11 is the same machine
for floats via fcmpu.  op13-op18 branch on those bits to [phase+232] + word@+4 --
the same phase-relative target form as the unconditional op12:

  13 bit0 set    beq      16 bits 2 then 0   ble
  14 bit0 clear  bne      18 bits 1 then 0   bge
  15 bit2 set    blt      17 bit1 set        bgt

13/14/15/17 are byte-identical apart from the bit index and the polarity.  All six
relations are present and each appears exactly once; that completeness is the check
that the reading is right, rather than the usage pattern -- which the item
explicitly warned against.

Operand order recorded because it is easy to reverse: LHS = (kind byte[1], word@+4),
RHS = (kind byte[0], word@+8).

Method note in the doc: the jump table at 0x822635FC holds THUNKS, and the handler
is the bl target inside each.  My first pass guessed handler addresses at a fixed
stride, landed mid-function, and produced a 20-line "difference" that was pure
misalignment.

isl.py names the ops; data/isl-stage02.txt is regenerated and every diff line pairs
exactly, only the op-name column changing (op10->cmp.i x5, op13->beq x4,
op14->bne x1).  data/isl-stage02-phase-ends.txt now shows the phase-3 poll loop
reading as one: unit_state(ADT308) -> op23 -> cmp.i -> beq back to 0xFEB4.

Left unnamed on purpose: op23 (0x82271C30) and op21 (0x82175C20).
This commit is contained in:
Sylpheed RE agent
2026-08-27 05:10:50 +00:00
parent f41847701c
commit bad96eb54a
6 changed files with 226 additions and 56 deletions

View File

@@ -73,6 +73,14 @@ 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'}
# op10 `cmp.i` resolves its two operands as
# LHS = resolve(kind byte[1], word@+4) `lbz r4,1(pc)` + `lwz r5,4(pc)`
# RHS = resolve(kind byte[0], word@+8) `lbz r4,0(pc)` + `lwz r5,8(pc)`
# and issues a SIGNED `cmp cr6, 0, LHS, RHS`. op11 `cmp.f` is the same shape
# through the float resolvers with `fcmpu`. So a listing line
# cmp.i k=01,02 00000000 00000002
# reads LHS = special[0], RHS = imm 2 -- "compare special[0] with 2".
# 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.
@@ -114,8 +122,15 @@ OPS = {
0: 'set.i', 1: 'set.f',
2: 'cmp.a', 4: 'cmp.a', 6: 'cmp.a', 8: 'cmp.a',
3: 'cmp.b', 5: 'cmp.b', 7: 'cmp.b', 9: 'cmp.b',
10: 'op10', 11: 'op11', 12: 'jmp', 13: 'op13', 14: 'op14', 15: 'op15',
16: 'op16', 17: 'op17', 18: 'op18', 19: 'call', 20: 'ret',
# 10-18: a condition-code architecture, read off the handlers. op10/op11
# COMPARE and write three bits into a bitset at `phase+24`
# bit 0 = EQ bit 1 = GT bit 2 = LT
# and 13-18 branch on those bits to `[phase+232] + word@+4` -- the same
# phase-relative target form as the unconditional op12. All six relations
# are present, which is itself the check that the reading is right.
10: 'cmp.i', 11: 'cmp.f', 12: 'jmp',
13: 'beq', 14: 'bne', 15: 'blt', 16: 'ble', 17: 'bgt', 18: 'bge',
19: 'call', 20: 'ret',
21: 'op21', 22: 'op22', 23: 'op23', 24: 'op24',
}