Files
BFM-decomp/cookbook/C0329.md
T

3.3 KiB
Raw Blame History

§295 — THE KERNEL-TRAP STUB: §81's jr-DETECTOR WITHOUT A TABLE IS A PsyQ SYSCALL TRAMPOLINE — ROUTE TO §265, NEVER INTO THE CARVE CHAIN (P31 S61; wave m0a, 9 cards byte-proven; resolves §182's held cluster)

§81's detector is a CONJUNCTION: a mid-function jr on a non-$ra register AND a jtbl_<addr> in the object's data. NINE m0a cards fired the jr half with NO table anywhere (grepped fleet-wide) and each independently re-derived the same resolution — the §283-class signal of a missing section. The shape:

addiu $t2, $zero, 0xA0|0xB0   # kernel A/B dispatch vector
jr    $t2
addiu $t1, $zero, <fn#>       # in the jr's DELAY SLOT
(pad nops)

A live constant in an indirect-jr delay slot with no epilogue is unreachable from any C body (§179-C's mechanism) — the lane is §265 form 1, file-scope verbatim asm. docs/psyq-worklist.md:72 names the population ("libapi 800c3 cluster — ~22 4-ins BIOS syscall stubs"); §182/§188 had already proven this cluster unreachable by the §177 epilogue lever (six members are prebuilt SDK objects) — this is the lane that finally banked it. Banked exemplar (src/800c3.c:66):

__asm__(
    ".text\n" ".align 2\n" ".globl WaitEvent\n" ".ent\tWaitEvent\n"
    "WaitEvent:\n" ".frame $sp, 0, $31\n" ".mask 0x00000000, 0\n" ".fmask 0x00000000, 0\n"
    ".set\tnoreorder\n"
    "addiu $t2, $0, 176\n"
    "jr    $t2\n"
    "addiu $t1, $0, 10\n"
    ".set\treorder\n" ".end\tWaitEvent\n"
    "nop\n");

Family facts, each byte-proven this wave:

  1. The pad nops are LOAD-BEARING OUTPUT. The stubs sit at a fixed slot stride (0x10/0x18 bytes); omitting the trailing nops shifts every later stub (TestEvent's first compile came back LENGTH-DRIFT/−1 until an explicit nop; _96_remove carries THREE, banked). Placement relative to .end is byte-inert — WaitEvent banks its pad AFTER .end, _96_remove INSIDE the block; both green. match_one's insns_from_s reads past endlabel and attributes the pad FORWARD, so a "MATCH (4 ins)" verdict on a 3-ins stub is the tool counting the pad — expected, not a defect.
  2. Spell addiu $tN, $zero, K, never li — the retail opcode is addiu (0x24); li leaves the expansion to the assembler (TestEvent).
  3. The decimal rule's TRUE scope: maspsx int()-parses MEMORY DISPLACEMENTS — those must be decimal; ALU immediates may stay hex. TestEvent is BANKED with addiu $t2, $zero, 0xB0 / addiu $t1, $zero, 0xB (src/800c3.c:85) while its neighbours use 176/10 — both assemble byte-identically. The blanket "maspsx rejects hex inside __asm__ strings" repeated by several cards is REFUTED (§300-R2); keep offsets decimal, immediates as you like.
  4. Zero relocations ⇒ Law-1c's symbol walk is vacuously clean; ship NO externs (§265 rule).
  5. One grep rules the class in: no jtbl_ in the object's data + a $t2 = 0xA0/0xB0 constant. func_800626C8 is the pointer-typed cousin (lw $t1 from a single data slot, jr $t1 — a slot the handwritten bootstrap writes is not a switch table); same §265 verdict.

Cards: WaitEvent · TestEvent · OpenEvent · DeliverEvent · DisableEvent · FlushCache · InitHeap · _96_remove · func_800626C8 (EnableEvent/CloseEvent banked as the same one-immediate edit). Remaining members of the ~22 fall to the identical template.