From fc644dddb4de1f9753ee29ee2cd66c8622c0ee8e Mon Sep 17 00:00:00 2001 From: Drew T <50529377+Druthulu@users.noreply.github.com> Date: Thu, 3 Sep 2026 17:05:47 -0600 Subject: [PATCH] feat(decomp): bank 9 main functions, including main itself and the 670-ins giant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BANKED 9 of 28 after bisection, 143dbb89 BYTE-IDENTICAL. Verified from the SOURCE (every stub gone), not from the gate's own count. main 509 ins the game's entry point func_800226C0 670 ins the largest function in the project func_800215F4 465 func_800623A4 36 · func_80062434 36 · func_8005D410 42 func_8005D4B8 14 · func_8005D4F0 18 · StopRCnt 13 Reached by iterating the gate and dropping the compile-conflict culprit it named each round: func_8005E79C, func_8005E3AC, func_8005EAE8, func_8001FC08. Each of those is a §376/§378 declaration conflict, not a bad body — they go to the recovery chain, not the bin. Several were only reachable because of this session's oracle fixes: the 800c3 functions had been recorded as §182/§188 epilogue walls by an oracle modelling maspsx + as -O1 for a TU the Makefile builds through reorder_passthrough + as -O2. func_800226C0 came from the §476 finding that a hard-register pin strips nonzero_bits and reg_n_sets==1. --- src/800.c | 895 +++++++++++++++++++++++++++++++++++++++++++++++++- src/800c2_2.c | 43 ++- src/800c3.c | 46 ++- src/boot.c | 219 +++++++++++- 4 files changed, 1194 insertions(+), 9 deletions(-) diff --git a/src/800.c b/src/800.c index 84c58eb9a..47458baa1 100644 --- a/src/800.c +++ b/src/800.c @@ -12126,7 +12126,441 @@ void func_80021284(s32 arg0) } -INCLUDE_ASM("asm/nonmatchings/800", func_800215F4); + +/* ---- PsyQ GTE inline macros (same spelling as the matched src/800.c blocks) ---- */ +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) + +#define gte_ldv3(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() __asm__ volatile ("nop;nop;rtps") +#define gte_rtpt() __asm__ volatile ("nop;nop;rtpt") +#define gte_nclip() __asm__ volatile ("nop;nop;nclip") + +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stsxy3(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_stopz(r0) __asm__ volatile ( \ + "swc2 $24, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +#define gte_stszotz(r0) __asm__ volatile ( \ + "mfc2 $12, $19;" \ + "nop;" \ + "sra $12, $12, 2;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +typedef struct { + s16 vx, vy, vz, pad; +} SV_215F4; /* 8 */ + +typedef struct { + /* 0x00 */ s32 unk00; + /* 0x04 */ u32 unk04; + /* 0x08 */ u8 pad08[6]; + /* 0x0E */ s16 unk0E; + /* 0x10 */ u8 pad10[14]; + /* 0x1E */ s16 unk1E; + /* 0x20 */ u8 *unk20; + /* 0x24 */ u8 pad24[8]; + /* 0x2C */ u16 unk2C; + /* 0x2E */ u16 unk2E; + /* 0x30 */ u16 unk30; + /* 0x32 */ u16 unk32; + /* 0x34 */ s32 unk34; +} Obj_215F4; + +extern u8 D_800A6610[]; +extern u16 D_800B9A02; + +/* func_8001E094 / func_8001E378 are DEFINED in src/800.c (file-scope decls at + * 10238/10239, bodies at 10453/10588) with these exact prototypes -- do not + * narrow them (cookbook 376/378). The target's missing `move $a0,$s1` before + * the E094 call is cse_insn deleting the argument copy in the FALL-THROUGH arm + * (a0 still equals the parameter); the else arm is a fresh basic block, so its + * copy survives. + * + * func_800226C0 MUST stay unprototyped here: src/800.c already carries a + * file-scope `extern void func_800226C0(Obj_80021D38 *, u8 *, DVec_80021D38 *, + * u8 *)` at 12214, i.e. AFTER this function's slot at 12129, so a prototyped + * decl of our own would be a conflicting-types build error. An unspecified + * parameter list is compatible with both that decl and the `extern void + * func_800226C0()` at 12556, and it is codegen-identical here (all four + * arguments are pointers, unaffected by the default argument promotions). + * func_80023138's spelling below is verbatim from its definition at 12688. */ +extern void func_8001E094(s32 arg0); +extern void func_8001E378(s32 param_1); +extern void func_800226C0(); +extern void func_80023138(s32 *arg0, u8 *col, u8 *pts, u32 *ot); + +void func_800215F4(Obj_215F4 *obj) +{ + SV_215F4 v[4]; /* 0x10 */ + s32 xy[36]; /* 0x30 */ + s32 flag; /* 0xC0 */ + s32 otz; /* 0xC4 */ + s32 flag2; /* 0xC8 */ + + u32 *ot; + u8 *vp; + /* pinned to the registers the target itself allocates (cookbook 179-F / 347) */ + register s32 i __asm__("$8"); /* $t0 loop counter, every loop */ + register u8 *q __asm__("$7"); /* $a3 loop1/3 walked ptr */ + register s32 o1 __asm__("$9"); /* $t1 */ + register s32 o2 __asm__("$10"); /* $t2 */ + register s32 d1 __asm__("$11"); /* $t3 */ + register s32 d2 __asm__("$12"); /* $t4 */ + register s32 d3 __asm__("$13"); /* $t5 */ + register u8 *xb __asm__("$14"); /* $t6 */ + register SV_215F4 *pv1 __asm__("$25"); + register SV_215F4 *pv2 __asm__("$24"); + register SV_215F4 *pv3 __asm__("$15"); + register u16 bx __asm__("$5"); + register u16 by __asm__("$4"); + register u16 bz __asm__("$6"); + register u8 *rs __asm__("$3"); + /* loop2 */ + register SV_215F4 *pw1 __asm__("$13"); + register u8 *xb2 __asm__("$11"); + register s32 *pf __asm__("$10"); + register s32 *dzp __asm__("$9"); + register s32 e __asm__("$7"); + register u8 *rw __asm__("$6"); + /* The three clamp sites: `x & 0xFFF` is computed SEPARATELY IN EACH ARM, + * never hoisted above the `== 0xC000` test. reorg then merges the two + * identical copies into the bne's delay slot -- which is the only way the + * andi can reuse the $v0 that held the 0xC000 compare constant (a hoisted + * copy is born BEFORE the bne, so it overlaps the constant and has to take + * a third register). In the two inner loops the temp additionally has to + * be PINNED off c2: fill_slots_from_thread rejects a delay-slot candidate + * that sets a resource the opposite arm still reads, so an in-place + * `andi $a0,$a0,0xFFF` over c2's own $a0 blocks the merge and costs +2. + * `oz` likewise stops the otz load coalescing into dz's pinned $v1. */ + /* loop4 */ + register SV_215F4 *pw4 __asm__("$11"); + register u8 *xb4 __asm__("$10"); + /* loopC */ + register SV_215F4 *pa1 __asm__("$24"); + register SV_215F4 *pa2 __asm__("$15"); + register SV_215F4 *pa3 __asm__("$14"); + register u8 *xbc __asm__("$13"); + register s32 dc3 __asm__("$12"); + register s32 oc2 __asm__("$11"); + register s32 oc1 __asm__("$10"); + register u8 *qc __asm__("$9"); + register u32 dm __asm__("$2"); + register s32 oz __asm__("$2"); + u16 qx; + u32 k; + register s32 *pd __asm__("$4"); + register s32 dz __asm__("$3"); + register u32 c2 __asm__("$4"); + s32 *pz; + u32 t, c; + + ot = (u32 *)(D_800B9A02 * 0x4000 + (u32)D_800A6610); + vp = obj->unk20; + if (obj->unk34 != 0) { + func_8001E094((s32)obj); + } else { + func_8001E378((s32)obj); + } + + if (obj->unk04 & 0x100) { + v[2].vx = 0x10; + v[3].vx = -0x10; + v[1].vy = -0x13; + v[3].vy = 8; + v[2].vy = 8; + v[1].vx = 0; + v[3].vz = 0; + v[2].vz = 0; + v[1].vz = 0; + gte_ldv3(&v[1], &v[2], &v[3]); + gte_rtpt(); + gte_stflg(&flag); + if (flag & ~0x1000) { + return; + } + gte_nclip(); + gte_stopz(&otz); + if (otz <= 0) { + return; + } + } + + v[0].vx = obj->unk2E; + v[0].vy = obj->unk30; + v[0].vz = obj->unk32; + gte_ldv0(&v[0]); + gte_rtps(); + gte_stsxy(&xy[0]); + gte_stflg(&flag); + gte_stszotz(&otz); + if (flag & ~0x1000) { + return; + } + + t = otz + 1; + c = obj->unk2C; + if (c & 0xC000) { + if ((c & 0xC000) == 0xC000) { + t -= c & 0xFFF; + if ((s32)t < 0) { + t = 0; + } + } else { + t += c & 0xFFF; + } + } + if (t < 0x1000) { + i = 0; + if ((obj->unk1E & 0x8000) || obj->unk0E != 0) { + pv1 = &v[1]; + pv2 = &v[2]; + pv3 = &v[3]; + xb = (u8 *)xy; + d3 = 0x14; + d2 = 0xC; + d1 = 4; + o2 = 0x10; + o1 = 8; + q = vp; + do { + bx = v[0].vx; + qx = *(u16 *)(q + 0x10); + bz = v[0].vz; + by = v[0].vy; + v[1].vx = bx + qx; + v[1].vy = by + *(u16 *)(q + 0x12); + v[1].vz = bz; + rs = vp + o1; + v[2].vx = bx + *(u16 *)(rs + 0x10); + v[2].vy = by + *(u16 *)(rs + 0x12); + v[2].vz = bz; + rs = vp + o2; + v[3].vx = bx + *(u16 *)(rs + 0x10); + v[3].vy = by + *(u16 *)(rs + 0x12); + v[3].vz = bz; + gte_ldv3(pv1, pv2, pv3); + gte_rtpt(); + gte_stsxy3(xb + d1, xb + d2, xb + d3); + d3 += 0x18; + d2 += 0x18; + d1 += 0x18; + o2 += 0x18; + o1 += 0x18; + i += 6; + q += 0x18; + } while (i < 12); + + i = 1; + pw1 = &v[1]; + xb2 = (u8 *)xy; + pf = &flag2; + dzp = &xy[13]; + e = 8; + rw = vp + 4; + do { + v[1].vx = v[0].vx + *(u16 *)(rw + 0x10); + v[1].vy = v[0].vy + *(u16 *)(rw + 0x12); + v[1].vz = v[0].vz; + gte_ldv0(pw1); + gte_rtps(); + gte_stsxy(xb2 + e); + gte_stflg(pf); + pz = &otz; + gte_stszotz(pz); + oz = otz; + dz = oz + 1; + c2 = obj->unk2C; + if (c2 & 0xC000) { + k = 0xC000; + if ((c2 & 0xC000) == k) { + dm = c2 & 0xFFF; + dz -= dm; + if (dz < 1) { + dz = 0; + } + } else { + dm = c2 & 0xFFF; + dz += dm; + } + } + if (dz >= 0x1000) { + k = 0xFFF; + dz = k; + } + pz = &dzp[i >> 1]; + *pz = dz; + e += 8; + i += 2; + rw += 8; + } while (i < 12); + + if (obj->unk0E != 0) { + i = 0; + pv1 = &v[1]; + pv2 = &v[2]; + pv3 = &v[3]; + xb = (u8 *)xy; + d3 = 0x5C; + d2 = 0x54; + d1 = 0x4C; + o2 = 0x10; + o1 = 8; + q = vp; + v[0].vz = v[0].vz + obj->unk0E; + do { + bx = v[0].vx; + qx = *(u16 *)(q + 0x10); + bz = v[0].vz; + by = v[0].vy; + v[1].vx = bx + qx; + v[1].vy = by + *(u16 *)(q + 0x12); + v[1].vz = bz; + rs = vp + o1; + v[2].vx = bx + *(u16 *)(rs + 0x10); + v[2].vy = by + *(u16 *)(rs + 0x12); + v[2].vz = bz; + rs = vp + o2; + v[3].vx = bx + *(u16 *)(rs + 0x10); + v[3].vy = by + *(u16 *)(rs + 0x12); + v[3].vz = bz; + gte_ldv3(pv1, pv2, pv3); + gte_rtpt(); + gte_stsxy3(xb + d1, xb + d2, xb + d3); + d3 += 0x18; + d2 += 0x18; + d1 += 0x18; + o2 += 0x18; + o1 += 0x18; + i += 6; + q += 0x18; + } while (i < 12); + + i = 1; + pw4 = &v[1]; + xb4 = (u8 *)xy; + dzp = &xy[13]; + e = 0x50; + rw = vp + 4; + do { + v[1].vx = v[0].vx + *(u16 *)(rw + 0x10); + v[1].vy = v[0].vy + *(u16 *)(rw + 0x12); + v[1].vz = v[0].vz; + gte_ldv0(pw4); + gte_rtps(); + gte_stsxy(xb4 + e); + pz = &flag2; + gte_stflg(pz); + pz = &otz; + gte_stszotz(pz); + oz = otz; + dz = oz + 1; + c2 = obj->unk2C; + if (c2 & 0xC000) { + k = 0xC000; + if ((c2 & 0xC000) == k) { + dm = c2 & 0xFFF; + dz -= dm; + if (dz < 1) { + dz = 0; + } + } else { + dm = c2 & 0xFFF; + dz += dm; + } + } + if (dz >= 0x1000) { + k = 0xFFF; + dz = k; + } + pd = &dzp[i >> 1]; + if (dz < *pd) { + *pd = dz; + } + e += 8; + i += 2; + rw += 8; + } while (i < 12); + func_80023138((s32 *)obj, vp, (u8 *)xy, ot); + } else { + func_800226C0((s32 *)obj, vp, (u8 *)xy, ot); + } + } else { + pa1 = &v[1]; + pa2 = &v[2]; + pa3 = &v[3]; + xbc = (u8 *)xy; + dc3 = 0xC; + oc2 = 8; + oc1 = 4; + qc = vp; + do { + bx = v[0].vx; + qx = *(u16 *)(qc + 0x10); + bz = v[0].vz; + by = v[0].vy; + v[1].vx = bx + qx; + v[1].vy = by + *(u16 *)(qc + 0x12); + v[1].vz = bz; + rs = vp + oc1; + v[2].vx = bx + *(u16 *)(rs + 0x10); + v[2].vy = by + *(u16 *)(rs + 0x12); + v[2].vz = bz; + rs = vp + oc2; + v[3].vx = bx + *(u16 *)(rs + 0x10); + v[3].vy = by + *(u16 *)(rs + 0x12); + v[3].vz = bz; + gte_ldv3(pa1, pa2, pa3); + gte_rtpt(); + gte_stsxy3(xbc + oc1, xbc + oc2, xbc + dc3); + dc3 += 0xC; + oc2 += 0xC; + oc1 += 0xC; + i += 3; + qc += 0xC; + } while (i < 12); + func_800226C0((s32 *)obj, vp, (u8 *)xy, ot + t); + } + } +} /* 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 @@ -12680,7 +13114,464 @@ void func_800221A8(Obj221A8 *o) { } -INCLUDE_ASM("asm/nonmatchings/800", func_800226C0); +/* func_800226C0 (main, src/800.c) — emit the 12-segment POLY_G4 strip for one Obj_80021D38 + * (plus the E1 draw-mode prims around it) into the D_800A5E60 bump allocator. + * + * Levers that closed it (S76y, from the S75 pinned draft at closeness 545 -> MATCH): + * - `t` is ONE plain `s16` local, read UNINITIALISED at the top (`sll $v0,$s2,16; beqz` = + * an HImode pseudo whose nonzero-bits are unknown before its set) and later assigned from + * the s32 `tt`; the `addu $v0,$s2,$zero; beqz $v0; addu $t2,$v0,$zero` chain at 228E4 is + * the sign-extension of that HImode pseudo folded to a copy (combine get_last_value) and + * then reused by cse2 for the loop's hoisted multiplier -- a pinned $18 can never do it + * (hard regs have no nonzero-bits). `mul` must stay an unpinned pseudo (global alloc never + * coalesces a pseudo copy; a $10 pin swallows the $v0 temp). + * - `o`, `col`, `sh23`, `abr` unpinned: gcc's own param copies give the prologue order and the + * t/flags callee-saved order (density tie); the `or $v0,$v1,$v0` tie needs `abr` as a pseudo. + * - The !useIdx single-prim block at 850 is the inline addPrim form with block-local temps: + * a pinned `$a1` mask has reg_n_sets != 1 -> no birthing boost -> placed first in the block. + * Symbols: only D_800A5E60 (14 %hi/%lo pairs in the .s) -- verified against the relocations. + * The two struct typedefs are copies of src/800.c:12191-12209 for the standalone compile; the + * harvest strips them (cdecl.typedef_names above the splice point). + */ + + +extern u8 *D_800A5E60; + +void func_800226C0(Obj_80021D38 *arg0, u8 *col, DVec_80021D38 *pts, u8 *ot) +{ + s16 t; + s16 work[24]; + u32 flags; + u16 f16; + s32 tt; + s32 uu; + s32 tsh; + u32 sh23; + + u8 *p; + u8 *s; + u8 *w; + u8 *e; + s32 k; + s32 m; + s32 h; + u32 x; + u32 y; + register u32 useIdx __asm__("$25"); + register u8 code __asm__("$24"); + register s32 i __asm__("$11"); + register s32 shadow __asm__("$17"); + register s32 *zp __asm__("$4"); + + f16 = *(u16 *)((u8 *)arg0 + 0x1E); + flags = arg0->flags; + useIdx = f16 & 0x8000; + shadow = 0; + if (flags & 0x800000) { + p = D_800A5E60; + shadow = 1; + if (useIdx) { + if (t) { + register u8 *q __asm__("$5"); + register u32 e1 __asm__("$13"); + register u8 one __asm__("$14"); + register u32 maskAddr __asm__("$10"); + register u32 maskLen __asm__("$12"); + + D_800A5E60 = p + 0x60; + i = 0; + one = 1; + e1 = 0xE1000000; + maskAddr = 0x00FFFFFF; + maskLen = 0xFF000000; + q = p + 4; + do { + h = i >> 1; + i++; + zp = (s32 *)((h << 2) + (u32)((u8 *)pts)); + q[-1] = one; + *(u32 *)q = e1; + q += 8; + x = *(u32 *)p; + y = ((u32 *)ot)[zp[13]]; + x &= maskLen; + y &= maskAddr; + x |= y; + *(u32 *)p = x; + ((u32 *)ot)[zp[13]] = (((u32 *)ot)[zp[13]] & maskLen) | ((u32)p & maskAddr); + p += 8; + } while (i < 12); + } else { + register u8 *q __asm__("$10"); + register s32 *zq __asm__("$5"); + register u32 e1 __asm__("$14"); + register u8 one __asm__("$15"); + register u32 maskAddr __asm__("$12"); + register u32 maskLen __asm__("$13"); + + D_800A5E60 = p + 0x30; + i = 0; + one = 1; + e1 = 0xE1000000; + maskAddr = 0x00FFFFFF; + maskLen = 0xFF000000; + q = p + 4; + zq = (s32 *)((u8 *)pts); + do { + i++; + q[-1] = one; + *(u32 *)q = e1; + q += 8; + x = *(u32 *)p; + y = ((u32 *)ot)[zq[13]]; + x &= maskLen; + y &= maskAddr; + x |= y; + *(u32 *)p = x; + ((u32 *)ot)[zq[13]] = (((u32 *)ot)[zq[13]] & maskLen) | ((u32)p & maskAddr); + zq = (s32 *)((u8 *)zq + 4); + p += 8; + } while (i < 6); + } + } else { + D_800A5E60 = p + 8; + p[3] = 1; + *(u32 *)(p + 4) = 0xE1000000; + *(u32 *)p = (*(u32 *)p & 0xFF000000) | (((u32 *)ot)[0] & 0x00FFFFFF); + ((u32 *)ot)[0] = (((u32 *)ot)[0] & 0xFF000000) | ((u32)p & 0x00FFFFFF); + } + } + + f16 = *(u16 *)((u8 *)arg0 + 0x1E); + p = D_800A5E60; + tt = f16 & 0x1FFF; + t = tt; + D_800A5E60 = tt ? p + 0x1B0 : p + 0xD8; + code = (flags & 0x40000000) ? 0x3A : 0x38; + + if (t) { + { + s32 mul; + register s16 *dst __asm__("$5"); + register s16 *src __asm__("$4"); + + i = 0; + mul = t; + dst = work; + src = (s16 *)((u8 *)pts); + do { + *dst = *(s16 *)((u8 *)pts) + (((src[2] - *(s16 *)((u8 *)pts)) * mul) >> 12); + dst[1] = *(s16 *)(((u8 *)pts) + 2) + (((src[3] - *(s16 *)(((u8 *)pts) + 2)) * mul) >> 12); + i++; + src += 2; + dst += 2; + } while (i < 12); + } + { + register u32 maskAddr __asm__("$12"); + register u32 maskLen __asm__("$15"); + register s32 kk __asm__("$14"); + register u8 *ww __asm__("$13"); + register u8 *ss __asm__("$10"); + register u8 *d __asm__("$5"); + + i = 0; + maskAddr = 0x00FFFFFF; + maskLen = 0xFF000000; + d = p + 0x22; + kk = 4; + ww = (u8 *)work; + ss = ((u8 *)pts); + do { + d[-0x1F] = 8; + d[-0x1B] = code; + d[-0x1E] = col[4]; + d[-0x1D] = col[5]; + d[-0x1C] = col[6]; + d[-0x16] = col[4]; + d[-0x15] = col[5]; + d[-0x14] = col[6]; + d[-0x0E] = col[0]; + d[-0x0D] = col[1]; + d[-0x0C] = col[2]; + d[-0x06] = col[0]; + d[-0x05] = col[1]; + d[-0x04] = col[2]; + *(u16 *)(d - 0x1A) = *(u16 *)(ss + 4); + *(u16 *)(d - 0x18) = *(u16 *)(ss + 6); + *(u16 *)(d - 0x12) = *(u16 *)(ss + 8); + *(u16 *)(d - 0x10) = *(u16 *)(ss + 0xA); + *(u16 *)(d - 0x0A) = *(u16 *)(ww); + *(u16 *)(d - 0x08) = *(u16 *)(ww + 2); + e = (u8 *)work + kk; + *(u16 *)(d - 0x02) = *(u16 *)e; + *(u16 *)(d) = *(u16 *)(e + 2); + if (useIdx) { + h = i >> 1; + zp = (s32 *)((h << 2) + (u32)((u8 *)pts)); + x = *(u32 *)p; + y = ((u32 *)ot)[zp[13]]; + x &= maskLen; + y &= maskAddr; + x |= y; + *(u32 *)p = x; + ((u32 *)ot)[zp[13]] = (((u32 *)ot)[zp[13]] & maskLen) | ((u32)p & maskAddr); + } else { + x = *(u32 *)p; + y = ((u32 *)ot)[0]; + x &= maskLen; + y &= maskAddr; + x |= y; + *(u32 *)p = x; + ((u32 *)ot)[0] = (((u32 *)ot)[0] & maskLen) | ((u32)p & maskAddr); + } + kk += 4; + ww += 4; + ss += 4; + i++; + d += 0x24; + p += 0x24; + } while (i < 12); + } + + *(u16 *)(p - 0x14) = *(u16 *)(((u8 *)pts) + 4); + *(u16 *)(p - 0x12) = *(u16 *)(((u8 *)pts) + 6); + *(u16 *)(p - 0x04) = *(u16 *)work; + *(u16 *)(p - 0x02) = *(u16 *)((u8 *)work + 2); + + if (*(u16 *)((u8 *)arg0 + 0x1E) & 0x4000) { + register u32 maskAddr __asm__("$10"); + register u32 maskLen __asm__("$15"); + register s32 mm __asm__("$14"); + register s32 kk __asm__("$13"); + register u8 *ww __asm__("$12"); + register u8 *d __asm__("$5"); + + i = 0; + maskAddr = 0x00FFFFFF; + maskLen = 0xFF000000; + mm = 8; + kk = 4; + p = D_800A5E60; + ww = (u8 *)work; + d = p + 0x22; + D_800A5E60 = p + 0xD8; + do { + d[-0x1F] = 8; + d[-0x1B] = code; + d[-0x1E] = col[0x0C]; + d[-0x1D] = col[0x0D]; + d[-0x1C] = col[0x0E]; + d[-0x16] = col[0x0C]; + d[-0x15] = col[0x0D]; + d[-0x14] = col[0x0E]; + d[-0x0E] = col[0x08]; + d[-0x0D] = col[0x09]; + d[-0x0C] = col[0x0A]; + d[-0x06] = col[0x0C]; + d[-0x05] = col[0x0D]; + d[-0x04] = col[0x0E]; + *(u16 *)(d - 0x1A) = *(u16 *)(ww); + *(u16 *)(d - 0x18) = *(u16 *)(ww + 2); + e = (u8 *)work + kk; + *(u16 *)(d - 0x12) = *(u16 *)e; + *(u16 *)(d - 0x10) = *(u16 *)(e + 2); + *(u16 *)(d - 0x0A) = *(u16 *)(((u8 *)pts)); + *(u16 *)(d - 0x08) = *(u16 *)(((u8 *)pts) + 2); + e = (u8 *)work + mm; + *(u16 *)(d - 0x02) = *(u16 *)e; + *(u16 *)(d) = *(u16 *)(e + 2); + if (useIdx) { + h = i >> 1; + zp = (s32 *)((h << 2) + (u32)work); + x = *(u32 *)p; + y = ((u32 *)ot)[zp[13]]; + x &= maskLen; + y &= maskAddr; + x |= y; + *(u32 *)p = x; + ((u32 *)ot)[zp[13]] = (((u32 *)ot)[zp[13]] & maskLen) | ((u32)p & maskAddr); + } else { + x = *(u32 *)p; + y = ((u32 *)ot)[0]; + x &= maskLen; + y &= maskAddr; + x |= y; + *(u32 *)p = x; + ((u32 *)ot)[0] = (((u32 *)ot)[0] & maskLen) | ((u32)p & maskAddr); + } + mm += 8; + kk += 8; + ww += 8; + i += 2; + d += 0x24; + p += 0x24; + } while (i < 12); + + *(u16 *)(p - 0x04) = *(u16 *)work; + *(u16 *)(p - 0x02) = *(u16 *)((u8 *)work + 2); + } + } else { + register u32 maskAddr __asm__("$12"); + register u32 maskLen __asm__("$13"); + register u8 *ss __asm__("$10"); + register u8 *d __asm__("$5"); + + i = 1; + maskAddr = 0x00FFFFFF; + maskLen = 0xFF000000; + d = p + 0x22; + ss = ((u8 *)pts) + 4; + do { + d[-0x1F] = 8; + d[-0x1B] = code; + d[-0x1E] = col[4]; + d[-0x1D] = col[5]; + d[-0x1C] = col[6]; + d[-0x16] = col[4]; + d[-0x15] = col[5]; + d[-0x14] = col[6]; + d[-0x0E] = col[0]; + d[-0x0D] = col[1]; + d[-0x0C] = col[2]; + d[-0x06] = col[4]; + d[-0x05] = col[5]; + d[-0x04] = col[6]; + *(u16 *)(d - 0x1A) = *(u16 *)(ss); + *(u16 *)(d - 0x18) = *(u16 *)(ss + 2); + *(u16 *)(d - 0x12) = *(u16 *)(ss + 4); + *(u16 *)(d - 0x10) = *(u16 *)(ss + 6); + *(u16 *)(d - 0x0A) = *(u16 *)(((u8 *)pts)); + *(u16 *)(d - 0x08) = *(u16 *)(((u8 *)pts) + 2); + *(u16 *)(d - 0x02) = *(u16 *)(ss + 8); + *(u16 *)(d) = *(u16 *)(ss + 0x0A); + if (useIdx) { + h = i >> 1; + zp = (s32 *)((h << 2) + (u32)((u8 *)pts)); + x = *(u32 *)p; + y = ((u32 *)ot)[zp[13]]; + x &= maskLen; + y &= maskAddr; + x |= y; + *(u32 *)p = x; + ((u32 *)ot)[zp[13]] = (((u32 *)ot)[zp[13]] & maskLen) | ((u32)p & maskAddr); + } else { + x = *(u32 *)p; + y = ((u32 *)ot)[0]; + x &= maskLen; + y &= maskAddr; + x |= y; + *(u32 *)p = x; + ((u32 *)ot)[0] = (((u32 *)ot)[0] & maskLen) | ((u32)p & maskAddr); + } + ss += 8; + i += 2; + d += 0x24; + p += 0x24; + } while (i < 13); + + *(u16 *)(p - 0x04) = *(u16 *)(((u8 *)pts) + 4); + *(u16 *)(p - 0x02) = *(u16 *)(((u8 *)pts) + 6); + } + + if (flags & 0x40000000) { + u32 abr; + + sh23 = flags >> 23; + abr = sh23 & 0x60; + if (useIdx) { + i = 0; + if (t) { + register u8 *q __asm__("$5"); + u32 e1s; + u32 e1n; + register u8 one __asm__("$14"); + register u32 maskAddr __asm__("$9"); + register u32 maskLen __asm__("$10"); + + one = 1; + e1s = abr | 0xE1000200; + e1n = abr | 0xE1000000; + maskAddr = 0x00FFFFFF; + maskLen = 0xFF000000; + q = p + 4; + D_800A5E60 = D_800A5E60 + 0x60; + do { + q[-1] = one; + if (shadow) { + *(u32 *)q = e1s; + } else { + *(u32 *)q = e1n; + } + h = i >> 1; + i++; + zp = (s32 *)((h << 2) + (u32)((u8 *)pts)); + q += 8; + x = *(u32 *)p; + y = ((u32 *)ot)[zp[13]]; + x &= maskLen; + y &= maskAddr; + x |= y; + *(u32 *)p = x; + ((u32 *)ot)[zp[13]] = (((u32 *)ot)[zp[13]] & maskLen) | ((u32)p & maskAddr); + p += 8; + } while (i < 12); + } else { + register u8 *q __asm__("$5"); + u32 e1s; + u32 e1n; + register u8 one __asm__("$14"); + register u32 maskAddr __asm__("$9"); + register u32 maskLen __asm__("$10"); + register s32 *zq __asm__("$6"); + + one = 1; + e1s = abr | 0xE1000200; + e1n = abr | 0xE1000000; + maskAddr = 0x00FFFFFF; + maskLen = 0xFF000000; + q = p + 4; + D_800A5E60 = D_800A5E60 + 0x30; + zq = (s32 *)((u8 *)pts); + do { + q[-1] = one; + if (shadow) { + *(u32 *)q = e1s; + } else { + *(u32 *)q = e1n; + } + i++; + q += 8; + x = *(u32 *)p; + y = ((u32 *)ot)[zq[13]]; + x &= maskLen; + y &= maskAddr; + x |= y; + *(u32 *)p = x; + ((u32 *)ot)[zq[13]] = (((u32 *)ot)[zq[13]] & maskLen) | ((u32)p & maskAddr); + zq = (s32 *)((u8 *)zq + 4); + p += 8; + } while (i < 6); + } + } else { + register u32 maskAddr __asm__("$4"); + register u32 maskLen __asm__("$5"); + + D_800A5E60 = D_800A5E60 + 8; + p[3] = 1; + *(u32 *)(p + 4) = abr | (shadow ? 0xE1000200 : 0xE1000000); + maskAddr = 0x00FFFFFF; + maskLen = 0xFF000000; + x = *(u32 *)p; + y = ((u32 *)ot)[0]; + x &= maskLen; + y &= maskAddr; + x |= y; + *(u32 *)p = x; + ((u32 *)ot)[0] = (((u32 *)ot)[0] & maskLen) | ((u32)p & maskAddr); + } + } +} extern u8 *D_800A5E60; diff --git a/src/800c2_2.c b/src/800c2_2.c index c7f950d14..cfb362063 100644 --- a/src/800c2_2.c +++ b/src/800c2_2.c @@ -24,10 +24,49 @@ s32 func_80062394(void) return D_80072A24; } -INCLUDE_ASM("asm/nonmatchings/800c2_2", func_800623A4); +extern void func_80062768(void); +extern void func_8005CF08(void); +extern void func_800626F0(void); +extern void func_8005CF18(void); +extern s32 ChangeClearPAD(s32 mode); +extern void func_8006252C(void); +extern s32 PAD_init2(s32, s32, s32, s32); +extern s32 D_80072A24; + +void func_800623A4(s32 a0, s32 a1, s32 a2, s32 a3) +{ + func_80062768(); + func_8005CF08(); + func_800626F0(); + func_8005CF18(); + ChangeClearPAD(0); + func_8006252C(); + PAD_init2(a0, a1, a2, a3); + D_80072A24 = 1; +} -INCLUDE_ASM("asm/nonmatchings/800c2_2", func_80062434); + +extern void func_80062768(void); +extern void func_8005CF08(void); +extern void func_800626F0(void); +extern void func_8005CF18(void); +extern s32 ChangeClearPAD(s32 mode); +extern void func_8006252C(void); +extern void InitPAD2(s32 a0, s32 a1, s32 a2, s32 a3); +extern s32 D_80072A24; + +void func_80062434(s32 a0, s32 a1, s32 a2, s32 a3) +{ + func_80062768(); + func_8005CF08(); + func_800626F0(); + func_8005CF18(); + ChangeClearPAD(0); + func_8006252C(); + InitPAD2(a0, a1, a2, a3); + D_80072A24 = 1; +} extern void StartPAD2(void); extern s32 ChangeClearPAD(s32 mode); diff --git a/src/800c3.c b/src/800c3.c index 357d86cea..57d474d62 100644 --- a/src/800c3.c +++ b/src/800c3.c @@ -299,7 +299,16 @@ s32 StartRCnt(s32 spec) { return t < 3; } -INCLUDE_ASM("asm/nonmatchings/800c3", StopRCnt); +extern s32 D_80072934; +extern s32 D_8007293C[]; + +s32 StopRCnt(s32 spec) { + s32 t; + + t = spec & 0xFFFF; + *(s32 *)(D_80072934 + 4) &= ~D_8007293C[t]; + return 1; +} extern u32 D_80072938; @@ -427,11 +436,40 @@ extern void* (*D_80072970)(void); extern void* (*D_80072970)(void); -INCLUDE_ASM("asm/nonmatchings/800c3", func_8005D410); +extern void* (*D_80072970)(void); -INCLUDE_ASM("asm/nonmatchings/800c3", func_8005D4B8); +s32 func_8005D410(s32 arg0, s32 arg1, s32 arg2) { + u8 *p; -INCLUDE_ASM("asm/nonmatchings/800c3", func_8005D4F0); + p = (u8 *)(*D_80072970)(); + if (arg1 < 0) { + return *(p + 0xEA); + } + if (arg1 >= *(p + 0xEA)) { + return 0; + } + p = *(u8 **)(p + 8) + (arg1 << 3); + if (arg2 < 0) { + return *p; + } + if (arg2 >= *p) { + return 0; + } + return *(*(u8 **)(p + 4) + arg2); +} + +extern void* (*D_80072970)(void); +extern void func_8005E79C(); + +void func_8005D4B8(void *a0, void *a1) { + func_8005E79C(D_80072970(), a1); +} + +extern void* (*D_80072970)(void); + +void func_8005D4F0(s32 arg0, s32 arg1, s32 arg2) { + func_8005E8E8(D_80072970(), (u8)arg1, (u8)arg2); +} INCLUDE_ASM("asm/nonmatchings/800c3", func_8005D538); diff --git a/src/boot.c b/src/boot.c index 928469e6d..6f82b0cda 100644 --- a/src/boot.c +++ b/src/boot.c @@ -175,7 +175,224 @@ __asm__(".text\n" ".set\treorder\n" ".end\t__do_global_dtors\n"); -INCLUDE_ASM("asm/nonmatchings/boot", main); + +extern u8 D_800BA118; +extern u8 D_800AF630[]; +extern s32 D_80074778; +extern s32 D_800A2B7C; +extern s32 D_800C7C70; +extern s16 D_800C7C74; +extern s32 D_800A5E60; +extern u8 D_8007BA70[]; +extern u8 D_800A4F48[]; +extern s32 D_800A651C; +extern u8 D_800A6528[]; +extern u8 D_800A6610[]; +extern u8 D_800AA60C[]; +extern s32 D_800AE7BC; +extern s32 D_800AE7C8; +extern u8 D_800BA0D8[]; +extern u8 D_800BA0E4[]; + +extern void func_80043060(s32); +extern void func_800141F0(void); +extern void func_8005FC68(s32); +extern void func_8005FCB8(void); +extern void func_80018918(void); +extern s32 func_80043300(void); +extern void func_8002C8F4(void); +extern void LoaderInitFileTable(void); +extern void func_80014238(void); +extern void func_80014390(void); +extern void func_800191A8(void); +extern void func_80010A98(void); +extern void func_800189A8(void); +extern void func_80015208(void); +extern void func_80059BFC(s32, s32); +extern void GameModeDispatch(void); +extern void func_80015498(void); +extern void func_8001C00C(void); +extern void func_800D25FC(void); +extern void func_800184F0(void); +extern void func_800596F4(s32); +extern s32 VSync(s32); +extern void func_80059FC0(u8 *); +extern void func_80059D68(u8 *); +extern void CatPrim(s32, s32); +extern s32 func_80059CF4(s32); +extern void func_8003500C(void); +extern void func_8002D034(void); +extern void func_8001AF34(void); +extern void func_8001513C(void); +extern void func_80042610(void (*)(void)); + +struct MainSlot { s32 unk0; s32 unk4; s32 unk8; }; + +// @class: other +// @stuck: none -- MATCH (509 ins, -O0 boot object). +/* + * main (0x80010178) -- the game's outer frame loop, compiled -O0 like the rest of + * src/boot.c (frame-pointer prologue, every global re-loaded per use). + * + * The four gcc-2.7.2 -O0 levers this needed (all NEW, none were in the cookbook): + * + * 1. TWO `register` locals, in declaration order, hold the two bases the whole + * function addresses through: $s0 = &D_800BA118 (loaded, NEVER used again -- + * it must still be declared or the lui/addiu pair and the $s0 save/restore + * both vanish) and $s1 = D_800AF630. Same lever GameModeDispatch/func_8001099C + * already use in this TU; the >0x7FFF member offsets then assemble to the + * +0x10000/-0x5Cxx split. + * + * 2. `s32 pad[6];` -- 24 bytes of DECLARED-BUT-UNUSED stack. At -O0 gcc reserves a + * slot for every local whether or not it is read, and the target's frame is + * 0x38 (16 outgoing-arg + 24 vars + 16 saved). Without it the frame is 0x20 and + * every sp offset in the prologue/epilogue is wrong. The original's six words + * were presumably dead locals left in the source. + * + * 3. THE SHIFT-FORM MULTIPLY. Inside a MEMORY ADDRESS, `base + i * K` with a + * CONSTANT K expands (expand_expr MULT under EXPAND_SUM) to a `(mult reg K)` + * rtx that force_operand emits with the INDEX FIRST: `addu d,index,base`. + * Writing the same value as `base + ((i * (K>>n)) << n)` materialises the index + * into a plain register first, and the address then comes out BASE-first -- + * `addu $v0,$s1,$a0` / `addu $at,$at,$v0` + `%lo(sym)($at)` -- which is what the + * target has. Measured: `p + i*12` -> `addu $4,$3,$2` (wrong), `p + ((i*3)<<2)` + * -> `addu $4,$2,$3` (right). Only address context is affected; in VALUE context + * (`(s32)(D_800A6610 + i*16384)`) the plain multiply already comes out base-first. + * + * 4. The three 12-byte-strided stores go through a struct pointer, not + * `*(s32 *)(p + ... + 8)`: only a COMPONENT_REF folds the member offset into the + * store (`sw $v1,0x8($v0)`); the pointer-arithmetic spelling materialises it as + * a separate `addiu`. + * + * Two smaller ones: `(*(u16 *)(p + 0xA3A8))++` emits the extra `addu $v0,$v1,$zero` + * that `+= 1` does not; and CatPrim's second argument is written + * ` + D_80074778 * 4` (NOT `D_80074778 * 4 + `) -- with a MEM as operand + * 0 gcc emits the MEM's address first, then operand 1, then the load, and the addu + * comes out operand-1-first, exactly the target's interleave. + * + * The two scratchpad-stack switches around func_80015498/func_8001C00C/func_800D25FC + * are the §261 idiom: the $sp repointing is inline asm, the calls stay C (so -O0 + * supplies the `jal` + delay-slot nop). $a2 here, not the overlays' $v1. + */ +void main(void) { + register u8 *q = &D_800BA118; + register u8 *p = D_800AF630; + s32 pad[6]; + + func_80043060(0); + D_80074778 = 0x3E0; + func_800141F0(); + func_8005FC68(0); + func_8005FCB8(); + func_80018918(); + D_800A2B7C = func_80043300(); + func_8002C8F4(); + LoaderInitFileTable(); + + while (1) { + func_80014238(); + func_80014390(); + func_800191A8(); + func_80010A98(); + + while (*(u16 *)(p + 0xA3D4) == 0) { + D_800C7C70++; + D_800C7C70 = D_800C7C70 ? D_800C7C70 : 1; + D_800C7C74 = !D_800C7C74; + *(u16 *)(p + 0xA3D2) = D_800C7C74; + + D_800A5E60 = (s32)(D_8007BA70 + *(u16 *)(p + 0xA3D2) * 80000); + ((struct MainSlot *)(p + ((*(u16 *)(p + 0xA3D2) * 3) << 2)))->unk8 = + (s32)(D_800A4F48 + *(u16 *)(p + 0xA3D2) * 1600); + ((struct MainSlot *)(p + ((*(u16 *)(p + 0xA3D2) * 3) << 2)))->unk0 = 0x640; + ((struct MainSlot *)(p + ((*(u16 *)(p + 0xA3D2) * 3) << 2)))->unk4 = 0; + + func_800189A8(); + func_80015208(); + + if (*(u8 *)(p + 0xA3E3) == 0) { + (*(u16 *)(p + 0xA3A8))++; + *(u16 *)(p + 0xA3AC) += *(u16 *)(p + 0xA3A8); + + *(s32 *)((u8 *)&D_800A651C + ((*(u16 *)(p + 0xA3D2) * 5) << 2)) = + (s32)(D_800A6610 + *(u16 *)(p + 0xA3D2) * 16384); + *(s32 *)(D_800A6528 + ((*(u16 *)(p + 0xA3D2) * 5) << 2)) = + *(u16 *)(p + 0xA3D2) * 16384 + (s32)D_800AA60C; + func_80059BFC(*(s32 *)((u8 *)&D_800A651C + ((*(u16 *)(p + 0xA3D2) * 5) << 2)), 0x1000); + *(s32 *)((u8 *)&D_800AE7BC + ((*(u16 *)(p + 0xA3D2) * 5) << 2)) = + (s32)(D_800BA0D8 + *(u16 *)(p + 0xA3D2) * 16); + *(s32 *)((u8 *)&D_800AE7C8 + ((*(u16 *)(p + 0xA3D2) * 5) << 2)) = + *(u16 *)(p + 0xA3D2) * 16 + (s32)D_800BA0E4; + func_80059BFC(*(s32 *)((u8 *)&D_800AE7BC + ((*(u16 *)(p + 0xA3D2) * 5) << 2)), 4); + GameModeDispatch(); + + __asm__ __volatile__( + "lui $a2, 0x1f80\n" + "ori $a2, $a2, 0x03fc\n" + "addu $t0, $a2, $zero\n" + "sw $sp, 0($t0)\n" + "addiu $t0, $t0, -4\n" + "addu $sp, $t0, $zero\n" : : : "memory"); + func_80015498(); + __asm__ __volatile__( + "addiu $sp, $sp, 4\n" + "lw $sp, 0($sp)\n" : : : "memory"); + + __asm__ __volatile__( + "lui $a2, 0x1f80\n" + "ori $a2, $a2, 0x03fc\n" + "addu $t0, $a2, $zero\n" + "sw $sp, 0($t0)\n" + "addiu $t0, $t0, -4\n" + "addu $sp, $t0, $zero\n" : : : "memory"); + func_8001C00C(); + __asm__ __volatile__( + "addiu $sp, $sp, 4\n" + "lw $sp, 0($sp)\n" : : : "memory"); + } else { + *(s32 *)((u8 *)&D_800AE7BC + ((*(u16 *)(p + 0xA3D2) * 5) << 2)) = + (s32)(D_800BA0D8 + *(u16 *)(p + 0xA3D2) * 16); + *(s32 *)((u8 *)&D_800AE7C8 + ((*(u16 *)(p + 0xA3D2) * 5) << 2)) = + *(u16 *)(p + 0xA3D2) * 16 + (s32)D_800BA0E4; + func_80059BFC(*(s32 *)((u8 *)&D_800AE7BC + ((*(u16 *)(p + 0xA3D2) * 5) << 2)), 4); + + __asm__ __volatile__( + "lui $a2, 0x1f80\n" + "ori $a2, $a2, 0x03fc\n" + "addu $t0, $a2, $zero\n" + "sw $sp, 0($t0)\n" + "addiu $t0, $t0, -4\n" + "addu $sp, $t0, $zero\n" : : : "memory"); + func_800D25FC(); + __asm__ __volatile__( + "addiu $sp, $sp, 4\n" + "lw $sp, 0($sp)\n" : : : "memory"); + } + + func_800184F0(); + func_800596F4(0); + VSync(*(s32 *)(p + 0xA3E8)); + + *(u16 *)(p + 0x188) = *(u16 *)(p + 0x18A); + (*(u16 *)(p + 0x18A))++; + if (*(u16 *)(p + 0x18A) > 1) { + *(u16 *)(p + 0x18A) = 0; + } + func_80059FC0(&p[*(u16 *)(p + 0x188) * 20 + 0x14C]); + func_80059D68(&p[*(u16 *)(p + 0x18A) * 92 + 0x38]); + + CatPrim(*(s32 *)((u8 *)&D_800A651C + ((*(u16 *)(p + 0xA3D2) * 5) << 2)) + 0x3FF0, + *(s32 *)((u8 *)&D_800A651C + ((*(u16 *)(p + 0xA3D2) * 5) << 2)) + D_80074778 * 4); + func_80059CF4(*(s32 *)((u8 *)&D_800A651C + ((*(u16 *)(p + 0xA3D2) * 5) << 2)) + 0x3FFC); + func_80059CF4(*(s32 *)((u8 *)&D_800AE7BC + ((*(u16 *)(p + 0xA3D2) * 5) << 2)) + 0xC); + func_8003500C(); + func_8002D034(); + func_8001AF34(); + func_8001513C(); + } + func_80042610(0); + } +} void func_8001096C(void) { extern void func_8002CDD8(void);