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:
@@ -97,6 +97,50 @@ Argument passing is visible in the disassembly: pairs of
|
||||
slots, then `call`. Floats are staged the same way — e.g. `40080000` = 3.0
|
||||
immediately before several calls.
|
||||
|
||||
## ✅ The four operand kinds, and how arguments are passed
|
||||
|
||||
Resolver table `0x82271D74`, four entries:
|
||||
|
||||
| kind | code | meaning |
|
||||
|---|---|---|
|
||||
| 0 | `lis 0x828E` / `bl 82454A40` / `lwzx` | **global[i]** — indexed global array |
|
||||
| 1 | `mr r3,r31` | **immediate** — the operand word itself |
|
||||
| 2 | `[phase+164]` if `i==0` else `[phase+168]` | **special[i]** — two scratch registers |
|
||||
| 3 | `addi r3,r3,20` / `lwzx` | **local[i]** — `[phase+20 + i]` |
|
||||
|
||||
Byte[0] is the rvalue's kind (operand word@+8) and byte[1] the lvalue's
|
||||
(word@+4). That turns the recurring pair into something readable:
|
||||
|
||||
```
|
||||
set.i k=01,02 <A> <V> special[A] = V (immediate -> special)
|
||||
set.i k=02,03 <B> <0> local[B] = special[0]
|
||||
```
|
||||
|
||||
— i.e. **argument staging**. Values land in `local[]` at byte offsets
|
||||
0, 4, 8, 0xC…, and the following `call` consumes them; a built-in's arguments
|
||||
are not in its own instruction. `isl.py` now tracks the staging and prints them.
|
||||
|
||||
⚠️ **Immediates in `set.f` are DOUBLES**, carried as two words — op 1 stores with
|
||||
`stfd`. Reading only the high word as a *float* gives `2.125` where the script
|
||||
means **3.0**, which is exactly the sort of plausible-but-wrong number that would
|
||||
have been believed. The 16-byte `set.f` form is `high, low`.
|
||||
|
||||
With that, the run-up to the first `END PHASE` in Stage 02 reads:
|
||||
|
||||
```
|
||||
0050F4 builtin=64(0x42, 0x2, 0x1, 0x9, 0x1, -1)
|
||||
005160 builtin=120
|
||||
005188 builtin=59(3)
|
||||
0051B0 builtin=85(3)
|
||||
0051D8 builtin=4(3)
|
||||
0051E4 builtin=6 <-- end phase
|
||||
0051F0 builtin=11
|
||||
```
|
||||
|
||||
Three separate built-ins taking `3` immediately before the phase ends — a
|
||||
plausible "wait 3 seconds" family, **unconfirmed** until the built-in table is
|
||||
read.
|
||||
|
||||
## ❔ What this does not settle
|
||||
|
||||
* **The 147 built-ins are uncharacterised.** Without them the disassembly is
|
||||
@@ -105,7 +149,7 @@ immediately before several calls.
|
||||
* Opcodes 2–11 and 13–18 are named only by handler address. The four-way sharing
|
||||
(2/4/6/8 and 3/5/7/9) suggests the handler re-reads the opcode to pick a
|
||||
comparison or a type, but that is not yet read.
|
||||
* Operand *kinds* (4 of them) are not decoded — the `k=01,02` / `k=02,03` pairs
|
||||
are recorded literally.
|
||||
* The four-way opcode sharing (2/4/6/8 and 3/5/7/9) suggests the handler
|
||||
re-reads the opcode to pick a comparison or a type; not yet read.
|
||||
* The mission-level stream at `+0x24` of a `.ssb` — as opposed to this ISL
|
||||
stream — is still only partly read.
|
||||
|
||||
Reference in New Issue
Block a user