Slot-B module image layout Confirmed
Sixty-four PROT entries hold the choreography of every Seru summon and every capture-class cast - one file format wearing 64 different spells. They all load at the same address, one at a time, into the second overlay window. This page is the file: which bytes of one of those images are a jump table, which are code, and which are the particle records the spell spawns.
At a glance
- Where
- PROT.DAT entries
0903..0966, one per spell slot - Load base
0x801F69D8- the shared slot-B overlay window; only one image is resident at a time- Regions
- head jump table, frame-matched code, spawn-record band, inherited mastering tail
- In the port
legaia_overlay_images::slot_b_module; the same records are what the engine's cast effect pool stages- Behaviour
- the phase machine, entry tables and damage shape are on cast modules
The regions
An image reads, from its first byte, as up to four regions. Each is recovered from the image's own bytes:
| Region | Contents | Recovered by |
|---|---|---|
| head table | 0 to 256 words of in-image addresses: the jump table of one of the module's two switches. The sltiu bound at the dispatcher says which. | the leading run of in-image words, stopping at the first framed function |
| code | the tick's phase machine, the spawn stager 0x801F6734, the capture-class trampolines | frame matching |
| spawn-record band | particle / part records the spell spawns | the consumer's own pointer-forming instruction |
| inherited tail | a byte-identical copy of an earlier image's bytes at the same file offset - mastering residue | see cast modules |
Frame matching
A body opens at addiu sp, sp, -F and closes at the first jr ra whose delay slot is addiu sp, sp, +F for the same F. That survives an early jr ra inside a body and a jr ra word sitting in data. What it cannot see is a frameless leaf - no prologue, nothing to match - and the head jump table is what names those. PROT 0949 is the case: its stager forms a table base six words into the image's address run and bounds it with sltiu 0x8, so it owns eight arms, seven of them 20-byte frameless leaves (one eight-step tint ramp on the victim). Read the dispatcher's table base and bound before reading the frame partition.
Not always head-to-tail
PROT 0943 (Curse) and PROT 0961 (Dead End Crisis) carry framed code above their record band. Those bodies sit above each image's own-content cut and are byte-identical to PROT 0942 / 0960 at the same offsets - inherited residue, not the image's own routines. Either way, a record claim is cut at the next function's prologue, never run to the image end.
The spawn-record band
The record format is the summon part record shared with the PROT 0898 effect prototypes:
| Field | Meaning |
|---|---|
model_sel | -1 = a transform node; a small library index = a modelled part; 0x4000 / 0x4001 = the two render-mode nodes the spawn helper special-cases |
reserved | Never read: the spawn helper loads only the first halfword, and the move VM starts the program at halfword index 2. Zero in every record. |
| bytecode | The part's move-VM program |
How a record is found
A record is named by an instruction, never by a statistic. The module builds the record's address and passes it in $a2 to the actor-spawn helper FUN_80021B04 or its pool wrapper FUN_80050ED4. One record's end is the next record's start, so both ends of a claim are addresses the module itself computes - no length field, no terminator scan.
- Find every spawn calla
jalto either helper, inside a framed body of this image (an inherited tail carries a sibling's calls). - Read
$a2backwards from the delay slotalui/addiupair above the call or completed in its delay slot; a copy from a saved register (move a2,s0); or aswitchwhose arms each load$a2before jumping to one shared call. - Keep only addresses inside the imageplus guards that a pointer is not inside a framed function and its
model_selis one the helper dispatches. - Sort and cuteach record ends where the next begins, or at the next function prologue.
Bounding the highest record
Nothing above the highest record computes an address, so its own program bounds it: walk the move-VM opcode widths to a terminator - 0x08 HALT, or an armed 0x19 / 0x1B idle loop with no HALT right after - and round up to 4, since records are word-aligned. Only where neither turns up does a 0x09 WAIT with the maximal operand 0x0FFF (over a minute; no spell part lasts that long) serve as the bound.
Above it the bytes often keep reading as [header][program], and the walk chains them, stopping at eight zero bytes - the padding below an inherited tail, which would otherwise walk as a legal mesh-0 record and run into the donor's records.
How well the walk reproduces the measured extents
Where the band itself bounds a record, the chained walk reproduces the extent exactly almost every time and never overruns a measured end; the few misses stop below it. So the rule never claims bytes the band does not bound. The program counter can still step past an end on a mis-stepped width before dying on a non-opcode - the claim is what is safe, not the walk. Of the five pointer filters, only two fire on this disc: the address-inside-image test and the call-site-inside-a-framed-body test. The others are guards.
Why the band matters to the coverage tools
The coverage instrument's data-segment rule rejects any run holding a lui of a RAM page. A
record's body is move-VM bytecode - arbitrary bytes - so a long enough band contains that word by accident, and
the opcode statistic then scored the records as code. Naming the band with a parser takes those bytes out of the code
denominator. See byte accounting and
disc coverage.