diff --git a/src/800.c b/src/800.c index ade036834..f281a83d7 100644 --- a/src/800.c +++ b/src/800.c @@ -9462,7 +9462,265 @@ void func_8001D8C4(s32 a0, u8 * a1_p, u8 * a2_p) { *(s16 *)(a2 + 0x22) = out[1]; } -INCLUDE_ASM("asm/nonmatchings/800", func_8001DA34); +#include "common.h" + +/* func_8001DA34 (main, 408 ins) -- POLY_FT4 sprite-list emitter. + * + * LEVERS (all byte-witnessed; match_one MATCH 408/408): + * - §193-F loop.c move_movables: the 0xFF000000 / 0x00FFFFFF OT masks are + * life-9/life-6 movables in a 203-insn loop, so 29*1*9 >= 203 hoisted the + * first one into a callee-saved reg, spilling `ot` and growing the frame + * 0xB8 -> 0xC8. HARD-REGISTER PINS ($a1/$a2) take them out of the movable + * list entirely (loop.c never considers hard regs) -- cheaper than trying to + * push insn_count past 261. + * - ONE record pointer (RecDA34 *rec), not rec + rec+3: two source pointers made + * loop.c synthesise a THIRD biv at rec+2 and rebase 3 of the 4 field reads + * onto it. The target's $s2 = rec+3 is a giv gcc builds by itself. + * - fold DOES associate BIT_IOR: `tp7 | 0x20 | Y1 | ...` folds to + * `(tp7|Y1)|0x20`. The abr=1 arm needs the constant nested in its own + * statement (`tb`) to reproduce `ori v0,Y1,0x20; or v0,s6,v0`. + * - §205 chained assignment `ua = ub = E;` is what keeps the `move $a0,$v1` + * alive: a plain `ua = E; ub = ua + w - 1;` lets cse copy-propagate ua away. + * - §199-F / §164-36b target-head fence: without the empty asm at the head of + * the else arm, dbr STEALS `move $a0,$v1` into the beqz delay slot and jump2 + * then deletes the arm's `j` -- one instruction short, diamond collapsed. + * - §194-A / §3-B scheduling fences + the $a0 pin on pxv: a pinned hard-reg SET + * is placed FIRST in its block, so the fences pick which block it heads and + * thereby the loop-head load order (id, dx, px, py, dy) and the $a0/$a1 pair. + * - Frame 0xB8 needs 0x7C of locals in declaration order: mtx[3] (0x10, only + * mtx[1] used), cin[2] (0x70), cout[2] (0x78), nrm (0x80), flag (0x88). + */ + +typedef struct { s16 m[3][3]; s16 pad; s32 t[3]; } MtxDA34; /* 0x20 */ +typedef struct { s16 vx, vy, vz, pad; } SVecDA34; /* 0x08 */ +typedef struct { u8 r, g, b, cd; } CVecDA34; /* 0x04 */ +typedef struct { + u16 id; /* 0x00 */ + u8 w; /* 0x02 */ + u8 h; /* 0x03 */ + s16 dx; /* 0x04 */ + u16 dy; /* 0x06 */ + u16 px; /* 0x08 */ + u16 py; /* 0x0A */ +} RecDA34; /* 0x0C */ + +extern MtxDA34 D_800A63F0; +extern u16 D_800B9A02; +extern u8 D_800A6610[]; +extern u8 *D_800A5E60; +extern void func_8004978C(s16 *a0, void *a1); +extern void func_8004917C(); +void func_8001E094(s32 arg0); +void func_8001E378(s32 param_1); +s32 func_8001E668(); + +#define SETROT_DA34(r0) __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"( r0 ) : "$12", "$13", "$14" ) + +#define LDCLMV_DA34(r0) __asm__ volatile ( \ + "lhu $12, 0( %0 );" \ + "lhu $13, 6( %0 );" \ + "lhu $14, 12( %0 );" \ + "mtc2 $12, $9;" \ + "mtc2 $13, $10;" \ + "mtc2 $14, $11" \ + : : "r"( r0 ) : "$12", "$13", "$14" ) + +#define RTIR_DA34() __asm__ volatile ("nop;nop;mvmva 1, 0, 3, 3, 0") + +#define STCLMV_DA34(r0) __asm__ volatile ( \ + "mfc2 $12, $9;" \ + "mfc2 $13, $10;" \ + "mfc2 $14, $11;" \ + "sh $12, 0( %0 );" \ + "sh $13, 6( %0 );" \ + "sh $14, 12( %0 )" \ + : : "r"( r0 ) : "$12", "$13", "$14", "memory" ) + +#define LDV0_DA34(r0) __asm__ volatile ("lwc2 $0, 0( %0 );lwc2 $1, 4( %0 )" : : "r"( r0 ) : "memory") +#define LDRGB_DA34(r0) __asm__ volatile ("lwc2 $6, 0( %0 )" : : "r"( r0 ) : "memory") +#define NCCS_DA34() __asm__ volatile ("nop;nop;nccs") +#define STRGB_DA34(r0) __asm__ volatile ("swc2 $22, 0( %0 )" : : "r"( r0 ) : "memory") + +void func_8001DA34(s32 param_1) +{ + MtxDA34 mtx[3]; + CVecDA34 cin[2]; + CVecDA34 cout[2]; + SVecDA34 nrm; + s32 flag; + + RecDA34 *rec; + u8 *prim; + MtxDA34 *src; + s16 *pa; + s16 *pb; + u32 *ot; + u32 attr; + s32 mode, sh, tp7; + u16 id; + u32 x, y; + register u32 pxv __asm__("$4"); + u16 sx, sy; + s32 tpage; + s32 tb; + s32 tc; + s32 dxv; + s32 ua, ub; + u32 *ott; + u32 cc; + s32 ct; + s32 idx; + u16 pp; + register u32 m24 __asm__("$5"); + register u32 mFF __asm__("$6"); + + rec = *(RecDA34 **)(param_1 + 0x20); + attr = *(u32 *)(param_1 + 4); + nrm.vx = nrm.vy = 0; + nrm.vz = -0x1000; + mode = (attr >> 24) & 3; + sh = 2 - mode; + ot = (u32 *)(D_800A6610 + ((u32)D_800B9A02 << 14)); + + if (!(attr & 0x1040)) { + src = *(MtxDA34 **)(param_1 + 0x34); + if (src != 0) { + mtx[1] = *src; + pa = &mtx[1].m[0][0]; + SETROT_DA34(&D_800A63F0); + LDCLMV_DA34(pa); RTIR_DA34(); STCLMV_DA34(pa); + LDCLMV_DA34(pa + 1); RTIR_DA34(); STCLMV_DA34(pa + 1); + LDCLMV_DA34(pa + 2); RTIR_DA34(); STCLMV_DA34(pa + 2); + func_8004917C(pa); + } else { + pb = &mtx[1].m[0][0]; + func_8004978C((s16 *)(param_1 + 0x10), pb); + SETROT_DA34(&D_800A63F0); + LDCLMV_DA34(pb); RTIR_DA34(); STCLMV_DA34(pb); + LDCLMV_DA34(pb + 1); RTIR_DA34(); STCLMV_DA34(pb + 1); + LDCLMV_DA34(pb + 2); RTIR_DA34(); STCLMV_DA34(pb + 2); + func_8004917C(pb); + } + } + + if (*(s32 *)(param_1 + 0x34) != 0) + func_8001E094(param_1); + else + func_8001E378(param_1); + + tp7 = mode << 7; + do { + prim = D_800A5E60; + id = rec->id; + __asm__ __volatile__(""); + dxv = rec->dx; + __asm__ __volatile__(""); + pxv = *(u16 *)(param_1 + 0x28); + x = pxv + (dxv >> sh); + sx = x; + y = *(u16 *)(param_1 + 0x2A) + rec->dy; + sy = y; + D_800A5E60 = prim + 0x28; + prim[3] = 9; + prim[7] = 0x2C; + if (attr & 0x40000000) { + prim[7] = 0x2E; + tpage = tp7 | (((attr >> 28) & 3) << 5) | ((y & 0x100) >> 4) | + ((x & 0x3C0) >> 6) | ((y & 0x200) << 2); + } else { + tb = ((y & 0x100) >> 4) | 0x20; + tpage = tp7 | tb | ((x & 0x3C0) >> 6) | ((y & 0x200) << 2); + } + *(s16 *)(prim + 0x16) = tpage; + prim[7] |= (attr & 0x40) >> 6; + __asm__ __volatile__(""); + ua = ub = (sx - ((tpage & 0xF) << 6)) << sh; + ub += rec->w; + ub -= 1; + if ((u16)ub >= 0x100) ub = 0xFF; + if (id & 0x100) { + prim[0x14] = prim[0x24] = ua; + prim[0x0C] = prim[0x1C] = ub; + } else { + prim[0x0C] = prim[0x1C] = ua; + prim[0x14] = prim[0x24] = ub; + } + + tc = tpage & 0x10; + __asm__ __volatile__(""); + ub = sy; + if (tc) { + ua = ub - 0x100; + } else { + __asm__ __volatile__(""); + ua = ub; + } + ub = ua + rec->h - 1; + if ((u16)ub >= 0x100) ub = 0xFF; + if (id & 0x200) { + prim[0x1D] = prim[0x25] = ua; + prim[0x0D] = prim[0x15] = ub; + } else { + prim[0x0D] = prim[0x15] = ua; + prim[0x1D] = prim[0x25] = ub; + } + + cc = *(u8 *)(param_1 + 0x27); + ct = (cc + 0x100) << 6; + *(s16 *)(prim + 0xE) = (cc < 0xE0) ? (ct | 0x16) : (ct | 0x10); + + if (attr & 0x1040) { + prim[4] = *(u8 *)(param_1 + 0x24); + prim[5] = *(u8 *)(param_1 + 0x25); + prim[6] = *(u8 *)(param_1 + 0x26); + } else { + cin[0].r = *(u8 *)(param_1 + 0x24); + cin[0].g = *(u8 *)(param_1 + 0x25); + cin[0].b = *(u8 *)(param_1 + 0x26); + LDV0_DA34(&nrm); + LDRGB_DA34(&cin[0]); + NCCS_DA34(); + STRGB_DA34(&cout[0]); + prim[4] = cout[0].r; + prim[5] = cout[0].g; + prim[6] = cout[0].b; + } + + idx = func_8001E668(param_1, (s32)rec, prim, &flag); + if ((flag & ~0x1000) == 0) { + idx = idx + 1; + pp = *(u16 *)(param_1 + 0x2C); + if (pp & 0xC000) { + if ((pp & 0xC000) == 0xC000) { + idx -= pp & 0xFFF; + if (idx < 0) idx = 0; + } else { + idx += pp & 0xFFF; + } + } + if ((u32)idx >= 0x1000) return; + m24 = 0xFFFFFF; + ott = (u32 *)(idx * 4 + (s32)ot); + __asm__ __volatile__(""); + mFF = 0xFF000000; + *(u32 *)prim = (*(u32 *)prim & mFF) | (*ott & m24); + *ott = (*ott & mFF) | ((u32)prim & m24); + } + rec++; + } while ((id & 0xFF) != 0xFF); +} + extern u8 D_800AF648; @@ -10882,7 +11140,308 @@ void func_80021284(s32 arg0) INCLUDE_ASM("asm/nonmatchings/800", func_800215F4); -INCLUDE_ASM("asm/nonmatchings/800", func_80021D38); +/* func_80021D38 — main / src/800.c — 284 ins. Splat marks it "Handwritten + * function" only because of the cop2 opcodes: it is ordinary gcc-2.7.2 -O2 C + * over PsyQ inline-GTE macros (same spelling family as func_80016C28, + * func_8001D8C4 and func_8001D70C already in this TU). + * + * WHAT IT DOES. Projects a 12-point (4 groups of 3) sprite/billboard cage for + * object `p`, writing DVECTORs into out[0..12] (out[0] = the projected origin, + * out[1..12] = the cage), then hands the object + its point table + the screen + * coords to func_800226C0 with the OT slot &ot[d] for the current frame buffer + * (ot = D_800A6610 + (D_800B9A02 << 14), the §-standard double-buffered OT). + * p->flags & 0x40000 selects a pre-projected path (out[0] copied straight out + * of p, otz taken from p->pos.vz) over the RTPS/RTPT GTE path, and the same bit + * inside the loop selects 3× MVMVA(sf=1,rot,V0,TR)+stlvnl over one RTPT+stsxy3. + * + * FRAME PROOF (why the local declaration ORDER is load-bearing): outgoing args + * 0x00..0x0F, then locals in declaration order, each BLKmode aggregate 8-byte + * aligned — sv[3] 0x10, out[14] 0x28 (52 bytes used, 0..12), mtx 0x60, + * vec[4] 0x80, flag 0x90, otz 0x94, sz 0x98, saved s0/s1/s2/ra 0xA0..0xAF, + * total 0xB0. out[] is sized 14 (not 13) so the mtx slot lands on 0x60 whether + * or not the 8-byte rule is applied. + * + * THE FOUR LEVERS (284 → 268 → 13 → 3 → 0): + * 1. §242 (`*k` vs `<pos.vz; sz = p->pos.vz << 2;` in that order + * reproduces both loads and the 0x94-before-0x98 store order. `out[0] = + * *(DVec *)&p->pos` is a 2-aligned 4-byte struct assignment, which is what emits + * the lwl/lwr + swl/swr pair. `mtx.t[0] = mtx.t[1] = mtx.t[2] = 0` (chained) + * gives the target's descending 0x7C/0x78/0x74 store order. + * + * Symbols verified against this .s's own relocation lines (SYS law 1c): + * D_800AF630, D_800B9A02, D_800A6610 (hi/lo) and jal func_800226C0 — and all + * four internal `j` destinations (0x118, 0x160, 0x160, 0x398) checked by hand + * because masked_diff.mask_for zeroes them (§195-D). + * Declarations copied from the card's authoritative `tu` rows; they are the + * spelling src/800.c already uses everywhere, so no §376/§378 conflict. + */ +#include "common.h" + +typedef struct { u16 vx, vy; } DVec_80021D38; /* 4 bytes, align 2 */ +typedef struct { s16 vx, vy, vz, pad; } SVec_80021D38; /* 8 bytes */ +typedef struct { s16 m[3][3]; s32 t[3]; } Mtx_80021D38; /* 32 bytes */ + +typedef struct { + /* 0x00 */ s32 unk00; + /* 0x04 */ u32 flags; + /* 0x08 */ SVec_80021D38 pos; + /* 0x10 */ s32 unk10; + /* 0x14 */ s32 unk14; + /* 0x18 */ u16 sx; + /* 0x1A */ u16 sy; + /* 0x1C */ s32 unk1C; + /* 0x20 */ u8 *pts; + /* 0x24 */ s32 unk24; + /* 0x28 */ s32 unk28; + /* 0x2C */ u16 depth; + /* 0x2E */ u16 unk2E; +} Obj_80021D38; + +extern u8 D_800AF630[]; +extern u8 D_800A6610[]; +extern u16 D_800B9A02; +extern void func_800226C0(Obj_80021D38 *, u8 *, DVec_80021D38 *, u8 *); + +#define gte_SetRotMatrix_21D38(r0) __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"( r0 ) \ + : "$12", "$13", "$14" ) + +#define gte_SetTransMatrix_21D38(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) + +#define gte_ldv0_21D38(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) + +#define gte_ldv3_21D38(r0, r1, r2) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 );" \ + "lwc2 $2, 0( %1 );" \ + "lwc2 $3, 4( %1 );" \ + "lwc2 $4, 0( %2 );" \ + "lwc2 $5, 4( %2 )" \ + : \ + : "r"( r0 ), "r"( r1 ), "r"( r2 ) ) + +#define gte_rtps_21D38() __asm__ volatile ("nop;nop;rtps") +#define gte_rtpt_21D38() __asm__ volatile ("nop;nop;rtpt") +#define gte_rt_21D38() __asm__ volatile ("nop;nop;mvmva 1, 0, 0, 0, 0") + +#define gte_stsxy_21D38(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stsxy3_21D38(r0, r1, r2) __asm__ volatile ( \ + "swc2 $12, 0( %0 );" \ + "swc2 $13, 0( %1 );" \ + "swc2 $14, 0( %2 )" \ + : \ + : "r"( r0 ), "r"( r1 ), "r"( r2 ) \ + : "memory" ) + +#define gte_stlvnl_21D38(r0) __asm__ volatile ( \ + "swc2 $25, 0( %0 );" \ + "swc2 $26, 4( %0 );" \ + "swc2 $27, 8( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stflg_21D38(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +#define gte_stsz_21D38(r0) __asm__ volatile ( \ + "swc2 $19, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stszotz_21D38(r0) __asm__ volatile ( \ + "mfc2 $12, $19;" \ + "nop;" \ + "sra $12, $12, 2;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +void func_80021D38(Obj_80021D38 *p) +{ + SVec_80021D38 sv[3]; /* sp+0x10 */ + DVec_80021D38 out[14]; /* sp+0x28 */ + Mtx_80021D38 mtx; /* sp+0x60 */ + s32 vec[4]; /* sp+0x80 */ + s32 flag; /* sp+0x90 */ + s32 otz; /* sp+0x94 */ + s32 sz; /* sp+0x98 */ + u8 *base; + u8 *ot; + u32 flags; + u8 *pts; + s32 i; + s32 j; + s32 k; + s32 d; + u32 t; + + base = D_800AF630; + ot = (u8 *)((D_800B9A02 << 14) + (u32)D_800A6610); + flags = p->flags; + pts = p->pts; + + if (flags & 0x40000) { + out[0] = *(DVec_80021D38 *)&p->pos; + otz = p->pos.vz; + sz = p->pos.vz << 2; + } else { + gte_SetRotMatrix_21D38(base + 0x18); + gte_SetTransMatrix_21D38(base + 0x18); + gte_ldv0_21D38(&p->pos); + gte_rtps_21D38(); + gte_stsxy_21D38(&out[0]); + gte_stflg_21D38(&flag); + gte_stszotz_21D38(&otz); + if (flag & ~0x1000) { + return; + } + gte_stsz_21D38(&sz); + } + + d = otz + 1; + t = p->depth; + if (t != 0) { + if ((t & 0xC000) != 0) { + if ((t & 0xC000) == 0xC000) { + d -= (t & 0xFFF); + if (d < 0) { + d = 0; + } + } else { + d += (t & 0xFFF); + } + } else { + d = t & 0xFFF; + } + } + if ((u32)d >= 0x1000) { + return; + } + + mtx.m[0][0] = p->sx; + mtx.m[0][1] = 0; + mtx.m[0][2] = 0; + mtx.m[1][0] = 0; + mtx.m[1][1] = p->sy; + mtx.m[1][2] = 0; + mtx.m[2][0] = 0; + mtx.m[2][1] = 0; + mtx.m[2][2] = 0x1000; + mtx.t[0] = mtx.t[1] = mtx.t[2] = 0; + gte_SetRotMatrix_21D38(&mtx); + gte_SetTransMatrix_21D38(&mtx); + + for (i = 0; i < 12; i += 3) { + sv[0].vx = *(u16 *)(pts + (i << 2) + 0x10); + sv[0].vy = *(u16 *)(pts + (i << 2) + 0x12); + sv[0].vz = sz; + j = i + 1; + sv[1].vx = *(u16 *)(pts + (j << 2) + 0x10); + sv[1].vy = *(u16 *)(pts + (j << 2) + 0x12); + sv[1].vz = sz; + k = i + 2; + sv[2].vx = *(u16 *)(pts + (k << 2) + 0x10); + sv[2].vy = *(u16 *)(pts + (k << 2) + 0x12); + sv[2].vz = sz; + + if (flags & 0x40000) { + gte_ldv0_21D38(&sv[0]); + gte_rt_21D38(); + gte_stlvnl_21D38(vec); + gte_stflg_21D38(&flag); + out[j].vx = vec[0]; + out[j].vy = vec[1]; + + gte_ldv0_21D38(&sv[1]); + gte_rt_21D38(); + gte_stlvnl_21D38(vec); + gte_stflg_21D38(&flag); + out[k].vx = vec[0]; + out[k].vy = vec[1]; + + gte_ldv0_21D38(&sv[2]); + gte_rt_21D38(); + gte_stlvnl_21D38(vec); + gte_stflg_21D38(&flag); + out[i + 3].vx = vec[0]; + out[i + 3].vy = vec[1]; + } else { + gte_ldv3_21D38(&sv[0], &sv[1], &sv[2]); + gte_rtpt_21D38(); + gte_stsxy3_21D38(&out[i + 1], &out[i + 2], &out[i + 3]); + } + + out[i + 1].vx += out[0].vx; + out[i + 1].vy += out[0].vy; + out[i + 2].vx += out[0].vx; + out[i + 2].vy += out[0].vy; + out[i + 3].vx += out[0].vx; + out[i + 3].vy += out[0].vy; + } + + func_800226C0(p, pts, out, ot + (d << 2)); +} + /* func_800221A8 (src/800.c, main, 326 ins) -- byte-proven MATCH. * @@ -11006,7 +11565,7 @@ extern void func_80049CAC(s32 a0, s32 a1); extern void func_8004901C(void *a0, void *a1); extern void func_800547D8(s32 a0, void *a1); extern s32 func_80021174(s32 a0, s32 a1); -extern void func_800226C0(s32 a0, s32 a1, s32 a2, s32 a3); +extern void func_800226C0(); void func_800221A8(Obj221A8 *o) { SV221A8 sv0;