diff --git a/src/800.c b/src/800.c index 6e22e1d3c..cbe4cb936 100644 --- a/src/800.c +++ b/src/800.c @@ -15511,7 +15511,273 @@ void func_8002374C(s32 arg0) } -INCLUDE_ASM("asm/nonmatchings/800", func_80023BF0); +/* func_80023BF0 (main, src/800.c, 281 ins) -- MATCH, closeness 0. + * Real TU (rtu_match --tu src/800.c) agrees: MATCH. Came in at 18 (the S79w opus + * draft) / 11 (the S80 permuter waypoint, which was semantically UNSOUND -- it had + * sunk `m24 = 0xFFFFFF` into the `c == 0x3870` arm of the sprite loop, leaving the + * mask register undefined on any first iteration that takes the other arm). + * + * ============================================================================ + * WHAT CLOSED IT: THE OT LINK IS A libgpu P_TAG 24-BIT BITFIELD, NOT A MASK/OR + * ============================================================================ + * Cookbook §364 + the func_8017FD64 addendum (L30700) describe this residual + * EXACTLY and this function is a third witness for the -O2 half of it: + * + * symptom : a hand-written `(A & 0xFF000000) | (B & 0xFFFFFF)` OT link that + * plateaus at closeness 10-18, where every remaining row is + * (a) the paired lui/ori mask registers two-cycle-swapped against + * the OT-slot pointer ($t3 <-> $t4 here), and + * (b) the loop PREHEADER emitting the two mask constants BEFORE the + * `sll/addu` that forms &otab[idx], where the target emits them + * after -- in BOTH loop preheaders and again in the epilogue. + * fix : write it as the bitfield store the SDK macro expands to + * typedef struct { u32 addr : 24; u32 len : 8; } PTag; + * ((PTag *)pkt)->addr = otab[idx].addr; -- setaddr(p, getaddr(ot)) + * otab[idx].addr = (u32)pkt; -- setaddr(ot, p) + * ((PTag *)pkt)->len = 1; -- was pkt[3] = 1 + * mechanism: expand_assignment routes a bitfield destination through + * store_field -> store_fixed_bit_field, which expands the VALUE + * (the RHS bitfield EXTRACT: &otab[idx], lw, and-0xFFFFFF) before it + * touches the destination (lw pkt, and-0xFF000000, ior, sw). That + * single ordering change does three things at once: + * * loop.c's movable list is built in body order, so the hoists + * come out as [&otab[idx] ; 0xFFFFFF ; 0xFF000000] -- the + * target's preheader order, in both arms; + * * expand_binop(ior_optab, temp, value) puts the DEST-masked word + * first, giving the target's `or $v1,$v1,$v0` operand roles and + * therefore its $v0/$v1 assignment; + * * the store-side `expand_and(value, mask)` is what emits the + * target's `and $v1,$t1,$t3` (pkt & 0xFFFFFF) AHEAD of the slot + * reload, which no hand-written spelling reproduces. + * Writing the OR by hand fixes the emitted body but is unreachable + * in the preheader: swapping the `|` operands to `(otab[idx] & + * 0xFFFFFF) | (*(u32*)pkt & 0xFF000000)` DOES buy the target's hoist + * order (measured: the preheader goes byte-exact) but then inverts + * the body's two loads and the ior roles -- 35. The two halves are + * mutually exclusive from a hand-written mask/or, and the bitfield + * satisfies both because the pass order, not the source order, is + * what differs. §364's -O2 veto (MEM_IN_STRUCT_P letting the alias + * oracle CSE a global load across the tag store) does NOT bite here: + * the only global read near the links is D_800B9A02, loaded once in + * the prologue, so there is nothing to CSE away. + * + * The whole 90 -> 18 chain from the S79w draft was NOT wasted and is still load + * bearing -- keep every one of these, each re-measured against this body: + * 1. THE §194-A FENCE IN **BOTH** ARMS (the `__asm__ __volatile__("")` before + * `c = *prims;`). Removing it: WIDTH, lhu != lbu. It stops sched1 hoisting + * `lhu c` to the top of the block, which shortens c1's live_length 24 -> 16 + * and lifts its allocno priority above giv1's -- the only way to the target's + * `c1 == arg0 == $a2` / `giv1 == $a3` / `x == $t0` triple. + * 2. `code` SPLIT FROM ITS 0x74/0x76 BASE, accumulated compound + * (`code = b26<<16; code |= (b25<<8)|base; code |= b24;`) so the first insn's + * scratch is what set_preference (global.c:1535) hands `code`, plus + * 3. `register u32 base __asm__("$3")`. Still REQUIRED (dropping the pin + * re-colours the whole file from insn 9 on): base is a global allocno and so + * invisible to local-alloc, so only a hard reg makes regs_live_at see $v1 + * occupied over the scratch's 2-insn window and push the scratch to $a0. + * 4. THIRTEEN zero-byte fences after `pkt = D_800A5E60;` (pure §47 live-length + * arithmetic). Re-measured on THIS body: 0 -> 15, 8 -> 13, 10/11/12 -> 7, + * 13 -> MATCH. 13 is the minimum; 14 and 16 also match. + * DROPPED as no longer needed once the bitfield lands (each re-measured MATCH): + * `register u32 m24 __asm__("$11")` (the 0xFFFFFF pin -- the bitfield hoists + * it into $t3 on its own, which is the point), and `register u32 e1 + * __asm__("$4")` for 0xE1000015 (now a plain literal). + * + * STRUCTURE (inherited from S69/S76/S77/S79, all still byte-verified -- keep it): + * the dispatch is a balance_case_nodes median tree, source case order 0x1850, + * 0x1858, 0x3870, 0x3871, 0x3872, default; 0x1850/0x1858 SHARE one body whose + * head is stolen into the beq delay slot (§164-36b); the emit block appears + * ONCE; packet fields are written PKT-RELATIVE so loop.c emits ONE combined + * giv; `c` is declared INSIDE each arm; `tpage` is built through the `tp` temp, + * never in place; and an intermediate the target computes in a SCRATCH register + * is a NESTED EXPRESSION, not a second assignment to the same variable. + * Do NOT introduce an explicit `u32 *ot = &otab[idx]` (function-scope, arm-local + * or pinned): re-measured here too, it is an extra global allocno, buys a 4th + * callee-saved register and costs +3 instructions (284). + * + * Symbols audited against this target's own relocation lines (law 1c): the .s + * carries %hi/%lo of D_800AF630, D_800A6610, D_800B9A02, D_800A5E60 and no jal. + */ +extern u8 D_800AF630[]; +extern u8 D_800A6610[]; +extern u16 D_800B9A02; +extern u8 *D_800A5E60; + +typedef struct { s16 vx, vy, vz, pad; } SVEC_80023BF0; + +typedef struct { u32 addr : 24; u32 len : 8; } PTag_80023BF0; + +#define SetRot_80023BF0(m) __asm__ volatile ( \ + "lw $12, 0(%0);" "lw $13, 4(%0);" "ctc2 $12, $0;" "ctc2 $13, $1;" \ + "lw $12, 8(%0);" "lw $13, 12(%0);" "lw $14, 16(%0);" \ + "ctc2 $12, $2;" "ctc2 $13, $3;" "ctc2 $14, $4" \ + : : "r"(m) : "$12", "$13", "$14") +#define SetTrans_80023BF0(m) __asm__ volatile ( \ + "lw $12, 20(%0);" "lw $13, 24(%0);" "ctc2 $12, $5;" \ + "lw $14, 28(%0);" "ctc2 $13, $6;" "ctc2 $14, $7" \ + : : "r"(m) : "$12", "$13", "$14") + +void func_80023BF0(s32 arg0) +{ + SVEC_80023BF0 v; + u32 xy; + u32 sxy2; + u32 mac0; + s32 z; + u32 flags; + u16 *prims; + u16 b9; + u16 idx; + s32 x; + s32 y; + PTag_80023BF0 *otab; + u8 *pkt; + u32 code; + s32 tpage; + s32 tp; + u8 *mat; + register u32 base __asm__("$3"); + + mat = D_800AF630; + prims = *(u16 **)(arg0 + 0x20); + b9 = D_800B9A02; + flags = *(u32 *)(arg0 + 4); + otab = (PTag_80023BF0 *)(b9 * 0x4000 + D_800A6610); + + if (flags & 0x4000000) { + u8 *m = mat + 0x18; + SetRot_80023BF0(m); + SetTrans_80023BF0(m); + v.vx = *(u16 *)(arg0 + 8) + *(u16 *)(arg0 + 0x2E); + v.vy = *(u16 *)(arg0 + 0xA) + *(u16 *)(arg0 + 0x30); + v.vz = *(u16 *)(arg0 + 0xC) + *(u16 *)(arg0 + 0x32); + __asm__ volatile("lwc2 $0, 0(%0);lwc2 $1, 4(%0)" : : "r"(&v)); + __asm__ volatile("nop;nop;rtps"); + __asm__ volatile("swc2 $14, 0(%0)" : : "r"(&xy) : "memory"); + __asm__ volatile("swc2 $8, 0(%0)" : : "r"(&sxy2) : "memory"); + __asm__ volatile("cfc2 $12, $31;nop;sw $12, 0(%0)" : : "r"(&mac0) : "$12", "memory"); + __asm__ volatile("mfc2 $12, $19;nop;sra $12, $12, 2;sw $12, 0(%0)" : : "r"(&z) : "$12", "memory"); + if (z <= 0) { + return; + } + idx = z + 1; + { + u32 t = xy; + x = *(u16 *)(arg0 + 0xE) + t; + y = t >> 16; + } + } else { + x = *(u16 *)(arg0 + 0xE) + (*(u16 *)(arg0 + 8) + *(u16 *)(arg0 + 0x2E)); + idx = *(u16 *)(arg0 + 0x2C); + y = *(u16 *)(arg0 + 0xA) + *(u16 *)(arg0 + 0x30); + } + + tp = (*(u8 *)(arg0 + 0x27) + 0x100) << 6; + if (*(u8 *)(arg0 + 0x27) < 0xE0) { + tpage = tp | 0x16; + } else { + tpage = tp | 0x10; + } + pkt = D_800A5E60; + __asm__ __volatile__(""); + __asm__ __volatile__(""); + __asm__ __volatile__(""); + __asm__ __volatile__(""); + __asm__ __volatile__(""); + __asm__ __volatile__(""); + __asm__ __volatile__(""); + __asm__ __volatile__(""); + __asm__ __volatile__(""); + __asm__ __volatile__(""); + __asm__ __volatile__(""); + __asm__ __volatile__(""); + __asm__ __volatile__(""); + + if (!(flags & 0x400000)) { + u16 c; + base = 0x74000000; + if (flags & 0x40000000) { + base = 0x76000000; + } + code = *(u8 *)(arg0 + 0x26) << 16; + code = code | ((*(u8 *)(arg0 + 0x25) << 8) | base); + code = code | *(u8 *)(arg0 + 0x24); + __asm__ __volatile__(""); + c = *prims; + if (c != 0xFFFF) { + do { + switch (c) { + case 0x1850: + case 0x1858: + *(u16 *)(pkt + 8) = x + 4; + *(u16 *)(pkt + 0xA) = y - 7; + break; + case 0x3870: + x += 7; + prims++; + continue; + case 0x3871: + x += 4; + prims++; + continue; + case 0x3872: + x += 2; + prims++; + continue; + default: + *(u16 *)(pkt + 8) = x; + x += 7; + *(u16 *)(pkt + 0xA) = y; + break; + } + *(u32 *)pkt = 0x3000000; + *(u32 *)(pkt + 4) = code; + *(u16 *)(pkt + 0xC) = *prims++; + *(u16 *)(pkt + 0xE) = tpage; + ((PTag_80023BF0 *)pkt)->addr = otab[idx].addr; + otab[idx].addr = (u32)pkt; + pkt += 0x10; + } while ((c = *prims) != 0xFFFF); + } + } else { + u16 c; + base = 0x64000000; + if (flags & 0x40000000) { + base = 0x66000000; + } + code = *(u8 *)(arg0 + 0x26) << 16; + code = code | ((*(u8 *)(arg0 + 0x25) << 8) | base); + code = code | *(u8 *)(arg0 + 0x24); + __asm__ __volatile__(""); + c = *prims; + if (c != 0xFFFF) { + do { + if (c == 0x3870) { + x += 0x10; + prims++; + } else { + *(u16 *)(pkt + 8) = x; + x += 0x10; + *(u16 *)(pkt + 0xA) = y; + *(u32 *)pkt = 0x4000000; + *(u32 *)(pkt + 4) = code; + *(u16 *)(pkt + 0xC) = *prims++; + *(u16 *)(pkt + 0xE) = tpage; + *(u32 *)(pkt + 0x10) = 0x180010; + ((PTag_80023BF0 *)pkt)->addr = otab[idx].addr; + otab[idx].addr = (u32)pkt; + pkt += 0x14; + } + } while ((c = *prims) != 0xFFFF); + } + } + + ((PTag_80023BF0 *)pkt)->len = 1; + *(u32 *)(pkt + 4) = ((flags >> 23) & 0x60) | 0xE1000015; + ((PTag_80023BF0 *)pkt)->addr = otab[idx].addr; + D_800A5E60 = pkt + 0x40; + otab[idx].addr = (u32)pkt; +} s32 func_80024054(u8 *arg0, s16 *arg1)