# ✅ The ISL branch ops — a condition-code machine, read off the handlers [`BACKLOG.md`](../BACKLOG.md) had this as the last thing between the flat decode and a per-phase clear condition: five unread handlers, and an explicit instruction not to name a branch from a pattern. They are read now, from `sylpheed.db`. Nothing below is inferred from usage. ## Getting the real handler addresses The 25-entry jump table at `0x822635FC` holds **thunks** inside the dispatcher, not the handlers. Each thunk is three register moves and a `bl`; the `bl` target is the handler. Taking the addresses any other way gets them wrong — my first attempt guessed them at a fixed stride and landed mid-function, which produced a 20-line "difference" that was pure misalignment. | op | thunk | handler | |---|---|---| | 10 | `0x822636F8` | `0x82271598` | | 11 | `0x8226370C` | `0x822716E0` | | 12 | `0x82263720` | *inline in the thunk* | | 13 | `0x82263738` | `0x82271830` | | 14 | `0x8226374C` | `0x822718C8` | | 15 | `0x82263760` | `0x82271960` | | 16 | `0x82263774` | `0x822719F8` | | 17 | `0x8226379C` | `0x82271AC8` | | 18 | `0x82263788` | `0x82271B60` | ## ✅ op10 / op11 are COMPARE, and they write three condition bits `0x82271598` resolves two operands and compares them: ``` 822715B0 lwz r5, 8(r29) ; word@+8 822715B4 lbz r4, 0(r29) ; kind byte[0] 822715B8 bl 0x82271D40 ; integer operand resolver -> r28 = RHS 822715C0 lwz r5, 4(r29) ; word@+4 822715C8 lbz r4, 1(r29) ; kind byte[1] 822715CC bl 0x82271D40 -> r27 = LHS 822715D4 addi r31, r31, 24 ; the condition bitset lives at phase+24 822715D8 cmp cr6, 0, r27, r28 ; SIGNED ``` It then writes **three** bits, each set-or-cleared by its own `cmp`: | bit | written at | condition | set / clear | |---|---|---|---| | **0** | `0x822715D8` | `EQ` | `or` if equal, `andc` if not | | **1** | `0x8227162C` | `GT` | `or` / `andc` on `cr6+gt` | | **2** | `0x82271678` | `LT` | `or` / `andc` on `cr6+lt` | **`op11` is the same machine for floats** — `0x822716E0` resolves through the float resolvers (`0x82271F10` / `0x82271E30`), issues `fcmpu cr6, f30, f31`, and writes the same bitset at `phase+24`. **Operand order matters and is easy to get backwards:** LHS is `(kind byte[1], word@+4)` and RHS is `(kind byte[0], word@+8)`. So a listing line ``` cmp.i k=01,02 00000000 00000002 ``` is *compare `special[0]` against immediate `2`* — `k=01` is the RHS kind (`imm`), `k=02` the LHS kind (`special`). ## ✅ op13–op18 are the six relational branches Every one of them tests bits in that same bitset and, when taken, sets the pc to `[phase+232] + word@+4` — **the identical phase-relative target form as the unconditional `op12`**. | op | bits tested | taken when | name | |---|---|---|---| | 13 | 0 | set | **`beq`** | | 14 | 0 | clear | **`bne`** | | 15 | 2 | set | **`blt`** | | 17 | 1 | set | **`bgt`** | | 16 | 2, then 0 | either set | **`ble`** | | 18 | 1, then 0 | either set | **`bge`** | 13, 14, 15 and 17 are **byte-identical to each other** apart from two instructions — the bit index (`addi r5, r0, N`) and the polarity (`cmpli r11, 0x1` vs `0x0`). 16 and 18 are longer because they test a second bit after the first fails, which is exactly `<=` and `>=`. 🔑 **All six relations are present and each appears exactly once.** That completeness is the check: a mis-read would not produce a closed, non-redundant relational set. ## ✅ This closes the `op10`+`op13` "switch" question [`isl-builtins.md`](isl-builtins.md) recorded it as 🟡 *"Consecutive small immediates each paired with their own code offset is the shape of a case/branch dispatch … **Not confirmed** — the handlers have not been read."* They are read now, and the shape is what it looked like — a chain of compare-and-branch-if-equal: ``` cmp.i k=01,02 00000000 00000001 beq -> 0x4E2C cmp.i k=01,02 00000000 00000002 beq -> 0x4ED4 cmp.i k=01,02 00000000 00000003 beq -> 0x4F7C ``` *"switch (special[0]) { case 1: … case 2: … case 3: … }"*, lowered to sequential compares. Confirmed from the handlers, not from the pattern. ## ✅ And a phase-3 clear condition now reads as one From [`../data/isl-stage02-phase-ends.txt`](../data/isl-stage02-phase-ends.txt): ``` 0349D0 call unit_state(ADT308) 0349DC op23 ; takes the call result 0349E0 cmp.i k=02,02 special[0], special[1] 0349EC beq -> 0xFEB4 ; loop back while it holds 0349F4 call end_coroutine ``` A coroutine polling `unit_state` on unit `ADT308` and branching **back** while the comparison is equal — the poll loop the previous iteration could only describe by shape. ## 🟡 Not settled * ~~**`op23`** (`0x82271C30`) is left unnamed.~~ 🔴 **My error — the corpus already had it.** [`isl-bytecode.md`](isl-bytecode.md), which owns the opcode table, names 21–24 `push.i`/`push.f`/`pop.i`/`pop.f`. `op23` is `pop.i`, into `[phase+168]` = `special[1]`. Verified from the thunks and closed in [isl-builtin-dispatch](isl-builtin-dispatch.md), which also settles where a built-in's result goes: `[phase+164]` = `special[0]`. * **`op21`** (`0x82175C20`) has a different shape from all of these — its thunk passes `phase+168` and `phase+44`, not the pc. That is `push.i`: it pushes `special[1]` onto the deque at `phase+44`. The generic deque helper itself is still unread. * **The bitset container at `phase+24`.** `op10` reaches it with `0x822749B0` (by address, `phase+24`) and `op13` with `0x82274CC0` (through a word loaded from `phase+32`). Both land on the same bits, but the exact container layout is not established, so `phase+32` is *not* asserted to be its data pointer. * **Which value `special[0]` holds at a given site.** Naming the branch does not by itself give the clear condition for every stage — that needs the operand chain feeding each compare, which is the next step.