#include "common.h" #include "800_shared.h" #include "psyq/libcd.h" typedef struct { u16 unk00; u8 pad[14]; } T16; /* 0x10 */ typedef struct { s32 words[38]; } Blk98_80029274; typedef struct { s32 a; s32 b[4]; } OtBlk_80016450; typedef struct { /* 0x00 */ u16 type; /* 0x02 */ u16 unk02; /* 0x04 */ u16 x; /* 0x06 */ u16 y; /* 0x08 */ u16 w; /* 0x0A */ u16 h; /* 0x0C */ void *data; } DispItem_800184F0; typedef struct { /* 0x00 */ s16 flag; /* 0x02 */ s16 unk02; /* 0x04 */ DispItem_800184F0 *list; /* 0x08 */ u16 unk08; /* 0x0A */ u16 unk0A; /* 0x0C */ u8 unk0C; /* 0x0D */ u8 unk0D; /* 0x0E */ u16 unk0E; } DispSlot_800184F0; typedef struct { s16 flag; s16 pad02; s32 id; u16 x; u16 y; u8 c; u8 d; u16 pad0E; } Rec; typedef struct { u8 pad[0xA1A8]; Rec rec[0x20]; } Base; typedef struct { s32 w[9]; } Blk36_800296F8; typedef struct { u32 pad0; u32 rgb0; u32 rgb1; u32 rgb2; u16 n0; u16 v0; u16 v1; u16 v2; } TmdG3; /* hoisted by gate_main so drafts above can reuse them (§181) */ /* hoisted by gate_main so drafts above can reuse them (§181) */ /* hoisted by gate_main so drafts above can reuse them (§181) */ /* hoisted by gate_main so drafts above can reuse them (§181) */ /* hoisted by gate_main so drafts above can reuse them (§181) */ typedef struct { s16 vx, vy, vz, pad; } SVEC2; /* 0x08 */ typedef struct { /* 0x14 */ u32 w0; /* 0x00 */ u32 rgb; /* 0x04 */ u16 n0; /* 0x08 */ u16 v0; /* 0x0A */ u16 n1; /* 0x0C */ u16 v1; /* 0x0E */ u16 n2; /* 0x10 */ u16 v2; /* 0x12 */ } Face2; typedef struct { /* 0x1C */ u32 tag; u32 rgb0; s16 x0, y0; u32 rgb1; s16 x1, y1; u32 rgb2; s16 x2, y2; } G3P; typedef struct { /* 0x18 */ u32 w0; /* 0x00 */ u32 rgb; /* 0x04 */ u16 n0; /* 0x08 */ u16 v0; /* 0x0A */ u16 n1; /* 0x0C */ u16 v1; /* 0x0E */ u16 n2; /* 0x10 */ u16 v2; /* 0x12 */ u16 n3; /* 0x14 */ u16 v3; /* 0x16 */ } Face3; typedef struct { /* 0x24 */ u32 tag; /* 0x00 */ u32 rgb0; /* 0x04 */ s16 x0, y0; /* 0x08 */ u32 rgb1; /* 0x0C */ s16 x1, y1; /* 0x10 */ u32 rgb2; /* 0x14 */ s16 x2, y2; /* 0x18 */ u32 rgb3; /* 0x1C */ s16 x3, y3; /* 0x20 */ } G4P; /* hoisted by gate_main so drafts above can reuse them (§181) */ typedef struct { u32 a, b, c, d; } Blk16; typedef struct { u32 a, b, c; } Blk12; extern s32 func_8004787C(s32 angle); extern s32 func_80047948(s32 angle); extern void func_8001282C(); void func_800123F0(s32 param_1, s32 param_2) { s32 s0; s32 s1; s32 cos_val; s32 sin_val; register s32 v1 __asm__("$3"); s16 matrix[16]; s1 = (s32)param_1; s0 = param_2; sin_val = func_8004787C(s0); cos_val = func_80047948(s0); /* Build Z-rotation matrix on stack */ v1 = -sin_val; *(s16 *)((s32)matrix + 0x00) = cos_val; *(s16 *)((s32)matrix + 0x08) = cos_val; *(s16 *)((s32)matrix + 0x02) = v1; *(s16 *)((s32)matrix + 0x04) = 0; *(s16 *)((s32)matrix + 0x06) = sin_val; *(s16 *)((s32)matrix + 0x0A) = 0; *(s16 *)((s32)matrix + 0x0C) = 0; *(s16 *)((s32)matrix + 0x0E) = 0; *(s16 *)((s32)matrix + 0x10) = 0x1000; __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;" "addiu $2, $sp, 16;" "lhu $12, 0($2);" "lhu $13, 6($2);" "lhu $14, 12($2);" "mtc2 $12, $9;" "mtc2 $13, $10;" "mtc2 $14, $11;" "nop;" "nop;" "mvmva 1, 0, 3, 3, 0;" "mfc2 $12, $9;" "mfc2 $13, $10;" "mfc2 $14, $11;" "sh $12, 0(%0);" "sh $13, 6(%0);" "sh $14, 12(%0);" "addiu $2, $sp, 18;" "lhu $12, 0($2);" "lhu $13, 6($2);" "lhu $14, 12($2);" "mtc2 $12, $9;" "mtc2 $13, $10;" "mtc2 $14, $11;" "nop;" "nop;" "mvmva 1, 0, 3, 3, 0;" "addiu $2, %0, 2;" "mfc2 $12, $9;" "mfc2 $13, $10;" "mfc2 $14, $11;" "sh $12, 0($2);" "sh $13, 6($2);" "sh $14, 12($2);" "addiu $2, $sp, 20;" "lhu $12, 0($2);" "lhu $13, 6($2);" "lhu $14, 12($2);" "mtc2 $12, $9;" "mtc2 $13, $10;" "mtc2 $14, $11;" "nop;" "nop;" "mvmva 1, 0, 3, 3, 0;" "addiu $2, %0, 4;" "mfc2 $12, $9;" "mfc2 $13, $10;" "mfc2 $14, $11;" "sh $12, 0($2);" "sh $13, 6($2);" "sh $14, 12($2)" : : "r"(s1) : "$12", "$13", "$14", "$2" ); func_8001282C(s1); } extern s32 func_8004787C(s32 angle); extern s32 func_80047948(s32 angle); extern void func_8001282C(); void func_80012558(s32 param_1, s32 param_2) { s32 s0; s32 s1; s32 cos_val; s16 sin_val; s32 v1; s16 matrix[16]; s1 = (s32)param_1; s0 = param_2; sin_val = func_8004787C(s0); cos_val = func_80047948(s0); /* Build rotation matrix on stack */ v1 = 0x1000; *(s16 *)((s32)matrix + 0x04) = sin_val; *(s16 *)((s32)matrix + 0x00) = cos_val; *(s16 *)((s32)matrix + 0x02) = 0; *(s16 *)((s32)matrix + 0x06) = 0; *(s16 *)((s32)matrix + 0x08) = v1; *(s16 *)((s32)matrix + 0x0A) = 0; *(s16 *)((s32)matrix + 0x0C) = -sin_val; *(s16 *)((s32)matrix + 0x0E) = 0; *(s16 *)((s32)matrix + 0x10) = cos_val; __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;" "addiu $2, $sp, 16;" "lhu $12, 0($2);" "lhu $13, 6($2);" "lhu $14, 12($2);" "mtc2 $12, $9;" "mtc2 $13, $10;" "mtc2 $14, $11;" "nop;" "nop;" "mvmva 1, 0, 3, 3, 0;" "mfc2 $12, $9;" "mfc2 $13, $10;" "mfc2 $14, $11;" "sh $12, 0(%0);" "sh $13, 6(%0);" "sh $14, 12(%0);" "addiu $2, $sp, 18;" "lhu $12, 0($2);" "lhu $13, 6($2);" "lhu $14, 12($2);" "mtc2 $12, $9;" "mtc2 $13, $10;" "mtc2 $14, $11;" "nop;" "nop;" "mvmva 1, 0, 3, 3, 0;" "addiu $2, %0, 2;" "mfc2 $12, $9;" "mfc2 $13, $10;" "mfc2 $14, $11;" "sh $12, 0($2);" "sh $13, 6($2);" "sh $14, 12($2);" "addiu $2, $sp, 20;" "lhu $12, 0($2);" "lhu $13, 6($2);" "lhu $14, 12($2);" "mtc2 $12, $9;" "mtc2 $13, $10;" "mtc2 $14, $11;" "nop;" "nop;" "mvmva 1, 0, 3, 3, 0;" "addiu $2, %0, 4;" "mfc2 $12, $9;" "mfc2 $13, $10;" "mfc2 $14, $11;" "sh $12, 0($2);" "sh $13, 6($2);" "sh $14, 12($2)" : : "r"(s1) : "$12", "$13", "$14", "$2" ); func_8001282C(s1); } extern s32 func_8004787C(s32 angle); extern s32 func_80047948(s32 angle); extern void func_8001282C(); void func_800126C4(s32 param_1, s32 param_2) { s32 s0; s32 s1; s32 cos_val; s16 sin_val; s32 v1; s16 matrix[16]; s1 = (s32)param_1; s0 = param_2; sin_val = func_8004787C(s0); cos_val = func_80047948(s0); /* Build X-rotation matrix on stack */ v1 = 0x1000; *(s16 *)((s32)matrix + 0x00) = v1; *(s16 *)((s32)matrix + 0x02) = 0; *(s16 *)((s32)matrix + 0x04) = 0; *(s16 *)((s32)matrix + 0x06) = 0; *(s16 *)((s32)matrix + 0x08) = cos_val; *(s16 *)((s32)matrix + 0x0A) = -sin_val; *(s16 *)((s32)matrix + 0x0C) = 0; *(s16 *)((s32)matrix + 0x0E) = sin_val; *(s16 *)((s32)matrix + 0x10) = cos_val; __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;" "addiu $2, $sp, 16;" "lhu $12, 0($2);" "lhu $13, 6($2);" "lhu $14, 12($2);" "mtc2 $12, $9;" "mtc2 $13, $10;" "mtc2 $14, $11;" "nop;" "nop;" "mvmva 1, 0, 3, 3, 0;" "mfc2 $12, $9;" "mfc2 $13, $10;" "mfc2 $14, $11;" "sh $12, 0(%0);" "sh $13, 6(%0);" "sh $14, 12(%0);" "addiu $2, $sp, 18;" "lhu $12, 0($2);" "lhu $13, 6($2);" "lhu $14, 12($2);" "mtc2 $12, $9;" "mtc2 $13, $10;" "mtc2 $14, $11;" "nop;" "nop;" "mvmva 1, 0, 3, 3, 0;" "addiu $2, %0, 2;" "mfc2 $12, $9;" "mfc2 $13, $10;" "mfc2 $14, $11;" "sh $12, 0($2);" "sh $13, 6($2);" "sh $14, 12($2);" "addiu $2, $sp, 20;" "lhu $12, 0($2);" "lhu $13, 6($2);" "lhu $14, 12($2);" "mtc2 $12, $9;" "mtc2 $13, $10;" "mtc2 $14, $11;" "nop;" "nop;" "mvmva 1, 0, 3, 3, 0;" "addiu $2, %0, 4;" "mfc2 $12, $9;" "mfc2 $13, $10;" "mfc2 $14, $11;" "sh $12, 0($2);" "sh $13, 6($2);" "sh $14, 12($2)" : : "r"(s1) : "$12", "$13", "$14", "$2" ); func_8001282C(s1); } extern void func_800128B4(void* a0); extern void func_80013FE0(void* a0, void* a1); extern void func_80013FBC(void* a0, void* a1); extern void func_80013F98(void* a0, void* a1); extern void func_80013ED0(void* a0, void* a1, void* a2, void* a3); void func_8001282C(void *a0) { s16 b10[4]; s16 b18[4]; s16 b20[4]; func_80013FE0(a0, b20); func_800128B4(b20); func_80013FBC(a0, b18); func_800128B4(b18); func_80013F98(a0, b10); func_800128B4(b10); func_80013ED0(a0, b10, b18, b20); } extern void func_80014070(void* a0, void* a1); extern void func_800128EC(void* a0, void* a1); void func_800128B4(void* a0) { s32 local_buffer[3]; func_80014070(a0, local_buffer); func_800128EC(local_buffer, a0); } typedef struct { s32 x, y, z, w; } Vec4_800128EC; extern s32 func_80049440(s32 arg0); extern void func_80047E58(void *arg0, void *arg1); void func_800128EC(void *a0, void *a1) { Vec4_800128EC vec; s32 mn; s32 t; vec = *(Vec4_800128EC *)a0; mn = func_80049440(vec.x); t = func_80049440(vec.y); if (t < mn) { mn = t; } t = func_80049440(vec.z); if (t < mn) { mn = t; } if (mn < 0x12) { mn = 0x12 - mn; vec.x >>= mn; vec.y >>= mn; vec.z >>= mn; } func_80047E58(&vec, a1); } extern s32 ratan2(s32 a0, s32 a1); void func_800129CC(s16 *s0, s16 *s1) { if (s0[2] != 0) { s32 angle = ratan2(-s0[1], s0[2]); s1[0] = (s16)((angle - 0x400) & 0xFFF); } else { s1[0] = 0; } s1[1] = 0; if (s0[0] != 0) { s32 angle = ratan2(s0[1], s0[0]); s1[2] = (s16)((angle + 0x400) & 0xFFF); } else { s1[2] = 0; } } extern s32 func_80012AB0(s32 a0, s32 a1); s32 func_80012A60(s16 a0, s16 a1) { s32 w = func_80012AB0(a0, a1); if ((s16)w > 0x7FF) { w = 0xFFF - w; } return (s16)w; } s32 func_80012AB0(s32 arg0, s32 arg1) { return (arg0 - arg1) & 0xFFF; } extern s32 func_80012B04(s32 a0, s32 a1, s32 a2); s32 func_80012ABC(s32 a0, s32 a1, s32 a2) { s32 s0 = a0; return (s0 + func_80012B04((s16)s0, (s16)a1, (s16)a2)) & 0xFFF; } extern s16 func_80012B58(s16 a0, s16 a1, s16 a2, s16 *a3); s32 func_80012B04(s32 a0, s32 a1, s32 a2) { s16 sp10[4]; sp10[0] = 4; if ((s16)a0 == (s16)a1) { return 0; } return func_80012B58((s16)a0, (s16)a1, (s16)a2, sp10); } s16 func_80012B58(s16 a0, s16 a1, s16 a2, s16 *a3) { s16 diff = a1 - a0; s16 h = *a3; s32 t; s16 dv; if (h != 0) { t = a2; t <<= 16; dv = t >> 16; if (dv != 0) { goto block2; } } { s16 out = diff; if (diff >= 0x801) { out = diff - 0x1000; } if (out < -0x800) { out += 0x1000; } return out; } block2: { s16 out = diff; s16 q; if (diff >= 0x801) { out = diff - 0x1000; } if (out < -0x800) { out += 0x1000; } q = out / dv; if (q != 0) { return q; } *a3 = h - 1; return func_80012B58(a0, a1, (dv + ((u32)t >> 31)) >> 1, a3); } } extern s32 func_80012CB8(s32 a0, s32 a1, s32 a2); s32 func_80012C6C(s32 a0, s32 a1, s32 a2) { s32 s0 = a0; return (s16)(s0 + func_80012CB8((s16)s0, (s16)a1, (s16)a2)); } extern s16 func_80012D0C(s32 x0, s32 x1, s32 d, s16 *ctr); s32 func_80012CB8(s32 a0, s32 a1, s32 a2) { s16 out = 4; if ((s16)a0 == (s16)a1) { return 0; } return func_80012D0C((s16)a0, (s16)a1, (s16)a2, &out); } s16 func_80012D0C(s32 x0, s32 x1, s32 d, s16 *ctr) { s32 diff; s32 q; diff = x1 - x0; if (*ctr == 0 || (s16)d == 0) return (s16)diff; q = (s16)((s16)diff / (s16)d); if (q != 0) return q; *ctr = *ctr - 1; return func_80012D0C((s16)x0, (s16)x1, ((s16)d + (s32)(((u32)d << 16) >> 31)) >> 1, ctr); } s32 func_80012DBC(s32 a0, s32 a1, s32 a2, s32 a3) { return (a0 + func_80012E0C((s16)a0, (s16)a1, (s16)a2, (s16)a3)) & 0xFFF; } extern s16 func_80012E6C(s32 a0, s32 a1, s32 a2, s32 a3, s16 *a4); s32 func_80012E0C(s16 a0, s16 a1, s32 a2, s32 a3) { s16 out = 4; if (a0 == a1) { return 0; } return func_80012E6C(a0, a1, (s16)a2, (s16)a3, &out); } s16 func_80012E6C(s32 a, s32 b, s32 c, s32 d, s16 *f) { s32 z; s32 x; s32 y; s32 q; register s32 zr __asm__("$0"); z = b - a; y = z + zr; x = y; if (*f == 0 || (s16)c == 0) { if ((s16)z > 0x800) x = z - 0x1000; if ((s16)x < -0x800) x = x + 0x1000; return (s16)x; } if ((s16)z > 0x800) y = z - 0x1000; if ((s16)y < -0x800) y = y + 0x1000; q = (s16)((s16)y * (s16)d / (s16)c); if (q != 0) return q; if ((s16)z <= 0) return -1; return 1; } extern s32 func_80012FC8(s32 a0, s32 a1, s32 a2, s32 a3); s32 func_80012F74(s32 x, s32 y, s32 z, s32 w) { return (s16)(x + func_80012FC8((s16)x, (s16)y, (s16)z, (s16)w)); } s32 func_80012FC8(s32 a0, s32 a1, s32 a2, s32 a3) { s16 local = 4; if ((s16)a0 == (s16)a1) { return 0; } return (s16)func_80013028((s16)a0, (s16)a1, (s16)a2, (s16)a3, &local); } s32 func_80013028(s32 x, s32 y, s16 n, s16 d, s16 *p) { s32 r; y -= x; if (*p == 0 || n == 0) { r = (s16)y; } else { r = (s16)((s16)y * d / n); if (r == 0) { r = -1; if ((s16)y > 0) { r = 1; } } } return r; } extern s16 func_8001311C(s16 a0, s16 a1, s16 a2); s32 func_800130D0(s32 arg0, s32 arg1, s32 arg2) { return (s16)(arg0 + ((s32 (*)(s16, s16, s16))func_8001311C)(arg0, arg1, arg2)); } extern s32 func_80013154(s16 x, s16 y, s16 step); s16 func_8001311C(s16 a0, s16 a1, s16 a2) { return ((s32 (*)())func_80013154)(a0, a1, a2); } /* func_80013154 — cookbook §265 VERBATIM-ASM bank (file-scope form), generated by * tools/asm_verbatim.py from asm/nonmatchings/800/func_80013154.s. * Byte-equivalent to the INCLUDE_ASM stub by construction; the function is NOT * decompiled (§265 ACCOUNTING). Immediates/offsets are decimal for maspsx (§383). */ /* func_80013154 (main) — step a 16-bit value toward a target; returns 0 when already * there, the (signed) step while a full step fits, else the remaining distance. * * Parameters MUST be s16 (no PROMOTE_FUNCTION_ARGS: an s16 param is an HImode pseudo * copied from $aN at entry, and cse's canon_reg reads the raw $a0/$a1/$a2 inside the * entry extended block — that is the `addu $t0,$a2` / `negu $t0,$a2` / `subu $v0,$a1,$a0` * shape). `nx > y` (not `y < nx`) orders the two extensions in the join block. * TU plumbing: the existing extern in src/800.c is `s32 (s32,s32,s32)`; it must be * synced to this prototype — the wrapper func_8001311C compiles byte-identically either way. */ s32 func_80013154(s16 x, s16 y, s16 step) { s16 nx; if (x == y) { return 0; } if (y < x) { step = -step; } nx = x + step; if (nx >= y && x <= y) { return (s16)(y - x); } if (nx > y) { return step; } if (x < y) { return step; } return (s16)(y - x); } extern s32 func_80013228(s32 *a0, s32 *a1); extern s32 func_80047D3C(s32 a0); s32 func_80013200(s32 *a0, s32 *a1) { return func_80047D3C(func_80013228(a0, a1)); } extern void Square0(s32 *a0, s32 *a1); s32 func_80013228(s32 *a0, s32 *a1) { s32 diff[3]; s32 result[3]; diff[0] = a0[0] - a1[0]; diff[1] = a0[1] - a1[1]; diff[2] = a0[2] - a1[2]; Square0(diff, result); return result[0] + result[1] + result[2]; } extern s32 func_80047D3C(s32 a0); typedef struct { s16 vx, vy, vz, pad; } SVECTOR_80013294; s32 func_80013294(SVECTOR_80013294 *a0, SVECTOR_80013294 *a1) { return func_80047D3C(func_800132BC(a0, a1)); } extern void Square0(s32 *a0, s32 *a1); typedef struct { s16 vx, vy, vz, pad; } SVECTOR; s32 func_800132BC(SVECTOR *a0, SVECTOR *a1) { s32 diff[3]; s32 result[3]; diff[0] = a0->vx - a1->vx; diff[1] = a0->vy - a1->vy; diff[2] = a0->vz - a1->vz; Square0(diff, result); return result[0] + result[1] + result[2]; } extern s32 func_80047D3C(s32 a0); void func_80013328(s32 *a0, s32 *a1) { s32 result; result = func_80013350(a0, a1); func_80047D3C(result); } extern void Square0(s32 *a0, s32 *a1); s32 func_80013350(s32 *a0, s32 *a1) { s32 diff[3]; s32 result[3]; diff[0] = *(s16 *)((u8 *)a0 + 2) - *(s16 *)((u8 *)a1 + 2); diff[1] = *(s16 *)((u8 *)a0 + 6) - *(s16 *)((u8 *)a1 + 6); diff[2] = *(s16 *)((u8 *)a0 + 10) - *(s16 *)((u8 *)a1 + 10); Square0(diff, result); return result[0] + result[1] + result[2]; } extern s32 func_80047D3C(s32 a0); s32 func_800133BC(void) { s32 result = func_800133E4(); return func_80047D3C(result); } extern void Square0(s32 *a0, s32 *a1); s32 func_800133E4(s16 *a0, s16 *a1) { s32 diff[3]; s32 result[3]; diff[0] = a0[0] - a1[1]; diff[1] = a0[1] - a1[3]; diff[2] = a0[2] - a1[5]; Square0(diff, result); return result[0] + result[1] + result[2]; } extern s32 func_80013478(void); extern s32 func_80047D3C(s32 a0); s32 func_80013450(void) { s32 result = func_80013478(); return func_80047D3C(result); } s32 func_80013478(void) { register s16 *a0 __asm__("$4"); register s16 *a1 __asm__("$5"); volatile s32 diff[3]; s32 result[3]; diff[0] = a0[1] - a1[0]; diff[1] = a0[3] - a1[1]; diff[2] = a0[5] - a1[2]; __asm__ volatile("lwc2 $9, 0(%0)\n\tlwc2 $10, 4(%0)\n\tlwc2 $11, 8(%0)" : : "r"(diff)); __asm__ volatile("nop\n\tnop\n\tsqr 0"); __asm__ volatile("swc2 $25, 0(%0)\n\tswc2 $26, 4(%0)\n\tswc2 $27, 8(%0)" : : "r"(result) : "memory"); return result[0] + result[1] + result[2]; } extern void func_80013F3C(void *ptr); extern s32 func_8004787C(s32 angle); extern s32 func_80047948(s32 angle); extern void func_8001282C(); extern void func_800484EC(s32 a0, s32 a1, s32 a2); void func_800134FC(s16 param_1, s32 param_2, s32 param_3) { s16 m0[16]; s16 m1[16]; s32 sin_val; s32 cos_val; register s32 v1 __asm__("$3"); func_80013F3C(m0); sin_val = func_8004787C(param_1); cos_val = func_80047948(param_1); v1 = 0x1000; *(s16 *)((s32)m1 + 0x00) = v1; v1 = -sin_val; *(s16 *)((s32)m1 + 0x08) = cos_val; *(s16 *)((s32)m1 + 0x10) = cos_val; *(s16 *)((s32)m1 + 0x0E) = sin_val; *(s16 *)((s32)m1 + 0x02) = 0; *(s16 *)((s32)m1 + 0x04) = 0; *(s16 *)((s32)m1 + 0x06) = 0; *(s16 *)((s32)m1 + 0x0A) = v1; *(s16 *)((s32)m1 + 0x0C) = 0; __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;" "lhu $12, 0(%1);" "lhu $13, 6(%1);" "lhu $14, 12(%1);" "mtc2 $12, $9;" "mtc2 $13, $10;" "mtc2 $14, $11;" "nop;" "nop;" "mvmva 1, 0, 3, 3, 0;" "mfc2 $12, $9;" "mfc2 $13, $10;" "mfc2 $14, $11;" "sh $12, 0(%0);" "sh $13, 6(%0);" "sh $14, 12(%0);" "addiu $2, $sp, 50;" "lhu $12, 0($2);" "lhu $13, 6($2);" "lhu $14, 12($2);" "mtc2 $12, $9;" "mtc2 $13, $10;" "mtc2 $14, $11;" "nop;" "nop;" "mvmva 1, 0, 3, 3, 0;" "addiu $2, $sp, 18;" "mfc2 $12, $9;" "mfc2 $13, $10;" "mfc2 $14, $11;" "sh $12, 0($2);" "sh $13, 6($2);" "sh $14, 12($2);" "addiu $2, $sp, 52;" "lhu $12, 0($2);" "lhu $13, 6($2);" "lhu $14, 12($2);" "mtc2 $12, $9;" "mtc2 $13, $10;" "mtc2 $14, $11;" "nop;" "nop;" "mvmva 1, 0, 3, 3, 0;" "addiu $2, $sp, 20;" "mfc2 $12, $9;" "mfc2 $13, $10;" "mfc2 $14, $11;" "sh $12, 0($2);" "sh $13, 6($2);" "sh $14, 12($2)" : : "r"(m0), "r"(m1) : "$12", "$13", "$14" ); func_8001282C(m0); func_800484EC(m0, param_2, param_3); } extern void func_80013F3C(void *a0); extern s32 func_8004787C(s32 angle); extern s32 func_80047948(s32 angle); extern void func_8001282C(void *a0); extern void ApplyMatrixSV(void *a0, void *a1, void *a2); void func_80013694(s16 angle, void *a1, void *a2) { s16 matrix[16]; s16 vec0[9]; s16 pad[4]; s32 sin_val; s32 cos_val; register s32 v1 __asm__("$3"); register s16 *v0p __asm__("$2"); s16 *m0p; func_80013F3C(matrix); sin_val = func_8004787C((s32)angle); cos_val = func_80047948((s32)angle); v1 = 0x1000; vec0[0] = (s16)v1; v1 = -sin_val; vec0[4] = (s16)cos_val; vec0[8] = (s16)cos_val; __asm__ __volatile__(""); v0p = vec0; vec0[7] = (s16)sin_val; m0p = matrix; vec0[1] = 0; vec0[2] = 0; vec0[3] = 0; __asm__ __volatile__(""); vec0[5] = (s16)v1; vec0[6] = 0; __asm__ __volatile__ ( "lw $12, 0(%1);" "lw $13, 4(%1);" "ctc2 $12, $0;" "ctc2 $13, $1;" "lw $12, 8(%1);" "lw $13, 12(%1);" "lw $14, 16(%1);" "ctc2 $12, $2;" "ctc2 $13, $3;" "ctc2 $14, $4;" "lhu $12, 0(%0);" "lhu $13, 6(%0);" "lhu $14, 12(%0);" "mtc2 $12, $9;" "mtc2 $13, $10;" "mtc2 $14, $11;" "nop;" "nop;" "mvmva 1, 0, 3, 3, 0;" "mfc2 $12, $9;" "mfc2 $13, $10;" "mfc2 $14, $11;" "sh $12, 0(%1);" "sh $13, 6(%1);" "sh $14, 12(%1);" "addiu $2, $sp, 50;" "lhu $12, 0($2);" "lhu $13, 6($2);" "lhu $14, 12($2);" "mtc2 $12, $9;" "mtc2 $13, $10;" "mtc2 $14, $11;" "nop;" "nop;" "mvmva 1, 0, 3, 3, 0;" "addiu $2, $sp, 18;" "mfc2 $12, $9;" "mfc2 $13, $10;" "mfc2 $14, $11;" "sh $12, 0($2);" "sh $13, 6($2);" "sh $14, 12($2);" "addiu $2, $sp, 52;" "lhu $12, 0($2);" "lhu $13, 6($2);" "lhu $14, 12($2);" "mtc2 $12, $9;" "mtc2 $13, $10;" "mtc2 $14, $11;" "nop;" "nop;" "mvmva 1, 0, 3, 3, 0;" "addiu $2, $sp, 20;" "mfc2 $12, $9;" "mfc2 $13, $10;" "mfc2 $14, $11;" "sh $12, 0($2);" "sh $13, 6($2);" "sh $14, 12($2)" : : "r"(v0p), "r"(m0p) : "$12", "$13", "$14", "$2" ); func_8001282C(matrix); ApplyMatrixSV(matrix, a1, a2); } extern void func_80013F3C(); extern s32 func_8004787C(s32 angle); extern s32 func_80047948(s32 angle); extern void func_8001282C(); extern void func_800484EC(); void func_8001382C(s16 angle, void *a1, void *a2) { s16 matrix[16]; s16 rot[16]; s32 cos_val; s16 sin_val; s32 v1; void *rotp; func_80013F3C(matrix); sin_val = func_8004787C(angle); cos_val = func_80047948(angle); v1 = 0x1000; *(s16 *)((s32)rot + 0x04) = sin_val; *(s16 *)((s32)rot + 0x00) = cos_val; rotp = &rot[0]; *(s16 *)((s32)rot + 0x02) = 0; *(s16 *)((s32)rot + 0x06) = 0; *(s16 *)((s32)rot + 0x08) = v1; *(s16 *)((s32)rot + 0x0A) = 0; *(s16 *)((s32)rot + 0x0C) = -sin_val; *(s16 *)((s32)rot + 0x0E) = 0; *(s16 *)((s32)rot + 0x10) = cos_val; __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"(matrix) : "$12", "$13", "$14"); __asm__ __volatile__( "lhu $12, 0(%0);" "lhu $13, 6(%0);" "lhu $14, 12(%0);" "mtc2 $12, $9;" "mtc2 $13, $10;" "mtc2 $14, $11;" : : "r"(rotp) : "$12", "$13", "$14", "memory"); __asm__ __volatile__("nop;nop;mvmva 1, 0, 3, 3, 0"); __asm__ __volatile__( "mfc2 $12, $9;" "mfc2 $13, $10;" "mfc2 $14, $11;" "sh $12, 0(%0);" "sh $13, 6(%0);" "sh $14, 12(%0);" : : "r"(&matrix[0]) : "$12", "$13", "$14", "memory"); __asm__ __volatile__( "lhu $12, 0(%0);" "lhu $13, 6(%0);" "lhu $14, 12(%0);" "mtc2 $12, $9;" "mtc2 $13, $10;" "mtc2 $14, $11;" : : "r"(&rot[1]) : "$12", "$13", "$14", "memory"); __asm__ __volatile__("nop;nop;mvmva 1, 0, 3, 3, 0"); __asm__ __volatile__( "mfc2 $12, $9;" "mfc2 $13, $10;" "mfc2 $14, $11;" "sh $12, 0(%0);" "sh $13, 6(%0);" "sh $14, 12(%0);" : : "r"(&matrix[1]) : "$12", "$13", "$14", "memory"); __asm__ __volatile__( "lhu $12, 0(%0);" "lhu $13, 6(%0);" "lhu $14, 12(%0);" "mtc2 $12, $9;" "mtc2 $13, $10;" "mtc2 $14, $11;" : : "r"(&rot[2]) : "$12", "$13", "$14", "memory"); __asm__ __volatile__("nop;nop;mvmva 1, 0, 3, 3, 0"); __asm__ __volatile__( "mfc2 $12, $9;" "mfc2 $13, $10;" "mfc2 $14, $11;" "sh $12, 0(%0);" "sh $13, 6(%0);" "sh $14, 12(%0);" : : "r"(&matrix[2]) : "$12", "$13", "$14", "memory"); func_8001282C(matrix); func_800484EC(matrix, a1, a2); } #define gte_SetRotMatrix_139C8(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_ldclmv_139C8(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 gte_rtir_139C8() __asm__ volatile ("nop;nop;mvmva 1, 0, 3, 3, 0") #define gte_stclmv_139C8(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" ) typedef struct { s16 m[3][3]; s16 pad; s32 t[3]; } MTX_139C8; extern void ApplyMatrixSV(void *a0, void *a1, void *a2); void func_80013F3C(void *ptr); void func_800139C8(s16 param_1, void *param_2, void *param_3) { s16 sin_val; s32 cos_val; s16 *ip; s16 *rp; MTX_139C8 ident; MTX_139C8 rotm; func_80013F3C(&ident); sin_val = func_8004787C(param_1); cos_val = func_80047948(param_1); rotm.m[0][2] = sin_val; rotm.m[1][0] = rotm.m[0][1] = 0; rotm.m[1][1] = 0x1000; rotm.m[0][0] = cos_val; rotm.m[2][2] = cos_val; rp = &rotm.m[0][0]; rotm.m[2][0] = -sin_val; ip = &ident.m[0][0]; rotm.m[1][2] = 0; rotm.m[2][1] = 0; gte_SetRotMatrix_139C8(ip); gte_ldclmv_139C8(rp); gte_rtir_139C8(); gte_stclmv_139C8(ip); gte_ldclmv_139C8(rp + 1); gte_rtir_139C8(); gte_stclmv_139C8(ip + 1); gte_ldclmv_139C8(rp + 2); gte_rtir_139C8(); gte_stclmv_139C8(ip + 2); func_8001282C(ip); ApplyMatrixSV(ip, param_2, param_3); } extern void func_80013F3C(); extern s32 func_8004787C(s32 angle); extern s32 func_80047948(s32 angle); extern void func_8001282C(); extern void func_800484EC(); void func_80013B64(s32 param_1, s32 param_2, s32 param_3) { s16 ident[16]; s16 rot[16]; s32 sin_val; s32 cos_val; register s32 v1 __asm__("$3"); register s16 *v0p __asm__("$2"); s16 *m0p; func_80013F3C(ident); sin_val = func_8004787C((s16)param_1); cos_val = func_80047948((s16)param_1); v1 = -sin_val; __asm__ __volatile__(""); rot[0] = (s16)cos_val; rot[4] = (s16)cos_val; rot[8] = 0x1000; __asm__ __volatile__(""); v0p = rot; rot[3] = (s16)sin_val; m0p = ident; rot[1] = (s16)v1; rot[2] = 0; rot[5] = 0; rot[6] = 0; rot[7] = 0; __asm__ __volatile__ ( "lw $12, 0(%1);" "lw $13, 4(%1);" "ctc2 $12, $0;" "ctc2 $13, $1;" "lw $12, 8(%1);" "lw $13, 12(%1);" "lw $14, 16(%1);" "ctc2 $12, $2;" "ctc2 $13, $3;" "ctc2 $14, $4;" "lhu $12, 0(%0);" "lhu $13, 6(%0);" "lhu $14, 12(%0);" "mtc2 $12, $9;" "mtc2 $13, $10;" "mtc2 $14, $11;" "nop;" "nop;" "mvmva 1, 0, 3, 3, 0;" "mfc2 $12, $9;" "mfc2 $13, $10;" "mfc2 $14, $11;" "sh $12, 0(%1);" "sh $13, 6(%1);" "sh $14, 12(%1);" "addiu $2, $sp, 50;" "lhu $12, 0($2);" "lhu $13, 6($2);" "lhu $14, 12($2);" "mtc2 $12, $9;" "mtc2 $13, $10;" "mtc2 $14, $11;" "nop;" "nop;" "mvmva 1, 0, 3, 3, 0;" "addiu $2, $sp, 18;" "mfc2 $12, $9;" "mfc2 $13, $10;" "mfc2 $14, $11;" "sh $12, 0($2);" "sh $13, 6($2);" "sh $14, 12($2);" "addiu $2, $sp, 52;" "lhu $12, 0($2);" "lhu $13, 6($2);" "lhu $14, 12($2);" "mtc2 $12, $9;" "mtc2 $13, $10;" "mtc2 $14, $11;" "nop;" "nop;" "mvmva 1, 0, 3, 3, 0;" "addiu $2, $sp, 20;" "mfc2 $12, $9;" "mfc2 $13, $10;" "mfc2 $14, $11;" "sh $12, 0($2);" "sh $13, 6($2);" "sh $14, 12($2)" : : "r"(v0p), "r"(m0p) : "$12", "$13", "$14", "$2" ); func_8001282C(m0p); func_800484EC(m0p, param_2, param_3); } #define gte_SetRotMatrixX(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_ldclmvX(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 gte_rtirX() __asm__ __volatile__ ("nop; nop; mvmva 1, 0, 3, 3, 0") #define gte_stclmvX(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") void func_80013CFC(short param_1, void *param_2, void *param_3) { s16 matrix[16]; s16 rot[16]; s32 sin_val; s32 cos_val; register s32 v1 __asm__("$3"); s16 *rotp; s16 *matp; func_80013F3C(matrix); sin_val = func_8004787C(param_1); cos_val = func_80047948(param_1); v1 = -sin_val; __asm__ __volatile__("" ::: "memory"); *(s16 *)((s32)rot + 0x00) = cos_val; *(s16 *)((s32)rot + 0x08) = cos_val; *(s16 *)((s32)rot + 0x10) = 0x1000; rotp = rot; *(s16 *)((s32)rot + 0x06) = sin_val; matp = matrix; *(s16 *)((s32)rot + 0x02) = v1; *(s16 *)((s32)rot + 0x04) = 0; *(s16 *)((s32)rot + 0x0A) = 0; *(s16 *)((s32)rot + 0x0C) = 0; *(s16 *)((s32)rot + 0x0E) = 0; gte_SetRotMatrixX(matp); gte_ldclmvX(rotp); gte_rtirX(); gte_stclmvX(matp); gte_ldclmvX(rotp + 1); gte_rtirX(); gte_stclmvX(matp + 1); gte_ldclmvX(rotp + 2); gte_rtirX(); gte_stclmvX(matp + 2); func_8001282C(matp); ApplyMatrixSV(matp, param_2, param_3); } void func_80013E94(u16 *src, u32 *dst) { *dst = ((((u32)src[0] << 19) >> 22) << 20) | ((((u32)src[1] << 19) >> 22) << 10) | (((u32)src[2] << 19) >> 22); } void func_80013ED0(void* a0, void* a1, void* a2, void* a3) { ((u16*)a0)[0] = ((u16*)a1)[0]; ((u16*)a0)[1] = ((u16*)a2)[0]; ((u16*)a0)[2] = ((u16*)a3)[0]; ((u16*)a0)[3] = ((u16*)a1)[1]; ((u16*)a0)[4] = ((u16*)a2)[1]; ((u16*)a0)[5] = ((u16*)a3)[1]; ((u16*)a0)[6] = ((u16*)a1)[2]; ((u16*)a0)[7] = ((u16*)a2)[2]; ((u16*)a0)[8] = ((u16*)a3)[2]; } void func_80013F3C(void *ptr) { s16 *p = (s16 *)ptr; p[0] = 0x1000; p[1] = 0; p[2] = 0; p[3] = 0; p[4] = 0x1000; p[5] = 0; p[6] = 0; p[7] = 0; p[8] = 0x1000; } void func_80013F68(s16 *a0) { s16 v0; v0 = -0x1000; a0[2] = v0; v0 = 0x1000; a0[0] = 0; a0[1] = 0; a0[3] = 0; a0[4] = v0; a0[5] = 0; a0[6] = v0; a0[7] = 0; a0[8] = 0; } void func_80013F98(void *a0, void *a1) { *(u16 *)((s32)a1 + 0x0) = *(u16 *)((s32)a0 + 0x0); *(u16 *)((s32)a1 + 0x2) = *(u16 *)((s32)a0 + 0x6); *(u16 *)((s32)a1 + 0x4) = *(u16 *)((s32)a0 + 0xC); } void func_80013FBC(void *a0, void *a1) { *(u16 *)((s32)a1 + 0x0) = *(u16 *)((s32)a0 + 0x2); *(u16 *)((s32)a1 + 0x2) = *(u16 *)((s32)a0 + 0x8); *(u16 *)((s32)a1 + 0x4) = *(u16 *)((s32)a0 + 0xE); } void func_80013FE0(void *a0, void *a1) { *(u16 *)((s32)a1 + 0x0) = *(u16 *)((s32)a0 + 0x4); *(u16 *)((s32)a1 + 0x2) = *(u16 *)((s32)a0 + 0xA); *(u16 *)((s32)a1 + 0x4) = *(u16 *)((s32)a0 + 0x10); } void func_80014004(unsigned short *a0, unsigned short *a1) { a0[0] = a1[0]; a0[3] = a1[1]; a0[6] = a1[2]; } void func_80014028(s32 *a0, s32 *a1) { *(u16 *)((s32)a0 + 0x2) = *(u16 *)((s32)a1 + 0x0); *(u16 *)((s32)a0 + 0x8) = *(u16 *)((s32)a1 + 0x2); *(u16 *)((s32)a0 + 0xE) = *(u16 *)((s32)a1 + 0x4); } void func_8001404C(s32 *a0, s32 *a1) { *(u16 *)((s32)a0 + 0x4) = *(u16 *)((s32)a1 + 0x0); *(u16 *)((s32)a0 + 0xA) = *(u16 *)((s32)a1 + 0x2); *(u16 *)((s32)a0 + 0x10) = *(u16 *)((s32)a1 + 0x4); } void func_80014070(void* a0, void* a1) { ((s32 *)a1)[0] = ((s16 *)a0)[1 - 1]; ((s32 *)a1)[1] = ((s16 *)a0)[2 - 1]; ((s32 *)a1)[2] = ((s16 *)a0)[3 - 1]; } void func_80014094(s32 *arg0, void *arg1) { *(s16 *)((u8 *)arg1 + 0x0) = arg0[0]; *(s16 *)((u8 *)arg1 + 0x2) = arg0[1]; *(s16 *)((u8 *)arg1 + 0x4) = arg0[2]; } void func_800140B8(s32 a0, s32 a1, s32 a2) { __asm__ __volatile__( ".set\tnoreorder\n" "lw $12, 0($4)\n" "lw $13, 4($4)\n" "ctc2 $12, $0\n" "ctc2 $13, $1\n" "lw $12, 8($4)\n" "lw $13, 12($4)\n" "lw $14, 16($4)\n" "ctc2 $12, $2\n" "ctc2 $13, $3\n" "ctc2 $14, $4\n" "lwc2 $0, 0($5)\n" "lwc2 $1, 4($5)\n" "nop\n" "nop\n" "mvmva 1, 0, 0, 3, 0\n" "swc2 $25, 0($6)\n" "swc2 $26, 4($6)\n" "swc2 $27, 8($6)\n" ".set\treorder\n" : : : "$12", "$13", "$14", "memory"); *(s32 *)a2 <<= 16; *(s32 *)(a2 + 4) <<= 16; *(s32 *)(a2 + 8) <<= 16; } extern void ApplyMatrixSV(void *a0, void *a1, void *a2); void func_80014128(void *a0, void *a1, void *a2) { ApplyMatrixSV(a0, a1, a2); } void func_80014148(s32 a0, s32 a1, s32 a2) { func_800484EC(a0, a1, a2); } typedef struct { u8 b[8]; } Blk8; extern void ApplyMatrixSV(void *a0, void *a1, void *a2); void func_80014168(void *a0, void *a1) { Blk8 local; local = *(Blk8 *)a1; ApplyMatrixSV(a0, &local, a1); } void func_800141AC(s32 a0, void *a1) { struct B16x { u32 a, b, c, d; } local; local = *(struct B16x *)a1; func_800484EC(a0, &local, a1); } extern void func_80059234(s32); extern void func_800594CC(s32); extern void func_80059658(s32); extern void func_80016714(void *a0, s32 a1); extern void func_800142C8(void); extern u8 D_800BA118; void func_800141F0(void) { func_80059234(0); func_800594CC(0); func_80059658(0); func_80016714(&D_800BA118, 0x98); func_800142C8(); } extern u8 D_800AF630[]; void func_80014238(void) { func_80014928(); func_80059234(1); func_80059658(0); VSync(0); func_80016714(&D_800AF630, 0xA444); func_800143AC(); func_80014444(); func_8001C044(); func_80014588(); func_80015310(); func_80028A48(); func_80028C50(); func_800142C8(); func_80059658(1); } extern void func_80029044(void); extern void func_8002906C(void); extern void func_80029094(void); extern void func_80029218(void); extern void func_8001903C(void); extern void func_80018FC8(void); extern void func_8001B324(void); extern void func_80028FDC(s32); extern void func_80029020(s32); extern void func_80028DBC(s32); extern void func_80028E00(s32); void func_800142C8(void) { func_80029044(); func_8002906C(); func_80029094(); func_80029218(); func_8001903C(); func_80018FC8(); func_8001B324(); func_80028FDC(0x7); func_80029020(0x7); func_80028DBC(0); func_80028E00(0); } extern void func_80029044(void); extern void func_8002906C(void); extern void func_80029094(void); extern void func_80028FDC(s32); extern void func_80029020(s32); extern void func_80028DBC(s32); extern void func_80028E00(s32); extern void func_80029274(void); void func_80014338(void) { func_80029044(); func_8002906C(); func_80029094(); func_80028FDC(0x7); func_80029020(0x7); func_80028DBC(0); func_80028E00(0); func_80029274(); } void func_80014390(void) { extern s16 D_800B9A04[]; extern s32 D_800B9A18; D_800B9A04[0] = 0; D_800B9A18 = 2; } extern void func_800145EC(s32 a0); extern int VSync(int mode); extern u8 D_800AF7C4; extern u8 D_800AF7C3; extern u8 D_800AF7C2; extern u8 D_800A6610[]; extern u8 D_800A6518[]; extern u8 D_800A6520[]; extern u8 D_800A6524[]; void func_800143AC(void) { extern OtBlk_80016450 D_800A651C[]; register s32 ptr __asm__("4"); register s32 i __asm__("5"); register s32 idx __asm__("3"); register s32 c __asm__("6"); func_800145EC(1); VSync(0); i = 0; c = 0xC; ptr = (s32)D_800A6610; idx = 0; D_800AF7C4 = 0; D_800AF7C3 = 0; D_800AF7C2 = 0; do { *(s32*)((u8*)D_800A651C + idx) = ptr; ptr += 0x4000; *(s32*)(D_800A6518 + idx) = c; *(s32*)(D_800A6520 + idx) = 0; *(s32*)(D_800A6524 + idx) = 0; idx += 0x14; i++; } while (i < 2); } extern void func_80047CB4(void); extern void func_8004923C(s32 a0); extern void func_80053218(void); extern void func_800144D4(void); extern void func_8001455C(void); extern void func_8002850C(s32 a0, s32 a1, s32 a2); extern void func_8002823C(void); extern void func_80052D90(s32 a0, void *a1); extern void func_800538EC(s32 a0); extern s32 D_80126950; extern u8 D_800AF630[0x300]; void func_80014444(void) { u8 *s0; func_80047CB4(); D_80126950 = 0x3E8; func_8004923C(0x3E8); func_80053218(); func_800144D4(); func_8001455C(); func_8002850C(0x800, 0x800, 0x800); func_8002823C(); s0 = &D_800AF630[0]; func_80052D90(0, s0 + 0x1A0); func_80052D90(1, s0 + 0x1F0); func_800538EC(0); } extern void func_80053308(s32); extern void func_80053F6C(); extern s32 D_80126984; extern s32 D_80126988; extern s32 D_8012698C; extern s32 D_80126990; extern s32 D_80126994; extern s32 D_80126998; extern s32 D_8012699C; extern s32 D_801269A0; void func_800144D4(void) { extern s32 D_80126950; D_80126950 = 300; func_80053308(300); D_80126984 = 0; D_80126988 = 0; D_8012698C = -300; D_80126990 = 0; D_80126994 = 0; D_80126998 = 0; D_8012699C = 0; D_801269A0 = 0; func_80053F6C(&D_80126984); } void func_80014554(void) { } void func_8001455C(void) { } void func_80014564(void *arg0) { extern void func_8005C358(void *a0, s32 a1, s32 a2); func_8005C358(arg0, 0, 0x10); } typedef struct { /* 0x00 */ s16 flag; /* 0x02 */ s16 unk02; /* 0x04 */ void *list; /* 0x08 */ u16 unk08; /* 0x0A */ u16 unk0A; /* 0x0C */ u8 unk0C; /* 0x0D */ u8 unk0D; /* 0x0E */ u16 unk0E; } DispSlot_800184F0_80014588; void func_80014588(void) { extern void func_8005C358(void *a0, s32 a1, s32 a2); extern DispSlot_800184F0_80014588 D_800B97D8[]; DispSlot_800184F0_80014588 *slot; s16 i; slot = D_800B97D8; for (i = 0; i < 32; i++) { slot->flag = 0; func_8005C358(slot, 0, 0x10); slot++; } } void func_800145EC(s32 mode) { extern void func_800525DC(s32 w, s32 h, s32 mode2, s32 a3, s32 st); extern void func_80053178(s32 a0, s32 a1, s32 a2, s32 a3); extern void func_80059234(s32); extern void func_80053218(void); extern void func_80014774(void); extern void func_800147B8(void); extern u8 D_80062A3C[]; extern u8 D_80062A3E[]; extern u8 D_80062A40[]; extern u16 D_800AF7BC; extern u16 D_800AF7BE; extern u16 D_800AF7C0; s32 off; off = (mode & 0xFFFF) * 6; D_800AF7BC = *(u16 *) (D_80062A3C + off); D_800AF7BE = *(u16 *) (D_80062A3E + off); D_800AF7C0 = *(u16 *) (D_80062A40 + off); func_800525DC(D_800AF7BC, D_800AF7BE, D_800AF7C0 | 4, 0, 0); if (D_800AF7BE == 0x1E0) { func_80053178(0, 0, 0, 0); } else { func_80053178(0, D_800AF7BE, 0, 0); } func_80059234(1); func_80053218(); func_80014774(); func_800147B8(); } void func_800146B0(s32 mode) { extern void GsInitGraph2(s32 w, s32 h, s32 mode, s32 a3, s32 st); extern void func_80053EEC(s32 a0, s32 a1, s32 a2, s32 a3); extern void func_80059234(s32); extern void func_80053218(void); extern void func_80014774(void); extern void func_800147B8(void); extern u8 D_80062A3C[]; extern u8 D_80062A3E[]; extern u8 D_80062A40[]; extern u16 D_800AF7BC; extern u16 D_800AF7BE; extern u16 D_800AF7C0; s32 off; off = (mode & 0xFFFF) * 6; D_800AF7BC = *(u16 *) (D_80062A3C + off); D_800AF7BE = *(u16 *) (D_80062A3E + off); D_800AF7C0 = *(u16 *) (D_80062A40 + off); GsInitGraph2(D_800AF7BC, D_800AF7BE, D_800AF7C0 | 4, 0, 0); if (D_800AF7BE == 0x1E0) { func_80053EEC(0, 0, 0, 0); } else { func_80053EEC(0, D_800AF7BE, 0, 0); } func_80059234(1); func_80053218(); func_80014774(); func_800147B8(); } extern void func_80059888(void *a0, s32 a1, s32 a2, s32 a3); void func_80014774(void) { s16 local[4]; local[0] = 0; local[1] = 0; local[2] = 0x280; local[3] = 0x1E0; func_80059888((void *)local, 0, 0, 0); } extern u8 D_800AF630[]; extern u16 D_800AF7BE; extern u16 D_800AF7BC; extern u16 D_800AF672; extern u16 D_800AF670; extern u16 D_800AF6CC; extern u16 D_800AF6D6; extern u16 D_800AF6D4; extern u16 D_800AF67A; extern u16 D_800AF678; extern u16 D_800AF6CE; extern u8 D_800AF6DA; extern u8 D_800AF67E; extern u8 D_800AF6DC; extern u8 D_800AF680; extern void *func_80058A4C(void *env, s32 x, s32 y, s32 w, s32 h); extern void *SetDefDispEnv(void *env, s32 x, s32 y, s32 w, s32 h); extern void func_80014960(void); void func_800147B8(void) { u8 *base = D_800AF630; register s32 raw_h __asm__("$3") = D_800AF7BE; s32 h_offset = raw_h; u16 halfW; u16 halfH; s16 h; if (h_offset == 0x1E0) { h_offset = 0; } func_80058A4C(base + 0x38, 0, 0xA, D_800AF7BC, D_800AF7BE - 0x14); SetDefDispEnv(base + 0x14C, 0, 0, D_800AF7BC, D_800AF7BE); func_80058A4C(base + 0x94, 0, h_offset + 0xA, D_800AF7BC, D_800AF7BE - 0x14); SetDefDispEnv(base + 0x160, 0, h_offset, D_800AF7BC, D_800AF7BE); halfW = D_800AF7BC >> 1; halfH = D_800AF7BE >> 1; D_800AF672 = halfH; D_800AF670 = halfW; D_800AF6CC = halfW; h = (D_800AF7BE == 0xF0) ? 0x168 : 0xF0; D_800AF6D6 = 0x100; D_800AF6D4 = 0x100; D_800AF67A = 0x100; D_800AF678 = 0x100; D_800AF6CE = h; D_800AF6DA = 0; D_800AF67E = 0; D_800AF6DC = 1; D_800AF680 = 1; func_80014960(); } extern void func_8005CF08(void); extern void InitHeap(void *a0, s32 a1); extern void func_8005CF18(void); void func_80014928(void) { func_8005CF08(); InitHeap((void *)0x801FF800, 0x800); func_8005CF18(); } extern void func_80014998(void); extern u8 D_800AF7C4; extern u8 D_800AF7C3; extern u8 D_800AF7C2; void func_80014960(void) { D_800AF7C4 = 0; D_800AF7C3 = 0; D_800AF7C2 = 0; func_80014998(); } extern u8 D_800AF630[]; void func_80014998(void) { u8 *p; s32 i; p = D_800AF630; for (i = 0; i < 2; i++) { p[i * 0x5C + 0x51] = p[0x192]; p[i * 0x5C + 0x52] = p[0x193]; p[i * 0x5C + 0x53] = p[0x194]; } } extern u8 D_80078DD2; extern u8 func_80014C28(u32 a0); extern u8 func_80014CF8(u32 a0, u32 a1); extern u8 func_80014D30(u32 a0, u32 a1); s32 func_800149E0(s32 a0) { u8 b0; u16 val; s32 c; u16 h1; u16 h2; u16 m1; u16 m2; u16 v1; b0 = a0 & 0xFF; val = *(u16*)((u32)&D_80078DD2 + (u32)(b0 * 76)); if (val != 0) { return val; } c = func_80014C28(b0) & 0xFF; if (c != 'S') { if (c < 'T') { return 0; } if (c != 's') { return 0; } } b0 = a0 & 0xFF; h1 = func_80014D30(b0, 5) & 0xFF; h2 = func_80014D30(b0, 4) & 0xFF; m1 = func_80014CF8(b0, 5) & 0xFF; m2 = func_80014CF8(b0, 4) & 0xFF; v1 = 0; if (h1 == 0x80) { if (m1 != h1) { if (m1 >= 0x80) { v1 = 0x4000; } else { v1 = 0x1000; } } } if (h2 != 0x80) { return v1; } if (m2 != h2) { if (m2 < 0x80) { v1 |= 0x8000; } else { v1 |= 0x2000; } } return v1; } extern u8 D_80078DCA; extern u8 func_80014C28(u32 a0); extern u8 func_80014CF8(u32 a0, u32 a1); s32 func_80014B10(s32 a0) { u8 b0; u16 val; s32 c; u16 m1; u16 m2; u16 v1; b0 = a0 & 0xFF; val = *(u16 *)((u32)&D_80078DCA + (u32)(b0 * 76)); if (val != 0) { return val; } c = func_80014C28(b0) & 0xFF; if (c != 'S') { if (c < 'T') { return 0; } if (c != 's') { return 0; } } b0 = a0 & 0xFF; m1 = func_80014CF8(b0, 5) & 0xFF; m2 = func_80014CF8(b0, 4) & 0xFF; v1 = 0; if (m1 != 0x80) { if (m1 >= 0x80) { v1 = 0x4000; } else { v1 = 0x1000; } } if (m2 == 0x80) { return v1; } else if (m2 < 0x80) { v1 |= 0x8000; } else { v1 |= 0x2000; } return v1; } extern u16 D_80078DDA; u16 func_80014BFC(u32 a0) { u32 offset; a0 &= 0xFF; offset = a0 * 76; return *(u16*)((u32)&D_80078DDA + offset); } extern u8 D_80078D98; u8 func_80014C28(u32 a0) { u32 offset; a0 &= 0xFF; offset = a0 * 76; offset = offset + (u32)&D_80078D98; return *(u8*)(offset); } extern u8 D_80078D98; /* dual flag-half tester: selects unk32/unk3A of record (a0&0xFF)*76 by (a1&0xFF)==1, * returns ((sel & a2) == a2); gcc keeps one 'and' per arm, one riding the j delay slot */ s32 func_80014C54(s32 a0, s32 a1, s32 a2) { u8 *p; u16 result; p = (u8 *)((u32)&D_80078D98 + (a0 & 0xFF) * 76); a1 &= 0xFF; if (a1 == 1) { result = *(u16 *)(p + 0x32); } else { result = *(u16 *)(p + 0x3A); } return (u32)(((u32)result & (u32)a2) ^ (u32)a2) < 1; } extern u8 D_80078DCA; extern u8 D_80078DD2; u8 func_80014CAC(u32 arg0, u32 arg1) { extern u16 D_80078E16; extern u16 D_80078E1E; u16 var_v0; if ((arg0 & 0xFF) == 1) { var_v0 = *(u16 *)&D_80078DCA | D_80078E16; } else { var_v0 = *(u16 *)&D_80078DD2 | D_80078E1E; } return (var_v0 & arg1) != 0; } extern u8 D_80078D98; u8 func_80014CF8(u32 a0, u32 a1) { u32 offset; a0 &= 0xFF; offset = a0 * 76; offset = offset + (u32)&D_80078D98; a1 &= 0xFF; offset = offset + a1; return *(u8*)(offset + 0x32); } extern u8 D_80078D98; u8 func_80014D30(u32 a0, u32 a1) { u32 v0; a0 = a0 & 0xFF; v0 = a0 << 2; v0 = v0 + a0; v0 = v0 << 2; v0 = v0 - a0; v0 = v0 << 2; v0 = v0 + (u32)&D_80078D98; a1 = a1 & 0xFF; v0 = v0 + a1; return *(u8*)(v0 + 0x42); } extern u8 D_80078DCA; u16 func_80014D68(u32 a0) { u32 offset; a0 &= 0xFF; offset = a0 * 76; offset = offset + (u32)&D_80078DCA; return *(u16*)offset; } extern u8 D_80078DD2; s32 func_80014D94(s32 a0) { a0 &= 0xFF; return *(u16*)((u32)&D_80078DD2 + (a0 * 76)); } extern u8 D_80078D98; u8 func_80014DC0(u32 a0) { u32 offset; a0 &= 0xFF; offset = a0 * 76; offset = offset + (u32)&D_80078D98; return *(u8*)offset; } extern u8 D_80078D98; u8 func_80014DEC(s32 a0, s32 a1) { register s32 v0 __asm__("$2"); a0 = a0 & 0xFF; v0 = (a0 << 2) + a0; v0 = (v0 << 2) - a0; v0 = v0 << 2; v0 = v0 + (s32)&D_80078D98; a1 = a1 & 0xFF; v0 = v0 + a1; return *(u8 *)(v0 + 0x32); } extern s32 func_80015018(s32 a0); extern s32 func_80014ED4(s32 a0); s32 func_80014E24(s32 a0, s32 a1, s32 a2) { u16 result; s32 tmp; /* a0's masked-byte value is genuinely consumed as an argument to BOTH callees below * (their bodies happen to ignore it, but the caller must still materialize it) -- * the opaque asm keeps gcc-2.7.2 from folding this into a per-branch recompute or * eliding it outright, which is what a plain "a0 &= 0xFF;" statement does when a0 * is otherwise unused. See matching-cookbook.md L2916 (opaque asm-COPY) for the * precedent: a real, non-hard-reg opcode asm is used only when no pure-C spelling * reproduces the target's single, pre-branch materialization. */ __asm__("andi %0, %1, 0xff" : "=r"(tmp) : "r"(a0)); a1 &= 0xFF; if (a1 == 1) { result = func_80015018(tmp); } else { result = func_80014ED4(tmp); } result &= 0xFFFF; // Check if all bits of a2 are set in result return (u32)(((u32)result & (u32)a2) ^ (u32)a2) < 1; } extern s32 func_80015018(s32 a0); extern s32 func_80014ED4(s32 a0); s32 func_80014E80(s32 a0, s32 a1) { s32 result; a0 = a0 & 0xFF; if (a0 == 1) { result = func_80015018(0); } else { result = func_80014ED4(0); } result = result & 0xFFFF; result = result & a1; return (result != 0) ? 1 : 0; } extern u8 D_80078D98; extern void func_80015118(u16 *a0); s32 func_80014ED4(s32 a0) { u8 *p; u8 *p2; u16 val; s32 c; u16 buf[4]; s32 flags; p = (u8 *)((u32)&D_80078D98 + (a0 & 0xFF) * 76); val = *(u16 *)(p + 0x3A); if (val != 0) { return val; } c = *p; if (c != 'S') { if (c < 'T') { return 0; } if (c != 's') { return 0; } } p2 = (u8 *)((u32)&D_80078D98 + (a0 & 0xFF) * 76); buf[0] = *(p2 + 0x47); buf[1] = *(p2 + 0x46); buf[2] = *(p2 + 0x37); buf[3] = *(p2 + 0x36); func_80015118(&buf[0]); func_80015118(&buf[1]); func_80015118(&buf[2]); func_80015118(&buf[3]); flags = 0; if (buf[0] == 0x80) { if (buf[2] != 0x80) { if (buf[2] >= 0x80) { flags = 0x4000; } else { flags = 0x1000; } } } if (buf[1] == 0x80) { if (buf[3] != 0x80) { flags |= (buf[3] < 0x80) ? 0x8000 : 0x2000; } } return flags & 0xFFFF; } extern u8 D_80078D98; extern void func_80015118(u16 *arg); s32 func_80015018(s32 a0) { u8 *p; u8 *q; u16 val; u16 tmp[2]; s32 result; p = (u8 *)&D_80078D98 + (a0 & 0xFF) * 76; val = *(u16 *)(p + 0x32); if (val != 0) { return val; } { s32 c = *p; if (c != 0x53) { if (c < 0x54) { return 0; } if (c != 0x73) { return 0; } } } q = (u8 *)&D_80078D98 + (a0 & 0xFF) * 76; tmp[0] = *(q + 0x37); tmp[1] = *(q + 0x36); func_80015118(&tmp[0]); func_80015118(&tmp[1]); result = 0; if (tmp[0] != 0x80) { if (tmp[0] >= 0x80) { result = 0x4000; } else { result = 0x1000; } } if (tmp[1] != 0x80) { if (tmp[1] < 0x80) { result |= 0x8000; } else { result |= 0x2000; } } return result & 0xFFFF; } void func_80015118(u16 *a0) { if ((u32)(*a0 - 0x15) < 0xD7) { *a0 = 0x80; } } void func_8001513C(void) { } extern s32 CdQueueBusy(void); extern void func_8002D4C8(s32 a0, s32 a1); extern void func_8003500C(void); extern void func_8002D034(void); extern int VSync(int mode); extern s32 func_8001AA78(void); extern void func_8002F67C(void); extern void LoaderResetReadState(void); extern u8 D_800AF630[]; extern s16 D_800B9A04[]; s32 func_80015144(void) { u8* base = D_800AF630; if (CdQueueBusy() != 0) { func_8002D4C8(2, 0); while (1) { func_8003500C(); func_8002D034(); VSync(*(s32*)(base + 0xA3E8)); if (CdQueueBusy() == 0) { break; } } *(s16*)(base + 0xA3D4) = 1; } else { if (func_8001AA78() != 0) { return 0; } D_800B9A04[0] = 1; func_8002D4C8(2, 0); } func_8002F67C(); LoaderResetReadState(); return 1; } extern u8 D_800B9A64; extern u8 D_800B9A10; extern u8 D_800B9A17; extern u8 D_800B9A15; extern u8 D_800B9A16; extern u8 D_800B9A13; extern u8 D_80078DD2; extern s32 D_800A5E78; extern u8 D_800AF630[]; extern void func_801281D8(void); void func_80015208(void) { u8 flag0; s32 v; register u8 *base __asm__("$5"); flag0 = D_800B9A64; base = D_800AF630; if (flag0) { return; } __asm__ __volatile__("" : "=r"(base) : "0"(base)); if (D_800B9A10 != 1) { return; } if (D_800B9A17 != D_800B9A10) { return; } if (D_800B9A15 == D_800B9A17) { return; } if (D_800B9A16 != 0) { D_800B9A15 = 1; return; } if (D_800B9A13 != 0) { return; } { s32 mask = 0x800; __asm__ __volatile__("" : "=r"(mask) : "0"(mask)); if ((*(u16 *)&D_80078DD2 & 0x800) == mask) { v = 1; __asm__ __volatile__("" : "=r"(v) : "0"(v)); } else { v = 0; } if (!v) { if (D_800A5E78 != 0) { return; } } v = 1; base[0xA3E5] = v; func_801281D8(); } } extern s16 D_800B93D8[]; void func_800152F4(s32 idx) { D_800B93D8[((idx << 16) >> 16) * 8] = 0; } extern s16 D_800B93D8[]; void func_80015310(void) { extern DispSlot_800184F0 D_800B97D8[]; s32 pad[2]; DispSlot_800184F0 *p; DispSlot_800184F0 *end; p = (DispSlot_800184F0 *)D_800B93D8; end = D_800B97D8; for (; p < end; p++) { p->flag = 0; } } extern u8 D_800AF630[]; void func_8001534C(s32 idx, s32 a1, s16 x, s16 y, u8 arg4, u8 arg5) { u8 *p; s32 off; p = D_800AF630; off = (s16)idx * 16 + 0x9DA8; func_800153A4(p + off, a1, x, y, arg4, arg5); } void func_800153A4(void *a0, s32 a1, s16 a2, s16 a3, u8 arg4, u8 arg5) { *(s16 *)(a0 + 0) = 1; *(s16 *)(a0 + 2) = a2; *(s16 *)(a0 + 4) = a3; *(s32 *)(a0 + 8) = a1; *(u8 *)(a0 + 12) = arg4; *(u8 *)(a0 + 13) = arg5; } extern u8 D_800AF630[]; void func_800153CC(s32 arg0, s32 arg1, s16 arg2, s16 arg3, u8 arg4, u8 arg5) { u8 *base; base = D_800AF630; func_80015424(&base[((arg0 << 16) >> 12) + 0x9DA8], arg1, arg2, arg3, arg4, arg5); } void func_80015424(void *a0, s32 a1, s16 a2, s16 a3, s8 a4, s8 a5) { *(s16 *)a0 = 2; *(s16 *)(a0 + 2) = a2; *(s16 *)(a0 + 4) = a3; *(s32 *)(a0 + 8) = a1; *(s8 *)(a0 + 0xC) = a4; *(s8 *)(a0 + 0xD) = a5; } extern s16 D_800B93D8[]; s16 *func_8001544C(void) { extern DispSlot_800184F0 D_800B97D8[]; s16 *p; s16 *end; s16 v; s32 pad[2]; p = D_800B93D8; end = (s16 *)D_800B97D8; if (p >= end) { return 0; } do { v = *p; if (v == 0) { return p; } p += 8; } while (p < end); return 0; } extern u16 D_800B9A02; extern u8 D_800B9A11; extern u8 D_800AF630[]; extern u8 *D_800A5E60; extern s32 GetTPage(s32, s32, s32, s32); extern void func_80015608(s32, s32); extern void func_80015760(); void func_80015498(void) { extern OtBlk_80016450 D_800A651C[]; u8 *base; u32 *ot; u8 *entry; u8 *end; u8 *p; s16 v1; s32 tp; register u32 tag0 __asm__("$4"); register u32 maskLo __asm__("$5"); base = D_800AF630; ot = (u32 *)D_800A651C[D_800B9A02].a; if (D_800B9A11 != 1) { entry = base + 0x9DA8; if (entry < base + 0xA1A8) { register s16 one __asm__("$20") = 1; register s16 two __asm__("$19") = 2; end = base + 0xA1A8; do { v1 = *(s16 *)entry; if (v1 == one) { goto do_call1; } if (v1 < 2) { goto incr_shared; } if (v1 == two) { goto do_call2; } entry += 0x10; continue; do_call1: func_80015608((s32)entry, (s32)ot); entry += 0x10; continue; do_call2: ((void (*)())func_80015760)((s32)entry, (s32)ot); incr_shared: entry += 0x10; } while (entry < end); } p = D_800A5E60; p[3] = 1; tp = GetTPage(0, 0, 0x140, 0x100); maskLo = 0xFFFFFF; tag0 = *(u32 *)p; *(u32 *)(p + 4) = (tp & 0x9FF) | 0xE1000000; *(u32 *)p = (tag0 & 0xFF000000) | (*ot & maskLo); p = (u8 *)((u32)p & maskLo); { u32 g = (u32)D_800A5E60; u32 o = *ot; D_800A5E60 = (u8 *)(g + 8); *ot = (o & 0xFF000000) | (u32)p; } } } extern u16 D_800AF7BC; extern u16 D_800AF7BE; extern u8 *D_800A5E60; extern s16 *func_80015908(s32 a0, s32 a1); void func_80015608(s32 arg0, s32 arg1) { int new_var2; u8 *icon; int new_var; s32 x0; s32 flags; register s32 blockSize __asm__("$22"); register u32 code __asm__("$3"); register s32 htmp __asm__("$3"); u8 *cur; u32 rgbc; s32 y0; u32 y0hi; u16 *tex; u32 *otp; register u32 tagconst __asm__("$20"); register u32 mask __asm__("$3"); otp = (u32 *) arg1; icon = *((u8 **) (((u8 *) arg0) + 8)); htmp = *((u16 *) (((u8 *) arg0) + 2)); x0 = htmp - (D_800AF7BC >> 1); flags = *(((u8 *) arg0) + 0xC); htmp = *((u16 *) (((u8 *) arg0) + 4)); y0 = htmp - (D_800AF7BE >> 1); new_var = 0x03000000; blockSize = (flags & 0x80) ? (0x10) : (8); code = blockSize & 0xFF; if (code == 8) { code = 0x74000000; } else { code = 0x7C000000; } cur = D_800A5E60; tagconst = new_var; if ((*icon) != 0) { rgbc = code | 0x808080; y0hi = y0 << 16; do { *((u32 *) (cur + 4)) = rgbc; *((u32 *) (cur + 8)) = ((u16) x0) | y0hi; { s32 xt = blockSize + x0; x0 = xt; } tex = func_80015908(*icon, (u16) flags); icon++; mask = 0x00FFFFFF; new_var2 = 0x40560000; new_var = 0x10; *((u32 *) (cur + 0xC)) = (*tex) | new_var2; *((u32 *) cur) = ((*otp) & mask) | tagconst; *otp = ((u32) cur) & mask; cur += new_var; } while ((*icon) != 0); } D_800A5E60 = cur; } typedef struct { u16 unk0; u16 x; u16 y; u16 unk6; s32 value; u8 flags; } Obj_80015760; extern u16 D_800AF7BC; extern u16 D_800AF7BE; extern u8 *D_800A5E60; extern u8 D_80062B78[]; extern u32 func_80015A74(u32 param_1); extern s16 *func_80015908(s32 a0, s32 a1); void func_80015760(Obj_80015760 *obj, s32 *ot) { s32 x0; register u32 tx __asm__("$5"); s32 y0; s32 flags; u32 value; u32 digits; register s32 count __asm__("$2"); u8 digitCount; u8 width; u32 tagHi; u32 color; u32 yWord; register u32 tagCode __asm__("$21"); s32 *pkt; tx = obj->x; x0 = ((s32) tx) - (D_800AF7BC >> 1); flags = obj->flags; y0 = ((s32) obj->y) - (D_800AF7BE >> 1); value = (u32) obj->value; count = flags & 0xF; digitCount = count; if (count == 0) { digitCount = 8; } if (flags & 0x40) { digits = func_80015A74(value); } else { digits = value; } width = 8; if (flags & 0x80) { y0++; y0--; width = 0x10; } tagHi = width; if (tagHi == 8) { tagHi = 0x74000000; } else { tagHi = 0x7C000000; } tagCode = 0x03000000; pkt = (s32 *) D_800A5E60; if (digitCount != 0) { color = tagHi | 0x00808080; yWord = (u32) (y0 << 16); do { u32 xLow; u32 idx; s32 tile; u16 *uvPtr; u32 uvc; register u32 mask __asm__("$3"); xLow = (u16) x0; x0 = width + x0; idx = (digits >> ((digitCount - 1) << 2)) & 0xF; tile = D_80062B78[idx]; pkt[1] = (s32) color; digitCount--; pkt[2] = (s32) (xLow | yWord); uvPtr = func_80015908(tile, (u16) flags); /* the caller's original prototype took u16: the target masks (andi) before the call */ uvc = 0x40560000; pkt[3] = (s32) ((*uvPtr) | uvc); mask = 0x00FFFFFF; pkt[0] = (s32) (((*ot) & mask) | tagCode); *ot = (s32) (((u32) pkt) & mask); pkt += 4; } while (digitCount != 0); } D_800A5E60 = (u8 *) pkt; } extern s16 D_80062A78[]; extern s16 D_80062AF8[]; s16 *func_80015908(s32 a0, s32 a1) { switch (a1 & 0x80) { case 0: { s32 idx0 = (a0 & 0xFF) - 0x20; return &D_80062A78[idx0]; } case 0x80: { s32 idx1 = (a0 & 0xFF) - 0x20; return &D_80062AF8[idx1]; } } } void func_80015954(s32 a0, s32 a1) { *(u16 *)(a1 + 0x2) = *(u16 *)(a0 + 0x0); *(u16 *)(a1 + 0x6) = *(u16 *)(a0 + 0x2); *(u16 *)(a1 + 0xA) = *(u16 *)(a0 + 0x4); } void func_80015978(s32 *a0, s32 *a1) { *(u16 *)((s32)a1 + 0x0) = *(u16 *)((s32)a0 + 0x2); *(u16 *)((s32)a1 + 0x2) = *(u16 *)((s32)a0 + 0x6); *(u16 *)((s32)a1 + 0x4) = *(u16 *)((s32)a0 + 0xA); } void func_8001599C(s32 a0, s32 a1) { *(s32 *)(a1 + 0x0) = *(s32 *)(a0 + 0x0); *(s32 *)(a1 + 0x4) = *(s32 *)(a0 + 0x4); *(s32 *)(a1 + 0x8) = *(s32 *)(a0 + 0x8); } void func_800159C0(s32 a0, s32 a1) { *(s32 *)(a1 + 0x0) = *(s32 *)(a0 + 0x0); *(s32 *)(a1 + 0x4) = *(s32 *)(a0 + 0x4); *(s32 *)(a1 + 0x8) = *(s32 *)(a0 + 0x8); } void func_800159E4(s32 *a0, s32 *a1) { *(u16 *)((s32)a1 + 0x2) = *(u16 *)((s32)a0 + 0x2); *(u16 *)((s32)a1 + 0x6) = *(u16 *)((s32)a0 + 0x6); *(u16 *)((s32)a1 + 0xA) = *(u16 *)((s32)a0 + 0xA); } void func_80015A08(s32 *a0, s32 *a1) { *(u16 *)((s32)a1 + 0x0) = *(u16 *)((s32)a0 + 0x0); *(u16 *)((s32)a1 + 0x4) = *(u16 *)((s32)a0 + 0x4); *(u16 *)((s32)a1 + 0x8) = *(u16 *)((s32)a0 + 0x8); } void func_80015A2C(short* arg0, int* arg1) { arg1[0] = arg0[0]; arg1[1] = arg0[1]; arg1[2] = arg0[2]; } void func_80015A50(s32 *arg0, s16 *arg1) { arg1[0] = arg0[0]; arg1[1] = arg0[1]; arg1[2] = arg0[2]; } #ifdef NON_MATCHING /* uint -> packed BCD (9 digits, ÷ powers of 10). All idioms match under the pinned * triple (divu+break div-by-zero via --expand-div, multu 0xCCCCCCCD + srl 3 for ÷10); * residual diff is a loop-setup instruction-scheduling permutation (li counter vs the * hoisted magic constant) — a decomp-permuter target. See docs/matching-cookbook.md. */ u32 func_80015A74(u32 param_1) { u32 result; s32 i; u32 divisor; divisor = 1000000000; result = 0; for (i = 9; i >= 0; i--) { result = (result << 4) | (param_1 / divisor); param_1 = param_1 % divisor; divisor = divisor / 10; } return result; } #else unsigned int func_80015A74(unsigned int param_1) { unsigned int result; unsigned int divisor; int i; divisor = 1000000000; result = 0; for (i = 0; i < 10; i++) { result = (result << 4) | (param_1 / divisor); param_1 = param_1 % divisor; divisor = divisor / 10; } return result; } #endif extern s32 catan(s32 a0); s32 func_80015AD0(s16 param_1, s16 param_2) { s32 quotient; if (param_2 == 0) { param_2 = 1; } quotient = catan((param_1 << 12) / param_2); if (param_2 < 0) { if (param_1 < 0) { goto subtract; } quotient += 0x800; } return quotient; subtract: quotient -= 0x800; return quotient; } /* func_80015B6C — main/800. Allocate one 0x34-byte GPU packet from the frame * heap (func_80010A08) holding three primitives and link all three, in order, * into the current double-buffer's ordering table at depth 0xFFF * (*(u32 *)(D_800A651C[D_800B9A02].a + 0x3FFC)) with the open-coded PSY-Q * addPrim RMW pair (tag = {addr:24, len:8}), exactly as the matched sibling * func_80015D4C in this TU does: * * +0x00 DR_MODE-style header : tag.len = 1, code word 0xE1000000 * +0x08 POLY_G4 gradient quad: tag.len = 8, code 0x38, corners * (x,y) (x+w,y) (x,y+h) (x+w,y+h), top colour c1/c2/c3 on v0+v1, * bottom colour c4/c5/c6 on v2+v3 * +0x2C DR_MODE-style trailer: tag.len = 1, code word 0xE1000200 * * =================================================================== * WHY THE ODD-LOOKING SOURCE (all five levers were byte-verified; six earlier * attempts plateaued at 20..47 without them, see the journal notes) * =================================================================== * * 1. THE CORNER COPIES (`vx`/`vy`) + THE ZERO-BYTE INVALIDATORS. * The target keeps the ORIGINAL x,y in scratch regs (addu $a0,$s1,$zero / * addu $v1,$s2,$zero) and then DESTROYS $s1/$s2 with the sums * (addu $s1,$s1,$t1). That shape needs a source-level copy + an in-place * `x += w_`. But cse.c's canon_reg rewrites every use of `vx` back to `x` * while `x` is still unmodified (make_regs_eqv keeps the FIRST reg of the * quantity canonical, and a copy that dies inside the block can never * become canonical), which silently turned `sh $a0,0x8` into `sh $s1,0x8` * and deleted the `vy` copy outright (119 ins). * Moving the `+=` above the stores does defeat cse, but it also shortens * w_/h_'s live ranges, and local-alloc's priority * (log2(n_refs)*n_refs/live_length) then hands w_/h_ the last two * callee-saved registers and SPILLS TWO COLOURS (+5 ins). The target * spills w_/h_ to 0x10/0x18(sp) instead. * THE FIX that satisfies both: keep the `+=` in its natural late place and * kill the cse equivalence with a zero-byte opaque copy on x and y — * `__asm__("" : "=r"(x) : "0"(x));`. It emits nothing, re-defines x's * quantity so canon_reg stops substituting, and leaves the live ranges * (hence the spill decision) untouched. * * 2. THE `__asm__ __volatile__("")` SCHEDULING FENCES. gcc-2.7.2's sched1 * gives the block-2 compute chain (the corner copies, the two `+=`, the * packet-tag load, the 0xE1000200 constant) a longer path to the end of * the block than the twelve leaf colour `sb`s, so it hoists ALL of it in * front of the colours — and, without the first fence, hoists the trailer's * `lui 0xE100` all the way to the top of the post-call region. Source * order alone is INERT against this (measured: colours-first and * colours-last both give the same schedule). The four empty volatile asms * partition block 2 into exactly the target's five phases: * colours | v0/v1 corners | tag load + x += w_ + v1/v2 | y += h_ + v3 * | the addPrim pair. * They cost nothing: the load-delay slots the fences would otherwise strand * are filled from inside each phase (the tag load fills the first reload's * slot, `and $a2,$a2,$a3` the second's), which is exactly what the target * does at 0x80015C88 and 0x80015CA0. * * 3. `tag2` PINNED TO $6 ($a2) — the load-bearing regalloc lever, the same one * func_80015D4C's header documents. Left alone, the block-2 tag lands in * $a0, i.e. in the register the vx copy has just vacated, which drags * `sh $a0,0x8` and `sh $a0,0x18` in front of the colour block and lets the * trailer constant float free. With the tag in $a2 the vx copy stays live * across the colours and the schedule falls into place. * * 4. `tag2` IS REUSED FOR THE 0xE1000200 TRAILER WORD, and the assignment sits * AFTER the packet-tag store. gcc splits a large `li` into lui+ori only * after reload, so sched2 hoists the `lui` as far as the register lets it: * reusing $a2 pins it to the slot right after `sw $a2,0x0($v0)` — 0x80015CC8 * in the target. Declaring a second constant, or assigning it earlier, * moves the whole and/or chain out of $a2 (measured 5..36 mismatched). * * 5. `pmask` (the addPrim address mask) IS A NAMED LOCAL so that the second * RMW's `and $a0,$v0,$a1` can still be scheduled INSIDE the first RMW * (0x80015CB8) while fence 4 keeps the trailer's `lui` behind the store. * Without it the fence would drag that `and` out with everything else. * * `mFF` stays a pinned local ($7/$a3): unpinning it re-shuffles the four * post-call constants (measured 15 mismatched). `one`/`m24`/`otp`'s pins were * measured INERT and dropped. * * @class: MATCH — 120/120 byte-exact under tools/match_one.py AND * tools/rtu_match.py (whole-TU splice), and tools/reloc_identity.py AGREEs * (relocs_checked=3): jal func_80010A08, %hi/%lo(D_800B9A02) via lhu (u16, * per the TU's own declaration at src/800.c:2655), %hi/%lo(D_800A651C) via * lw (the `.a` member at offset 0, 20-byte stride). */ extern void *func_80010A08(s32 a0); extern u16 D_800B9A02; void func_80015B6C(s32 x, s32 y, s32 w_, s32 h_, u8 c1, u8 c2, u8 c3, u8 c4, u8 c5, u8 c6) { extern struct { s32 a; s32 b[4]; } D_800A651C[]; /* block scope, like func_80016450's sibling at src/800.c:3454 */ s32 otp; u8 *p; register u32 mFF __asm__("$7"); s32 vx, vy; u32 pmask; register u32 tag2 __asm__("$6"); otp = D_800A651C[D_800B9A02].a; p = (u8 *)func_80010A08(0x34); mFF = 0xFF000000; p[3] = 1; *(u32 *)(p + 4) = 0xE1000000; *(u32 *)p = (*(u32 *)p & mFF) | (*(u32 *)(otp + 0x3FFC) & 0xFFFFFF); *(u32 *)(otp + 0x3FFC) = (*(u32 *)(otp + 0x3FFC) & mFF) | ((u32)p & 0xFFFFFF); p += 8; p[3] = 8; vx = x; p[7] = 0x38; __asm__ __volatile__(""); vy = y; __asm__("" : "=r"(x) : "0"(x)); __asm__("" : "=r"(y) : "0"(y)); p[4] = c1; p[5] = c2; p[6] = c3; p[0xC] = c1; p[0xD] = c2; p[0xE] = c3; p[0x14] = c4; p[0x15] = c5; p[0x16] = c6; p[0x1C] = c4; p[0x1D] = c5; p[0x1E] = c6; *(s16 *)(p + 8) = vx; *(s16 *)(p + 0xA) = vy; __asm__ __volatile__(""); tag2 = *(u32 *)p; x += w_; *(s16 *)(p + 0x10) = x; *(s16 *)(p + 0x12) = vy; *(s16 *)(p + 0x18) = vx; __asm__ __volatile__(""); y += h_; *(s16 *)(p + 0x1A) = y; *(s16 *)(p + 0x20) = x; *(s16 *)(p + 0x22) = y; pmask = (u32)p & 0xFFFFFF; *(u32 *)p = (tag2 & mFF) | (*(u32 *)(otp + 0x3FFC) & 0xFFFFFF); __asm__ __volatile__(""); tag2 = 0xE1000200; *(u32 *)(otp + 0x3FFC) = (*(u32 *)(otp + 0x3FFC) & mFF) | pmask; p += 0x24; p[3] = 1; *(u32 *)(p + 4) = tag2; *(u32 *)p = (*(u32 *)p & mFF) | (*(u32 *)(otp + 0x3FFC) & 0xFFFFFF); *(u32 *)(otp + 0x3FFC) = (*(u32 *)(otp + 0x3FFC) & mFF) | ((u32)p & 0xFFFFFF); } /* func_80015D4C — allocate an 0x18-byte flat-shaded quad GPU packet, fill its * tag/color/code bytes and the four (x,y) corners from (a0,a1,a2,a3) = * (x, y, w, h), then link it into the current double-buffer's OT at depth * arg7 via the open-coded PSY-Q `addPrim` RMW pair, and (only when arg8 < 4) * notify func_80016638. * * Sibling idiom: src/md_SC07_004/md_SC07_004.c func_801AADA8 (MATCHED) — * the addPrim pair over D_800A651C[bidx].a in ARRAY_REF form, and the * func_80016638(&D_800A6518[bidx*20], depth, flag) call form. * * SECOND-PASS FIXES (this file was NEAR/40 before; both were required): * * 1. BRANCH POLARITY WAS INVERTED. The target is * sltiu $t2, $s0, 0x4 ; beqz $t2, * i.e. the 0x2A code byte is written and func_80016638 is called when * arg8 < 4, not when arg8 >= 4. The first pass wrote `arg8 >= 4`, which * emits the same sltiu but `bnez` — a real semantic difference that the * instruction-shape diff hid inside the 40-instruction register cascade. * * 2. tag0 PINNED TO $4 ($a0) — the load-bearing regalloc fix. Residual was * a *uniform +1 register-slot shift*: target allocates the call-clobbered * pseudo set {a0,a1,a2,a3,t0,t1,t2,t3}, the draft allocated * {v1,a0,a1,a2,a3,t0,t1,t2}. Root cause: gcc's sched1 placed the tag * load (`lw , 0(p)`) *after* the last use of the x+w temp, so tag0 * could reuse $v1; the target schedules the tag load 14 insns earlier, so * the two live ranges overlap and tag0 must take $a0, pushing every later * pseudo up one slot. * Statement-order (lever A) is INERT here — sweeping the `tag0 = *(u32*)p` * statement through all 6 legal source positions between the p[7] * conditional and the addPrim pair gave 40 mismatched every time; sched1 * normalises the DAG regardless of source position. The register pin is * what changes the allocation, and pinning tag0 (the value whose slot * *causes* the cascade) is the right handle — this is exactly the lever * func_801AADA8's own header documents ("tag0 ... pinned to $4 ($a0) — * needed to reproduce the target's exact a2/a3/t0/t1 register picks for * the addPrim RMW pair"). * Pin sweep, for the record: $4 -> MATCH, $5 -> 9, $6 -> 16, $7 -> 16, * $8 -> 31. Note the first pass pinned temp_v0 instead ($8) and got a * spurious extra move — pinning the *consumer* of the cascade rather than * its cause (cookbook §175 lever B: pin the interloper, not the value you * care about; here the interloper is the tag word aliasing $v1). * * vx/vy stay pinned to $18/$17 ($s2/$s1) from the first pass — the target's * `addu $s2,$s3,$zero` / `addu $s1,$s4,$zero` corner-copy pair. * * Symbol audit (§174 law 1c, done after MATCH): every symbol re-checked * against the target .s relocation lines — jal func_80010A08, jal * func_80016638, %hi/%lo(D_800B9A02) via lhu (u16, not s16), %hi/%lo * (D_800A651C) via lw, %lo(D_800A6518) via addiu (address-of). Store sides * verified: `sw $a0,0($t0)` is the packet tag, `sw $v0,0($a1)` is the OT slot. * src/800.c declares none of these, so no TU-declaration conflict (law 2/3). */ extern void *func_80010A08(s32 a0); extern u16 D_800B9A02; extern u8 D_800A6518[]; extern void func_80016638(void *a0, s32 a1, s32 a2); void func_80015D4C(s32 a0, s32 a1, s32 a2, s32 a3, u8 arg4, u8 arg5, u8 arg6, u16 arg7, u16 arg8) { extern OtBlk_80016450 D_800A651C[]; /* block scope: engine_core.h's DEFINE_ macros declare this symbol scalar in their own bodies */ void *temp_v0; register s16 vx __asm__("$18"); register s16 vy __asm__("$17"); register u32 tag0 __asm__("$4"); s32 depth4; temp_v0 = func_80010A08(0x18); *(u8 *)((u8 *)temp_v0 + 3) = 5; *(u8 *)((u8 *)temp_v0 + 7) = 0x28; vx = a0; vy = a1; if (arg8 < 4) { *(u8 *)((u8 *)temp_v0 + 7) = 0x2A; } *(u8 *)((u8 *)temp_v0 + 4) = arg4; *(u8 *)((u8 *)temp_v0 + 5) = arg5; *(u8 *)((u8 *)temp_v0 + 6) = arg6; *(s16 *)((u8 *)temp_v0 + 8) = vx; *(s16 *)((u8 *)temp_v0 + 0xA) = vy; *(s16 *)((u8 *)temp_v0 + 0xC) = a0 + a2; *(s16 *)((u8 *)temp_v0 + 0xE) = vy; *(s16 *)((u8 *)temp_v0 + 0x10) = vx; tag0 = *(u32 *)temp_v0; *(s16 *)((u8 *)temp_v0 + 0x12) = a1 + a3; *(s16 *)((u8 *)temp_v0 + 0x14) = a0 + a2; *(s16 *)((u8 *)temp_v0 + 0x16) = a1 + a3; depth4 = arg7 * 4; *(u32 *)temp_v0 = (tag0 & 0xFF000000) | (*(u32 *)(depth4 + D_800A651C[D_800B9A02].a) & 0xFFFFFF); *(u32 *)(depth4 + D_800A651C[D_800B9A02].a) = (*(u32 *)(depth4 + D_800A651C[D_800B9A02].a) & 0xFF000000) | ((u32)temp_v0 & 0xFFFFFF); if (arg8 < 4) { func_80016638(&D_800A6518[D_800B9A02 * 20], arg7, arg8); } } /* func_80015F04 — main/800. Build a 4-segment closed LINE_F2 wireframe * rectangle: fill an 8-halfword stack corner list from (x, y, w, h) = * (a0, a1, a2, a3), allocate one 0x40-byte GPU block (4 x 0x10 LINE_F2), * then loop i = 0..3 writing one segment per packet (corner i -> corner i+1, * code 0x40, or 0x42 = semi-transparent when arg8 < 4) and linking each packet * into the current OT at depth arg7 with the open-coded PSY-Q addPrim RMW * pair. The i == 3 iteration reads pts[4] (one past the array — the original * code's own overrun); the segment is closed afterwards by rewriting the last * packet's (x1, y1) from pts[0]. Finally, when arg8 < 4, notify * func_80016638 with the env block. * * @class: MATCH — 131/131 byte-exact (relocation-masked), and * tools/reloc_identity.py AGREE (relocs_checked=6): every jal / %hi / %lo * names the same symbol the target .s names. * * =================================================================== * THE ENTRY-BLOCK FILLER PROBLEM AND THE THREE LEVERS THAT SOLVE IT * * The prologue is emitted as [addiu $sp][$v0=$a0][sw $s0][la $s0] then five * (save, stack-arg-load, FILLER) triples. The five fillers are exactly * A = `addiu $a0,$zero,0x40` (the func_80010A08 argument) * X0 = `addu $t0,$v0,$zero` (x0 = a0) * Y0 = `addu $v1,$a1,$zero` (y0 = a1) * X1 = `addu $v0,$v0,$a2` (a0 += a2) * Y1 = `addu $a1,$a1,$a3` (a1 += a3) * and the target order is A, X0, Y0, X1, Y1. All five tie at INSN_PRIORITY 1, * so the order is decided in sched1 by `rank_for_schedule`'s LUID tie-break and * by `adjust_priority`'s BIRTHING BOOST (sched.c:2507 / birthing_insn_p:2469 — * a `SET(REG,…)` whose dest is live and has REG_N_SETS == 1 is raised to * max_priority, dump tell `(7f000001)`). sched1 picks BACKWARD and emits in * reverse, so a boosted def is picked EARLY and therefore emitted LATE. * * Reading the `-dS` ready-list trace, the block resolves like this (AC probe): * T-19 pick Y1 (Y1 is the anti-dep gate on Y0) * T-20..T-24 the five boosted stack-arg loads * T-25 pick X1 (X1 is the anti-dep gate on X0: X1 writes the pseudo X0 * reads, so X0 cannot become ready until X1 is scheduled) * T-26 pick ? ready = {Y0, X0, A, base} * T-27 pick ? * T-28 A, T-29 base * Emission is the reverse of the pick order, so the TARGET needs Y0 picked at * T-26 and X0 at T-27. With both copies tying at priority 1 that falls out of * the LUID order (`x0 = a0;` written before `y0 = a1;`). Any boost on X0 * makes X0 win T-26 and the two swap — which is the entire residual the first * two passes were stuck on. * * LEVER 8 — PIN THE CALL ARGUMENT AND EMIT IT AS ASM. gcc sets a call's * argument registers immediately before the `jal`, so A always had the * HIGHEST LUID in the block, was always picked first, and was therefore * always emitted LAST (filler #5 instead of #1). The call cannot be moved * earlier — `flush_pending_lists` gives it a REG_DEP_ANTI on all eight `sh` * stores — so the fix is to give the `li` a LUID at its SOURCE position: * register s32 sz __asm__("$4"); * __asm__("addiu %0,$zero,0x40" : "=r"(sz)); * A plain `sz = 0x40;` does NOT work (cse copies REG_EQUAL 64 onto the * call-site no-op `(set $a0 $a0)` and flow dead-store-eliminates the early * set; `volatile` on the register var costs 25/131). Emitting the * instruction FROM the asm sticks: one RTL insn, the target's own bytes * 0x24040040, LUID at the source position, and the call's `$a0 = sz` * collapses to a deleted no-op move. Worth 2 instructions. * * LEVER 9 — KILL Y0's BOOST FOR FREE BY MERGING IT WITH `ob`. y0 and the * loop's second-addPrim OT base are BOTH $v1 with disjoint live ranges, so * they are ONE variable. That gives the pseudo a second SET, REG_N_SETS * becomes 2, and the boost dies at zero instruction cost. (This is why * there is no `y0` local below — `ob` carries both roles.) * * LEVER 10 (THE THIRD PASS'S LEVER, the 2 -> 0) — KILL X0's BOOST WITH A * RE-TIE THAT CARRIES A "memory" CLOBBER. * __asm__("" : "=r"(x0) : "0"(x0) : "memory"); * The §30 #3 re-tie gives x0 a second SET and kills its boost, which fixes * all five fillers — but a BARE re-tie then costs one swap somewhere else * (`sw $ra,0x38($sp)` and `sh $t0,0x10($sp)` trade places, probes ACq1-ACq6, * all 2/131). Reason: the bare asm's only ordering constraint is the * anti-dependence on the one store that reads the OLD x0, so sched2 is free * to treat it as a SIXTH entry-block filler; with six fillers for five * (save, load) triples the extra one drags `sh $t0,0x10($sp)` up past the * `sw $ra` save. * ADDING "memory" fixes exactly that: the clobber makes the asm a * memory-ordering barrier, so `sched_analyze` ties it to the whole pending * store list instead of to one register, it can no longer float into the * (save, load, filler) interleave, and the five real fillers keep the * triples to themselves. It is a pure scheduling fence — zero emitted * bytes, zero registers, no effect on allocation. * Byte-verified: the "memory" re-tie MATCHes at every placement from before * `pts[0].x` through before `pts[3].x` (probes Rm0-Rm6); the bare re-tie is * 2/131 at every one of those placements (Rp1-Rp6). The `volatile` variant * is 18/131 — do NOT reach for volatile here, the clobber is the lever. * * LEVERS 1-7 (inherited; each byte-verified by removal): * 1. `x0 = a0; ob = a1; a0 += a2; a1 += a3;` — the DESTRUCTIVE update of the * parameter is what forces the target's two extra copies and lets the sums * reuse the dying param registers. It is also load-bearing for the * LENGTH: writing `x1 = a0 + a2` into a fresh local makes that pseudo * single-set, gcc coalesces it with $a0, both copies are copy-propagated * away and the entry copy disappears — 129 ins (probe A), 130 even with * $t0/$v1 pins (probe B). The statement ORDER here is also load-bearing: * it is the LUID order that decides the T-25/T-26 picks above. All six * legal interleavings were measured; only this one is right. * 2. ONE local `cb` carries 3 / 0x40 / 0x42. Three separate literals are * three single-set pseudos and loop.c HOISTS the invariant `li 3` and * `li 0x40` out of the loop (2 extra preheader regs, +7 mismatched). * 3. `i = 0;` HOISTED OUT OF THE `for` HEADER, so `addu $a2,$zero,$zero` is * the first preheader insn as in the target. * 4. THE MASK LOCALS ARE ASSIGNED INSIDE THE LOOP so they become loop.c * movables hoisted AFTER the `addiu $t2,$sp,0x10` array-base movable — * the target's preheader order (i=0, &pts, m24, mFF). As explicit * preheader statements they land BEFORE $t2 instead (probes AK/AL). * 5. `register s32 i4 __asm__("$9")` — with the masks hoisted, m24 and i4 * tie in global-alloc and land swapped; pinning i4 to $t1 lets m24 take * $t0. * 6. ADDPRIM ASYMMETRY (inherited from the matched sibling func_80016450): * the FIRST half inlines the OT-slot address expression fully, the SECOND * half binds it. Symmetric shapes cost 20+ instructions. * 7. `register s32 ob __asm__("$3")` for the second half's OT base, so the * add is the target's self-accumulating `addu $v1,$t1,$v1`. * * SYMBOL AUDIT (§174 law 1c — match_one masks jal/HI16/LO16, so every symbol * was re-checked against this .s's own relocation lines; tools/reloc_identity.py * then confirmed all 6 relocations AGREE. The .s names exactly five relocated * symbols: D_800AF630, D_800A6518, D_800A651C, func_80010A08, func_80016638): * - `lui/addiu %hi/%lo(D_800AF630)` -> $s0 is the ONLY named data base; the * OT-buffer index is reached as `*(u16 *)(base + 0xA3D2)` * (`lui $at,0x1; addu $at,$s0,$at; lhu -0x5C2E($at)`; 0x10000-0x5C2E = * 0xA3D2, i.e. 0x800B9A02). D_800B9A02 IS declared in src/800.c and IS * used by the siblings, but it does NOT appear in THIS .s — do not * substitute it here. `lhu` => u16, not s16. * - `jal func_80010A08` with $a0 = 0x40; `jal func_80016638` with * $a0 = &D_800A6518[idx*20] (`lui %hi` + `addiu %lo` = address-of), * $a1 = arg7 ($s5 from 0x5C($sp)), $a2 = arg8 (`andi $s4,0xFFFF`). * - D_800A651C read with `lw %lo(...)($at)` = the `.a` member at offset 0; * the index is scaled *20 (`sll 2; addu; sll 2`), which is exactly * sizeof(OtBlk_80016450_80015F04) = {s32 a; s32 b[4]}. * - Store sides: `sw $v1,0($a3)` is the packet tag word ($a3 = p), * `sw $v0,0($v1)` is the OT slot. With $a1 = p + 0xE: `sb -0xB` = len(3), * `sb -0x7` = code(7), `sb -0xA/-0x9/-0x8` = r0/g0/b0 <- $s3/$s2/$s1 <- * 0x50/0x54/0x58 in that order; `sh -0x6/-0x4/-0x2/0x0` = x0/y0/x1/y1. * - Epilogue reloads pts[0] FROM THE STACK (`lhu 0x10($sp)`, `lhu 0x12`), * so the closing fixup must read `pts[0]`, never the x0/ob registers. * * TU CHECK (§174 laws 2/3) — src/800.c holds * `INCLUDE_ASM("asm/nonmatchings/800", func_80015F04);` at line 1166: * - There is NO prototype for func_80015F04 in src/800.c or in include/, and * src/800.c does not include shared/engine_core.h, so the parameter list * below is unconstrained (no DEF-side wall here). * - `extern void *func_80010A08(s32 a0);` (src/800.c:1119), * `extern void func_80016638(void *a0, s32 a1, s32 a2);` (:1122), * `extern u8 D_800A6518[];` (:1121), `extern u8 D_800AF630[];` (:930) and * the block-scope `extern OtBlk_80016450_80015F04 D_800A651C[];` (:1126) all * ALREADY exist at file scope ABOVE line 1166 and the declarations below * are copied to agree with them exactly. Duplicate compatible externs are * legal, so they may stay or go when banking. * * >>> INTEGRATION NOTE: src/800.c ALREADY defines * >>> typedef struct { s32 a; s32 b[4]; } OtBlk_80016450_80015F04; * >>> at FILE scope (src/800.c:1117, i.e. ABOVE this function's slot at 1166). * >>> DROP the duplicate typedef line below when banking — C89 rejects the * >>> redefinition. It is present here only so the draft compiles standalone * >>> under match_one. `Pt_80015F04` and `LineF2_80015F04` do not collide * >>> with anything in src/800.c. */ typedef struct { s32 a; s32 b[4]; } OtBlk_80016450_80015F04; /* DUPLICATE — see note */ typedef struct { u16 x, y; } Pt_80015F04; /* stack corner list */ typedef struct { /* 0x10 LINE_F2 */ u8 addr[3]; u8 len; u8 r0, g0, b0, code; s16 x0, y0; s16 x1, y1; } LineF2_80015F04; extern void *func_80010A08(s32); extern void func_80016638(void *a0, s32 a1, s32 a2); extern u8 D_800AF630[]; extern u8 D_800A6518[]; void func_80015F04(s32 a0, s32 a1, s32 a2, s32 a3, u8 arg4, u8 arg5, u8 arg6, u16 arg7, u16 arg8) { extern OtBlk_80016450_80015F04 D_800A651C[]; /* block scope: engine_core.h's DEFINE_ macros declare this symbol scalar in their own bodies */ Pt_80015F04 pts[4]; LineF2_80015F04 *p; u8 *base; u32 *otp; register s32 ob __asm__("$3"); /* LEVER 9: y0 AND the 2nd addPrim OT */ s32 i; /* base — one $v1 variable, two roles, */ register s32 i4 __asm__("$9"); /* so REG_N_SETS==2 and the boost dies */ s32 x0; register s32 sz __asm__("$4"); /* LEVER 8: the func_80010A08 argument, */ s32 semi; /* pinned to $a0 so its `li` gets a LUID */ s32 cb; /* at THIS source position, not at the jal */ register u32 m24; register u32 mFF; base = D_800AF630; /* LEVER 8. Emitting the argument load from asm (rather than `sz = 0x40;`) * is what stops cse folding REG_EQUAL 64 onto the call-site no-op move and * dead-store-eliminating this insn. Bytes are the target's own * 0x24040040 `addiu $a0,$zero,0x40`; the call's `$a0 = sz` becomes a * deleted no-op. Removing this line costs 2 instructions. */ __asm__("addiu %0,$zero,0x40" : "=r"(sz)); /* LUID order below is load-bearing — see the ready-list trace in the * header. x0 before ob, and each destructive update after its own copy. */ x0 = a0; ob = a1; a0 += a2; a1 += a3; /* LEVER 10. Second SET of x0 -> REG_N_SETS==2 -> no birthing boost, so * X0 stops out-ranking Y0 at the T-26 pick. The "memory" clobber is the * load-bearing half: without it the asm becomes a sixth entry-block filler * and drags `sh $t0,0x10($sp)` above `sw $ra,0x38($sp)` (2/131). With it * the asm is a store-ordering fence that cannot enter the * (save, load, filler) interleave. Emits nothing. */ __asm__("" : "=r"(x0) : "0"(x0) : "memory"); pts[0].x = x0; pts[0].y = ob; pts[1].x = a0; pts[1].y = ob; pts[2].x = a0; pts[2].y = a1; pts[3].x = x0; pts[3].y = a1; p = (LineF2_80015F04 *)func_80010A08(sz); i = 0; semi = (arg8 < 4); i4 = arg7 * 4; for (; i < 4; i++) { cb = 3; p->len = cb; cb = 0x40; p->code = cb; if (semi) { cb = 0x42; p->code = cb; } p->r0 = arg4; p->g0 = arg5; p->b0 = arg6; p->x0 = pts[i].x; p->y0 = pts[i].y; p->x1 = pts[i + 1].x; p->y1 = pts[i + 1].y; m24 = 0x00FFFFFF; mFF = 0xFF000000; *(u32 *)p = (*(u32 *)p & mFF) | (*(u32 *)(i4 + D_800A651C[*(u16 *)(base + 0xA3D2)].a) & m24); ob = D_800A651C[*(u16 *)(base + 0xA3D2)].a; otp = (u32 *)(i4 + ob); *otp = (*otp & mFF) | ((u32)p & m24); p++; } p[-1].x1 = pts[0].x; p[-1].y1 = pts[0].y; if (arg8 < 4) { func_80016638(&D_800A6518[*(u16 *)(base + 0xA3D2) * 20], arg7, arg8); } } /* Same shapes as the matched sibling func_80016450 (src/800.c) — a fixed * 0x18-byte POLY_F4 quad allocator + a single addPrim() into D_800A651C's OT * slot, then func_80016638. This one has no parameters: r0=g0=b0=0 (black), * code is always 0x2A (no branch), and the addPrim/func_80016638 call use * D_800A651C/D_800A6518 directly (matching the sibling's "else" arm shape, * but against D_800A651C instead of D_800AE7BC, and args (0,0) not (0,1)). * Field writes go through raw pointer arithmetic (not p->member) — a * struct-member store is MEM_IN_STRUCT_P and gcc-2.7.2's alias code treats * it as non-conflicting with the *(u32*)p full-word read, letting the * scheduler hoist that read arbitrarily early; plain casts keep everything * on the same alias footing so the load stays where source order puts it. */ extern void *func_80010A08(s32 a0); extern void func_80016638(void *a0, s32 a1, s32 a2); extern u16 D_800B9A02; extern u8 D_800A6518[]; void func_80016110(void) { extern OtBlk_80016450 D_800A651C[]; /* block scope: engine_core.h's DEFINE_ macros declare this symbol scalar in their own bodies */ void *p; u32 *ot; register u32 m24 __asm__("$6"); register u32 pv __asm__("$4"); p = func_80010A08(0x18); m24 = 0x00FFFFFF; *(u8 *)((u8 *)p + 3) = 5; *(u8 *)((u8 *)p + 7) = 0x2A; *(s16 *)((u8 *)p + 8) = *(s16 *)((u8 *)p + 0x10) = -160; *(s16 *)((u8 *)p + 0xC) = *(s16 *)((u8 *)p + 0x14) = 160; *(s16 *)((u8 *)p + 0xA) = *(s16 *)((u8 *)p + 0xE) = -120; *(u8 *)((u8 *)p + 4) = *(u8 *)((u8 *)p + 5) = *(u8 *)((u8 *)p + 6) = 0; *(s16 *)((u8 *)p + 0x12) = *(s16 *)((u8 *)p + 0x16) = 120; pv = *(u32 *)p; *(u32 *)p = (pv & 0xFF000000) | (*(u32 *)D_800A651C[D_800B9A02].a & m24); __asm__ __volatile__("" : "=r"(pv)); /* zero-byte 2nd SET: kills the sched1 birthing boost */ ot = (u32 *)D_800A651C[D_800B9A02].a; *ot = (*ot & 0xFF000000) | ((u32)p & m24); func_80016638(&D_800A6518[D_800B9A02 * 20], 0, 0); } /* Bank-safe form: NO file-scope typedef (src/800.c:92 already defines * OtBlk_80016450; a second identical typedef is a redefinition there). * The OT-block externs instead carry an inline structurally-identical * anonymous struct, which only yields a benign redecl warning in-TU and * compiles standalone here. File-scope externs below reproduce decls that * already exist in src/800.c (:553/:652/:653/:1255/:1429/:1432) purely so * this unit compiles alone; compatible redeclaration is warning-only. */ extern u16 D_800AF7BE; extern u16 D_800AF7BC; extern u8 D_800A6518[]; extern u16 D_800B9A02; extern void *func_80010A08(s32 a0); extern void func_80016638(void *a0, s32 a1, s32 a2); void func_80016224(s32 a0, s32 a1) { extern struct { s32 a; s32 b[4]; } D_800A651C[]; extern struct { s32 a; s32 b[4]; } D_800AE7BC[]; extern u8 D_800AE7B8[]; void *p; s16 xl; s16 xr; s16 yl; s16 yr; s32 idx; p = func_80010A08(0x18); *(u8 *)((u8 *)p + 3) = 5; *(u8 *)((u8 *)p + 7) = 0x2A; *(u8 *)((u8 *)p + 6) = a0; *(u8 *)((u8 *)p + 5) = a0; *(u8 *)((u8 *)p + 4) = a0; xl = -(s32)D_800AF7BC / 2; *(s16 *)((u8 *)p + 0x10) = xl; *(s16 *)((u8 *)p + 0x08) = xl; __asm__ __volatile__("" ::: "memory"); xr = D_800AF7BC / 2; *(s16 *)((u8 *)p + 0x14) = xr; *(s16 *)((u8 *)p + 0x0C) = xr; yl = -(s32)D_800AF7BE / 2; *(s16 *)((u8 *)p + 0x0E) = yl; *(s16 *)((u8 *)p + 0x0A) = yl; __asm__ __volatile__("" ::: "memory"); yr = D_800AF7BE / 2; idx = a1 & 0xFFFF; *(s16 *)((u8 *)p + 0x16) = yr; *(s16 *)((u8 *)p + 0x12) = yr; if (idx != 0) { register u32 m24 __asm__("$7") = 0x00FFFFFF; register u32 mFF __asm__("$8") = 0xFF000000; register s32 i4 __asm__("$6") = idx * 4; register u32 pv __asm__("$4"); pv = *(u32 *)p; *(u32 *)p = (pv & mFF) | (*(u32 *)(i4 + D_800A651C[D_800B9A02].a) & m24); i4 += D_800A651C[D_800B9A02].a; *(u32 *)i4 = (*(u32 *)i4 & mFF) | ((u32)p & m24); func_80016638(&D_800A6518[D_800B9A02 * 20], idx, 2); } else { register u32 m24 = 0x00FFFFFF; register u32 pv __asm__("$3"); u32 *ot; pv = *(u32 *)p; *(u32 *)p = (pv & 0xFF000000) | (*(u32 *)D_800AE7BC[D_800B9A02].a & m24); __asm__ __volatile__("" : "=r"(pv)); ot = (u32 *)D_800AE7BC[D_800B9A02].a; *ot = (*ot & 0xFF000000) | ((u32)p & m24); func_80016638(&D_800AE7B8[D_800B9A02 * 20], 0, 2); } } /* func_80016450 — main/800: allocate a 0x18-byte flat semi-transparent quad * (POLY_F4, code 0x2A) covering the whole 320x240 screen at brightness a0, * PSY-Q addPrim() it onto one of two OT tables selected by (u16)a1, then hand * the owning env block to func_80016638. * * @class: (was regalloc-order / sched) * @stuck: none — MATCH, 122/122 instructions, byte-exact under match_one, and * all 27 relocations hand-checked one-to-one against asm/nonmatchings/800/ * func_80016450.s (same offsets, same symbols, same order): func_80010A08, * 6x D_800B9A02, 2x D_800A651C, D_800A6518, 2x D_800AE7BC, D_800AE7B8, * func_80016638. Destination TU src/800.c already declares * `extern void func_80016450(s32 a0, s32 a1);` (L680) — adopted verbatim. * No other file-scope decl of any symbol used here exists in the * preprocessed TU, so these externs merge silently. * * --------------------------------------------------------------------------- * WHAT THIS FUNCTION ACTUALLY IS * * The and-0xFF000000 / and-0x00FFFFFF / or triple is the libgpu P_TAG 24-bit * `addr` BITFIELD, i.e. the PSY-Q macro pair * addPrim(ot, p) == setaddr(p, getaddr(ot)), setaddr(ot, p) * The macro argument `ot` is textually duplicated, which is why D_800B9A02 and * the OT-base load are each emitted TWICE per branch. Recognising that is what * fixes the instruction count; the rest is register/schedule steering. * * --------------------------------------------------------------------------- * LEVERS (each byte-verified by removing it and re-scoring) * * 1. CHAINED ASSIGNMENT reproduces the reversed store pairs. * `p->x0 = p->x2 = K` emits sh K,0x10 THEN sh K,0x8 and keeps K in ONE * constant register across both stores. Same for `p->r0=p->g0=p->b0=a0`. * (Inherited from the first pass — natural source order gives four separate * constant registers and the wrong store order.) * * 2. `*(u32 *)p`, NOT `p->tag`. A struct-member store does NOT kill a plain * global's CSE in gcc-2.7.2, so `p->tag` cached D_800B9A02/D_800A651C and * produced 110 ins instead of 122. A non-MEM_IN_STRUCT_P store kills * everything and reproduces all six reloads exactly. (First pass.) * * 3. ⚠ THE ONE THAT UNSTUCK THE 23-MISMATCH RESIDUAL — DO NOT bind the OT slot * address to a function-scope local used in BOTH addPrim halves. The first * pass wrote `ot = ; …*ot…; ot = ; *ot = …;` in each arm. One * pseudo with two def/use pairs = ONE live range spanning the whole arm, so * local-alloc gives it a DEDICATED register ($t0) and shifts every other * temp one slot down. The target instead computes the address into a * register that DIES on the same insn (`addu $v0,$a2,$v0` in the first half, * `addu $a2,$a2,$v0` in the second). The fix is the HYBRID shape used by * the sibling match func_80185944 (src/ov_SC03_119/..._jr_8017FB84.c): * - first half: the address expression FULLY INLINED (no local at all); * - second half: its own short-lived binding. * This alone rebuilt the whole `addu/lw` shape (23 -> the register-name * residual below). Note the first pass had measured "fully inlining" (45) * and "splitting it in two" (44) as WORSE — because it applied each to BOTH * halves. It is the ASYMMETRY that matches. * * 4. OPERAND ORDER IS SOURCE ORDER: `i4 + D_800A651C[…].a`, not * `D_800A651C[…].a + i4`. gcc-2.7.2 emits `addu $v0,$a2,$v0` from the * former and `addu $v0,$v0,$a2` from the latter. (13 -> ...) * * 5. SELF-ACCUMULATE TO REUSE THE DYING REGISTER. The second half's * `addu $a2,$a2,$v0` writes the OT address back into the SAME register that * held idx*4. A fresh local can never produce that (gcc will not clobber a * still-named pinned value), but `i4 += D_800A651C[…].a;` — reusing the * variable as its own accumulator — does, exactly. 9 -> 6. * * 6. THE ELSE ARM'S LAST 6 WERE A SCHEDULE SWAP, NOT ALLOCATION. Target fills * the `lhu D_800B9A02` load-delay slot with the tag load `lw $v1,0($t1)` * and the `lw %lo(D_800AE7BC)` slot with `lui $a0,0xff00`; we had them * exactly swapped, which also flipped D_800B9A02 $a0<->$v1. This is * gcc-2.7.2 sched.c's BIRTHING BOOST on a single-SET destination (the boost * sinks the load to just before its consumer, and `potential_hazard` breaks * the tie in the load's favour, so it is picked first = placed LAST). * Cure = the func_80185944 recipe, both halves needed: * (a) PIN the tag value (`register u32 pv __asm__("$3")`), and * (b) give `pv` a SECOND SET with a zero-byte * `__asm__ __volatile__("" : "=r"(pv));` — REG_N_SETS != 1 kills the * boost. A plain C reassignment does NOT work (cse/flow removes it). * PLACEMENT MATTERS: the dead asm must sit immediately after `pv`'s last * use INSIDE the arm. Putting it after the func_80016638 call (as the * sibling does, where the call is in the same block) makes gcc keep $v1 * live across the jal here and costs +3 instructions (125 ins, 53 diff). * 6 -> 0. * * PIN-TRIM LOG (all re-scored; three pins are the verified minimum): * drop `m24` $a3 pin (then arm) — MATCH (kept as plain `register`) * drop `mFF` $t0 pin (then arm) — MATCH (kept as plain `register`) * drop `m24` $a2 pin (else arm) — MATCH (kept as plain `register`) * all three dropped together — MATCH <= this file * replace the mask LOCALS with bare literals — 3 mismatched (the locals are * load-bearing: a literal is re-materialised per use and reschedules) * drop `i4` $a2 pin — 2 mismatched * drop `pv` $a0 pin (then arm) — 4 mismatched * drop `pv` $v1 pin (else arm) — 7 mismatched * pin `p` to $t1 — 123 ins, 113 mismatched (never pin p) */ /* engine_types.h-style OT block: 0x14 stride, the OT base pointer at +0 */ /* 0x18-byte POLY_F4 */ typedef struct { u8 addr[3]; u8 len; u8 r0, g0, b0, code; s16 x0, y0; s16 x1, y1; s16 x2, y2; s16 x3, y3; } F4_80016450; extern void *func_80010A08(s32); extern void func_80016638(void *a0, s32 a1, s32 a2); extern u16 D_800B9A02; extern u8 D_800A6518[]; extern OtBlk_80016450 D_800AE7BC[]; extern u8 D_800AE7B8[]; void func_80016450(s32 a0, s32 a1) { extern OtBlk_80016450 D_800A651C[]; /* block scope: engine_core.h's DEFINE_ macros declare this symbol scalar inside their own bodies */ F4_80016450 *p; u32 *ot; s32 idx; p = (F4_80016450 *)func_80010A08(0x18); p->len = 5; p->code = 0x2A; p->r0 = p->g0 = p->b0 = a0; p->x0 = p->x2 = -160; p->x1 = p->x3 = 160; p->y0 = p->y1 = -120; p->y2 = p->y3 = 120; idx = a1 & 0xFFFF; if (idx != 0) { register u32 m24 = 0x00FFFFFF; register u32 mFF = 0xFF000000; register s32 i4 __asm__("$6") = idx * 4; register u32 pv __asm__("$4"); pv = *(u32 *)p; *(u32 *)p = (pv & mFF) | (*(u32 *)(i4 + D_800A651C[D_800B9A02].a) & m24); i4 += D_800A651C[D_800B9A02].a; *(u32 *)i4 = (*(u32 *)i4 & mFF) | ((u32)p & m24); func_80016638(&D_800A6518[D_800B9A02 * 20], idx, 1); } else { register u32 m24 = 0x00FFFFFF; register u32 pv __asm__("$3"); pv = *(u32 *)p; *(u32 *)p = (pv & 0xFF000000) | (*(u32 *)D_800AE7BC[D_800B9A02].a & m24); __asm__ __volatile__("" : "=r"(pv)); /* zero-byte 2nd SET: kills the sched1 birthing boost */ ot = (u32 *)D_800AE7BC[D_800B9A02].a; *ot = (*ot & 0xFF000000) | ((u32)p & m24); func_80016638(&D_800AE7B8[D_800B9A02 * 20], 0, 1); } } extern void *func_80010A08(s32 a0); void func_80016638(void *a0, s32 a1, s32 a2) { void *s2 = a0; s32 s1 = a1; s32 s0 = a2; void *v0; register u32 mask1 __asm__("$5"); register u32 mask2 __asm__("$6"); register u32 pv __asm__("$4"); u32 v1; register u32 tmp __asm__("$2"); v0 = func_80010A08(8); mask1 = 0xFFFFFF; { u32 one = 1; s0 = (s0 << 5) & 0x9FF; *(u8 *)((u8 *)v0 + 3) = (u8)one; __asm__ __volatile__("" : "=r"(one) : "0"(one)); } s0 = s0 | 0xE1000000; s1 = s1 << 2; mask2 = 0xFF000000; *(u32 *)((u8 *)v0 + 4) = s0; /* First addPrim RMW: new primitive's link = OT slot's current low24, * primitive keeps its own top-byte tag. Mirrors func_80015D4C / * func_80016450's matched addPrim idiom in this same TU. */ v1 = *(u32 *)((u8 *)s2 + 4); pv = *(u32 *)v0; v1 = *(u32 *)(s1 + v1); pv = pv & mask2; v1 = v1 & mask1; pv = pv | v1; *(u32 *)v0 = pv; /* Second addPrim RMW: OT slot := (OT slot's own top-byte tag) | * (new primitive's address, masked to low24) — head-insert the new * primitive into the OT chain. */ v1 = *(u32 *)((u8 *)s2 + 4); s1 = s1 + v1; v1 = *(u32 *)s1; tmp = (u32)v0 & mask1; v1 = v1 & mask2; v1 = v1 | tmp; *(u32 *)s1 = v1; } void func_800166E8(void *a0, s32 a1) { s32 local_buffer[2]; register s32 v0 __asm__("$2"); register s32 v1 __asm__("$3"); __asm__ volatile(""); // Barrier to force stack allocation first if (a1 == 0) goto skip; v0 = a1 - 1; v1 = -1; do { *(u8 *)a0 = 0; v0--; a0 = (void *)((s32)a0 + 1); } while (v0 != v1); skip: return; } #ifdef NON_MATCHING /* bzero(p, n): n<4 byte path; else align-to-4 -> word-fill (sw) -> remainder bytes. * Logically correct and instruction-identical to the target EXCEPT gcc 2.7.2 emits a * phantom empty 16-byte stack frame here (target is frameless) — that one frame * prologue/epilogue is the entire residual. A decomp-permuter candidate (structural * permutation can flip frame allocation). See docs/matching-cookbook.md §4. */ void func_80016714(s8 *a0, s32 a1) { s32 v1; if (a1 < 4) { a1 = a1 - 1; if (a1 != -1) { do { *a0 = 0; a1 -= 1; a0 += 1; } while (a1 != -1); } } else { v1 = (s32)a0; if (((s32)a0 & 3) != 0) { do { *a0 = 0; a0 += 1; v1 += 1; a1 -= 1; } while ((v1 & 3) != 0); } v1 = (a1 >> 2) - 1; if (v1 != -1) { do { *(s32 *)a0 = 0; v1 -= 1; a0 += 4; } while (v1 != -1); } a1 = (a1 & 3) - 1; if (a1 != -1) { do { *a0 = 0; a1 -= 1; a0 += 1; } while (a1 != -1); } } } #else void func_80016714(void *ptr, s32 len) { unsigned char *p = (unsigned char *)ptr; register s32 v0 __asm__("$2"); s32 v1; if (len < 4) { v0 = -1; len--; if (len != v0) { do { p[0] = 0; p++; len--; } while (len != v0); } } else { // Align pointer to 4-byte boundary v1 = (s32)p; while ((v1 & 0x3) != 0) { p[0] = 0; p++; v1++; len--; } // Clear 4-byte words v1 = len >> 2; v1--; v0 = -1; if (v1 != v0) { do { *(unsigned int *)p = 0; p += 4; v1--; } while (v1 != v0); } // Clear remaining bytes len = len & 0x3; len--; v0 = -1; if (len != v0) { do { p[0] = 0; p++; len--; } while (len != v0); } } } #endif extern void func_80016224(s32 a0, s32 a1); extern u16 D_800AF7CE; void func_800167B8(s32 a0) { a0 = ((u16)a0) < 4U ? 0xFF : 0x00; D_800AF7CE = 0; func_80016224(a0, 0); } extern u8 D_800AF630[]; extern u8 D_80062BA0[]; extern s32 D_800B9A18; extern u16 D_800AF7CE; extern void func_80016224(s32 a0, s32 a1); s32 func_800167F0(s32 arg0) { register u32 zr __asm__("$0"); register s32 v1 __asm__("$3"); s32 x = arg0 + zr; u8 *s0 = D_800AF630; s32 mask = arg0 & 3; u32 raw; u32 a0; u32 v0; if (mask == 0) { v1 = D_80062BA0[D_800B9A18]; } else { v1 = D_80062BA0[mask]; } D_800AF7CE = D_800AF7CE + v1; __asm__ __volatile__("" ::: "memory"); raw = *(u16 *)&s0[0x19E]; a0 = raw + zr; if (a0 >= 0xFFU) { a0 = 0xFF; } v0 = ((x & 0xFFFFU) < 4U) ? 0xFF - a0 : a0; func_80016224(v0 & 0xFF, 0); return *(u16 *)&s0[0x19E] >= 0xFF; } extern u16 D_800AF7CE; void func_800168B4(void) { D_800AF7CE = 0; } extern u8 D_800AF630[]; extern u16 D_800AF7CE; extern s32 D_800B9A18; extern u8 D_80062BA0[]; s32 func_800168C4(register s32 arg0) { register s32 t __asm__("$3"); u8 *base = D_800AF630; arg0 &= 3; if (arg0 == 0) { t = D_80062BA0[D_800B9A18]; } else { t = D_80062BA0[arg0]; } D_800AF7CE += t; __asm__ __volatile__("" : : : "memory"); t = *(u16 *) (base + 0x19E); if (t >= 0xFFU) { return *(u16 *) (base + 0x188) != 0; } return 0; } extern u16 D_800AF7CE; extern void func_80016450(s32 a0, s32 a1); void func_80016940(s32 a0) { s32 param = (((u32)a0 & 0xFFFF) < 4) ? 0xFF : 0x00; D_800AF7CE = 0; func_80016450(param, 0); } extern u8 D_800AF630[]; extern u8 D_80062BA0[]; extern s32 D_800B9A18; extern u16 D_800AF7CE; extern void func_80016450(s32 a0, s32 a1); s32 func_80016978(s32 arg0) { register u32 zr __asm__("$0"); register s32 v1 __asm__("$3"); s32 x = arg0 + zr; u8 *s0 = D_800AF630; s32 mask = arg0 & 3; u32 raw; u32 a0; u32 v0; if (mask == 0) { v1 = D_80062BA0[D_800B9A18]; } else { v1 = D_80062BA0[mask]; } D_800AF7CE = D_800AF7CE + v1; __asm__ __volatile__("" ::: "memory"); raw = *(u16 *)&s0[0x19E]; a0 = raw + zr; if (a0 >= 0xFFU) { a0 = 0xFF; } v0 = ((x & 0xFFFFU) < 4U) ? 0xFF - a0 : a0; func_80016450(v0 & 0xFF, 0); return *(u16 *)&s0[0x19E] >= 0xFF; } void func_80016A3C(void *arg0) { func_80016A7C(arg0, 0); } void func_80016A5C(void) { func_80016A7C(); } typedef struct { u32 xy; /* 0x0: vx (lo16) | vy (hi16) */ s16 z; /* 0x4 */ s16 pad; /* 0x6 */ } SV_80016A7C; /* 0x8 */ typedef struct { SV_80016A7C v[2]; /* 0x00 */ u32 rgb0; /* 0x10 */ u32 rgb1; /* 0x14 */ u32 attr; /* 0x18 */ } Src_80016A7C; typedef struct { u32 tag; /* 0x00 */ u32 rgb0; /* 0x04 */ u32 xy0; /* 0x08 */ u32 rgb1; /* 0x0C */ u32 xy1; /* 0x10 */ } LineG2_80016A7C; /* 0x14 */ extern void *func_80010A08(s32 size); extern void SetLineG2(void *p); extern void func_80017E8C(s32 arg0); extern s32 RotTransPers3(void *v0, void *v1, void *v2, s32 *sxy0, s32 *sxy1, s32 *sxy2, s32 *p, s32 *flag); extern void func_80018094(void *p, s32 otz, u32 flags); void func_80016A7C(Src_80016A7C *arg0, s32 arg1) { LineG2_80016A7C *p; s32 opz; s32 flag; s32 otz; p = (LineG2_80016A7C *)func_80010A08(0x14); p->rgb0 = arg0->rgb0; p->rgb1 = arg0->rgb1; SetLineG2(p); if (arg1 != 0) { func_80017E8C(arg1); otz = RotTransPers3(&arg0->v[0], &arg0->v[1], &arg0->v[1], (s32 *)&p->xy0, (s32 *)&p->xy1, (s32 *)&p->xy1, &opz, &flag); } else { p->xy0 = arg0->v[0].xy; p->xy1 = arg0->v[1].xy; __asm__ volatile(""); otz = arg0->v[0].z; flag = 0; } if ((flag & ~0x1000) == 0) { func_80018094(p, otz, arg0->attr); } } typedef struct { s16 vx, vy, vz, pad; } SVECTOR_80016B60; typedef struct { SVECTOR_80016B60 v[3]; /* 0x00 */ u32 rgb0; /* 0x18 */ u32 rgb1; /* 0x1C */ u32 rgb2; /* 0x20 */ u32 flags; /* 0x24 */ } Src_80016B60; typedef struct { u32 tag; /* 0x00 */ u32 rgb0; /* 0x04 */ u32 xy0; /* 0x08 */ u32 rgb1; /* 0x0C */ u32 xy1; /* 0x10 */ u32 rgb2; /* 0x14 */ u32 xy2; /* 0x18 */ u32 otz; /* 0x1C */ } LineG3_80016B60; extern void *func_80010A08(s32); extern void SetLineG3(LineG3_80016B60 *p); extern void func_80017E8C(s32 arg0); extern s32 RotTransPers3(void *v0, void *v1, void *v2, s32 *sxy0, s32 *sxy1, s32 *sxy2, s32 *p, s32 *flag); extern void func_80018094(void *p, s32 otz, u32 flags); void func_80016B60(Src_80016B60 *arg0, s32 arg1) { LineG3_80016B60 *p; s32 flag; s32 otz_unused; s32 otz; p = (LineG3_80016B60 *)func_80010A08(0x20); p->rgb0 = arg0->rgb0; p->rgb1 = arg0->rgb1; p->rgb2 = arg0->rgb2; SetLineG3(p); func_80017E8C(arg1); otz = RotTransPers3(&arg0->v[0], &arg0->v[1], &arg0->v[2], (s32 *)&p->xy0, (s32 *)&p->xy1, (s32 *)&p->xy2, &otz_unused, &flag); if ((flag & ~0x1000) == 0) { func_80018094(p, otz, arg0->flags); } } /* func_80016C28 — src/800.c (main, -O2). * * Builds one LINE_G4 (0x28 bytes) from a "src" descriptor: * 0x00..0x1F four SVECTORs (v0..v3) * 0x20..0x2C four rgb+code words * 0x30 extra word passed through to func_80018094 as "flags" * Runs the GTE unconditionally (RTPT on v0..v2, RTPS on v3, AVSZ4 -> otz), * then hands the prim to func_80018094 iff the combined GTE flag word is * clean of everything except bit 0x1000. * * Mirrors the already-matched func_80017930 (same file) almost exactly — * same 4-SVECTOR layout, same gte_ld/rtp/stsxy/avsz4/stotz macro sequence * and ordering — just building a LINE_G4 (SetLineG4) instead of a * POLY_GT4 (SetPolyGT4), and calling SetLineG4 right after the alloc * (before the rgb copies) instead of after them, and with the GTE call * unconditional (no "if (arg1 != 0)" guard around func_80017E8C/GTE block). */ typedef struct { s16 vx, vy, vz, pad; } SV_80016C28; typedef struct { SV_80016C28 v[4]; /* 0x00 */ u32 rgb0; /* 0x20 */ u32 rgb1; /* 0x24 */ u32 rgb2; /* 0x28 */ u32 rgb3; /* 0x2C */ u32 flags; /* 0x30 */ } Src_80016C28; typedef struct { u32 tag; /* 0x00 */ u32 rgb0; /* 0x04 */ u32 xy0; /* 0x08 */ u32 rgb1; /* 0x0C */ u32 xy1; /* 0x10 */ u32 rgb2; /* 0x14 */ u32 xy2; /* 0x18 */ u32 rgb3; /* 0x1C */ u32 xy3; /* 0x20 */ u32 term; /* 0x24 */ } LineG4_80016C28; /* 0x28 */ extern void *func_80010A08(s32); extern void SetLineG4(LineG4_80016C28 *p); extern void func_80017E8C(s32 arg0); extern void func_80018094(void *p, s32 otz, u32 flags); #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_avsz4() __asm__ volatile ("nop;nop;avsz4") #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_stotz(r0) __asm__ volatile ( \ "swc2 $7, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stflg(r0) __asm__ volatile ( \ "cfc2 $12, $31;" \ "nop;" \ "sw $12, 0( %0 )" \ : \ : "r"( r0 ) \ : "$12", "memory" ) void func_80016C28(Src_80016C28 *arg0, s32 arg1) { LineG4_80016C28 *p; s32 flag; s32 flag2; s32 otz; p = (LineG4_80016C28 *)func_80010A08(0x28); SetLineG4(p); p->rgb0 = arg0->rgb0; p->rgb1 = arg0->rgb1; p->rgb2 = arg0->rgb2; p->rgb3 = arg0->rgb3; func_80017E8C(arg1); gte_ldv3(&arg0->v[0], &arg0->v[1], &arg0->v[2]); gte_rtpt(); gte_stflg(&flag); gte_stsxy3(&p->xy0, &p->xy1, &p->xy2); gte_ldv0(&arg0->v[3]); gte_rtps(); gte_stflg(&flag2); flag |= flag2; gte_stsxy(&p->xy3); gte_avsz4(); gte_stotz(&otz); if ((flag & ~0x1000) == 0) { func_80018094(p, otz, arg0->flags); } } /* Standalone draft for func_80016D78 (src/800.c, main, mass lane). * Fresh crack: sibling func_80016F18 in the same TU (already MATCHED) is the * FT4 / 4-vertex version of this exact shape (alloc via func_80010A08, * SetPolyFT4/FT3, GetTPage, GetClut, func_80018194). This function is the * FT3 / 3-vertex sibling: it always takes the GTE-hardware path (a single * unconditional RotTransPers3 call, no inline-GTE-macro branch) and always * passes tp=0 to GetTPage (no tpage-select branch either) -- matching the * target .s exactly (no branch until the final `sltiu`/`beqz` clut compare * and the trailing `bnez` flag test). */ /* 8-byte vertex entry, same shape as the matched sibling's V16F18. */ typedef struct { /* 0x0 */ u32 xy; /* 0x4 */ s16 z; /* 0x6 */ s16 pad; } V16D78; typedef union { u32 w; u16 h[2]; } UV16D78; /* input struct: 3 vertices + 3 uv words + rgbc + attr + clut byte */ typedef struct { /* 0x00 */ V16D78 v[3]; /* 0x18 */ UV16D78 uv[3]; /* 0x24 */ u32 rgbc; /* 0x28 */ u32 attr; /* 0x2C */ u8 clut; } S16D78; /* output POLY_FT3-shaped packet, 0x20 bytes, same field-by-field shape as * the matched sibling's FT4_16F18 (minus the 4th vertex). */ typedef struct { /* 0x00 */ u32 tag; /* 0x04 */ u32 rgbc; /* 0x08 */ u32 xy0; /* 0x0C */ u8 u0; /* 0x0D */ u8 v0; /* 0x0E */ u16 clut; /* 0x10 */ u32 xy1; /* 0x14 */ u8 u1; /* 0x15 */ u8 v1; /* 0x16 */ u16 tpage; /* 0x18 */ u32 xy2; /* 0x1C */ u8 u2; /* 0x1D */ u8 v2; /* 0x1E */ u16 pad0; } FT3_16D78; extern void *func_80010A08(s32); extern void SetPolyFT3(FT3_16D78 *); extern void func_80017E8C(s32); extern s32 RotTransPers3(void *v0, void *v1, void *v2, s32 *sxy0, s32 *sxy1, s32 *sxy2, s32 *p, s32 *flag); extern s32 GetTPage(s32, s32, s32, s32); extern s32 GetClut(s32, s32); extern void func_80018194(void *, s32, u32); void func_80016D78(S16D78 *arg0, s32 arg1) { FT3_16D78 *p; u32 t; u32 c; s32 otz_unused; s32 flag; s32 otz; p = (FT3_16D78 *)func_80010A08(0x20); p->rgbc = arg0->rgbc; SetPolyFT3(p); func_80017E8C(arg1); otz = RotTransPers3(&arg0->v[0], &arg0->v[1], &arg0->v[2], (s32 *)&p->xy0, (s32 *)&p->xy1, (s32 *)&p->xy2, &otz_unused, &flag); t = arg0->uv[0].w; p->u0 = t; p->v0 = t >> 16; t = arg0->uv[1].w; p->u1 = t; p->v1 = t >> 16; t = arg0->uv[2].w; p->u2 = t; p->v2 = t >> 16; p->tpage = GetTPage(0, (arg0->attr >> 28) & 3, (arg0->uv[0].h[0] >> 2) & 0x3FC0, arg0->uv[0].h[1] & 0xFF00); c = arg0->clut; p->clut = GetClut(c < 0xE0 ? 0x160 : 0x100, c | 0x100); if ((flag & ~0x1000) == 0) { func_80018194((void *)p, otz, (u32)arg0->attr); } } void func_80016EB0(void *arg0) { func_80016F18(arg0, 0, 1); } void func_80016ED4(void *arg0) { func_80016F18(arg0, 0, 0); } void func_80016EF8(void *arg0, s32 arg1) { func_80016F18(arg0, arg1, 0); } /* ---- PsyQ GTE inline macros (same spelling as the matched ov_SC03_* TUs) ---- */ #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_avsz4() __asm__ volatile ("nop;nop;avsz4") #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_stflg(r0) __asm__ volatile ( \ "cfc2 $12, $31;" \ "nop;" \ "sw $12, 0( %0 )" \ : \ : "r"( r0 ) \ : "$12", "memory" ) #define gte_stotz(r0) __asm__ volatile ( \ "swc2 $7, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) /* ---- types ---- */ typedef struct { /* 0x0 */ u32 xy; /* 0x4 */ s16 z; /* 0x6 */ s16 pad; } V16F18; /* 8 bytes */ typedef union { u32 w; u16 h[2]; } UV16F18; typedef struct { /* 0x00 */ V16F18 v[4]; /* 0x20 */ UV16F18 uv[4]; /* 0x30 */ u32 rgbc; /* 0x34 */ u32 attr; /* 0x38 */ u8 clut; } S16F18; typedef struct { /* 0x00 */ u32 tag; /* 0x04 */ u32 rgbc; /* 0x08 */ u32 xy0; /* 0x0C */ u8 u0; /* 0x0D */ u8 v0; /* 0x0E */ u16 clut; /* 0x10 */ u32 xy1; /* 0x14 */ u8 u1; /* 0x15 */ u8 v1; /* 0x16 */ u16 tpage; /* 0x18 */ u32 xy2; /* 0x1C */ u8 u2; /* 0x1D */ u8 v2; /* 0x1E */ u16 pad0; /* 0x20 */ u32 xy3; /* 0x24 */ u8 u3; /* 0x25 */ u8 v3; /* 0x26 */ u16 pad1; } FT4_16F18; extern void *func_80010A08(s32); extern void SetPolyFT4(FT4_16F18 *); extern void func_80017E8C(s32); extern s32 GetTPage(s32, s32, s32, s32); extern s32 GetClut(s32, s32); extern void func_80018194(void *, s32, u32); void func_80016F18(S16F18 *arg0, s32 arg1, s32 arg2) { s32 flag; s32 flag2; s32 otz; FT4_16F18 *p; u32 t; u32 c; p = (FT4_16F18 *)func_80010A08(0x28); p->rgbc = arg0->rgbc; SetPolyFT4(p); if (arg1 != 0) { func_80017E8C(arg1); gte_ldv3(&arg0->v[0], &arg0->v[1], &arg0->v[2]); gte_rtpt(); gte_stflg(&flag); gte_stsxy3(&p->xy0, &p->xy1, &p->xy2); gte_ldv0(&arg0->v[3]); gte_rtps(); gte_stflg(&flag2); flag |= flag2; gte_stsxy(&p->xy3); gte_avsz4(); gte_stotz(&otz); } else { p->xy0 = arg0->v[0].xy; p->xy1 = arg0->v[1].xy; p->xy2 = arg0->v[2].xy; p->xy3 = arg0->v[3].xy; flag = 0; otz = arg0->v[0].z; } t = arg0->uv[0].w; p->u0 = t; p->v0 = t >> 16; t = arg0->uv[1].w; p->u1 = t; p->v1 = t >> 16; t = arg0->uv[2].w; p->u2 = t; p->v2 = t >> 16; t = arg0->uv[3].w; p->u3 = t; p->v3 = t >> 16; if (arg2 != 0) { p->tpage = GetTPage(2, (arg0->attr >> 28) & 3, (arg0->uv[0].h[0] >> 2) & 0x3FC0, 0); } else { p->tpage = GetTPage(0, (arg0->attr >> 28) & 3, (arg0->uv[0].h[0] >> 2) & 0x3FC0, arg0->uv[0].h[1] & 0xFF00); } c = arg0->clut; p->clut = GetClut(c < 0xE0 ? 0x160 : 0x100, c | 0x100); if ((flag & ~0x1000) == 0) { func_80018194((void *)p, otz, (u32)arg0->attr); } } /* 8-byte vertex entry: vx/vy packed as one word (xy), then z, then pad. * Same layout convention as SVec3_800173DC above. */ typedef struct { u32 xy; /* 0x0: vx (lo16) | vy (hi16) */ s16 z; /* 0x4 */ s16 pad; /* 0x6 */ } SVec_80017168; /* 0x8 */ /* Source descriptor: three vertices + packed rgb/code word + trailing attr * word forwarded verbatim to func_80018094. */ typedef struct { SVec_80017168 v[3]; /* 0x00 */ u32 rgbc; /* 0x18 */ u32 attr; /* 0x1C */ } Src_80017168; /* POLY_F3 (libgpu), 0x14 bytes. */ typedef struct { u32 tag; /* 0x00 */ u32 rgbc0; /* 0x04 */ u32 xy0; /* 0x08 */ u32 xy1; /* 0x0C */ u32 xy2; /* 0x10 */ } F3_80017168; /* 0x14 */ extern void *func_80010A08(s32 size); extern void SetPolyF3(F3_80017168 *p); extern void func_80017E8C(s32 arg0); extern s32 RotTransPers3(void *v0, void *v1, void *v2, s32 *sxy0, s32 *sxy1, s32 *sxy2, s32 *p, s32 *flag); extern void func_80018094(void *p, s32 otz, u32 flags); void func_80017168(Src_80017168 *arg0, s32 arg1) { F3_80017168 *p; s32 flag; s32 otzp; s32 otz; p = (F3_80017168 *)func_80010A08(0x14); p->rgbc0 = arg0->rgbc; SetPolyF3(p); if (arg1 != 0) { func_80017E8C(arg1); otz = RotTransPers3(&arg0->v[0], &arg0->v[1], &arg0->v[2], (s32 *)&p->xy0, (s32 *)&p->xy1, (s32 *)&p->xy2, &otzp, &flag); } else { p->xy0 = arg0->v[0].xy; p->xy1 = arg0->v[1].xy; p->xy2 = arg0->v[2].xy; __asm__ volatile(""); otz = arg0->v[0].z; flag = 0; } if ((flag & ~0x1000) == 0) { func_80018094(p, otz, arg0->attr); } } void func_80017254(void *arg0) { func_80017294(arg0, 0); } void func_80017274(void) { func_80017294(); } /* func_80017294 (main, src/800.c) — mass-lane fresh crack. * * Builds one POLY_F4 (0x18 bytes) from a "quad descriptor" arg0: * 0x00..0x1F four SVECTOR-ish V0 entries (v0..v3), each { u32 xy; s16 z; s16 pad; } * 0x20 rgbc word (r0,g0,b0,code packed) * 0x24 flags word (passed straight through to func_80018094) * arg1 != 0 -> func_80017E8C(arg1) then RotTransPers4 projects v0..v3 into the * poly's xy0..xy3 and returns otz, with a separate `flag` out-param. * arg1 == 0 -> copy the xy words straight through, otz = arg0->v[0].z, flag = 0. * Finally, if (flag & ~0x1000) == 0, hand the poly to func_80018094(p, otz, flags). * * Structurally identical in shape to func_80017930/func_80016F18 in this same TU * (both already-matched siblings), and to func_8018F734 (src/ov_SC03_117/...) which * is the RotTransPers4-based POLY_G4 exemplar this draft's call shape is modeled on. */ extern void *func_80010A08(s32); extern void SetPolyF4(void *); extern void func_80017E8C(s32); /* src/800.c already declares this as (s32) at L1122/L1244 */ extern s32 RotTransPers4(void *, void *, void *, void *, s32 *, s32 *, s32 *, s32 *, s32 *, s32 *); extern void func_80018094(void *, s32, u32); typedef struct { u32 xy; s16 z; s16 pad; } V0_80017294; /* 8 bytes */ typedef struct { V0_80017294 v[4]; /* 0x00 */ u32 rgbc; /* 0x20 */ u32 flags; /* 0x24 */ } Src_80017294; typedef struct { u32 tag; u32 rgbc; u32 xy0; u32 xy1; u32 xy2; u32 xy3; } F4_80017294; /* 0x18 bytes, POLY_F4 layout */ void func_80017294(Src_80017294 *arg0, s32 arg1) { F4_80017294 *p; s32 otz; s32 flag; s32 opz; p = (F4_80017294 *)func_80010A08(0x18); p->rgbc = arg0->rgbc; SetPolyF4(p); if (arg1 != 0) { func_80017E8C(arg1); otz = RotTransPers4(&arg0->v[0], &arg0->v[1], &arg0->v[2], &arg0->v[3], (s32 *)&p->xy0, (s32 *)&p->xy1, (s32 *)&p->xy2, (s32 *)&p->xy3, &opz, &flag); } else { p->xy0 = arg0->v[0].xy; p->xy1 = arg0->v[1].xy; p->xy2 = arg0->v[2].xy; p->xy3 = arg0->v[3].xy; otz = arg0->v[0].z; /* zero-byte scheduling barrier: without it gcc-2.7.2's sched1 hoists * `flag = 0` into the load-delay slot after the xy3 load (sched1 * normalises statement order away, cf. func_80015D4C's note in this * same TU); the barrier keeps the store in its natural last position, * leaving a genuine nop in that slot as the target has. */ __asm__ __volatile__("" ::: "memory"); flag = 0; } if ((flag & ~0x1000) == 0) { func_80018094(p, otz, arg0->flags); } } void func_8001739C(void *arg0) { func_800173DC(arg0, 0); } void func_800173BC(void) { func_800173DC(); } /* SVECTOR-shaped vertex: vx/vy packed as one word (xy), then z, then pad. * Same layout convention as V16F18 in func_80016F18 (src/800.c). */ typedef struct { u32 xy; /* 0x0: vx (lo16) | vy (hi16) */ s16 z; /* 0x4 */ s16 pad; /* 0x6 */ } SVec3_800173DC; /* 0x8 */ /* Source descriptor: three vertices + three Gouraud colour words + a trailing * flags/attr word (only ever read, never as a vertex). */ typedef struct { SVec3_800173DC v0; /* 0x00 */ SVec3_800173DC v1; /* 0x08 */ SVec3_800173DC v2; /* 0x10 */ u32 rgbc0; /* 0x18 */ u32 rgbc1; /* 0x1C */ u32 rgbc2; /* 0x20 */ u32 attr; /* 0x24 */ } Src_800173DC; /* POLY_G3 (libgpu), 0x1C bytes. */ typedef struct { u32 tag; /* 0x00 */ u32 rgbc0; /* 0x04 */ u32 xy0; /* 0x08 */ u32 rgbc1; /* 0x0C */ u32 xy1; /* 0x10 */ u32 rgbc2; /* 0x14 */ u32 xy2; /* 0x18 */ } G3_800173DC; /* 0x1C */ extern void *func_80010A08(s32 size); extern void SetPolyG3(G3_800173DC *p); extern void func_80017E8C(s32 arg0); extern s32 RotTransPers3(void *v0, void *v1, void *v2, s32 *sxy0, s32 *sxy1, s32 *sxy2, s32 *p, s32 *flag); /* §183 TYPE-adopted-TU: src/800.c declares (and defines at 0x80018094) the * third parameter as u32; adopt the TU's spelling verbatim. arg0->attr is * already u32, so no cast is needed at the use site and codegen is unchanged. */ extern void func_80018094(void *a0, s32 a1, u32 a2); void func_800173DC(Src_800173DC *arg0, s32 arg1) { G3_800173DC *p; s32 otzp; s32 flg; s32 otz; p = (G3_800173DC *)func_80010A08(0x1C); p->rgbc0 = arg0->rgbc0; p->rgbc1 = arg0->rgbc1; p->rgbc2 = arg0->rgbc2; SetPolyG3(p); if (arg1 != 0) { func_80017E8C(arg1); otz = RotTransPers3(&arg0->v0, &arg0->v1, &arg0->v2, (s32 *)&p->xy0, (s32 *)&p->xy1, (s32 *)&p->xy2, &otzp, &flg); } else { p->xy0 = arg0->v0.xy; p->xy1 = arg0->v1.xy; p->xy2 = arg0->v2.xy; __asm__ volatile(""); otz = arg0->v0.z; flg = 0; } if ((flg & ~0x1000) == 0) { func_80018094(p, otz, arg0->attr); } } void func_800174DC(void *arg0) { func_8001751C(arg0, 0); } void func_800174FC(void) { func_8001751C(); } /* func_8001751C (POLY_GT3 builder) -- splat tags the target "Handwritten * function": hand-placed cop2 nops and delay-slot fills no -O2 C emits. * The GTE ops are written as single-statement inline asm (NO backslash- * continued macros, NO typedefs, externs block-scoped with the canonical * def-sigs) so the body is self-contained and liftable to any splice point * (cookbook recover_giant shape). * * Recovered semantics: alloc 0x28 POLY_GT3 via func_80010A08; copy the three * colour words; SetPolyGT3; if arg1: func_80017E8C + rtpt over the three * vertices (sxy -> p+8/0x14/0x20, IR0 -> sz, FLAG -> flag, SZ3>>2 -> otz), * else copy the xy words through, otz = (s16)v0.vz, sz = 0; split the three * uv words into u/v bytes; tpage/clut via GetTPage/GetClut; finally * if ((sz & ~0x1000) == 0) func_80018194(p, otz, attr). */ void func_8001751C(s32 *arg0, s32 arg1) { extern void *func_80010A08(s32); extern void SetPolyGT3(); extern void func_80017E8C(s32); extern s32 GetTPage(s32, s32, s32, s32); extern s32 GetClut(s32, s32); extern void func_80018194(void *, s32, u32); u8 *p; s32 *va1, *va2; u8 *d0, *d1, *d2; s32 sz, flag, otz; u32 t, c; p = (u8 *)func_80010A08(0x28); *(u32 *)(p + 4) = *(u32 *)(arg0 + 9); *(u32 *)(p + 16) = *(u32 *)(arg0 + 10); *(u32 *)(p + 28) = *(u32 *)(arg0 + 11); SetPolyGT3(p); if (arg1 != 0) { func_80017E8C(arg1); va1 = arg0 + 2; va2 = arg0 + 4; __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"(arg0), "r"(va1), "r"(va2)); __asm__ volatile ("nop;nop;rtpt"); d0 = p + 8; d1 = p + 20; d2 = p + 32; __asm__ volatile ( "swc2 $12, 0( %0 );" "swc2 $13, 0( %1 );" "swc2 $14, 0( %2 )" : : "r"(d0), "r"(d1), "r"(d2) : "memory"); __asm__ volatile ("swc2 $8, 0( %0 )" : : "r"(&sz) : "memory"); __asm__ volatile ( "cfc2 $12, $31;nop;sw $12, 0( %0 )" : : "r"(&flag) : "$12", "memory"); __asm__ volatile ( "mfc2 $12, $19;nop;sra $12, $12, 2;sw $12, 0( %0 )" : : "r"(&otz) : "$12", "memory"); } else { *(u32 *)(p + 8) = *(u32 *)(arg0 + 0); *(u32 *)(p + 20) = *(u32 *)(arg0 + 2); *(u32 *)(p + 32) = *(u32 *)(arg0 + 4); otz = *(s16 *)((s8 *)arg0 + 4); sz = 0; } t = *(u32 *)(arg0 + 6); *(p + 12) = t; *(p + 13) = t >> 16; t = *(u32 *)(arg0 + 7); *(p + 24) = t; *(p + 25) = t >> 16; t = *(u32 *)(arg0 + 8); *(p + 36) = t; *(p + 37) = t >> 16; *(u16 *)(p + 26) = GetTPage(((*(u32 *)(arg0 + 12)) >> 24) & 3, ((*(u32 *)(arg0 + 12)) >> 28) & 3, ((*(u16 *)((s8 *)arg0 + 24)) >> 2) & 0x3FC0, *(u16 *)((s8 *)arg0 + 26) & 0xFF00); c = *((u8 *)arg0 + 52); *(u16 *)(p + 14) = GetClut(c < 0xE0 ? 0x160 : 0x100, c | 0x100); if ((sz & ~0x1000) == 0) { func_80018194((void *)p, otz, *(u32 *)(arg0 + 12)); } } void func_800176F0(void *arg0) { func_80017778(arg0, 0, 1); } void func_80017714(void *arg0) { extern void func_80017778(); ((void (*)(void *, s32, s32))func_80017778)(arg0, 0, 0); } void func_80017738(void *a0, s32 a1) { func_80017778(a0, a1, 1); } void func_80017758(void *a0, s32 a1) { func_80017778(a0, a1, 0); } /* func_80017778 — src/800.c (main, -O2). * * Builds one POLY_G4 (0x24 bytes) from a "vector-quad" descriptor (arg0): * 0x00..0x1F four SVECTORs (v0..v3) * 0x20..0x2F four packed rgb+code words * 0x30 a flags-ish word forwarded verbatim to the submit call * arg1 != 0 -> run RotTransPers4 on v0..v3 (perspective project into the * packet's xy0..xy3), func_80017E8C(arg1) primes the matrix; * arg1 == 0 -> copy the already-projected xy words straight through and use * v0.vz as otz. * Then, unless the RotTransPers4 clip flag (bit 0x1000 masked off) is set, * hand the packet to func_80017F14 (arg2 != 0) or func_80018094 (arg2 == 0). * * Mirrors the already-matched sibling func_80017930 (same file) which builds * a POLY_GT4 the same way; here there is no uv/tpage/clut section and the GTE * work goes through the SDK helper RotTransPers4 instead of raw gte_* macros. */ extern void *func_80010A08(s32); extern void SetPolyG4(void *); extern void func_80017E8C(s32); extern s32 RotTransPers4(void *, void *, void *, void *, s32 *, s32 *, s32 *, s32 *, s32 *, s32 *); extern void func_80017F14(void *, s32, s32); extern void func_80018094(void *, s32, u32); typedef struct { s16 vx, vy, vz, pad; } SV_80017778; typedef struct { SV_80017778 v[4]; /* 0x00 */ u32 rgb[4]; /* 0x20 */ u32 flags; /* 0x30 */ } Src_80017778; typedef struct { u32 tag; u8 r0, g0, b0, code; s16 x0, y0; u8 r1, g1, b1, pad1; s16 x1, y1; u8 r2, g2, b2, pad2; s16 x2, y2; u8 r3, g3, b3, pad3; s16 x3, y3; } G4_80017778; void func_80017778(Src_80017778 *arg0, s32 arg1, s32 arg2) { G4_80017778 *p; s32 otz; s32 opz; s32 flag; p = (G4_80017778 *)func_80010A08(0x24); *(u32 *)&p->r0 = arg0->rgb[0]; *(u32 *)&p->r1 = arg0->rgb[1]; *(u32 *)&p->r2 = arg0->rgb[2]; *(u32 *)&p->r3 = arg0->rgb[3]; SetPolyG4(p); if (arg1 != 0) { func_80017E8C(arg1); otz = RotTransPers4(&arg0->v[0], &arg0->v[1], &arg0->v[2], &arg0->v[3], (s32 *)&p->x0, (s32 *)&p->x1, (s32 *)&p->x2, (s32 *)&p->x3, &opz, &flag); } else { *(u32 *)&p->x0 = *(u32 *)&arg0->v[0]; *(u32 *)&p->x1 = *(u32 *)&arg0->v[1]; *(u32 *)&p->x2 = *(u32 *)&arg0->v[2]; *(u32 *)&p->x3 = *(u32 *)&arg0->v[3]; otz = arg0->v[0].vz; flag = 0; } if ((flag & ~0x1000) == 0) { if (arg2 != 0) { func_80017F14(p, otz, arg0->flags); } else { func_80018094(p, otz, arg0->flags); } } } void func_800178C8(void *arg0) { func_80017930(arg0, 0, 1); } void func_800178EC(s32 a0) { func_80017930(a0, 0, 0); } void func_80017910(void *a0, s32 a1) { func_80017930(a0, a1, 0); } /* func_80017930 — src/800.c (main, -O2). * * Builds one POLY_GT4 (0x34 bytes) from a 0x48-byte "sprite/quad descriptor": * 0x00..0x1F four SVECTORs (v0..v3) * 0x20..0x2F four packed u/v words (u in bits 0..15, v in bits 16..31) * 0x30..0x3F four rgb+code words * 0x40 flag/tpage word (bits 24-25 = tp, bits 28-29 = abr) * 0x44 clut index byte * arg1 != 0 -> run the GTE (RTPT on v0..v2, RTPS on v3, AVSZ4 -> otz); * arg1 == 0 -> copy the xy pairs straight through and use v0.vz as otz. * Finally GetTPage/GetClut and hand the prim to func_8001820C (arg2 != 0) or func_80018194. */ extern void *func_80010A08(s32 size); extern void SetPolyGT4(void *p); extern void func_80017E8C(s32 arg0); extern s32 GetTPage(s32 tp, s32 abr, s32 x, s32 y); extern s32 GetClut(s32 x, s32 y); extern void func_8001820C(void *p, s32 otz, u32 flags); extern void func_80018194(void *p, s32 otz, u32 flags); typedef struct { s16 vx, vy, vz, pad; } SV_80017930; typedef struct { SV_80017930 v[4]; /* 0x00 */ u32 uv[4]; /* 0x20 */ u32 rgb[4]; /* 0x30 */ u32 flags; /* 0x40 */ u8 clut; /* 0x44 */ } Src_80017930; typedef struct { u32 tag; /* 0x00 */ u32 rgb0; /* 0x04 */ s16 x0, y0; /* 0x08 */ u8 u0, v0; /* 0x0C */ u16 clut; /* 0x0E */ u32 rgb1; /* 0x10 */ s16 x1, y1; /* 0x14 */ u8 u1, v1; /* 0x18 */ u16 tpage; /* 0x1A */ u32 rgb2; /* 0x1C */ s16 x2, y2; /* 0x20 */ u8 u2, v2; /* 0x24 */ u16 pad2; /* 0x26 */ u32 rgb3; /* 0x28 */ s16 x3, y3; /* 0x2C */ u8 u3, v3; /* 0x30 */ u16 pad3; /* 0x32 */ } GT4_80017930; /* 0x34 */ #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_avsz4() __asm__ volatile ("nop;nop;avsz4") #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_stotz(r0) __asm__ volatile ( \ "swc2 $7, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stflg(r0) __asm__ volatile ( \ "cfc2 $12, $31;" \ "nop;" \ "sw $12, 0( %0 )" \ : \ : "r"( r0 ) \ : "$12", "memory" ) void func_80017930(Src_80017930 *arg0, s32 arg1, s32 arg2) { s32 flag1; s32 flag2; s32 otz; GT4_80017930 *p; u32 w; u32 f; u32 c; p = (GT4_80017930 *)func_80010A08(0x34); p->rgb0 = arg0->rgb[0]; p->rgb1 = arg0->rgb[1]; p->rgb2 = arg0->rgb[2]; p->rgb3 = arg0->rgb[3]; SetPolyGT4(p); if (arg1 != 0) { func_80017E8C(arg1); gte_ldv3(&arg0->v[0], &arg0->v[1], &arg0->v[2]); gte_rtpt(); gte_stflg(&flag1); gte_stsxy3(&p->x0, &p->x1, &p->x2); gte_ldv0(&arg0->v[3]); gte_rtps(); gte_stflg(&flag2); flag1 |= flag2; gte_stsxy(&p->x3); gte_avsz4(); gte_stotz(&otz); } else { *(u32 *)&p->x0 = *(u32 *)&arg0->v[0]; *(u32 *)&p->x1 = *(u32 *)&arg0->v[1]; *(u32 *)&p->x2 = *(u32 *)&arg0->v[2]; *(u32 *)&p->x3 = *(u32 *)&arg0->v[3]; flag1 = 0; otz = arg0->v[0].vz; } w = arg0->uv[0]; p->u0 = w; p->v0 = w >> 16; w = arg0->uv[1]; p->u1 = w; p->v1 = w >> 16; w = arg0->uv[2]; p->u2 = w; p->v2 = w >> 16; w = arg0->uv[3]; p->u3 = w; p->v3 = w >> 16; f = arg0->flags; p->tpage = GetTPage((f >> 24) & 3, (f >> 28) & 3, (*(u16 *)&arg0->uv[0] >> 2) & 0x3FC0, *((u16 *)&arg0->uv[0] + 1) & 0xFF00); c = arg0->clut; p->clut = GetClut(c < 0xE0 ? 0x160 : 0x100, c | 0x100); if ((flag1 & ~0x1000) == 0) { if (arg2 != 0) { func_8001820C(p, otz, arg0->flags); } else { func_80018194(p, otz, arg0->flags); } } } /* func_80017B98 — main/800: build one 0x2C-byte GPU packet holding a PSY-Q SPRT * (code 0x64) preceded by an E1 draw-mode word, fill it from the caller's * 0x1C-byte sprite-request block (== engine_types.h T8016EC0C, the struct * func_8016EC0C fills before calling here), then link it into the current * double-buffer's OT at depth `otz` with the open-coded PSY-Q addPrim() RMW pair. * * Packet layout (prim = the 0x2C alloc; sp = (SPRT *)(prim + 4)): * prim+0x00 u32 tag = 0x05000000 (len 5, addr filled by addPrim) * sp +0x00 u32 tag = 0xE1000000 | tpage bits (GP0 E1 draw mode) * sp +0x04 r0,g0,b0,code = 0x64<<24 | b<<16 | g<<8 | b (NOTE: r0 == b0 == * src->b at +0x12; src->r at +0x10 is never read) * sp +0x08 x0,y0 = src->x - src->w/2, src->y - src->h/2 * sp +0x0C u0,v0,clut * sp +0x10 w,h * * @class: was regalloc-order + IOR-tree reassociation * @stuck: none — MATCH (128 ins), byte-exact under match_one. * * LEVERS (each verified by removing it and re-scoring): * 1. `sp->code = 0x64;` must sit AFTER the `sp->tag = ...` statement (51 -> 36). * Source order is the sched1 tie-break that lets the three tag operand loads * hoist above the code sb, which is what fixes the whole temp-register naming * of the tag chain. (cookbook §175 lever A: statement order.) * 2. THE CONSTANT 0x64000000 MUST BE A LOCAL VARIABLE, NOT A LITERAL. Written * literally, gcc-2.7.2's fold `associate` rewrites X | (Y | C) into * (X | C) | Y — it emits `or (b<<16), C` first, where the target emits * `or (g<<8), C` first. split_tree() only pulls a constant that is a DIRECT * operand, so binding it to `u32 code` (a VAR_DECL at tree level, folded back * to the same `lui` by RTL cse) leaves the source tree untouched. 51 -> 35 * with the correct or-tree. (`+ 0x64000000` also blocks the rewrite but * emits `addu` instead of `or` — 1 permanent mismatch.) * 3. `t = (n + 0x100) << 6;` must be its own local. Inline in the ternary, gcc * folds (n+0x100)<<6 | 0x16 into `sll n,6` + `ori 0x4016` (2 ins, wrong * shape); the shared temp keeps the target's addiu/sll/ori triple. * 4. The clut value needs its own local `cl` too — otherwise cse carries the * `sp = prim+4` equivalence into the join block and rewrites the store as * 0x12(prim) instead of the target's 0xE(sp). (1 ins.) * 5. The 0xFF000000 / 0x00FFFFFF masks must be LOCALS (m24/mFF), not literals: * as locals their `lui` is a join-block insn that delay-slot-fills the otz * clamp branch; as literals they are rematerialised per use and reorg * duplicates `sll d4,2` into the delay slot instead. Same lever as * func_80016450 in this file. * 6. `d4 += D_800A651C[...].a;` (self-accumulate) reproduces `addu $a1,$a1,$v0` * — the second addPrim half reusing the depth*4 register. Operand order * `d4 + D_800A651C[...].a` (not the reverse) gives `addu $v0,$a1,$v0`. * Both inherited from func_80016450's header. * 7. `*(u32 *)prim` / `*(u32 *)&sp->r0`, never a struct member, wherever the * store has to kill CSE — that is what forces the two reloads of * `s->flags` (0x14) and the two reloads of the D_800AF630 index. * * PIN-TRIM LOG (all re-scored; four pins are the verified minimum): * drop `m24` $6 pin — MATCH (dropped) * drop `mFF` $7 pin — MATCH (dropped) * drop both mask pins together — MATCH <= this file * drop `d4` $5 pin alone — MATCH, but 13/11/16 mismatched in every * combination with a mask pin dropped, so * it is kept (it is what makes otz land in * $a1 and the masks in $a2/$a3) * drop `pv` $4 pin — 4 mismatched * drop `bb` $5 pin — 5 mismatched * drop `pbase` $17 pin — 126 ins, 124 mismatched (D_800AF630 base * must live in a callee-saved reg) * pin `prim` to $8 — 28 mismatched (never pin prim; it lands * in $t0 by itself once d4/masks are pinned) * * SYMBOL AUDIT (§174 law 1c, done after MATCH) — every symbol re-checked against * asm/nonmatchings/800/func_80017B98.s's own relocation lines: * jal func_80010A08 (0x83AC) • %hi/%lo(D_800AF630) -> $s1 (0x8468/0x846C), * dereferenced at $s1+0x10000-0x5C2E == 0x800B9A02, the same u16 frame index * src/boot.c reads as `*(u16 *)(p + 0xA3D2)` • %hi/%lo(D_800A651C) via `lw`, * twice (0x8514/0x851C and 0x8558/0x8560). No other relocations exist. * Store sides: `sw $a0,0($t0)` is the packet tag, `sw $v0,0($a1)` is the OT * slot, `sw $v1,0x8($t0)`/`sw $v0,4($a0)` are both the r0g0b0code word. * Load sides: the colour word takes 0x12 ($a1) TWICE (low byte and byte 2) and * 0x11 ($v0) once — src->r at 0x10 is genuinely never read by this function. * * BANK NOTE (§174 law 2): src/800.c already defines * typedef struct { s32 a; s32 b[4]; } OtBlk_80016450; (file scope) * and both func_80015D4C and func_80016450 declare * extern OtBlk_80016450 D_800A651C[]; (block scope) * This draft reuses THAT name so the third block-scope extern agrees with them * (a differently-named but structurally identical typedef only produces * "type mismatch with previous external decl" warnings, but agreeing is free). * ==> DROP the OtBlk_80016450 typedef line below when banking into src/800.c; * it is present only so this file compiles standalone under match_one. * src/800.c also already declares `extern void *func_80010A08(s32 a0);` (L694) * — adopted verbatim. D_800AF630 is not declared anywhere in that TU nor in any * header it includes, so the `extern u8 D_800AF630[];` below is the canonical * repo-wide form (src/boot.c L14/L37, every ov_* sibling). * src/800.c contains no prototype for func_80017B98 itself, so the definition's * `void func_80017B98(void *arg0)` is unconstrained (law 3 clear); it matches the * callers' `extern void func_80017B98(void *a0);`. */ /* DROP WHEN BANKING — already defined at file scope in src/800.c */ /* PSY-Q SPRT laid at prim+4; its `tag` word doubles as the E1 draw-mode command */ typedef struct { /* 0x00 */ u32 tag; /* 0x04 */ u8 r0, g0, b0, code; /* 0x08 */ s16 x0, y0; /* 0x0C */ u8 u0, v0; /* 0x0E */ u16 clut; /* 0x10 */ u16 w, h; } Sprt_80017B98; /* the caller's request block == engine_types.h T8016EC0C */ typedef struct { /* 0x00 */ u16 x; /* 0x02 */ u16 y; /* 0x04 */ s32 otz; /* 0x08 */ u16 w; /* 0x0A */ u16 h; /* 0x0C */ u16 u; /* 0x0E */ u16 v; /* 0x10 */ u8 r; /* 0x11 */ u8 g; /* 0x12 */ u8 b; /* 0x13 */ u8 pad13; /* 0x14 */ u32 flags; /* 0x18 */ u8 tp; /* 0x19 */ u8 pad19[3]; } Src_80017B98; extern void *func_80010A08(s32 a0); extern u8 D_800AF630[]; void func_80017B98(void *arg0) { extern OtBlk_80016450 D_800A651C[]; /* block scope: matches func_80015D4C / func_80016450 in this TU */ Src_80017B98 *s = (Src_80017B98 *)arg0; u8 *prim; Sprt_80017B98 *sp; register u8 *pbase __asm__("$17"); u32 n; u32 t; u32 m24; u32 mFF; register u32 pv __asm__("$4"); register s32 d4 __asm__("$5"); prim = (u8 *)func_80010A08(0x2C); *(u32 *)prim = 0x05000000; sp = (Sprt_80017B98 *)(prim + 4); sp->tag = ((s->flags >> 23) & 0x60) | ((s->v >> 4) & 0x10) | ((s->u >> 8) & 0xF) | ((s->v & 0x200) << 2) | 0xE1000000; sp->code = 0x64; { u32 code = 0x64000000; register u32 bb __asm__("$5") = s->b; *(u32 *)&sp->r0 = (bb << 16) | ((s->g << 8) | code) | bb; } sp->x0 = s->x - (s->w >> 1); sp->y0 = s->y - (s->h >> 1); sp->u0 = s->u; pbase = D_800AF630; sp->v0 = s->v; n = s->tp; t = (n + 0x100) << 6; { u16 cl = (n < 0xE0) ? (t | 0x16) : (t | 0x10); sp->clut = cl; } sp->w = s->w; sp->h = s->h; if (s->flags & 0x40000000) { *(u32 *)&sp->r0 = *(u32 *)&sp->r0 | 0x02000000; } d4 = s->otz; if (d4 >= 0x1000) { d4 = 0xFFF; } m24 = 0x00FFFFFF; mFF = 0xFF000000; d4 = d4 * 4; pv = *(u32 *)prim; *(u32 *)prim = (pv & mFF) | (*(u32 *)(d4 + D_800A651C[*(u16 *)(pbase + 0xA3D2)].a) & m24); d4 += D_800A651C[*(u16 *)(pbase + 0xA3D2)].a; *(u32 *)d4 = (*(u32 *)d4 & mFF) | ((u32)prim & m24); } void func_80017D98(void *a0) { s16 val = 0x1000; s16 *p = (s16 *)a0; p[0] = val; p[1] = 0; p[2] = 0; p[3] = 0; p[4] = val; p[5] = 0; p[6] = 0; p[7] = 0; p[8] = val; } typedef struct { /* 0x00 */ u16 unk00; /* 0x02 */ u16 unk02; /* 0x04 */ u16 unk04; /* 0x06 */ u16 unk06; /* 0x08 */ u16 unk08; /* 0x0A */ u16 unk0A; /* 0x0C */ u16 unk0C; /* 0x0E */ u16 unk0E; /* 0x10 */ u16 unk10; } Dst17DC4; void func_80017DC4(u16 *a0, Dst17DC4 *a1) { a1->unk00 = a0[0]; a1->unk02 = 0; a1->unk04 = 0; a1->unk06 = 0; a1->unk08 = a0[1]; a1->unk0A = 0; a1->unk0C = 0; a1->unk0E = 0; a1->unk10 = a0[2]; } extern void func_8004978C(s16 *a0, void *a1); extern void func_80048EAC(void *a0, void *a1); void func_80017DF8(s16 *a0, void *a1) { s16 temp[16]; func_8004978C(a0, temp); func_80048EAC(temp, a1); } extern void func_80049CAC(s32 a0, s32 a1); extern void func_80048EAC(void *a0, void *a1); void func_80017E30(s32 a0, s32 a1) { s32 local_buf[8]; /* 0x20 bytes at sp+0x10 */ func_80049CAC(a0, (s32)local_buf); func_80048EAC(local_buf, (void *)a1); } void func_80017E68(short *arg0, int *arg1) { arg1[5] = arg0[0]; arg1[6] = arg0[1]; arg1[7] = arg0[2]; } typedef struct { s32 w[8]; } Blk32_80017E8C; extern u8 D_800AF648; extern void CompMatrix(void *, void *, void *); extern void func_8004914C(void *); extern void func_800491AC(void *); void func_80017E8C(s32 arg0) { Blk32_80017E8C m; m = *(Blk32_80017E8C *)arg0; CompMatrix(&D_800AF648, &m, &m); func_8004914C(&m); func_800491AC(&m); } /* func_80017F14 · m12 · cache-bust resubmission of the converged body. * Semantics identical to the wave_m10/shard4 leftover; text is fresh so any * hash-keyed verdict cache must re-gate rather than replay. Full ledger in * submit notes: guard covers ONLY the |=2; both tpage sequences unconditional; * sig unified to the TU's own (void*, s32, s32); anon inline struct = the * OtBlk_80016450 layout; scalar rival spelling measured worse (99 ins). */ void func_80017F14(void *a0, s32 a1, s32 a2) { extern u16 D_800B9A02; extern struct { s32 a; s32 b[4]; } D_800A651C[]; extern void *func_80010A08(s32); extern s32 AddPrim(s32, void *); extern s32 GetTPage(s32, s32, s32, s32); extern s32 func_8005A600(s32, s32, s32, s32, s32); void *p2; s32 sz; s32 tp; /* sz clamp: >= 0x1000 saturates to 0xFFF (sltiu/bnez/addiu triple) */ sz = a1; if ((u32)sz >= 0x1000) { sz = 0xFFF; } p2 = func_80010A08(0xC); tp = GetTPage(2, ((u32)a2 >> 28) & 3, 0x280, 0); func_8005A600((s32)p2, 0, 0, (u16)tp, 0); AddPrim(D_800A651C[D_800B9A02].a + sz * 4, p2); AddPrim(D_800A651C[D_800B9A02].a + sz * 4, a0); /* guard covers ONLY the code |=2 — the second tpage packet follows * UNCONDITIONALLY (beqz target sits before its jal func_80010A08) */ if ((u32)a2 & 0x40000000) { *(u8 *)((u8 *)a0 + 7) = *(u8 *)((u8 *)a0 + 7) | 2; } p2 = func_80010A08(0xC); tp = GetTPage(2, ((u32)a2 >> 28) & 3, 0x280, 0); func_8005A600((s32)p2, 0, 1, (u16)tp, 0); AddPrim(D_800A651C[D_800B9A02].a + sz * 4, p2); } extern void *func_80010A08(s32); extern s32 AddPrim(s32, void *); extern s32 GetTPage(s32, s32, s32, s32); extern s32 func_8005A600(s32, s32, s32, s32, s32); extern u16 D_800B9A02; void func_80018094(void *a0, s32 a1, u32 a2_p) { s32 a2 = (s32)a2_p; extern OtBlk_80016450 D_800A651C[]; void *p2; s32 sz; s32 tp; sz = a1; if ((u32)sz >= 0x1000) { sz = 0xFFF; } AddPrim(D_800A651C[D_800B9A02].a + sz * 4, a0); if (a2 & 0x40000000) { *(u8 *)((u8 *)a0 + 7) = *(u8 *)((u8 *)a0 + 7) | 2; p2 = func_80010A08(0xC); tp = GetTPage(2, ((u32)a2 >> 28) & 3, 0x280, 0); func_8005A600((s32)p2, 0, 0, (u16)tp, 0); AddPrim(D_800A651C[D_800B9A02].a + sz * 4, p2); } } void func_80018194(void *arg0, s32 arg1, u32 arg2) { extern s32 AddPrim(s32, void *); extern u16 D_800B9A02; extern struct { s32 a; s32 b[4]; } D_800A651C[]; void *a3; s32 sz; a3 = arg0; if (arg2 & 0x40000000) { *(u32 *)((u8 *)a3 + 4) |= 0x02000000; } sz = arg1; if ((u32)sz >= 0x1000) { sz = 0xFFF; } AddPrim(D_800A651C[D_800B9A02].a + sz * 4, a3); } void func_8001820C(void *a0, s32 a1, u32 a2) { extern void *func_80010A08(s32); extern s32 AddPrim(s32, void *); extern s32 GetTPage(s32, s32, s32, s32); extern s32 func_8005A600(s32, s32, s32, s32, s32); extern u16 D_800B9A02; extern OtBlk_80016450 D_800A651C[]; void *p2; s32 sz; s32 tp; sz = a1; if ((u32)a2 & 0x40000000) { *(u32 *)((u8 *)a0 + 4) = *(u32 *)((u8 *)a0 + 4) | 0x02000000; } if ((u32)sz >= 0x1000) { sz = 0xFFF; } p2 = func_80010A08(0xC); tp = GetTPage(2, ((u32)a2 >> 28) & 3, 0x280, 0); func_8005A600((s32)p2, 0, 0, (u16)tp, 0); AddPrim(D_800A651C[D_800B9A02].a + sz * 4, p2); AddPrim(D_800A651C[D_800B9A02].a + sz * 4, a0); p2 = func_80010A08(0xC); tp = GetTPage(2, ((u32)a2 >> 28) & 3, 0x280, 0); func_8005A600((s32)p2, 0, 1, (u16)tp, 0); AddPrim(D_800A651C[D_800B9A02].a + sz * 4, p2); } extern u32 D_800AE610; void func_80018384(void *a0, void *a1) { u32 *a2 = (u32 *)a0; u32 *a3 = (u32 *)a1; if ((u32)&D_800AE610 < (u32)a2) { for (;;) ; } *a3 = (*a3 & 0xFF000000) | (*a2 & 0x00FFFFFF); *a2 = (*a2 & 0xFF000000) | ((u32)a3 & 0x00FFFFFF); } extern u8 D_800AF630[]; s32 func_800183E0(s32 a0) { Base *b = (Base *)D_800AF630; s32 i; s32 ret; ret = 0; i = 0; do { if (b->rec[i].flag == 0) { b->rec[i].flag = 1; b->rec[i].id = a0; b->rec[i].d = 0; ret = 1; goto out; } i++; } while (i < 0x20); out: return ret; } s32 func_80018450(void *a0, s32 a1) { extern u8 D_800AF630[]; Base *b = (Base *)D_800AF630; s32 i; s32 ret; ret = 0; i = 0; do { if (b->rec[i].flag != 0) { i++; } else { b->rec[i].flag = 1; b->rec[i].id = a1; b->rec[i].d = 0; b->rec[i].x = *(u16 *)((u8 *)a0 + 0x28); b->rec[i].y = *(u16 *)((u8 *)a0 + 0x2A); b->rec[i].c = *(u8 *)((u8 *)a0 + 0x27); ret = 1; break; } } while (i < 0x20); return ret; } typedef struct { u16 x; u16 y; u16 w; u16 h; } SpadRect_800184F0; typedef struct { /* 0x00 */ u16 unk00; /* 0x02 */ u16 unk02; /* 0x04 */ u16 x; /* 0x06 */ u16 y; /* 0x08 */ u16 w; /* 0x0A */ u16 h; /* 0x0C */ void *data; /* 0x10 */ s32 unk10; /* 0x14 */ s32 unk14; /* 0x18 */ s32 unk18; /* 0x1C */ s32 unk1C; } TexInfo_800184F0; extern DispSlot_800184F0 D_800B97D8[]; extern void func_800599B8(SpadRect_800184F0 *rect, void *data); extern void func_80053D44(void *data, TexInfo_800184F0 *out); extern void func_80058890(void *data, s32 a1, s32 a2, s32 a3, s32 a4, s32 a5, s32 a6); void func_800184F0(void) { SpadRect_800184F0 *rect = (SpadRect_800184F0 *)0x1F800000; TexInfo_800184F0 tmp; DispSlot_800184F0 *e; DispItem_800184F0 *p; s32 i; s32 done; s32 t; for (i = 0, e = D_800B97D8; i < 32; i++, e++) { if (e->flag == 0) { continue; } p = e->list; done = 0; do { t = p->type; if (t == 9) { goto b9; } if (t < 10) { if (t == 0) { goto b0; } if (t == 5) { goto b5; } done = 1; goto cont; } if (t == 11) { goto b11; } if (t < 11) { goto cont; } goto dflt; b9: if (e->unk0D == 0) { rect->x = p->x; rect->y = p->y; goto tail; } rect->x = ((u32)e->unk0C > 0x20) << 8; rect->y = e->unk0C | 0x1E0; goto tail; b0: if (e->unk0D == 0) { rect->x = p->x; rect->y = p->y; goto tail; } rect->x = e->unk08; rect->y = e->unk0A; tail: rect->w = p->w; rect->h = p->h; func_800599B8(rect, p->data); goto cont; b5: func_80053D44(p->data, &tmp); rect->x = tmp.x; rect->y = tmp.y; rect->w = tmp.w; rect->h = tmp.h; func_800599B8(rect, tmp.data); goto cont; b11: func_80058890(p->data, 1, 0, (s16)p->x, (s16)p->y, (s16)p->w * 2, (s16)p->h); goto cont; dflt: done = 1; cont: p++; } while (!done); e->flag = 0; } } extern unsigned int lzss_state; /* 0x800C7D24 */ extern unsigned char *lzss_outPtr; /* 0x800747AC */ /* LZSS sector-state reset: state = 1 (fresh), outPtr = destination buffer. * Sole caller: CdReadSectorReadyCB PAC-type-4 case (passes cdReq_dest). * K&R unprototyped definition ON PURPOSE: compatible with the stale * `extern void func_80018714(void);` further down the TU, while still * reading $a0. Types spelled as builtins so this compiles both * standalone and inside src/800.c (u32==unsigned int, u8==unsigned char). */ void func_80018714(arg0) void *arg0; { lzss_state = 1; lzss_outPtr = (unsigned char *)arg0; } /* LZSS streaming sector decompressor (resumable coroutine state machine). * Decodes up to 0x800 input bytes per call out of an LZSS stream into a 0x400-byte * scratchpad ring (lzss_ringBuffer @ 0x1F800000) and to lzss_outPtr. lzss_state is the * resume point (0=idle/done, 1=fresh, 2=token loop, 3=have code low byte, 4=advance bit). * Returns 1 if the input sector was consumed mid-stream (resume next call), 0 at the * stream terminator (back-ref offset 0) or when idle. */ /* MATCHED — asm-differ score 0, full-binary SHA1 green (Phase 7, session F, 2026-06-15). * Five constructs below are LOAD-BEARING for the byte-for-byte match against gcc 2.7.2 -O2; a * future reader who "cleans them up" WILL break the match. Each is annotated inline and the * compiler-internal root cause is in docs/matching-cookbook.md §10 (+ §5a). All five were * ground-truthed against the pinned gcc-2.7.2 source (reorg.c, jump.c, local-alloc.c). */ extern u8 lzss_curMask; /* 0x800747A0 */ extern u8 lzss_curToken; /* 0x800747A4 */ extern u8 *lzss_outPtr; /* 0x800747AC */ extern u32 lzss_ringIndex; /* 0x800747B0 */ extern u16 lzss_partialCode; /* 0x800747B4 */ extern u32 lzss_state; /* 0x800C7D24 */ s32 LzssDecodeSector(u8 *src) { s32 count = 0x800; u32 ringIdx = lzss_ringIndex; u8 mask = lzss_curMask; u8 token = lzss_curToken; u8 *out = lzss_outPtr; u16 code = lzss_partialCode; u8 *ring = (u8 *)0x1F800000; /* scratchpad ring; original uses the literal (lui 0x1f80), not the symbol */ u32 readIdx; s32 len; u8 b; u8 nb; /* low-byte (state-3) source byte */ u8 cb; u8 nh; /* LOAD-BEARING #1 (residual A): a SEPARATE high-byte source var, NOT a reuse of `nb`. * `nb` is shared between the low- and high-byte paths; reusing it couples their * register allocation, so any high-byte reshaping shoves `nb` out of $v1. A distinct * `nh` decouples them. Cookbook §10/A. */ s32 newState; s32 result; /* return-1 value carried in $v0 from each save predecessor (state-3/state-4) */ /* LOAD-BEARING #2 (residual B3): NO `default:` case and NO statement after the switch. The * switch's own range check (`sltiu $v0,state,5; beqz $v0,`) is the ONLY state>=5 * guard; the `beqz` REUSES the sltiu result ($v0==0 when state>=5) as the return-0 value by * threading straight to `jr ra`. An explicit `if(state>=5)return 0;`, a `default:`, or a * trailing `return 0;` each makes gcc emit a separate `move $v0,$zero` (+1 insn / wrong branch * target). Falling off the end here is deliberate (and matches the original). Cookbook §10/B3. */ switch (lzss_state) { case 0: goto term_ret; case 1: ringIdx = 1; mask = 1; token = *src++; count--; case 2: for (;;) { if (token & mask) { b = *src++; ring[ringIdx] = b; ringIdx = (ringIdx + 1) & 0x3FF; count--; *out++ = b; } else { nb = *src++; count--; code = (code & 0xFF00) | nb; if (count != 0) { goto have_low; } /* LOAD-BEARING #3 (residual B): `result` is set in the state-3 PREDECESSOR (here, * before `goto save`), not inside the shared `save:` tail. This makes gcc emit a * distinct `li $v0,1` per return site instead of cross-jumping/sinking one copy into * the tail's branch delay slot (reorg.c fill_simple_delay_slots). Cookbook §10/B. */ result = 1; newState = 3; goto save; have_low: case 3: nh = *src++; count--; /* LOAD-BEARING #1 (residual A), cont.: operand order `(code & 0xFF) | (nh << 8)`, * NOT `(nh<<8)|(code&0xFF)`. gcc local-alloc.c combine_regs ties a commutative OR's * result to the FIRST RTL operand that dies; writing `code & 0xFF` first ties the * result to $v0 (the target reg) → `or $v0,$v0,$v1`. Cookbook §10/A. */ code = (code & 0xFF) | (nh << 8); readIdx = code & 0x3FF; if (readIdx == 0) { lzss_state = 0; term_ret: return 0; } len = (code >> 10) + 2; while (len != 0) { cb = ring[readIdx]; readIdx = (readIdx + 1) & 0x3FF; ring[ringIdx] = cb; ringIdx = (ringIdx + 1) & 0x3FF; *out++ = cb; len--; } } if (count != 0) { goto next_bit; } result = 1; /* state-4 save predecessor — see LOAD-BEARING #3 */ newState = 4; save: lzss_state = newState; lzss_ringIndex = ringIdx; lzss_curMask = mask; lzss_curToken = token; lzss_outPtr = out; lzss_partialCode = code; return result; next_bit: case 4: if (mask == 0x80) { mask = 1; token = *src++; count--; if (count == 0) { /* state-2 reload save. The original keeps this as a SEPARATE copy of the 6 * stores (not shared with `save:`). Two more load-bearing constructs: */ register s32 r __asm__("$2"); /* LOAD-BEARING #4 (residual B, state-2): an explicit $v0 local pinned early by a * read-only input-asm. This forces `li $v0,1` to materialise BEFORE the stores * (the target schedules the return value first; gcc otherwise, since `li $v0,1`'s * only use is the shared epilogue, defers it to just before `jr ra`). Cookbook §10/B. */ r = 1; __asm__ __volatile__("" : : "r"(r)); newState = 2; lzss_state = newState; lzss_ringIndex = ringIdx; lzss_curMask = mask; lzss_curToken = token; lzss_outPtr = out; lzss_partialCode = code; /* LOAD-BEARING #5 (the cross-jump barrier): a zero-byte volatile asm. gcc 2.7.2 * -O2 jump.c find_cross_jump would otherwise MERGE this save with the identical * `save:` tail (→111 insns); an ASM_INPUT node makes it bail (lose=1) so both * survive (→122, the correct count). No -fno-crossjumping before gcc 3.3. Cookbook §5a. */ __asm__ __volatile__("" ::: "memory"); return r; } } else { mask <<= 1; } } } } extern u8 D_80078D98; extern u32 D_800AE610; extern u8 D_80078DA0; extern void ResetCallback(void); extern void PadInitDirect(void *a0, void *a1); extern void PadStartCom(void); void func_80018918(void) { u8 *p; u32 i; u32 w; i = 0; w = 8; p = &D_80078D98; D_800AE610 = 0; do { func_80016714(p, 0x4C); *(u32 *)(p + 4) = w; i++; p += 0x4C; } while (i < 2); func_80018FC8(); ResetCallback(); PadInitDirect(&D_80078DA0, &D_80078DA0 + 0x4C); PadStartCom(); } extern s32 func_80018A20(s32 arg0); extern void func_80018C64(void *a0); extern void func_80018CE8(u8 *p); extern void func_80018E9C(s32 a0); extern u8 D_80078D98; extern s32 D_800A5E78; void func_800189A8(void) { u32 i; u8 *p; D_800A5E78 = 1; i = 0; p = &D_80078D98; do { if (func_80018A20((s32)i) != 0) { func_80018C64(p); } func_80018CE8(p); func_80018E9C((s32)p); i++; p += 0x4C; } while (i < 2); } extern u8 D_80078D98; extern u32 D_800AE610; extern s32 D_800A5E78; extern u8 D_80062BAC; extern u8 D_80062BB4; extern u8 D_80062BBC; extern u8 D_80062BBD; extern u8 D_800747B8; extern u8 D_800747B9; extern s32 PadGetState(s32); extern s32 PadInfoMode(s32, s32, s32); extern void PadSetActAlign(s32, void*); extern void PadSetAct(s32, void*, s32); extern s32 func_80028D58(void); extern void func_80018F88(void*); extern void func_80018FC8(void); s32 func_80018A20(s32 arg0) { u8* p; s32 chan; p = (u8*)((u32)&D_80078D98 + arg0 * 76); switch (arg0) { case 0: chan = 0; break; case 1: chan = 16; break; } switch (PadGetState(chan)) { case 0: if (arg0 == 0) { if (D_800AE610++ >= 10) { D_800AE610 = 0; D_800A5E78 = 0; } } /* fallthrough */ case 1: func_80018F88(p); return 0; case 2: if (arg0 != 0) { return 1; } D_800AE610 = 0; if (PadInfoMode(chan, 2, 0) != 0) { return 1; } if (func_80028D58() == 0) { if (p[0x4A] | p[0x4B]) { D_80062BBD = 1; } else { D_80062BBD = 0; } p[0x4B] = 0; p[0x4A] = 0; } else { func_80018FC8(); } PadSetAct(chan, &D_80062BBC, 2); PadSetActAlign(chan, &D_80062BAC); return 1; case 6: if (arg0 != 0) { return 1; } D_800AE610 = 0; if (PadInfoMode(chan, 2, 0) == 0) { return 1; } if (func_80028D58() == 0) { D_800747B8 = p[0x4A]; D_800747B9 = p[0x4B]; p[0x4B] = 0; p[0x4A] = 0; } else { func_80018FC8(); } PadSetAct(chan, &D_800747B8, 2); PadSetActAlign(chan, &D_80062BB4); return 1; } } void func_80018C64(void *a0) { s32 i; u8 *q; u8 *p = (u8 *)a0; if (p[8] != 0) { func_80018F88(a0); return; } p[0] = p[9]; i = 0; q = &p[0x2B]; while (i < 2) { *q = ~*(p + i + 10); i++; q--; } i = 2; while (i < 6) { *(p + i + 0x2A) = *(p + i + 10); i++; } } extern void func_80016714(void *a0, s32 a1); extern void func_80018F20(s32 param_1); extern u8 D_80078DE4; void func_80018CE8(u8 *p) { s32 i; if (p == &D_80078DE4) { func_80016714(p, 0x4C); return; } switch (*p) { case 'A': for (i = 0; i < 2; i++) { (p + i)[0x42] = (p + i)[0x32]; (p + i)[0x3A] = (p + i)[0x2A] & ((p + i)[0x2A] ^ (p + i)[0x32]); (p + i)[0x32] = (p + i)[0x2A]; } return; case 'S': for (i = 0; i < 2; i++) { (p + i)[0x42] = (p + i)[0x32]; (p + i)[0x3A] = (p + i)[0x2A] & ((p + i)[0x2A] ^ (p + i)[0x32]); (p + i)[0x32] = (p + i)[0x2A]; } for (; i < 8; i++) { (p + i)[0x42] = (p + i)[0x32]; (p + i)[0x32] = (p + i)[0x2A]; } func_80018F20((s32) p); return; case 's': for (i = 0; i < 2; i++) { (p + i)[0x42] = (p + i)[0x32]; (p + i)[0x3A] = (p + i)[0x2A] & ((p + i)[0x2A] ^ (p + i)[0x32]); (p + i)[0x32] = (p + i)[0x2A]; } for (; i < 8; i++) { (p + i)[0x42] = (p + i)[0x32]; (p + i)[0x32] = (p + i)[0x2A]; } func_80018F20((s32) p); return; } func_80016714(p, 0x4C); } void func_80018E78(void) { extern void func_800291A0(s32, s32); func_800291A0(0xE2, 0); } extern u8 D_80078D98; extern u8 D_80078DCA; extern s32 func_800291B4(s32); extern void func_800291A0(s32, s32); extern void func_80028D78(s32); void func_80018E9C(s32 a0) { s32 s0; if (a0 == (s32)&D_80078D98) { if (*(u16 *)&D_80078DCA & 0x100) { s0 = func_800291B4(0xE2) + 1; if ((u32)(s0 & 0xFF) >= 0x5A) { s0 = 0; func_80028D78(1); } func_800291A0(0xE2, s0 & 0xFF); } else { func_800291A0(0xE2, 0); } } } void func_80018F20(s32 param_1) { u32 uVar3; s32 iVar2; u32 uVar1; uVar3 = 2; do { iVar2 = param_1 + uVar3; uVar1 = *(u8 *)(iVar2 + 0x32); if (uVar1 - 0x51 < 0x5f) { *(u8 *)(iVar2 + 0x32) = 0x80; } else if ((uVar1 & 0xff) < 8) { *(u8 *)(iVar2 + 0x32) = 0; } else if ((uVar1 & 0xff) >= 0xf8) { *(u8 *)(iVar2 + 0x32) = 0xff; } uVar3 = uVar3 + 1; } while (uVar3 < 8); } void func_80018F88(void* arg0) { int i; unsigned char* p; i = 1; p = (unsigned char*)arg0 + 1; do { p[0x2A] = 0; i--; p--; } while (i >= 0); for (i = 2; i < 6; i++) { ((unsigned char*)arg0 + i)[0x2A] = 0x80; } } extern u8 D_80078D98; extern u8 D_80062BBC; extern u8 D_80062BBD; extern u8 D_800747B8; extern u8 D_800747B9; void func_80018FC8(void) { u8 *p; s32 i; D_800747B9 = 0; D_800747B8 = 0; D_80062BBC = 0x40; D_80062BBD = 0; for (i = 0; i < 2; i++) { p = (u8 *)&D_80078D98 + i * 0x4C; p[0x4B] = 0; p[0x4A] = 0; } } extern s32 D_800747C8; void func_80019018(void) { D_800747C8 = 0; } extern s32 D_800747C8; void func_80019028(void) { D_800747C8 = 1; } extern void func_80016714(void *a0, s32 a1); extern s32 D_800747C0; void func_8001903C(void) { func_80016714(&D_800747C0, 8); } extern s32 D_800747C8; extern u8 D_800B9A64; extern s32 D_800747C0; extern u8 D_800747C4; extern u8 D_800747C5; void func_80019064(void *a0) { if (D_800747C8 != 0) return; if (D_800B9A64 != 0) return; D_800747C0 = a0; D_800747C4 = 0xFF; D_800747C5 = 0; } extern s32 D_800747C0; extern u8 D_800747C4; extern u8 D_800747C5; extern u8 D_80078DE2; extern u8 D_80078DE3; void func_800190AC(void) { s32 *p = &D_800747C0; u8 last; if (*p == 0) { return; } last = D_800747C5; if (last == 0) { u8 *q; s32 idx; idx = D_800747C4 + 1; D_800747C4 = idx; idx = idx & 0xff; q = (u8 *)((idx * 3) + D_800747C0); D_80078DE2 = q[1]; D_80078DE3 = q[0]; D_800747C5 = q[2]; if (D_800747C5 == 0) { D_80078DE2 = 0; D_80078DE3 = 0; D_800747C4 = 0xFF; *p = 0; } } else { u8 *r; u8 t; r = (u8 *)((D_800747C4 * 3) + D_800747C0); D_80078DE2 = r[1]; t = r[0]; D_800747C5 = last - 1; D_80078DE3 = t; } } extern s32 D_800747C0; s32 func_80019198(void) { return D_800747C0; } extern s32 D_80078E30; void func_800191A8(void) { D_80078E30 = -1; } extern s32 D_80078E30; void func_800191BC(s16 arg0) { D_80078E30 = arg0; } s32 func_800191D4(s32 a0) { extern s32 D_80078E30; extern s32 func_8001923C(); s32 *p = &D_80078E30; if (*p < 0) { return 1; } if (func_8001923C(a0) == 0) { return *p != a0; } return 1; } s32 func_8001923C(s32 a0) { if (a0 == 0xB3) return 0; if (a0 == 0xB4) return 0; if (a0 == 0x12E) return 0; if (a0 == 0x12F) return 0; if (a0 == 0x150) return 0; if (a0 == 0x152) return 0; if (a0 == 0x16E) return 0; if (a0 == 0x16F) return 0; if (a0 == 0xB9) return 0; if (a0 == 0xA1) return 0; if (a0 == 0xA2) return 0; if (a0 == 0xA3) return 0; if (a0 == 0x10B) return 0; if (a0 == 0x10C) return 0; if (a0 == 0x10D) return 0; if (a0 == 0x10F) return 0; if (a0 == 0x110) return 0; if (a0 == 0x111) return 0; if (a0 == 0x112) return 0; if (a0 == 0x113) return 0; if (a0 == 0x114) return 0; if (a0 == 0xAA) return 0; if (a0 == 0x115) return 0; if (a0 == 0x116) return 0; if (a0 != 0x117) return a0 != 0x118; return 0; } extern s32 D_800BA1B4; extern s32 D_800747E4; extern s32 D_800747E8; extern void func_8002D4C8(s32 a0, s32 a1); /* stream-state reset: clears loader phase 5 */ void func_8001931C(void) { if (D_800BA1B4 == 5) { if (D_800747E4 != 0) { func_8002D4C8(0x16, 0); } D_800BA1B4 = 0; D_800747E8 = 0; D_800747E4 = 0; } } extern s32 debugBinPresent; s32 func_80019378(void) { return debugBinPresent; } extern s32 debugBinPresent; void func_80019388(s32 arg0) { debugBinPresent = arg0; } extern s32 D_800747D4; s32 func_80019398(void) { return D_800747D4; } extern s32 D_800747D4; void func_800193A8(s32 arg0) { D_800747D4 = arg0; } extern void LoaderResetReadState(void); extern int VSync(int mode); extern s32 CdReadRequest(int *cdlFile, void *dest, s32 size, s32 mode); extern u8 D_80062C38; /* CdPathTable[0].file; entries stride 0x30 */ extern u8 D_80062C68; /* CdPathTable[1].file.pos (CdlLOC), stride 0x30 */ extern u8 D_80062DE8; /* CdPathTable[9].file; second entry at +0x210 */ extern s32 debugBinPresent; extern s32 D_80063074; extern s32 D_800747D8; extern u8 cdFileLocTable[]; /* {CdlLOC pos; u32 size} per sub-file, 8B stride */ extern u8 D_800AE834; /* cdFileLocTable + 4 (the size field) */ /* LIST.CD staging buffer @0x80180000 (== listCdBuffer). The original source used the * raw address, not the symbol: every reference is a bare `lui reg,0x8018` (one insn), * which is what a literal produces (`&listCdBuffer` would emit lui+addiu). */ #define LIST_BUF 0x80180000 void func_800193B8(s32 arg0) { CdlLOC loc; u8 hdr[0x90]; register s32 sector __asm__("$18"); s32 rdy; register s32 tmp __asm__("$3"); register u8 *q __asm__("$16"); register u8 *r __asm__("$17"); register int i __asm__("$23"); int base; int n; int fileIdx; int bufIdx; register int j __asm__("$19"); register int outer __asm__("$23"); int locOff; u8 *tbl; u8 *lbv; int dstOff; int srcOff; s32 *pCount; s32 *pOff; CdlLOC *p; register CdlLOC *pp __asm__("$5"); LoaderResetReadState(); sector = arg0 + 1; debugBinPresent = 0; D_80063074 = 0; D_800747D8 = 0; CdIntToPos(sector, &loc); do { rdy = CdReadRequest((int *)&loc, (void *)LIST_BUF, 0x800, 0); if (rdy != 0) { break; } VSync(0); } while (1); memcpy(hdr, (void *)LIST_BUF, 0x88); tmp = sector + 1; sector = tmp + ((*(u32 *)(hdr + 0x1C) + 0x7FF) >> 11); CdIntToPos(sector, &loc); do { rdy = CdReadRequest((int *)&loc, (void *)LIST_BUF, 0x800, 0); if (rdy != 0) { break; } VSync(0); } while (1); i = 2; sector += 1; q = &D_80062C38; pp = (CdlLOC *)q; /* sched fence: forces the `a1 = q` copy to be scheduled before the loop-pointer * init; without it gcc's list scheduler emits `addiu s1,s0,0x60` first. */ __asm__ ("" : "=r"(q) : "0"(q)); r = q + 0x60; CdIntToPos(sector + *(s32 *)(LIST_BUF + 0x00), pp); CdIntToPos(sector + *(s32 *)(LIST_BUF + 0x08), (CdlLOC *)(q + 0x30)); do { pp = (CdlLOC *)r; r += 0x30; i++; CdIntToPos(sector + *(s32 *)(LIST_BUF + 0x10), pp); } while (i < 9); q = &D_80062DE8; CdIntToPos(sector + *(s32 *)(LIST_BUF + 0x18), (CdlLOC *)q); CdIntToPos(sector + *(s32 *)(LIST_BUF + 0x20), (CdlLOC *)(q + 0x210)); do { rdy = CdReadRequest((int *)&D_80062C38, (void *)LIST_BUF, 0xE40, 0); if (rdy != 0) { break; } VSync(0); } while (1); tbl = cdFileLocTable; lbv = (u8 *)LIST_BUF; fileIdx = 0; bufIdx = 0; outer = 0; locOff = 0; pCount = (s32 *)LIST_BUF; do { base = CdPosToInt((CdlLOC *)(&D_80062C68 + locOff)); n = *pCount; pCount += 2; bufIdx++; j = 0; if (n > 0) { p = (CdlLOC *)((u32)(fileIdx * 8) + (u32)tbl); dstOff = fileIdx * 8; pOff = (s32 *)((u32)(bufIdx * 8) + (u32)lbv); srcOff = bufIdx * 8; do { if (*pOff != 0) { CdIntToPos(*pOff + base, p); *(s32 *)(&D_800AE834 + dstOff) = *(s32 *)(0x80180004 + srcOff); } pOff += 2; srcOff += 8; pCount += 2; bufIdx++; p += 2; dstOff += 8; j++; fileIdx++; } while (j < n); } outer++; locOff += 0x30; } while (outer < 8); } #ifdef NON_MATCHING extern void LoaderResetReadState(void); extern int VSync(int mode); extern CdlFILE D_80063058; extern char cdpath_DEBUG_BIN[]; extern u8 D_80062C38; /* path table entry[0].file (CdlFILE), entries stride 0x30; names at -0x14 */ extern u8 D_80062C68; /* path entry[1].file.pos (CdlLOC), the 8 .CD files, stride 0x30 */ extern s32 debugBinPresent; extern s32 D_800747D4; extern s32 D_80063074; extern s32 D_800747D8; extern u8 cdFileLocTable[]; /* out: {CdlLOC pos; u32 size} per sub-file, 8B stride */ extern u8 D_800AE834; /* cdFileLocTable + 4 (the size field) */ extern s32 listCdBuffer; /* LIST.CD content: 8B records {value; size} */ extern u8 D_80180004; /* listCdBuffer + 4 (the size field) */ /* file-loader directory resolver (boot @0x800101fc): probe \DEBUG.BIN;1, resolve the 21 * CdPathTable entries via CdSearchFile, read LIST.CD (0xE40 B) into listCdBuffer, then build * cdFileLocTable (CdlLOC+size per sub-file) over the 8 .CD files. Phase 3 T2. * NON_MATCHING: logically faithful, structurally close (138 vs 133 ins) but not byte-exact. * Residual is register allocation / loop-invariant hoisting — the target keeps &D_80063058, * &D_80062C38, &D_80062C68 in callee-saved regs and derives the name arg as (base - 0x14) * rather than a separate symbol; a multi-iteration / decomp-permuter target for a later pass. */ void LoaderInitFileTable(void) { CdlFILE *res; int tries; int pathOff; int pathN; int base; int n; int fileIdx; int bufIdx; int j; int outer; int locOff; int dstOff; int srcOff; int *pCount; int *pOff; CdlLOC *p; LoaderResetReadState(); tries = 0; do { res = CdSearchFile(&D_80063058, cdpath_DEBUG_BIN); tries++; if (res != (CdlFILE *)-1) { break; } } while (tries < 0x10); pathN = 0; pathOff = 0; debugBinPresent = (res != (CdlFILE *)0); D_800747D4 = 1; D_80063074 = 0; D_800747D8 = 0; do { do { res = CdSearchFile((CdlFILE *)(&D_80062C38 + pathOff), (char *)(&D_80062C38 - 0x14 + pathOff)); } while ((u32)((int)res + 1) < 2); pathN++; pathOff += 0x30; } while (pathN < 0x15); do { fileIdx = 0; n = CdReadRequest(&D_80062C38, &listCdBuffer, 0xE40, 0); if (n != 0) { break; } VSync(0); } while (1); bufIdx = 0; outer = 0; locOff = 0; pCount = &listCdBuffer; do { base = CdPosToInt((CdlLOC *)(&D_80062C68 + locOff)); n = *pCount; pCount += 2; bufIdx++; j = 0; if (n > 0) { dstOff = fileIdx * 8; p = (CdlLOC *)(cdFileLocTable + dstOff); srcOff = bufIdx * 8; pOff = &listCdBuffer + bufIdx * 2; do { if (*pOff != 0) { CdIntToPos(*pOff + base, p); *(s32 *)(&D_800AE834 + dstOff) = *(s32 *)(&D_80180004 + srcOff); } pOff += 2; srcOff += 8; pCount += 2; bufIdx++; p += 2; dstOff += 8; j++; fileIdx++; } while (j < n); } outer++; locOff += 0x30; } while (outer < 8); } #else extern void LoaderResetReadState(void); extern int VSync(int mode); extern s32 CdReadRequest(int *cdlFile, void *dest, s32 size, s32 mode); extern CdlFILE D_80063058; /* CdPathTable[22].file (\DEBUG.BIN;1); its name is at -0x14 */ extern u8 D_80062C38; /* CdPathTable[0].file; entries stride 0x30, names at -0x14 */ extern u8 D_80062C68; /* CdPathTable[1].file.pos (CdlLOC) — the 8 .CD files, stride 0x30 */ extern s32 debugBinPresent; extern s32 D_800747D4; extern s32 D_80063074; extern s32 D_800747D8; extern u8 cdFileLocTable[]; /* out: {CdlLOC pos; u32 size} per sub-file, 8B stride */ extern u8 D_800AE834; /* cdFileLocTable + 4 (the size field) */ /* LIST.CD staging buffer @0x80180000 (== listCdBuffer), 0xE40 bytes of 8B records * {startOffset; size}. The original source used the raw address, not the symbol: * every reference in the target is a bare `lui reg,0x8018` (one instruction), which is * what a literal produces — `&listCdBuffer` would emit lui+addiu. */ #define LIST_BUF 0x80180000 /* File-loader directory resolver (0x8001971C). * 1. probe \DEBUG.BIN;1 (up to 16 tries) -> debugBinPresent * 2. resolve the 21 CdPathTable entries via CdSearchFile (retry while res is -1 or 0) * 3. read LIST.CD (0xE40 B) into the staging buffer, VSync-spinning until accepted * 4. walk the 8 .CD containers: for each, CdPosToInt the container start, then convert * every non-zero sub-file offset into cdFileLocTable[k].pos / .size. * * Register pins (cookbook §17): this function's live set is exactly 9 callee-saved values * plus two caller-saved ones, so gcc's allocno priority order is one place off by default — * `n` wins a callee-saved reg and `outer` spills. The pins restore the target's split * (outer/tries/pathN share $s5, `n` -> $v1 with caller-save, base -> $a2). * `dead[4]` is load-bearing: the target frame is 0x58 and its locals start at 0x20, i.e. * 16 bytes below the first live slot. `tbl`/`lbv` are set once from constants so * update_equiv_regs marks them rematerializable — reload then re-emits each address into * the spill register $t0 (the same $t0 the locOff reloads use), which is what the target does. */ void LoaderInitFileTable(void) { CdlFILE *res; register int tries __asm__("$21"); register u8 *pathBase __asm__("$18"); register char *nameBase __asm__("$19"); register int pathOff __asm__("$17"); int offCopy; int dead[4]; register u32 pathN __asm__("$21"); int base; int n; int rdy; int fileIdx; int bufIdx; register int j __asm__("$19"); register int outer __asm__("$21"); int locOff; u8 *tbl; u8 *lbv; int dstOff; int srcOff; s32 *pCount; s32 *pOff; CdlLOC *p; LoaderResetReadState(); tries = 0; do { res = CdSearchFile(&D_80063058, (char *)&D_80063058 - 0x14); tries++; if (res != (CdlFILE *)-1) { break; } } while (tries < 0x10); pathN = 0; pathBase = &D_80062C38; nameBase = (char *)pathBase - 0x14; pathOff = 0; debugBinPresent = (res != (CdlFILE *)0); D_800747D4 = 1; D_80063074 = 0; D_800747D8 = 0; do { offCopy = pathOff; do { res = CdSearchFile((CdlFILE *)(&D_80062C38 + offCopy), nameBase + offCopy); } while ((u32)((int)res + 1) < 2); pathN++; pathOff += 0x30; } while (pathN < 0x15); do { rdy = CdReadRequest((int *)&D_80062C38, (void *)LIST_BUF, 0xE40, 0); if (rdy != 0) { break; } VSync(0); } while (1); tbl = cdFileLocTable; lbv = (u8 *)LIST_BUF; fileIdx = 0; bufIdx = 0; outer = 0; locOff = 0; pCount = (s32 *)LIST_BUF; do { base = CdPosToInt((CdlLOC *)(&D_80062C68 + locOff)); n = *pCount; pCount += 2; bufIdx++; j = 0; if (n > 0) { p = (CdlLOC *)((u32)(fileIdx * 8) + (u32)tbl); dstOff = fileIdx * 8; pOff = (s32 *)((u32)(bufIdx * 8) + (u32)lbv); srcOff = bufIdx * 8; do { if (*pOff != 0) { CdIntToPos(*pOff + base, p); *(s32 *)(&D_800AE834 + dstOff) = *(s32 *)(0x80180004 + srcOff); } pOff += 2; srcOff += 8; pCount += 2; bufIdx++; p += 2; dstOff += 8; j++; fileIdx++; } while (j < n); } outer++; locOff += 0x30; } while (outer < 8); } #endif extern s32 cdReq_sink; extern s32 cdReq_state; extern s32 D_800AE6F8; extern s32 D_800AE6FC; extern s32 D_800AE700; extern s32 cdReq_wordsRemaining; extern s32 cdReq_drainPhase; extern u8 D_800AE798; extern u8 D_800AE799; extern s32 D_800AE70C; void func_80019930(void) { cdReq_sink = 0x80010000; cdReq_state = 0; D_800AE6F8 = 0; D_800AE6FC = 0; D_800AE700 = 0; cdReq_wordsRemaining = 1; cdReq_drainPhase = 0; D_800AE798 = 0; D_800AE799 = 0; D_800AE70C = 0; } extern s32 cdReq_sink; extern s32 cdReq_curSector; extern s32 D_800BA1B4; extern s32 D_800747F0; extern s32 D_800747F4; extern s32 cdReq_state; extern s32 D_800AE6F8; extern s32 D_800AE6FC; extern s32 D_800AE700; extern s32 cdReq_wordsRemaining; extern s32 cdReq_drainPhase; extern u8 D_800AE798; extern u8 D_800AE799; extern s32 D_800AE70C; void LoaderResetReadState(void) { cdReq_sink = 0x80010000; cdReq_curSector = 0; D_800BA1B4 = 0; D_800747F0 = 0; D_800747F4 = 0; cdReq_state = 0; D_800AE6F8 = 0; D_800AE6FC = 0; D_800AE700 = 0; cdReq_wordsRemaining = 1; cdReq_drainPhase = 0; D_800AE798 = 0; D_800AE799 = 0; D_800AE70C = 0; } void func_80019A10(void) { extern s32 D_80078E30; D_80078E30 = -1; } extern s32 CdQueueBusy(void); extern void CdReadStateMachine(int); extern s32 cdReq_curSector; extern void *cdReq_dest; extern s32 cdReq_size; extern void *cdReq_cdlFile; extern s32 cdReq_result; extern s32 D_800AE720; extern s32 D_800AE724; /* Read-request dispatcher (Phase 3 T2). Refuse while CdQueueBusy(); dedup on the request's * start sector (*cdlFile) vs the in-flight cdReq_curSector; stash dest/size/cdlFile/mode into * the control block, set the "first read" flag D_800AE720 = (mode == 0), drive the state * machine, return cdReq_result. */ s32 CdReadRequest(int *cdlFile, void *dest, s32 size, s32 mode) { s32 sector; if (CdQueueBusy() != 0) { return 0; } if (cdReq_curSector == 0) { sector = *cdlFile; } else { sector = *cdlFile; if (sector != cdReq_curSector) { return 0; } } cdReq_dest = dest; cdReq_size = size; cdReq_cdlFile = cdlFile; D_800AE724 = mode; D_800AE720 = 0; cdReq_curSector = sector; if (mode == 0) { D_800AE720 = 1; } CdReadStateMachine(0); return cdReq_result; } #ifdef NON_MATCHING /* libcd low-level command API (the loader uses raw CdControl, NOT the PsyQ CdRead() wrapper; * not in psyq/libcd.h yet). */ extern int CdControl(u8 com, u8 *param, u8 *result); extern int CdSync(int mode, u8 *result); extern void CdFlush(void); extern void *CdReadyCallback(void *func); extern void CdReadSectorReadyCB(u8); /* §376: def takes u8; only ever passed BY ADDRESS */ extern void start(void); /* EXE entry — used as the idle "sink" sentinel */ extern int func_8002FD14(int buf, int len); /* per-sector consumer (drains the queued list) */ extern void func_8001A0FC(void); extern int func_8001A114(void); /* cdReq_* async-read control block (Phase 3 T2); the D_800AE* are its unnamed members. */ extern int cdReq_state; extern int cdReq_retry; extern int cdReq_result; /* return-by-global: 0 = still busy, 1 = done */ extern u8 cdReq_cdResult; /* CdControl status byte (bit 0x10 = error) */ extern void *cdReq_cdlFile; /* seek target (a CdlLOC*) */ extern int cdReq_posInt; extern int cdReq_timeout; extern int cdReq_size; /* bytes to read */ extern void *cdReq_dest; /* read destination */ extern s32 cdReq_sink; /* sector sink address (dest while reading, &start idle) */ extern int cdReq_wordsRemaining; extern int cdReq_drainPhase; /* 0 = reading, 1 = drained, 2 = finished */ extern void *cdReq_savedReadyCB; /* prior CdReadyCallback, restored on stop */ extern int cdReq_curSector; extern u8 D_800AE740; /* CdlSetmode mode-byte buffer (0xA0) */ extern int D_800AE6E8; /* CdSync result scratch */ extern int D_800AE6F8; extern s32 D_800AE70C; /* last/idle CdlLOC (held as a word) */ extern int D_800AE71C; /* count of queued sectors to drain (state 7) */ extern int D_800AE724; /* base of the queued {ptr,len} sector list */ extern int D_800AE6FC; extern int D_800AE700; extern u8 D_800AE798; extern u8 D_800AE799; /* Hand-rolled polled async CD reader: one state step per call, switch(cdReq_state). Sequence * SetMode(0xA0) -> SeekL(+CdPosToInt) -> ReadN(+CdReadSectorReadyCB sector drain) -> Pause/Flush. * Progress is returned via cdReq_result (0 busy / 1 done). The game does NOT use PsyQ CdRead()/ * CdReadSync(). pauseAfterSeek != 0 stops after the seek (state 8) instead of reading. * Provenance: static trace, Phase 3 T2 (verified). * NON_MATCHING: logically faithful to the Ghidra decompile; not byte-exact — a large switch state * machine whose register allocation + jump-table placement are a later byte-match pass. */ void CdReadStateMachine(int pauseAfterSeek) { int n; int i; int off; switch (cdReq_state) { case 0: cdReq_retry = 0; D_800AE71C = 0; D_800AE70C = 0; D_800AE740 = 0xA0; CdControl(0x0E, &D_800AE740, &cdReq_cdResult); /* CdlSetmode */ if ((cdReq_cdResult & 0x10) == 0) { cdReq_state++; goto state1; } break; case 1: state1: D_800AE6E8 = CdSync(1, &cdReq_cdResult); if (D_800AE6E8 == 0) { cdReq_result = 0; return; } if (D_800AE6E8 == 5) { cdReq_state = 0; cdReq_result = 0; return; } if (D_800AE6E8 != 2) { cdReq_result = 0; return; } cdReq_retry = 0; cdReq_state++; D_800AE6E8 = 2; state2: if (cdReq_retry + 1 < 3) { cdReq_retry++; cdReq_result = 0; return; } cdReq_state++; cdReq_retry = 0; cdReq_result = 0; return; case 2: goto state2; case 3: CdControl(0x15, (u8 *)cdReq_cdlFile, &cdReq_cdResult); /* CdlSeekL */ if ((cdReq_cdResult & 0x10) == 0) { cdReq_timeout = 0; cdReq_posInt = CdPosToInt((CdlLOC *)cdReq_cdlFile); cdReq_state++; goto state4; } break; case 4: state4: D_800AE6E8 = CdSync(1, &cdReq_cdResult); if (D_800AE6E8 == 5) { cdReq_retry++; flushRetry: CdFlush(); cdReq_state = 3; cdReq_result = 0; return; } if (D_800AE6E8 == 0) { cdReq_result = 0; return; } if (D_800AE6E8 != 2) { cdReq_result = 0; cdReq_timeout++; if (cdReq_timeout < 0xB5) { cdReq_result = 0; return; } goto flushRetry; } if (pauseAfterSeek == 0) { cdReq_state++; goto state5; } CdControl(0x09, (u8 *)0, &cdReq_cdResult); /* CdlPause */ if ((cdReq_cdResult & 0x10) == 0) { cdReq_state = 8; D_800AE70C = *(s32 *)cdReq_cdlFile; cdReq_result = 0; return; } break; case 5: state5: cdReq_drainPhase = 0; if (cdReq_size < 1) { D_800AE6F8 = 0; } else { int bytes = cdReq_size + 3; D_800AE6F8 = 3; if (bytes < 0) bytes = cdReq_size + 6; /* round-up word count, neg-safe */ cdReq_wordsRemaining = bytes >> 2; D_800AE798 = 1; cdReq_sink = (s32)cdReq_dest; } cdReq_savedReadyCB = CdReadyCallback(CdReadSectorReadyCB); CdControl(0x06, (u8 *)0, &cdReq_cdResult); /* CdlReadN */ if ((cdReq_cdResult & 0x10) == 0) { cdReq_retry = 0; cdReq_state++; goto state6; } break; case 6: state6: if (cdReq_drainPhase != 1) { if (cdReq_drainPhase == 2) { CdReadyCallback(cdReq_savedReadyCB); cdReq_state = 0; cdReq_result = 0; return; } cdReq_retry++; if (cdReq_retry > 299) { CdReadyCallback(cdReq_savedReadyCB); cdReq_state = 0; CdFlush(); cdReq_result = 0; return; } cdReq_result = 0; return; } CdReadyCallback(cdReq_savedReadyCB); CdControl(0x09, (u8 *)0, &cdReq_cdResult); /* CdlPause */ if ((cdReq_cdResult & 0x10) == 0) { cdReq_state += (D_800AE71C == 0) ? 2 : 1; /* -> 8 (no drain) or 7 (drain) */ cdReq_result = 0; return; } break; case 7: n = 0; if (D_800AE71C > 0) { off = 0; do { do { i = func_8002FD14(*(int *)(D_800AE724 + off), *(int *)(D_800AE724 + off + 4)); } while (i == 0); n++; off = n * 8; } while (n < D_800AE71C); } cdReq_state++; cdReq_result = 0; return; case 8: D_800AE6E8 = CdSync(1, &cdReq_cdResult); if (D_800AE6E8 == 2) { cdReq_sink = (s32)start; cdReq_state = 0; D_800AE6F8 = 0; D_800AE6FC = 0; D_800AE700 = 0; cdReq_curSector = 0; D_800AE70C = 0; cdReq_wordsRemaining = 1; cdReq_drainPhase = 0; D_800AE798 = 0; D_800AE799 = 0; cdReq_result = 1; return; } if (D_800AE6E8 != 5) { cdReq_result = 0; return; } CdFlush(); CdControl(0x09, (u8 *)0, &cdReq_cdResult); /* CdlPause */ if ((cdReq_cdResult & 0x10) == 0) { cdReq_result = 0; return; } break; case 9: func_8001A0FC(); cdReq_state++; /* fall through to state 10 */ case 10: D_800AE6E8 = func_8001A114(); if (D_800AE6E8 != 0) { cdReq_state = 0; cdReq_result = 0; return; } cdReq_result = 0; return; default: goto setDefault; } cdReq_state = 9; setDefault: cdReq_result = 0; return; } #else extern s32 func_800435CC(s32 mode, void *buf, void *param); /* CdControl */ extern s32 func_8004355C(s32 mode, u8 *result); /* CdSync */ extern void *CdReadyCallback(void *func); /* CdReadyCallback */ extern void func_800434BC(void); /* CdFlush */ extern void CdReadSectorReadyCB(u8); /* §376: def takes u8; only ever passed BY ADDRESS */ extern s32 func_8002FD14(s32, s32); extern void func_8001A0FC(void); extern s32 func_8001A114(void); extern s32 cdReq_state; extern s32 cdReq_retry; extern s32 cdReq_result; extern u8 cdReq_cdResult; extern void *cdReq_cdlFile; extern s32 cdReq_posInt; extern s32 cdReq_timeout; extern s32 cdReq_size; extern void *cdReq_dest; extern s32 cdReq_sink; extern s32 cdReq_wordsRemaining; extern s32 cdReq_drainPhase; extern void *cdReq_savedReadyCB; extern s32 cdReq_curSector; extern s32 D_800AE6E8; extern s32 D_800AE6F8; extern s32 D_800AE6FC; extern s32 D_800AE700; extern s32 D_800AE70C; extern s32 D_800AE71C; extern s32 D_800AE724; extern u8 D_800AE798; extern u8 D_800AE799; /* Hand-rolled polled async CD reader: one state step per call, switch(cdReq_state) over 11 states. * Sequence: CdlSetmode(0xA0) -> CdlSeekL(+CdPosToInt) -> CdlReadN (+CdReadSectorReadyCB sector * drain) -> CdlPause/Flush, with a recovery arm (states 9/10) driving func_8001A0FC/func_8001A114. * Progress is returned via cdReq_result (0 busy / 1 done); pauseAfterSeek != 0 stops after the seek. * * MATCHING NOTES (byte-proven 385/385): * - the cdReq_ block members reached at a NEGATIVE displacement from a held base * (D_800AE740 = retry[-0x10], cdReq_cdResult = retry[-0x18] / cdlFile[-0x6C] / * savedReadyCB[+4] / drainPhase[-0x14]) must be spelled through a pointer VARIABLE, and the * byte store needs its OWN variable (`q`): `*(p - 0x10) = 0xA0` folds back to * `lui $at,%hi(sym-0x10)` and costs +1 instruction, `q = p - 0x10; *q = 0xA0` does not. * - EVERY held pointer gets a DISTINCT local. A local assigned in two arms stops being a * single-set pseudo, so alias.c can no longer canonicalise `(mem (reg))` to its symbol and the * scheduler refuses to hoist an unrelated global load past it (a stray load-delay `nop`, +1). * - `s32 pad[2]` is the §333 frame dial: the target's 0x28 frame needs 8 bytes of declared-but- * unused locals. Without it the frame is 0x20 and every $sp displacement shifts. * - `setStateNine:` / `resetState:` are real labels, not cross-jump artefacts: they pin the * merged tails to case 0 and case 10 respectively (gcc's own merge picks the other end). * - case 10 assigns the callee's result straight to D_800AE6E8; a temp there buys a dead * `addu $v1,$v0,$zero` (the other three CdSync arms DO need the temp, since $v0 is reused for * the comparison constants). * Provenance: full byte-match against asm/nonmatchings/800/CdReadStateMachine.s (S72). */ void CdReadStateMachine(s32 pauseAfterSeek) { s32 *rp; s32 *dp; s32 *sp7; s32 *sp9; u8 *p; u8 *q; u8 *p2; u8 *p3; u8 *p8; void **fpp; s32 r; s32 st; s32 n; s32 i; s32 off; s32 pad[2]; switch (cdReq_state) { case 0: p = (u8 *)&cdReq_retry; *(s32 *)p = 0; D_800AE71C = 0; D_800AE70C = 0; q = p - 0x10; *q = 0xA0; func_800435CC(0xE, q, p - 0x18); if ((p[-0x18] & 0x10) != 0) { setStateNine: cdReq_state = 9; break; } cdReq_state++; /* fallthrough */ case 1: r = func_8004355C(1, &cdReq_cdResult); D_800AE6E8 = r; if (r == 0) { break; } if (r == 5) { goto resetState; } if (r != 2) { break; } cdReq_retry = 0; cdReq_state++; /* fallthrough */ case 2: rp = &cdReq_retry; *rp = *rp + 1; if (*rp < 3) { break; } *rp = 0; cdReq_state++; break; case 3: fpp = &cdReq_cdlFile; func_800435CC(0x15, (u8 *)*fpp, (u8 *)fpp - 0x6C); if ((*((u8 *)fpp - 0x6C) & 0x10) != 0) { goto setStateNine; } cdReq_timeout = 0; cdReq_posInt = CdPosToInt((CdlLOC *)*fpp); cdReq_state++; /* fallthrough */ case 4: p2 = &cdReq_cdResult; r = func_8004355C(1, p2); D_800AE6E8 = r; if (r == 5) { cdReq_retry++; flushRetry: func_800434BC(); cdReq_state = 3; break; } if (r == 0) { break; } if (r != 2) { cdReq_result = 0; cdReq_timeout++; if (cdReq_timeout < 0xB5) { return; } goto flushRetry; } if (pauseAfterSeek != 0) { func_800435CC(9, 0, p2); if ((*p2 & 0x10) != 0) { goto setStateNine; } cdReq_state = 8; D_800AE70C = *(s32 *)cdReq_cdlFile; break; } cdReq_state++; /* fallthrough */ case 5: cdReq_drainPhase = 0; if (cdReq_size < 1) { D_800AE6F8 = 0; } else { D_800AE6F8 = 3; cdReq_wordsRemaining = (cdReq_size + 3) / 4; D_800AE798 = 1; cdReq_sink = (s32)cdReq_dest; } p3 = (u8 *)&cdReq_savedReadyCB; *(void **)p3 = CdReadyCallback(CdReadSectorReadyCB); func_800435CC(6, 0, p3 + 4); if ((p3[4] & 0x10) != 0) { goto setStateNine; } cdReq_retry = 0; cdReq_state++; /* fallthrough */ case 6: dp = &cdReq_drainPhase; if (*dp == 1) { CdReadyCallback(cdReq_savedReadyCB); func_800435CC(9, 0, (u8 *)dp - 0x14); if ((*((u8 *)dp - 0x14) & 0x10) != 0) { goto setStateNine; } st = cdReq_state; n = D_800AE71C; cdReq_state = st + 1; if (n == 0) { cdReq_state = st + 2; } break; } if (*dp == 2) { CdReadyCallback(cdReq_savedReadyCB); goto resetState; } cdReq_retry++; if (cdReq_retry < 0x12C) { break; } CdReadyCallback(cdReq_savedReadyCB); cdReq_state = 0; func_800434BC(); break; case 7: i = 0; if (D_800AE71C > 0) { do { off = i * 8; do { r = func_8002FD14(*(s32 *)(off + D_800AE724), *(s32 *)(off + D_800AE724 + 4)); } while (r == 0); i++; } while (i < D_800AE71C); } sp7 = &cdReq_state; *sp7 = *sp7 + 1; break; case 8: p8 = &cdReq_cdResult; r = func_8004355C(1, p8); D_800AE6E8 = r; if (r == 2) { cdReq_sink = 0x80010000; cdReq_state = 0; D_800AE6F8 = 0; D_800AE6FC = 0; D_800AE700 = 0; cdReq_wordsRemaining = 1; cdReq_drainPhase = 0; D_800AE798 = 0; D_800AE799 = 0; D_800AE70C = 0; cdReq_result = 1; cdReq_curSector = 0; return; } if (r != 5) { break; } func_800434BC(); func_800435CC(9, 0, p8); if ((*p8 & 0x10) == 0) { break; } goto setStateNine; case 9: func_8001A0FC(); sp9 = &cdReq_state; *sp9 = *sp9 + 1; /* fallthrough */ case 10: D_800AE6E8 = func_8001A114(); if (D_800AE6E8 == 0) { break; } resetState: cdReq_state = 0; break; } cdReq_result = 0; } #endif extern s32 D_800AE6F4; extern s32 D_800AE70C; void func_8001A0FC(void) { D_800AE6F4 = 0; D_800AE70C = 0; } /* CD error-recovery state machine (0x8001A114), stepped from CdReadStateMachine's state 10. * One step per call; returns 1 only when the path table has been re-resolved (recovery done). * 0: CdFlush-ish reset + CdlNop -> 1 1: CdSync poll (2 -> advance, 0x10 -> restart) * 2: CdlSetmode(0x80) 3: burn 3 frames 4: CdSync poll * 5: re-run CdSearchFile on the path entry (up to 16 tries) -> done / restart * * §ADD-8 (re-tie barrier): the two constant arguments of the state-0 CdControl must issue * BEFORE the `la $s0,cdReq_cdResult`; sched1 otherwise ranks the address load first (it feeds * $a2 and so has the longer chain). The zero-byte `__volatile__` re-ties pin them to source * order. §20/§243 (held-pointer): cdReq_retry in state 3 and D_800AE6F4 on the shared * "advance" tail are spelled through a named pointer — that is what turns their %hi/%lo pairs * into a single base register, and keeping the two tails textually distinct is what stops * cross-jumping from merging .L8001A260 with .L8001A2AC. */ extern void func_800434BC(void); extern int func_80043830(int com, u8 *param, u8 *result); extern int func_80046630(int mode); extern int func_8004674C(void); extern s32 D_800AE6F4; /* recovery state */ extern u8 cdReq_cdResult; /* CdControl status byte (bit 0x10 = error) */ extern u8 D_800AE740; /* CdlSetmode mode-byte buffer; cdReq_cdResult is at -8 */ extern int cdReq_retry; extern CdlFILE D_80063028; /* CdPathTable entry; its name string sits at -0x14 */ int func_8001A114(void) { int ret; u8 *p; int r; int i; CdlFILE *fp; int *rp; s32 *sp; int c0; int c1; ret = 0; switch (D_800AE6F4) { case 0: func_800434BC(); c0 = 1; __asm__ __volatile__("" : "=r"(c0) : "0"(c0)); c1 = 0; __asm__ __volatile__("" : "=r"(c1) : "0"(c1)); p = &cdReq_cdResult; func_80043830(c0, (u8 *)c1, p); if ((*p & 0x10) != 0) { break; } D_800AE6F4 = D_800AE6F4 + 1; /* fallthrough */ case 1: r = func_80046630(0); if (r == 2) { goto bump; } if (r != 0x10) { break; } reset: D_800AE6F4 = 0; break; case 2: p = &D_800AE740; *p = 0x80; if (func_80043830(0xE, p, p - 8) == 0) { break; } if ((p[-8] & 0x10) != 0) { goto reset; } cdReq_retry = 0; D_800AE6F4 = D_800AE6F4 + 1; break; case 3: rp = &cdReq_retry; *rp = *rp + 1; if (*rp < 3) { break; } *rp = 0; D_800AE6F4 = D_800AE6F4 + 1; break; case 4: r = func_8004674C(); if ((r == 1) || (r == 0x10) || (r == 0)) { D_800AE6F4 = 0; } if (r != 2) { break; } bump: sp = &D_800AE6F4; *sp = *sp + 1; break; case 5: i = 0; fp = &D_80063028; do { r = (int)CdSearchFile(fp, (char *)fp - 0x14); if (r != -1) { break; } i = i + 1; } while (i < 0x10); if ((u32)(r + 1) < 2) { goto reset; } ret = 1; break; } return ret; } #ifdef NON_MATCHING typedef struct { short x, y, w, h; } RECT; /* libgpu RECT (VRAM rectangle) */ extern int CdGetSector(void *madr, int nsector); extern void LoadImage(RECT *rect, u32 *data); /* libgpu VRAM upload */ extern int func_8002FC64(int nbytes, u32 *src); /* stage/copy a payload run */ extern int func_8002FB08(int entry); /* kick off a queued list entry */ extern void func_8002FDC8(void); /* finalize the queued transfer */ extern void func_80018714(void); /* reset LZSS sector state */ /* shared cdReq_ control block (see CdReadStateMachine) + this callback's members */ extern int cdReq_posInt; extern int cdReq_size; extern int cdReq_wordsRemaining; extern int cdReq_drainPhase; extern void *cdReq_dest; extern s32 cdReq_sink; extern u32 lzss_sectorStagingBuf[]; /* 0x80079A70 — PAC header lands here ([0] = 'PAC' magic) */ extern u8 cdReq_sectorHdrBuf[]; /* sub-header scratch (3 words -> CdPosToInt) */ extern u8 D_80079A74; /* PAC type */ extern u8 D_80079A75; /* PAC flags -> D_800AE798 */ extern int D_80079A78; /* PAC sub-count (textures) */ extern int D_80079A7C; /* PAC payload size (bytes) */ extern int D_800AE6F8; /* load phase (PAC-type dispatcher state) */ extern u8 D_800AE798; /* saved PAC flags (nonzero = more sectors follow) */ extern int D_800AE704; /* texture sector index (0..3, round-robin) */ extern short D_800AE710, D_800AE712; /* VRAM dst x,y of the current tile (a RECT @0xAE710) */ extern short D_800AE714, D_800AE716; /* tile w,h (0x20 x 0x20) */ extern u8 D_800AE758[]; /* 64-byte present bitmask (which tiles are sent) */ extern u8 D_800AE79A, D_800AE79B; /* bit / byte cursor into the bitmask */ extern RECT *D_800AE718; /* current texture-rect pointer (PAC type 6) */ extern int D_800AE71C; /* queued-entry count */ extern int D_800AE720; /* "transfer already in progress" guard */ extern int D_800AE724; /* base of the queued {ptr,len} list */ extern void *D_80072C80; /* a fixed destination pointer (PAC type 7) */ extern u32 D_8007A280[]; /* texture RECT array (read from CD) */ extern u32 D_8007A310[]; /* texture pixel data (read from CD) */ /* CdReadyCallback for CdReadStateMachine (registered in its state 5). Per CD data-ready interrupt, * drain one sector and dispatch on the load phase D_800AE6F8: parse the PAC header, then per PAC * type either upload tiles to VRAM (LoadImage), copy raw sectors to cdReq_sink, or decompress via * LzssDecodeSector (this is its ONLY caller). reason: 1 = data-ready, 5 = end/error. * Provenance: static trace, Phase 3 T2/T4 (verified). * NON_MATCHING: faithful translation of the Ghidra decompile — logically faithful, not byte-verified * (large interrupt callback; register allocation + jump-table placement are a later byte-match pass). */ void CdReadSectorReadyCB(char reason) { void *dst; int pos; int i, n; if (reason != 1) goto endReason; CdGetSector(cdReq_sectorHdrBuf, 3); pos = CdPosToInt((CdlLOC *)cdReq_sectorHdrBuf); if (pos != cdReq_posInt) { /* sector out of order -> abort the drain */ if (D_800AE6F8 == 4) func_8002FDC8(); cdReq_drainPhase = 2; return; } cdReq_posInt = pos + 1; switch (D_800AE6F8) { case 0: /* expect a PAC header */ CdGetSector(lzss_sectorStagingBuf, 4); if (lzss_sectorStagingBuf[0] != 0x434150) goto abortDrain; /* "PAC" magic? */ D_800AE798 = D_80079A75; switch (D_80079A74) { /* PAC type */ case 0: case 5: /* tiled texture: scan the present-bitmask */ D_800AE704 = 0; CdGetSector(D_800AE758, 0x10); D_800AE716 = 0x20; D_800AE714 = 0x20; D_800AE712 = 0; D_800AE710 = 0; D_800AE79A = 1; D_800AE79B = 0; while (D_800AE79B < 0x40) { if ((D_800AE79A & D_800AE758[D_800AE79B]) != 0) goto tilePhase; if (D_800AE79A == 0x80) { D_800AE79A = 1; D_800AE79B++; } else D_800AE79A <<= 1; D_800AE710 += 0x20; if (D_800AE710 > 0x3FF) { D_800AE710 = 0; D_800AE712 += 0x20; } } D_800AE6F8 = (D_800AE798 == 0) ? 0 : 2; tilePhase: D_800AE6F8++; break; case 1: dst = cdReq_dest; goto setSink; case 2: if (D_800AE720 != 0) goto phaseDone; dst = *(void **)(D_800AE71C * 8 + D_800AE724 + 4); goto setSink; case 3: if (D_800AE720 != 0) goto phaseDone; do { i = func_8002FB08(*(int *)(D_800AE71C * 8 + D_800AE724)); } while (i == 0); D_800AE6F8 = 4; cdReq_wordsRemaining = D_80079A7C - 0x800; break; case 4: D_800AE6F8 = 5; cdReq_wordsRemaining = (D_80079A7C - 0x7FD) >> 2; cdReq_sink = (s32)cdReq_dest; func_80018714(); break; case 6: /* multiple TIMs: LoadImage each */ CdGetSector(D_8007A280, 0x1FC); n = D_80079A78; { u32 *p = D_8007A310; i = 0; D_800AE718 = (RECT *)D_8007A280; if (n > 0) { do { LoadImage(D_800AE718, p); i++; p += D_800AE718->w / 2; D_800AE718++; } while (i < n); } } goto sectorDone; case 7: dst = D_80072C80; if (cdReq_size < 0) goto phaseDone; setSink: D_800AE6F8 = 3; cdReq_wordsRemaining = (D_80079A7C - 0x7FD) >> 2; cdReq_sink = dst; break; case 8: D_800AE6F8 = 6; cdReq_wordsRemaining = (D_80079A7C - 0x7FD) >> 2; break; } break; case 1: /* stream tile pixels to VRAM */ i = D_800AE704 * 0x200; CdGetSector(&lzss_sectorStagingBuf[i], 0x200); LoadImage((RECT *)&D_800AE710, &lzss_sectorStagingBuf[i]); D_800AE704 = (D_800AE704 + 1) & 3; do { if (D_800AE79A == 0x80) { D_800AE79A = 1; D_800AE79B++; } else D_800AE79A <<= 1; if (D_800AE79B > 0x3F) goto sectorDone; D_800AE710 += 0x20; if (D_800AE710 > 0x3FF) { D_800AE710 = 0; D_800AE712 += 0x20; } } while ((D_800AE79A & D_800AE758[D_800AE79B]) == 0); break; case 3: /* raw copy to cdReq_sink */ if (cdReq_wordsRemaining < 0x201) { CdGetSector((void *)cdReq_sink, cdReq_wordsRemaining); sectorDone: D_800AE6F8 = (D_800AE798 == 0) ? 0 : 2; } else { CdGetSector((void *)cdReq_sink, 0x200); cdReq_wordsRemaining -= 0x200; cdReq_sink += 0x800; } break; case 4: /* staged copy via func_8002FC64 */ if (cdReq_wordsRemaining < 0x801) { n = cdReq_wordsRemaining + 3; if (n < 0) n = cdReq_wordsRemaining + 6; CdGetSector(lzss_sectorStagingBuf, n >> 2); if (func_8002FC64(cdReq_wordsRemaining, lzss_sectorStagingBuf) == 0) { stageFail: func_8002FDC8(); D_800AE6F8 = 7; } else { D_800AE71C++; if (D_800AE798 != 0) goto phaseDone; D_800AE6F8 = 0; } } else { CdGetSector(lzss_sectorStagingBuf, 0x200); i = func_8002FC64(0x800, lzss_sectorStagingBuf); cdReq_sink += 0x800; cdReq_wordsRemaining -= 0x800; if (i == 0) goto stageFail; } break; case 5: /* LZSS decompress one sector */ CdGetSector(lzss_sectorStagingBuf, 0x200); if (LzssDecodeSector(lzss_sectorStagingBuf) == 0) goto sectorDone; break; case 6: /* countdown the remaining words */ cdReq_wordsRemaining -= 0x200; if (cdReq_wordsRemaining < 1) { if (D_800AE798 != 0) goto phaseDone; D_800AE6F8 = 0; } break; } if (D_800AE6F8 == 2) { phaseDone: /* (reached by the switch above or by goto) */ D_800AE6F8 = 2; cdReq_drainPhase = 1; } else if (D_800AE6F8 == 7) { cdReq_drainPhase = 2; } endReason: if (reason == 5) { if (D_800AE6F8 == 4) func_8002FDC8(); abortDrain: cdReq_drainPhase = 2; } return; } #else typedef struct XRECT { /* 0x00 */ u16 x; /* 0x02 */ u16 y; /* 0x04 */ u16 w; /* 0x06 */ u16 h; } XRECT; typedef struct CdReq { /* 0x00 */ s32 state; /* 0x04 */ s32 unk04; /* 0x08 */ s32 phase; /* 0x0C */ s32 unk0C; /* 0x10 */ s32 unk10; /* 0x14 */ s32 texIdx; /* 0x18 */ s32 unk18; /* 0x1C */ s32 unk1C; /* 0x20 */ XRECT rect; /* 0x28 */ XRECT *rectp; /* 0x2C */ s32 count; /* 0x30 */ s32 busy; /* 0x34 */ s32 list; /* 0x38 */ s32 sink; /* 0x3C */ s32 dest; /* 0x40 */ s32 unk40[6]; /* 0x58 */ s32 words; /* 0x5C */ s32 drain; /* 0x60 */ s32 unk60[2]; /* 0x68 */ u8 mask[0x40]; /* 0xA8 */ u8 flags; /* 0xA9 */ u8 unkA9; /* 0xAA */ u8 bit; /* 0xAB */ u8 byteIdx; /* 0xAC */ s32 unkAC[3]; /* 0xB8 */ s32 size; /* 0xBC */ s32 posInt; } CdReq; extern s32 cdReq_state; extern s32 cdReq_posInt; extern s32 cdReq_drainPhase; extern s32 D_800AE6F8; extern u8 cdReq_sectorHdrBuf[]; extern u32 lzss_sectorStagingBuf[]; extern u8 D_80079A74; extern u8 D_80079A75; extern s32 D_80079A78; extern s32 D_80079A7C; extern u32 D_8007A280[]; extern void *D_80072C80; extern s32 func_80043994(void *buf, s32 n); /* func_800599B8 is declared by the TU at src/800.c:5480 (SpadRect_800184F0 *), ABOVE this * insertion point, so the draft must not redeclare it with a void* first parameter. */ extern s32 func_8002FB08(s32 a0); extern s32 func_8002FC64(s32 a0, u32 *a1); extern void func_8002FDC8(void); extern void func_80018714(void *); extern s32 LzssDecodeSector(u8 *src); void CdReadSectorReadyCB(u8 reason) { CdReq *p = (CdReq *)&cdReq_state; XRECT dead; u32 *q; XRECT *r; register s32 i __asm__("$18"); s32 n, pos, res, b; s32 tmp; s32 k80; s32 one; s32 dst; s32 cnt; s32 dst4; if (reason != 1) goto endReason; func_80043994(cdReq_sectorHdrBuf, 3); pos = CdPosToInt((CdlLOC *)cdReq_sectorHdrBuf); if (pos != cdReq_posInt) { if (D_800AE6F8 == 4) func_8002FDC8(); cdReq_drainPhase = 2; return; } cdReq_posInt = pos + 1; switch (D_800AE6F8) { case 0: func_80043994(lzss_sectorStagingBuf, 4); if (lzss_sectorStagingBuf[0] != 0x434150) goto abortDrain; p->flags = D_80079A75; switch (D_80079A74) { case 6: func_80043994(D_8007A280, 0x1FC); q = &D_8007A280[0x24]; n = D_80079A78; i = 0; p->rectp = (XRECT *)D_8007A280; if (n > 0) { do { func_800599B8(p->rectp, q); i++; r = p->rectp; q += (s16)r->w / 2; p->rectp = r + 1; } while (i < n); } goto sectorDone; setTwo: p->phase = 2; goto tilePhase; case 0: case 5: p->texIdx = 0; func_80043994(p->mask, 0x10); p->rect.h = 0x20; p->rect.w = 0x20; p->rect.y = 0; p->rect.x = 0; p->bit = 1; p->byteIdx = 0; for (;;) { if (p->byteIdx >= 0x40) { if (p->flags != 0) goto setTwo; p->phase = 0; goto tilePhase; } if ((p->mask[p->byteIdx] & p->bit) != 0) goto tilePhase; if (p->bit == 0x80) { p->bit = 1; p->byteIdx++; } else { p->bit <<= 1; } p->rect.x += 0x20; if ((s16)p->rect.x > 0x3FF) { p->rect.x = 0; p->rect.y += 0x20; } } tilePhase: p->phase++; break; case 7: if (p->size < 0) goto phaseDone; p->phase = 3; p->words = (u32)(D_80079A7C - 0x7FD) >> 2; p->sink = (s32)D_80072C80; break; case 1: p->phase = 3; p->words = (u32)(D_80079A7C - 0x7FD) >> 2; p->sink = p->dest; break; case 8: p->phase = 6; p->words = (u32)(D_80079A7C - 0x7FD) >> 2; break; case 2: if (p->busy != 0) goto phaseDone; p->phase = 3; dst = *(s32 *)(p->count * 8 + p->list + 4); tmp = D_80079A7C - 0x7FD; p->words = (u32)tmp >> 2; p->sink = dst; break; case 3: if (p->busy != 0) goto phaseDone; do { res = func_8002FB08(*(s32 *)(p->count * 8 + p->list)); } while (res == 0); p->phase = 4; p->words = D_80079A7C - 0x800; break; case 4: p->phase = 5; p->words = (u32)(D_80079A7C - 0x7FD) >> 2; dst4 = p->dest; p->sink = dst4; func_80018714((void *)dst4); break; } break; case 1: q = &lzss_sectorStagingBuf[p->texIdx * 0x200]; func_80043994(q, 0x200); func_800599B8(&p->rect, q); p->texIdx = (p->texIdx + 1) & 3; k80 = 0x80; one = 1; do { b = p->bit; if (b == k80) { p->bit = one; p->byteIdx++; } else { p->bit = b << 1; } if (p->byteIdx < 0x40) goto cont1; sectorDone: if (p->flags != 0) goto phaseDone; p->phase = 0; goto post; cont1: p->rect.x += 0x20; if ((s16)p->rect.x > 0x3FF) { p->rect.x = 0; p->rect.y += 0x20; } } while ((p->mask[p->byteIdx] & p->bit) == 0); break; case 3: if (p->words < 0x201) { func_80043994((void *)p->sink, p->words); goto sectorDone; } func_80043994((void *)p->sink, 0x200); p->sink += 0x800; p->words -= 0x200; break; case 4: if (p->words < 0x801) { cnt = (p->words + 3) / 4; func_80043994(lzss_sectorStagingBuf, cnt); if (func_8002FC64(p->words, lzss_sectorStagingBuf) == 0) goto stageFail; p->count++; if (p->flags != 0) goto phaseDone; p->phase = 0; break; } func_80043994(lzss_sectorStagingBuf, 0x200); res = func_8002FC64(0x800, lzss_sectorStagingBuf); p->sink += 0x800; p->words -= 0x800; if (res != 0) break; stageFail: func_8002FDC8(); p->phase = 7; break; case 5: func_80043994(lzss_sectorStagingBuf, 0x200); if (LzssDecodeSector((u8 *)lzss_sectorStagingBuf) == 0) goto sectorDone; break; case 6: p->words -= 0x200; if (p->words > 0) break; if (p->flags == 0) goto setZero; phaseDone: p->phase = 2; break; setZero: p->phase = 0; break; } post: if (p->phase == 2) { p->drain = 1; } else if (p->phase == 7) { p->drain = 2; } endReason: if (reason == 5) { if (p->phase == 4) func_8002FDC8(); abortDrain: p->drain = 2; } } #endif extern s32 CdQueueBusy(void); void func_8001A9D8(void) { CdQueueBusy(); } void func_8001A9F8(s32 a0) { extern u16 D_800B9A02; extern u8 D_800A6610[]; extern u32 D_80063108; extern u32 D_80063120; u32 *p = (u32 *)(D_800A6610 + (D_800B9A02 << 14)); u32 *q1; u32 *q2; q1 = &D_80063108; *q1 = (p[1] & 0xFFFFFF) | 0x5000000; p[1] = (p[1] & 0xFF000000) | ((u32)q1 & 0xFFFFFF); q2 = &D_80063120; *q2 = (p[1] & 0xFFFFFF) | 0x5000000; p[1] = (p[1] & 0xFF000000) | ((u32)q2 & 0xFFFFFF); } extern s32 cdReq_curSector; s32 func_8001AA78(void) { return cdReq_curSector; } extern s32 cdReq_curSector; void func_8001AA88(s32 arg0) { cdReq_curSector = arg0; } void func_8001AA98(void) { } extern s32 func_8001ABBC(s32 a0, s32 a1, s32 a2, s32 a3, s32 a4); void func_8001AAA0(s32 arg0) { func_8001ABBC(1, arg0, 0, 0, 0); } extern s32 D_800BA1B4; extern u8 D_80062C38; extern s32 func_8001ABBC(s32 a0, s32 a1, s32 a2, s32 a3, s32 a4); void func_8001AAD0(s32 arg0, s32 arg1) { s32 idx; switch (arg0) { case 0: idx = 9; break; case 1: idx = 10; break; case 2: idx = 11; break; case 3: idx = 12; break; case 4: idx = 13; break; case 5: idx = 14; break; case 6: idx = 15; break; case 7: idx = 16; break; case 8: idx = 17; break; case 9: idx = 18; break; case 10: idx = 19; break; case 11: idx = 20; break; default: idx = 0; break; } if (D_800BA1B4 == 5) { D_800BA1B4 = 0; } func_8001ABBC(3, arg1, (s32)(&D_80062C38 + idx * 0x30), 0, 0); } void func_8001ABB4(void) { } extern s32 resLoad_lastId; extern s32 resLoad_loadedFileIdx; extern u8 resourceIdMap[]; extern u8 D_8006313C; extern s32 D_800BA1B4; extern void func_8002D4C8(s32 a0, s32 a1); extern s32 func_8001AE90(s32 a0, s32 a1, s32 a2); extern s32 func_8001ACF0(s32 arg0, s32 arg1, s32 *arg2, s32 arg3, s32 arg4); s32 func_8001ABBC(s32 a0, s32 a1, s32 a2, s32 a3, s32 a4) { s16 val; if (a0 == 1) { if (a1 == resLoad_lastId) { return 1; } if (*(s16 *)(resourceIdMap + a1 * 6) == resLoad_loadedFileIdx) { val = *(s16 *)(&D_8006313C + a1 * 6); if (val != 0) { func_8002D4C8((u16)val, 0); resLoad_lastId = a1; } return 1; } } if (D_800BA1B4 != 0) { if (D_800BA1B4 != 3) { return 0; } if (func_8001AE90(a0, a1, a2) != 0) { D_800BA1B4 = 0; return 1; } } if (func_8001ACF0(a0, a1, (s32 *)a2, a3, a4) == 0) { return 0; } D_800BA1B4 = 0; return 1; } extern s32 D_800747F0; extern s32 D_800747F4; extern s32 D_800747E4; extern s32 D_800747E8; extern s32 D_800C6D2C; extern s32 D_800AE6E4; extern s32 D_800BA318; extern s32 D_800A6550; extern s32 D_800BA1B4; extern s32 D_800AE640; extern s32 D_800A6430; extern s32 resLoad_lastId; extern s32 resLoad_loadedFileIdx; extern u8 resourceIdMap[]; extern u8 D_8006313C; extern void func_8002D4C8(int arg0, int arg1); s32 func_8001ACF0(s32 arg0, s32 arg1, s32 *arg2, s32 arg3, s32 arg4) { D_800747F0 = arg0; switch (arg0) { case 0: { s32 tmp; D_800C6D2C = arg1; D_800AE6E4 = (s32)arg2; D_800BA318 = arg3; D_800A6550 = arg4; tmp = *arg2; D_800BA1B4 = 1; D_800747F4 = tmp; break; } case 1: { s16 v; D_800BA1B4 = 2; if (arg1 == resLoad_lastId) { return 1; } if (*(s16 *)(resourceIdMap + arg1 * 6) == resLoad_loadedFileIdx) { v = *(s16 *)(&D_8006313C + arg1 * 6); if (v != 0) { func_8002D4C8((u16)v, 0); resLoad_lastId = arg1; } return 1; } D_800AE640 = arg1; D_800747F4 = arg1; break; } case 2: { s32 tmp; D_800AE6E4 = (s32)arg2; tmp = *arg2; D_800BA1B4 = 4; D_800747F4 = tmp; break; } case 3: D_800A6430 = arg1; D_800AE6E4 = (s32)arg2; D_800747F4 = arg1; D_800BA1B4 = 5; D_800747E4 = 0; D_800747E8 = 1; break; } return 0; } extern s32 D_800747F4; s32 func_8001AE90(s32 a1, s32 a2, s32 a3) { switch (a1) { case 0: case 2: return *(s32 *) a3 == D_800747F4; case 1: return a2 == D_800747F4; } return 0; } extern s32 D_800BA1B4; s32 func_8001AF04(void) { s32 value = D_800BA1B4; if (value == 0) { return 0; } if (value == 3) { return 1; } return 2; } extern s32 D_800BA1B4; extern s32 D_800AE6E4; extern s32 D_800BA318; extern s32 D_800C6D2C; extern s32 D_800A6550; extern s32 cdReq_curSector; extern void *cdReq_dest; extern s32 cdReq_size; extern void *cdReq_cdlFile; extern s32 D_800AE724; extern s32 D_800AE720; extern s32 cdReq_result; extern s32 D_800AE640; extern s32 D_800A6430; extern s16 D_800A6430_h __asm__("D_800A6430"); extern s32 D_800747E4; extern s32 CdQueueBusy(void); extern void CdReadStateMachine(int); extern s32 func_8001B394(s32); extern s32 func_8001B7C4(void *); extern s32 func_8001B0D4(void *, s32); void func_8001AF34(void) { s32 *cdlFile; s32 dest; s32 size; s32 mode; s32 sector; s32 ret; CdQueueBusy(); switch (D_800BA1B4) { case 0: return; case 1: cdlFile = (s32 *)D_800AE6E4; dest = D_800BA318; size = D_800C6D2C; mode = D_800A6550; if (CdQueueBusy() != 0) { ret = 0; goto chk; } if (cdReq_curSector == 0) { sector = *cdlFile; } else { sector = *cdlFile; if (sector != cdReq_curSector) { ret = 0; goto chk; } } cdReq_dest = (void *)dest; cdReq_size = size; cdReq_cdlFile = (void *)cdlFile; D_800AE724 = mode; D_800AE720 = 0; cdReq_curSector = sector; if (mode == 0) { D_800AE720 = 1; } CdReadStateMachine(0); ret = cdReq_result; chk: if (ret == 0) { return; } D_800BA1B4 = 3; return; case 2: if (func_8001B394(D_800AE640) == 0) { return; } D_800BA1B4 = 3; return; case 3: return; case 4: if (func_8001B7C4((void *)D_800AE6E4) == 0) { return; } D_800BA1B4 = 3; return; case 5: if (func_8001B0D4((void *)D_800AE6E4, D_800A6430_h) == 0) { return; } D_800747E4 = 0; D_800BA1B4 = 3; return; } } extern s32 D_800747E0; extern s32 D_800747E4; extern s32 D_800747E8; extern void func_80036AF8(CdlLOC *loc, s32 flags); extern s32 CdQueueBusy(void); extern int func_8001A114(void); s32 func_8001B0D4(void *arg0, s32 arg1) { s32 t; switch (D_800747E4) { case 0: func_80036AF8((CdlLOC *)arg0, (s16)arg1); D_800747E8 = 1; D_800747E4 = D_800747E4 + 1; goto L_ret0; case 1: t = CdQueueBusy(); switch (t) { case 0: D_800747E0 = 1; goto L_ret0; case 8: D_800747E8 = 4; D_800747E4 = 4; goto L_ret0; case 2: case 4: D_800747E8 = t; D_800747E4 = D_800747E4 + 1; goto L_ret0; case 1: D_800747E8 = t; goto L_ret0; } goto L_ret0; case 2: t = CdQueueBusy(); switch (t) { case 1: case 2: case 4: D_800747E8 = t; goto L_ret0; case 0: case 8: D_800747E8 = 0; return 1; } goto L_ret0; case 3: if (func_8001A114() == 0) { goto L_ret0; } D_800747E4 = 0; goto L_ret0; case 4: D_800747E8 = 0; return 1; } L_ret0: return 0; } extern s32 D_800747E8; s32 func_8001B22C(void) { return D_800747E8; } extern s32 resLoad_curId; extern s32 resLoad_result; extern void func_8001B26C(void); s32 func_8001B23C(s32 a0) { resLoad_curId = a0; func_8001B26C(); return resLoad_result; } extern s32 D_800BA1B4; extern s32 resLoad_curId; extern s32 resLoad_result; extern s32 D_800C7C60; extern s32 *D_800C7C64; extern s32 D_800A2E20; extern s32 D_800AEC78; extern s32 func_8001AE90(s32 a0, s32 a1, s32 a2); extern s32 func_8001ACF0(s32 arg0, s32 arg1, s32 *arg2, s32 arg3, s32 arg4); void func_8001B26C(void) { register s32 *s0 __asm__("$16"); s32 v1; s32 tmp; if (resLoad_curId != 0) { goto exit; } s0 = &D_800C7C60; tmp = D_800BA1B4; *s0 = 0x8; D_800C7C64 = &D_800A2E20; if (tmp != 0) { if (tmp != 3) { v1 = 0; goto exit; } if (func_8001AE90(0, 0, (s32)&D_800AEC78) != 0) { goto set_flag; } } if (func_8001ACF0(0, 0, (s32 *)&D_800AEC78, 0, (s32)s0) == 0) { v1 = 0; goto exit; } set_flag: D_800BA1B4 = 0; v1 = 1; exit: resLoad_result = v1; } extern void func_8001B34C(void); extern s32 resLoad_state; void func_8001B324(void) { resLoad_state = 0; func_8001B34C(); } extern s32 resLoad_lastId; extern s32 resLoad_loadedFileIdx; extern s32 D_800A2B70; extern s32 D_800BA0FC; void func_8001B34C(void) { resLoad_lastId = 0; resLoad_loadedFileIdx = 0; D_800A2B70 = 0; D_800BA0FC = 0; } extern s32 resLoad_lastId; s32 func_8001B374(void) { return resLoad_lastId; } extern s32 resLoad_state; void func_8001B384(void) { resLoad_state = 0; } extern s32 resLoad_curId; extern s32 resLoad_result; extern void ResourceLoadStateMachine(void); s32 func_8001B394(s32 a0) { resLoad_curId = a0; ResourceLoadStateMachine(); return resLoad_result; } #ifdef NON_MATCHING extern int func_8001A114(void); extern void func_8001B710(void); extern void func_8002D4C8(int arg0, int arg1); extern void func_80036D58(s16); extern int StreamLoadStateMachine(int arg0, void *loc, int n); extern s32 CdQueueBusy(void); /* defined later in this file */ extern s32 ResourceGetCdLoc(s16 arg0); /* defined later in this file */ extern s32 resLoad_state; extern s32 resLoad_curId; extern s32 resLoad_lastId; extern s32 resLoad_loadedFileIdx; extern s32 resLoad_result; extern s32 cdReq_curSector; extern u8 resourceIdMap[]; /* 6B entries {s16 fileIdx; s16 D_8006313A; s16 D_8006313C} */ extern u8 D_8006313A; extern u8 D_8006313C; extern u8 cdFileLocTable[]; extern s32 D_800AE6F4; extern s32 D_800AE70C; /* loads a resource by resLoad_curId via resourceIdMap; load-once cache * (resLoad_lastId/resLoad_loadedFileIdx). field0<0 -> non-CD path func_80036D58; else * ResourceGetCdLoc -> StreamLoadStateMachine -> func_8002D4C8 post-process. Polled on * resLoad_state; done flag resLoad_result. Phase 3 T3. * NON_MATCHING: logically faithful (Ghidra-derived), body close but not byte-exact. Residuals: * (1) block placement — the target lays state blocks 0->1->2->3 in order under a top beq-chain * dispatch (order 1,0,2,3); this nested-if emits the state-2/3 block inline. (2) frame 0x38 vs * 0x28 (~16B reserved locals the original keeps). (3) func_8002D4C8 arg is field2 & 0xFFFF. * A switch() risks a rodata jump table (target has none). A later structural / permuter pass. */ void ResourceLoadStateMachine(void) { int result; result = 0; if (resLoad_state != 1) { if (resLoad_state != 0) { if (resLoad_state == 2) { D_800AE6F4 = 0; D_800AE70C = 0; resLoad_state = 3; } else if (resLoad_state != 3) { goto done; } if (func_8001A114() != 0) { resLoad_state = 0; } goto done; } if (resLoad_curId == resLoad_lastId) { resLoad_result = 1; return; } if (*(s16 *)(resourceIdMap + resLoad_curId * 6) == resLoad_loadedFileIdx && *(s16 *)(&D_8006313C + resLoad_curId * 6) != 0) { func_8002D4C8(*(s16 *)(&D_8006313C + resLoad_curId * 6), 0); resLoad_lastId = resLoad_curId; resLoad_result = 1; return; } if (CdQueueBusy() != 0) { goto done; } if (*(s16 *)(resourceIdMap + resLoad_curId * 6) < 0) { ((void (*)())func_80036D58)(*(s16 *)(&D_8006313A + resLoad_curId * 6)); resLoad_result = 1; return; } if (ResourceGetCdLoc((s16)resLoad_curId) == 0) { resLoad_result = 1; return; } if (cdReq_curSector != 0 && *(s32 *)(cdFileLocTable + *(s16 *)(resourceIdMap + resLoad_curId * 6) * 8) != cdReq_curSector) { goto done; } cdReq_curSector = *(s32 *)(cdFileLocTable + *(s16 *)(resourceIdMap + resLoad_curId * 6) * 8); resLoad_state++; } result = StreamLoadStateMachine(*(s16 *)(&D_8006313A + resLoad_curId * 6), cdFileLocTable + *(s16 *)(resourceIdMap + resLoad_curId * 6) * 8, 0x10); if (result == 2) { resLoad_state++; result = 0; } done: if (result != 0) { resLoad_loadedFileIdx = *(s16 *)(resourceIdMap + resLoad_curId * 6); if (*(s16 *)(&D_8006313C + resLoad_curId * 6) > 0) { func_8002D4C8(*(s16 *)(&D_8006313C + resLoad_curId * 6), 0); resLoad_lastId = resLoad_curId; if (resLoad_curId == 0x3D) { func_8001B710(); } } resLoad_state = 0; cdReq_curSector = 0; } resLoad_result = result; } #else /* resourceIdMap is a table of 6-byte rows. The destination TU (src/800.c) already declares this * column set as three byte-typed externs (u8 resourceIdMap[], u8 D_8006313A, u8 D_8006313C) in two * other functions (func_8001ACF0, ResourceGetCdLoc) — a second, differently-typed file-scope * extern of the same symbols is a hard C constraint violation, so those exact declarations are * adopted verbatim below. See the use-site comment for how the byte match is kept despite that. */ extern int func_8001A114(void); extern void func_8001B710(void); extern void func_8002D4C8(int arg0, int arg1); extern void func_80036D58(s16); extern int StreamLoadStateMachine(int arg0, void *loc, int n); extern s32 CdQueueBusy(void); extern s32 ResourceGetCdLoc(s16 arg0); extern s32 resLoad_state; extern s32 resLoad_curId; extern s32 resLoad_lastId; extern s32 resLoad_loadedFileIdx; extern s32 resLoad_result; extern s32 cdReq_curSector; extern u8 resourceIdMap[]; /* 6B entries {s16 fileIdx; s16 D_8006313A; s16 D_8006313C} */ extern u8 D_8006313A; extern u8 D_8006313C; extern u8 cdFileLocTable[]; /* 8B rows {s32 startSector; s32 size}; sites carry an explicit *8 */ extern s32 D_800AE6F4; extern s32 D_800AE70C; /* Byte-measured (both tried literally, both NON-MATCH at the same 109/211 site): a raw * u8-pointer-arithmetic use site (mirroring the other functions' own idiom), AND a * `(ResMapCol *) resourceIdMap` cast of the u8[] object, both leave gcc treating the address as a * computed POINTER value — it gets CSE'd into a callee-saved reg (`la $s0,resourceIdMap` hoisted * across the 6 uses) instead of rematerialising `lui %hi / addu / lo_sum` at every site the way the * target does. Only a genuine array-of-struct OBJECT TYPE avoids this (gcc builds a true ARRAY_REF * off the symbol instead of an INDIRECT_REF off a pointer expression) — but declaring `resourceIdMap` * that way conflicts with the u8[] extern already load-bearing elsewhere in this TU (func_8001ACF0, * ResourceGetCdLoc): two file-scope externs of one identifier with incompatible types is a hard C * constraint violation, which is exactly what the reconciler's refuse-TYPE caught. * The resolution (cookbook §37/§124 "data asm-label alias"): declare a SEPARATE C identifier bound * to the SAME linker symbol via `__asm__("resourceIdMap")`. A distinct identifier sidesteps the * redeclaration-type conflict entirely (the TU's own u8[] declarations, wherever they occur, are * untouched and still visible under their own name), while this alias gets the true array-of-struct * type gcc needs to avoid the CSE. Byte-measured MATCH. */ typedef struct { s16 v; s16 pad[2]; } ResMapCol; extern ResMapCol aResourceIdMap[] __asm__("resourceIdMap"); extern ResMapCol aD_8006313A[] __asm__("D_8006313A"); extern ResMapCol aD_8006313C[] __asm__("D_8006313C"); #define RES_FILE aResourceIdMap #define RES_ARG aD_8006313A #define RES_POST aD_8006313C /* Loads the resource named by resLoad_curId. Polled state machine (resLoad_state 0..3); the * caller reads the done flag out of resLoad_result. * state 0 arbitrate: load-once cache hit (resLoad_lastId / resLoad_loadedFileIdx), non-CD * resources (file index < 0), CD-queue busy, and the in-flight-sector dedup; on success * latch cdReq_curSector and FALL THROUGH into state 1. * state 1 pump StreamLoadStateMachine until it reports 2 (done). * state 2 reset the stream bookkeeping and FALL THROUGH into state 3. * state 3 poll func_8001A114 for the tail work, then idle. * * The switch index is read UNSIGNED: that is what makes gcc's case tree degrade to the target's * bare equality chain (tested 1,0,2,3 = balance_case_nodes' root-then-bounded-left order). Signed * (`switch (resLoad_state)`) inserts an `slti v0,v1,2` range test the target does not have. * resLoad_state itself keeps the TU-canonical s32 type; the cast lives at the use site (§20). */ void ResourceLoadStateMachine(void) { s32 result; /* The original keeps 8 bytes of frame the emitted code never touches (target frame 0x38, an * empty-locals build gets 0x30). Reserved as a local so the prologue/epilogue offsets match. */ s32 sp10[2]; result = 0; switch ((u32)resLoad_state) { case 0: if (resLoad_curId == resLoad_lastId) { resLoad_result = 1; return; } if (RES_FILE[resLoad_curId].v == resLoad_loadedFileIdx && RES_POST[resLoad_curId].v != 0) { func_8002D4C8((u16)RES_POST[resLoad_curId].v, 0); resLoad_lastId = resLoad_curId; resLoad_result = 1; return; } if (CdQueueBusy() != 0) { break; } if (RES_FILE[resLoad_curId].v < 0) { ((void (*)())func_80036D58)(RES_ARG[resLoad_curId].v); resLoad_result = 1; return; } if (ResourceGetCdLoc((s16)resLoad_curId) == 0) { resLoad_result = 1; return; } if (cdReq_curSector != 0 && *(s32 *)(cdFileLocTable + RES_FILE[resLoad_curId].v * 8) != cdReq_curSector) { break; } cdReq_curSector = *(s32 *)(cdFileLocTable + RES_FILE[resLoad_curId].v * 8); resLoad_state++; /* fallthrough */ case 1: result = StreamLoadStateMachine(RES_ARG[resLoad_curId].v, &cdFileLocTable[RES_FILE[resLoad_curId].v * 8], 0x10); if (result == 2) { resLoad_state++; result = 0; } break; case 2: D_800AE6F4 = 0; D_800AE70C = 0; resLoad_state = 3; /* fallthrough */ case 3: if (func_8001A114() != 0) { resLoad_state = 0; } break; } if (result != 0) { resLoad_loadedFileIdx = RES_FILE[resLoad_curId].v; if (RES_POST[resLoad_curId].v > 0) { func_8002D4C8((u16)RES_POST[resLoad_curId].v, 0); resLoad_lastId = resLoad_curId; if (resLoad_curId == 0x3D) { func_8001B710(); } } resLoad_state = 0; cdReq_curSector = 0; } resLoad_result = result; } #endif void func_8001B710(void) { if ((func_800291B4(0x44) & 0xFF) > 1) { func_8002D4C8(0x24, 0); } if ((func_800291B4(0x56) & 0xFF) > 1) { func_8002D4C8(0x25, 0); } if ((func_800291B4(0x5C) & 0xFF) > 1) { func_8002D4C8(0x26, 0); } } extern u8 resourceIdMap[]; extern u8 cdFileLocTable[]; s32 ResourceGetCdLoc(s16 arg0) { s32 idx = *(s16 *)(resourceIdMap + arg0 * 6); return *(s32 *)(cdFileLocTable + idx * 8); } extern s32 CdQueueBusy(void); extern void CdReadStateMachine(int); extern s32 cdReq_curSector; extern void *cdReq_cdlFile; extern s32 cdReq_result; extern s32 D_800AE70C; s32 func_8001B7C4(void *a0) { void *s0; s32 curSector; s32 result; if (CdQueueBusy() != 0) { return 0; } s0 = a0; curSector = cdReq_curSector; if (curSector == 0) { goto L8001B804; } if (curSector == -1) { goto L8001B804; } return 0; L8001B804: cdReq_curSector = -1; cdReq_cdlFile = s0; D_800AE70C = 0; CdReadStateMachine(1); result = cdReq_result; if (result == 1) { D_800AE70C = *(s32 *)s0; } return result; } extern s32 D_800747FC; void func_8001B85C(void) { D_800747FC = 0; } /* func_8001B86C — main/800: the 4-state "start a new frame" state machine driven * by D_800747FC. State 0 initialises the double-buffer OT block for the current * buffer index D_800B9A02 and kicks func_80059BFC/func_8001BA84; state 1 arms the * frame counter; state 2 runs the per-frame work; states 2 and 3 share the tail * that ticks D_80074804 and drives func_8001BADC. * * Three byte-verified levers were required (all measured, none guessed): * * 1. UNSIGNED SWITCH INDEX (cookbook §135-1 / §163b). The dispatch is a pure * equality chain `beq 1 / beqz / beq 2 / beq 3` with NO `slti` range test. * `switch (D_800747FC)` (signed) emits the median-split tree with an `slti`; * only an index whose type is still unsigned after the default promotions * reaches the no-range-test form, so the subject is cast to u32. src/800.c * already declares `extern s32 D_800747FC;` (L2770) — adopted verbatim and the * unsignedness moved into the switch subject (§174 law 3, DEF-side wall). * * 2. `break` + ONE trailing `return 0`, NOT `return 0` per arm. With a `return` * in every arm gcc gives the DECL_RESULT pseudo a live range that reaches into * each arm, it lands in $2, and two things break at once: the case-0 tail * allocates $v1/$a0 instead of the target's $v0/$v1, and reorg refuses to fill * the `beqz` delay slot with the following `addiu $v0,$zero,2` (a stray `nop`, * 135 ins vs 134). Writing `break` and a single `return 0` after the switch * frees $2 and both residuals close together — one source edit, 12 -> 0. * * 3. The OT table is touched through TWO declarations of the SAME symbol. The * target reloads D_800B9A02 three times inside case 0, which only happens when * the stores are plain INDIRECT_REFs: an ARRAY_REF store sets MEM_IN_STRUCT_P * and gcc-2.7.2's true_dependence() then drops the dependence against the * not-in-struct constant-address `lhu` (cookbook §135-2), collapsing the three * loads into one. But a plain store to `SYM+reg` *plus any read of the same * DECL* makes gcc allocate a dead 8-byte stack temp (frame 0x20 vs the target's * 0x18) — measured, and it survives every re-spelling of either access. Two * distinct DECLs of the same assembler symbol avoid the temp while keeping the * plain-store aliasing, so the byte view drives the stores and the s32 view * (`otTbl`, asm-labelled to D_800A651C) drives the loads. reloc_identity.py * reports AGREE / 34 relocations: every symbol here is the one the target's own * relocation lines name (§174 law 1c). * * @class: MATCH (was LENGTH-DRIFT/+1 -> ADDRESSING -> MATCH) */ extern s32 D_800747FC; extern s32 D_80074800; extern s32 D_80074804; extern s32 D_8007480C; extern u16 D_800B9A02; extern u8 D_800A6610[]; extern u8 *D_800A5E60; extern u8 D_8007BA70[]; extern u8 D_800AF630[]; extern void func_80059BFC(s32 a0, s32 a1); extern void func_8001BA84(void); extern void func_80059FC0(u8 *a0); extern void func_8001BB60(void); extern s32 func_8001BADC(s32 a0, s32 a1); s32 func_8001B86C(void) { /* block scope: src/800.c declares D_800A651C as OtBlk_80016450[] inside * func_80015D4C, so the byte view has to stay local too. */ extern u8 otBytes[] __asm__("D_800A651C"); extern u8 D_800A6528[]; /* = D_800A651C + 12 */ extern s32 otTbl[] __asm__("D_800A651C"); /* s32 view of D_800A651C — see note 3 */ u8 *p = D_800AF630; switch ((u32)D_800747FC) { case 0: { u8 *q = D_8007BA70 + D_800B9A02 * 80000; *(s32 *)(otBytes + D_800B9A02 * 20) = (s32)(D_800A6610 + (D_800B9A02 << 14)); *(s32 *)(D_800A6528 + D_800B9A02 * 20) = (s32)(D_800A6610 + 4 + (D_800B9A02 << 14)); D_800A5E60 = q; func_80059BFC(otTbl[D_800B9A02 * 5], 2); func_8001BA84(); D_80074800 = 0x80; D_800747FC++; break; } case 1: D_80074804 = 1; D_800747FC = 2; break; case 2: func_80059FC0(p + 0x160); func_80059BFC(otTbl[D_800B9A02 * 5], 2); func_8001BB60(); D_800747FC++; /* fallthrough */ case 3: if (D_80074804++ >= 2) { D_80074804 = 1; if (func_8001BADC(D_8007480C, 1) != 0) { return 1; } D_8007480C++; } else { func_8001BADC(D_8007480C, 0); } break; } return 0; } extern void func_8001BC6C(s32 a0, s32 a1); extern u8 D_800630E8[]; void func_8001BA84(void) { s32 addr = (s32)&D_800630E8; func_8001BC6C(addr, 0x80); func_8001BC6C(addr + 8, 0x80); func_8001BC6C(addr + 16, 0x80); func_8001BC6C(addr + 24, 0x80); } extern s32 D_80074808; s32 func_8001BADC(s32 arg0, s32 arg1) { register s32 orig __asm__("$10"); register s32 i __asm__("$8"); register s32 mask __asm__("$11"); register s32 off __asm__("$9"); register s32 c29 __asm__("$12"); register s32 colbase __asm__("$13"); register s32 c __asm__("$6"); register s32 p __asm__("$5"); s32 color; s32 idx; s32 *addr; orig = arg0; i = 0; mask = -(arg1 != 0); colbase = 0x62000000; c29 = 29; off = 0; while (i < 15) { c = 0; if (i < orig) { c = mask & 8; } color = (c << 16) | ((c << 8) | colbase) | c; idx = c29 - i; p = D_80074808; addr = (s32 *)(p + off); addr[2] = color; addr = (s32 *)(p + idx * 20); addr[2] = color; i++; off += 20; } return orig >= 44; } extern u8 *D_800A5E60; extern s32 D_80074808; extern s32 D_8007480C; extern void func_8001BBBC(s32 a0, s32 a1, u8 a2); void func_8001BB60(void) { s32 i; D_80074808 = (s32)D_800A5E60; for (i = 0; i < 30; i++) { func_8001BBBC(0, i * 8, 0); } D_8007480C = 0; } /* func_8001BBBC — main/800: build the 0x14-byte "scanline band" primitive at the * head of the current prim buffer (D_800A5E60) — a GPU draw-mode word (0xE1000040) * followed by a TILE (code 0x62, 0x140x8 at (a0-160, a1-120), colour a2) — then * PSY-Q addPrim() it onto OT entry 1 of the current buffer's table * (D_800A6610 + (D_800B9A02 << 14)) and bump D_800A5E60 past it. * * @class: MATCH (was SCHEDULE-REORDER/2) * * THE RESIDUAL AND ITS MECHANISM (the only non-obvious thing here; cookbook * material — sched.md S1/S2 + §37's 2-instruction-constant note): * * The target materialises the 24-bit mask as `lui $t1,0xff / addiu $v0,4 / * ori $t1,$t1,0xffff` — the independent `li 4` sits BETWEEN the two halves of * the mask. Written as one constant (`mFF = 0xFFFFFF`) that is UNREACHABLE: * mips.md:3208's `large_int` define_split fires inside sched1's try_split * (sched.c:4830), so the lui/ori get CONSECUTIVE LUIDs, and rank_for_schedule's * only tie-break among the priority-1 constants is exactly that LUID * (sched.c:2428). No statement order can put a third insn between two * consecutive LUIDs — a 63-variant statement-order sweep confirmed it (every * one scored the same 2). So the target's two halves are NOT a split pair: * they are two insns from expand. Two edits reproduce that: * * 1. Build the mask in two steps with a zero-byte re-tie between them * (`__asm__("" : "=r"(mFF) : "0"(mFF))`, the §30-#3 idiom already used by * func_80016110/func_80016450 in this file) so cse cannot fold * `0xFF0000 | 0xFFFF` back into one CONST_INT. Now the `ori` carries its * own LUID, taken at the point the `|=` is written. * 2. Route the len byte's 4 through the already-multi-set `tmp` ($2) instead of * an anonymous temp. An anonymous temp is single-set, so birthing_insn_p * (sched.c:2469) boosts it to LAUNCH_PRIORITY and it sinks BELOW the mask's * low half; a hard-reg pseudo with reg_n_sets>1 gets no boost and floats up * with the other priority-1 constants in plain ascending-LUID order — which, * with the `|=` written after the store, lands it exactly between the halves. * * Both are required: the split alone still scores 2, the boost-kill alone * moves the `li 4` one slot too far (ahead of the `lui`). * * Symbols hand-checked against asm/nonmatchings/800/func_8001BBBC.s (§174 law 1c): * D_800A5E60 (lw %lo + sw %lo), D_800B9A02 (lhu %lo), D_800A6610 (addiu %lo); * no calls. All three externs are copied verbatim from src/800.c L4489-4491 * (the same file-scope spellings func_8001B86C and func_8001D3FC already use). */ extern u16 D_800B9A02; extern u8 D_800A6610[]; extern u8 *D_800A5E60; void func_8001BBBC(s32 a0, s32 a1, u8 a2) { register u8 *p __asm__("$7"); register u32 tblAddr __asm__("$8"); register u32 tmp __asm__("$2"); register u32 val __asm__("$3"); register u32 mFF __asm__("$9"); register u32 m24 __asm__("$4"); register u32 tag0 __asm__("$10"); u32 *tbl; tag0 = 0xE1000040; mFF = 0xFF0000; __asm__("" : "=r"(mFF) : "0"(mFF)); /* zero-byte re-tie: keeps the mask's two * halves as two expand insns (see header) */ a0 -= 0xA0; p = D_800A5E60; tblAddr = D_800B9A02; a1 -= 0x78; tmp = 4; *(u8 *)(p + 3) = tmp; mFF |= 0xFFFF; *(u8 *)(p + 11) = 0x62; *(u16 *)(p + 16) = 0x140; *(u16 *)(p + 18) = 8; val = *(u32 *)p; tblAddr = (tblAddr << 14) + (u32)&D_800A6610; tbl = (u32 *)tblAddr; *(u16 *)(p + 12) = (u16)a0; *(u32 *)(p + 4) = tag0; *(u8 *)(p + 10) = a2; *(u8 *)(p + 9) = a2; *(u8 *)(p + 8) = a2; *(u16 *)(p + 14) = (u16)a1; tmp = tbl[1]; m24 = 0xFF000000; val = (val & m24) | (tmp & mFF); *(u32 *)p = val; val = (u32)(p + 0x14); D_800A5E60 = (u8 *)val; tmp = tbl[1]; tmp = (tmp & m24) | ((u32)p & mFF); tbl[1] = tmp; } /* addPrim-style sprite emitter (S83 MATCH — 28 -> 6 by the §500-C levers, 6 -> 0 by the two permuter_ils levers below): builds a 0x18-byte SPRT-with-tpage packet at the * D_800A5E60 prim-buffer cursor and addPrim()s it onto word [1] of the frame's * ordering table (D_800A6610 + D_800B9A02 * 0x4000). Same idiom as the -O0 * func_8001212C in src/boot.c. */ typedef struct { u32 addr : 24; u32 len : 8; } PTag_8001BC6C; extern u16 D_800B9A02; extern u8 D_800A6610[]; extern u8 *D_800A5E60; void func_8001BC6C(s32 a0, s32 a1) { short *p1 = (short *)a0; u8 *p; u32 *ot; register u32 color __asm__("$5"); register short f0 __asm__("$2"); u32 tag; u32 c; u32 c2; u32 k; u32 idx; u32 code; unsigned short f1; short f2; unsigned short f3; unsigned short x1; unsigned short y1; short t0; code = 0x100; /* §47 live-length lever (S83, found by permuter_ils then re-spelled well-defined): birthing `tag` * (and `k`) HERE lengthens their pseudos' live ranges so local-alloc's qty_compare ranks the OT * index above the base — the $v0/$v1 swap across the six OT-chain insns disappears. The value is * recomputed below; the early statement exists only for the allocator. Paired with `idx` computed * AFTER `color` (the second lever). Every well-defined alternative birth (tag = a1 << 8, tag = 0, * k first) measured 8–20 mismatches. */ k = 0; tag = (a1 << 8) | k; c = a1 << 16; k = 0x64000000; f0 = p1[0]; f1 = p1[1]; f2 = p1[2]; f3 = p1[3]; p = D_800A5E60; x1 = f0 - 0xA0; y1 = f1 - 0x78; t0 = f0; tag = (a1 << 8) | k; c2 = c | tag; color = c2 | a1; idx = D_800B9A02 << 14; ot = (u32 *)(idx + (u32)D_800A6610); if (t0 >= 0x80) { t0 -= 0x80; code = 0x102; } *(u8 *)(p + 3) = 5; *(u32 *)(p + 4) = code | 0xE1000000; *(u8 *)(p + 0xB) = 100; *(u32 *)(p + 0xC) = ((u32)y1 << 16) | (u32)x1; *(u32 *)(p + 0x10) = (u32)(((s32)((u32)f1 << 16) >> 8) | (s32)t0); *(u32 *)(p + 8) = color; *(u32 *)(p + 0x14) = ((u32)f3 << 16) | (u32)f2; ((PTag_8001BC6C *)p)->addr = ((PTag_8001BC6C *)(ot + 1))->addr; D_800A5E60 = p + 0x18; ((PTag_8001BC6C *)(ot + 1))->addr = (u32)p; } extern s32 CdQueueBusy(void); s32 SsGetMute(void) { return CdQueueBusy() != 0; } extern void func_8002CCD8(void); extern void func_800596F4(s32); extern void DrawSyncCallback(s32); extern void func_80059234(s32); extern void func_80059658(s32); extern int VSync(int mode); extern void PadStopCom(void); extern void StopRCnt(void); extern void func_80043300(void); extern void ResetCallback(void); extern void StopCallback(void); void func_8001BDA0(void) { func_8002CCD8(); func_800596F4(0); DrawSyncCallback(0); func_80059234(0); func_80059658(0); VSync(0); func_800596F4(0); func_80059234(3); PadStopCom(); StopRCnt(); func_80043300(); ResetCallback(); StopCallback(); } extern s32 D_800747EC; void func_8001BE20(void) { D_800747EC = 0; } extern s32 D_800747EC; extern s32 D_800747E4; extern s32 D_800747E8; extern s32 D_800BA1B4; extern s32 cdReq_curSector; extern s32 CdQueueBusy(void); extern void func_8002D4C8(int arg0, int arg1); s32 func_8001BE30(void) { s32 state = D_800747EC; s32 busy; s32 v0; if (state == 1) { goto L_case1; } if (state < 2) { if (state != 0) { return 0; } goto L_case0; } if (state == 2) { goto L_case2; } if (state == 3) { goto L_case3; } return 0; L_case0: busy = CdQueueBusy(); if (D_800BA1B4 != 5) { goto L_busy; } if (D_800747E4 != 0) { func_8002D4C8(0x16, 0); } D_800BA1B4 = 0; D_800747E8 = 0; D_800747E4 = 0; D_800747EC = 3; goto L_ret0; L_busy: if (busy == 1 || busy == 4 || busy == 2) { func_8002D4C8(0x16, 0); D_800747EC = D_800747EC + 1; goto L_ret0; } if (cdReq_curSector == 0) { return 1; } D_800747EC = 2; return 0; L_case1: v0 = CdQueueBusy(); goto L_shared; L_case2: v0 = cdReq_curSector; goto L_shared; L_case3: v0 = D_800747E8; L_shared: if (v0 == 0) { __asm__ __volatile__(""); return 1; } L_ret0: return 0; } extern s32 D_80063074; s32 func_8001BFA0(void) { return D_80063074 & 0x1; } extern s32 D_80063074; void func_8001BFB0(void) { D_80063074++; } extern s32 D_800A2B70; extern s32 D_800BA0FC; void func_8001BFD0(void) { D_800A2B70 = 0; D_800BA0FC = 0; } extern s32 D_800747D8; s32 func_8001BFE8(void) { return D_800747D8; } extern s32 D_800747D8; s32 func_8001BFF8(void) { return D_800747D8 = 1; } extern u8 D_800B9A11; extern void func_8001F9F8(void); extern void func_8001D1C4(void); void func_8001C00C(void) { if (D_800B9A11 != 1) { func_8001F9F8(); func_8001D1C4(); } } void func_8001C044(void) { extern void func_80016714(void *a0, s32 a1); extern u8 D_800AF630[]; u8 *base; u8 *p; s32 i; base = D_800AF630; p = &base[0x65A8]; i = 0; do { func_80016714(p, 0x38); i++; p += 0x38; } while (i < 0x100); p = &base[0x2A8]; i = 0; do { func_80016714(p, 0x84); i++; p += 0x84; } while (i < 0xC0); *(u16 *)&base[0x19C] = 0; } void func_8001C0C8(void) { extern void func_80016714(void *a0, s32 a1); extern u8 D_800AF630[]; u8 *base; u8 *p; s32 i; base = D_800AF630; p = &base[0x65A8]; i = 0; do { func_80016714(p, 0x38); i++; p += 0x38; } while (i < 0x100); p = &base[0x2A8]; i = 0; do { func_80016714(p, 0x84); i++; p += 0x84; } while (i < 0xC0); *(u16 *)&base[0x19C] = 0; } extern void func_80016714(void *a0, s32 a1); extern u8 D_800AF630[]; void func_8001C14C(void) { u8 *base; u8 *p; base = D_800AF630; p = base + 0x65A8; while (p < base + 0x6688) { func_80016714(p, 0x38); p += 0x38; } p = base + 0x2A8; while (p < base + 0x4B8) { func_80016714(p, 0x84); p += 0x84; } } extern void func_80054514(s32 a0, s32 a1); void func_8001C1E4(void* a0, s32 a1) { s32 local_buf[8]; *(s32 *)(a0 + 0x78) = a1 + 0x30; *(s32 *)(a0 + 0x30) = 0; func_80054514((s32)(a0 + 0x30), (s32)local_buf); } void func_8001C214(s32 a0, s32 a1) { extern void func_8001C9D0(void); extern void func_80052D90(s32 a0, void *a1); extern void func_80054514(s32 a0, s32 a1); extern void GsMapModelingData(u32 *p); extern u8 D_80063548[]; s32 sp10[8]; func_8001C9D0(); *(u16 *)a0 = 1; *(u16 *)(a0 + 2) = 0; func_80052D90(0, (void *)(a0 + 0x30)); func_80054514(a0 + 0x30, (s32)&sp10[0]); *(s32 *)(a0 + 0x24) = a1; if (a1 != 0) { *(s32 *)(a0 + 0x20) = (s32)D_80063548; if (*(s32 *)a1 != 0) { do { GsMapModelingData((u32 *)((*(s32 *)a1 & 0x80FFFFFF) + 4)); a1 += 4; } while (*(s32 *)a1 != 0); } } } extern void func_8001C9D0(void); extern void func_80052D90(s32 a0, void *a1); extern void func_80054514(s32 a0, s32 a1); void func_8001C2C4(s32 a0) { s32 stack_buf[8]; s32 *s1; func_8001C9D0(); s1 = (s32 *)(a0 + 0x30); *(s16 *)(a0 + 0x0) = 1; *(s16 *)(a0 + 0x2) = 0; func_80052D90(0, s1); func_80054514((s32)s1, (s32)stack_buf); *(s32 *)(a0 + 0x24) = 0; } void func_8001C320(s32 a0, s32 a1) { extern void func_8001C9D0(void); extern void func_80052D90(s32 a0, void *a1); extern void func_80054514(s32 a0, s32 a1); extern void GsMapModelingData(u32 *p); extern void GsLinkObject5(); extern void *GsPresetObject(); extern u8 D_800AF630[]; extern u8 D_80063548[]; extern u16 D_800AF7CC; extern s32 *D_800AF7C8; extern u8 D_800A5BD0[]; s32 sp10[8]; register s32 obj __asm__("$17"); register s32 next __asm__("$19"); register s32 work __asm__("$20"); s32 entry; u16 c2; obj = a0; func_8001C9D0(); *(u16 *)obj = 1; *(u16 *)(obj + 2) = 1; func_80052D90(0, (void *)(obj + 0x30)); func_80054514(obj + 0x30, (s32)&sp10[0]); work = (s32)D_800AF630; *(s32 *)(obj + 0x24) = a1; if (a1 != 0) { *(s32 *)(obj + 0x20) = (s32)D_80063548; next = (s32)D_800AF7C8; *(u16 *)(obj + 0x2E) = D_800AF7CC; if (*(s32 *)a1 != 0) { do { entry = (*(s32 *)a1 & 0x80FFFFFF) + 4; a1 += 4; __asm__ __volatile__("" : "=r"(entry) : "0"(entry)); GsMapModelingData((u32 *)entry); c2 = *(u16 *)(work + 0x19C); *(u16 *)(work + 0x19C) = c2 + 1; GsLinkObject5((u32 *)(entry + 8), (void *)&D_800A5BD0[(c2 << 2) + c2 << 2], 0); next = (s32)GsPresetObject( (void *)&D_800A5BD0[(c2 << 2) + c2 << 2], (void *)next); } while (*(s32 *)a1 != 0); } *(s32 *)(work + 0x198) = next; } } extern void func_8001C9D0(void); extern void func_80052D90(s32 a0, void *a1); extern void func_80054514(s32 a0, s32 a1); void func_8001C448(s32 a0) { s32 local_10[8]; s32 s0_val = a0; s32 s1_val; func_8001C9D0(); s1_val = s0_val + 0x30; *(s16 *)s0_val = 1; *(s16 *)(s0_val + 0x2) = 1; func_80052D90(0, (void *)s1_val); func_80054514(s1_val, (s32)&local_10[0]); *(s32 *)(s0_val + 0x24) = 0; } void func_8001C4A4(s32 a0, u32 *a1) { extern void func_8001C9D0(void); extern void func_80052D90(s32 a0, void *a1); extern void func_80054514(s32 a0, s32 a1); extern void GsMapModelingData(u32 *p); extern u8 D_80063548[]; s32 sp10[8]; func_8001C9D0(); *(u16 *)a0 = 1; *(u16 *)(a0 + 2) = 7; func_80052D90(0, (void *)(a0 + 0x30)); func_80054514(a0 + 0x30, (s32)&sp10[0]); *(s32 *)(a0 + 0x24) = (s32)a1; if (a1 != 0) { *(s32 *)(a0 + 0x20) = (s32)D_80063548; if (*a1 != 0) { do { GsMapModelingData((u32 *)((*a1 & 0x80FFFFFF) + 4)); a1 += 1; } while (*a1 != 0); } } } extern void func_8001C9D0(void); extern void func_80052D90(s32 a0, void *a1); extern void func_80054514(s32 a0, s32 a1); void func_8001C558(s32 a0) { u8 local_var[32]; func_8001C9D0(); *(s16 *)(a0 + 0x0) = 1; *(s16 *)(a0 + 0x2) = 7; func_80052D90(0, (void *)(a0 + 0x30)); func_80054514((s32)(a0 + 0x30), (s32)&local_var[0]); *(s32 *)(a0 + 0x24) = 0; } void func_8001C5B8(s32 a0, s32 a1) { extern void func_8001C9D0(void); extern void func_80052D90(s32 a0, void *a1); extern void func_80054514(s32 a0, s32 a1); extern void GsMapModelingData(unsigned long *p); extern void GsLinkObject5(); extern void *GsPresetObject(); extern u8 D_800AF630[]; extern u8 D_80063548[]; extern u16 D_800AF7CC; extern s32 *D_800AF7C8; extern u8 D_800A5BD0[]; s32 sp10[8]; register s32 obj __asm__("$17"); register s32 next __asm__("$19"); register s32 work __asm__("$20"); s32 entry; u16 c2; obj = a0; func_8001C9D0(); *(u16 *)obj = 1; *(u16 *)(obj + 2) = 8; func_80052D90(0, (void *)(obj + 0x30)); func_80054514(obj + 0x30, (s32)&sp10[0]); work = (s32)D_800AF630; *(s32 *)(obj + 0x24) = a1; if (a1 != 0) { *(s32 *)(obj + 0x20) = (s32)D_80063548; next = (s32)D_800AF7C8; *(u16 *)(obj + 0x2E) = D_800AF7CC; if (*(s32 *)a1 != 0) { do { entry = (*(s32 *)a1 & 0x80FFFFFF) + 4; a1 += 4; __asm__ __volatile__("" : "=r"(entry) : "0"(entry)); GsMapModelingData((unsigned long *)entry); c2 = *(u16 *)(work + 0x19C); *(u16 *)(work + 0x19C) = c2 + 1; GsLinkObject5((u32 *)(entry + 8), (void *)&D_800A5BD0[(c2 << 2) + c2 << 2], 0); next = (s32)GsPresetObject( (void *)&D_800A5BD0[(c2 << 2) + c2 << 2], (void *)next); } while (*(s32 *)a1 != 0); } *(s32 *)(work + 0x198) = next; } } extern void func_8001C9D0(void); extern void func_80052D90(s32 a0, void *a1); extern void func_80054514(s32 a0, s32 a1); void func_8001C6E4(void *a0) { s32 sp_buf[8]; ((void (*)(void *))func_8001C9D0)(a0); *(s16 *)a0 = 1; *(s16 *)((s32)a0 + 2) = 8; func_80052D90(0, (void *)((s32)a0 + 0x30)); func_80054514((s32)a0 + 0x30, (s32)sp_buf); *(s32 *)((s32)a0 + 0x24) = 0; } extern void func_8001C9D0(void); extern void func_80052D90(s32 a0, void *a1); extern void func_80054514(s32 a0, s32 a1); void func_8001C744(s32 a0, s32 a1) { char local_buf[32]; func_8001C9D0(); *(s16 *)a0 = 1; *(s16 *)(a0 + 2) = 2; func_80052D90(0, (void *)(a0 + 0x30)); func_80054514(a0 + 0x30, (s32)(&local_buf[0])); *(s32 *)(a0 + 0x24) = a1; } extern void func_8001C9D0(void); extern void func_80052D90(s32 a0, void *a1); extern void func_80054514(s32 a0, s32 a1); void func_8001C7B0(void *arg0) { s32 local_s1; u8 local_array[0x20]; ((void (*)(void *))func_8001C9D0)(arg0); local_s1 = (s32)((char *)arg0 + 0x30); *(s16 *)((char *)arg0 + 0x0) = 1; *(s16 *)((char *)arg0 + 0x2) = 2; func_80052D90(0, (void *)local_s1); func_80054514(local_s1, (s32)local_array); *(s32 *)((char *)arg0 + 0x24) = 0; } extern void func_8001C9D0(void); extern void func_80052D90(s32 a0, void *a1); extern void func_80054514(s32 a0, s32 a1); extern void GsMapModelingData(unsigned long *p); extern u8 D_80063548[]; void func_8001C810(s32 a0, s32 *a1) { s32 local_buf[8]; s32 base; func_8001C9D0(); base = a0 + 0x30; *(s16 *)(a0 + 0) = 1; *(s16 *)(a0 + 2) = 5; func_80052D90(0, (void *)base); func_80054514(base, (s32)local_buf); *(s32 *)(a0 + 0x24) = (s32)a1; if (a1 != 0) { *(s32 *)(a0 + 0x20) = (s32)&D_80063548; if (*a1 != 0) { do { GsMapModelingData((unsigned long *)((*a1 & 0x80FFFFFF) + 4)); a1++; } while (*a1 != 0); } } } extern void func_8001C9D0(void); extern void func_80052D90(s32 a0, void *a1); extern void func_80054514(s32 a0, s32 a1); void func_8001C8C4(s32 a0) { s32 local_10[4]; s32 local_20[4]; ((void (*)(s32))func_8001C9D0)(a0); *(s16 *)(a0 + 0x0) = 1; *(s16 *)(a0 + 0x2) = 5; func_80052D90(0, (void *)(a0 + 0x30)); func_80054514(a0 + 0x30, (s32)local_10); *(s32 *)(a0 + 0x24) = 0; } extern void GsMapModelingData(unsigned long *p); void func_8001C924(void *a0, s32 *a1) { s32 *s0; s32 v0; s0 = a1; *(s32 **)(a0 + 0x24) = s0; v0 = *s0; while (v0 != 0) { s32 tmp = *s0; s0++; GsMapModelingData((unsigned long *)((u32)tmp + 4)); v0 = *s0; } } extern void GsMapModelingData(unsigned long *p); void func_8001C97C(s32 *a0) { s32 *ptr = a0; s32 val; val = *ptr; if (val != 0) { do { s32 item = *ptr; ptr++; GsMapModelingData((unsigned long *)(item + 4)); val = *ptr; } while (val != 0); } } void func_8001C9D0(void) { register s32 a0v __asm__("$4"); s32 v; v = 0x1000; *(s16 *)(a0v + 0x1C) = v; *(s16 *)(a0v + 0x1A) = v; *(s16 *)(a0v + 0x18) = v; v = 0xC8; *(s16 *)(a0v + 0x14) = 0; *(s16 *)(a0v + 0x12) = 0; *(s16 *)(a0v + 0x10) = 0; *(s16 *)(a0v + 0x0C) = 0; *(s16 *)(a0v + 0x0A) = 0; *(s16 *)(a0v + 0x08) = 0; *(s32 *)(a0v + 0x04) = 0; *(s32 *)(a0v + 0x20) = 0; *(s16 *)(a0v + 0x28) = v; *(s16 *)(a0v + 0x2A) = v; *(s16 *)(a0v + 0x2C) = 0; *(s16 *)(a0v + 0x2E) = 0; *(s32 *)(a0v + 0x80) = 0; } extern void func_8001C9D0(void); extern void func_80052D90(s32, void *); extern void func_80054514(s32 a0, s32 a1); void func_8001CA1C(void *a0, void *a1) { register void *s0 __asm__("$16"); register void *s1 __asm__("$17"); register void *s2 __asm__("$18"); u32 local_pad[7]; s0 = a0; s2 = a1; func_8001C9D0(); s1 = (void *)((s32)s0 + 0x30); *(s16 *)s0 = 1; *(s16 *)((s32)s0 + 2) = 3; func_80052D90(0, s1); func_80054514((s32)s1, (s32)local_pad); *(s32 *)((s32)s0 + 0x20) = (s32)s2; } extern void func_8001C9D0(void); extern void func_80052D90(s32 a0, void *a1); extern void func_80054514(s32 a0, s32 a1); void func_8001CA88(s32 a0, s32 a1) { s32 local_10[8]; s32 s0_val = a0; s32 s2_val = a1; s32 s1_val; func_8001C9D0(); s1_val = s0_val + 0x30; *(s16 *)s0_val = 1; *(s16 *)(s0_val + 0x2) = 4; func_80052D90(0, (void *)s1_val); func_80054514(s1_val, (s32)&local_10[0]); *(s32 *)(s0_val + 0x20) = s2_val; *(s32 *)(s0_val + 0x74) = 0; *(s16 *)(s0_val + 0xE) = 0; *(s16 *)(s0_val + 0x1E) = 0; } extern void func_8001CF48(s32 a0); void func_8001CB00(s32 a0, void *a1, s32 a2, s32 a3) { func_8001CF48(a0); *(s16 *)(a0 + 0x2) = 1; *(s16 *)(a0 + 0x28) = (s16)a2; *(s16 *)(a0 + 0x2A) = (s16)a3; *(s32 *)(a0 + 0x20) = (s32)a1; *(s32 *)(a0 + 0x4) = 0x8000000; *(s16 *)(a0 + 0x2C) = 0; } extern void func_8001CF48(s32 a0); void func_8001CB6C(u8 *a0, s32 a1, s32 a2, s32 a3) { func_8001CF48(a0); *(s16 *)(a0 + 0x2) = 2; *(s16 *)(a0 + 0x28) = (s16)a2; *(s16 *)(a0 + 0x2A) = (s16)a3; *(s32 *)(a0 + 0x20) = (s32)a1; *(s32 *)(a0 + 0x4) = 0xC000040; *(s16 *)(a0 + 0x2C) = 0; } extern void func_8001CF48(s32 a0); void func_8001CBDC(s32 a0, void *a1, s32 a2, s32 a3) { func_8001CF48(a0); *(s16 *)(a0 + 0x2) = 0; *(s16 *)(a0 + 0x28) = (s16)a2; *(s16 *)(a0 + 0x2A) = (s16)a3; *(s32 *)(a0 + 0x20) = (s32)a1; *(s16 *)(a0 + 0x2C) = 0; } extern void func_8001CF48(s32 a0); void func_8001CC3C(s32 a0, void *a1, s32 a2, s32 a3) { func_8001CF48(a0); *(s16 *)(a0 + 0x2) = 3; *(s16 *)(a0 + 0x28) = (s16)a2; *(s16 *)(a0 + 0x2A) = (s16)a3; *(s32 *)(a0 + 0x20) = (s32)a1; *(s16 *)(a0 + 0x2C) = 0; } extern void func_8001CF48(s32 a0); void func_8001CCA0(s32 a0, s32 a1, s32 a2, s32 a3) { func_8001CF48(a0); *(s16 *)(a0 + 0x2) = 0x9; *(s16 *)(a0 + 0x28) = (s16)a2; *(s16 *)(a0 + 0x2A) = (s16)a3; *(s32 *)(a0 + 0x20) = a1; *(s16 *)(a0 + 0x2C) = 0; } extern void func_8001CF48(s32); void func_8001CD04(s32 a0, s32 a1) { func_8001CF48(a0); *(s16*)(a0 + 0x2) = 4; *(s32*)(a0 + 0x20) = a1; *(s16*)(a0 + 0xE) = 0; *(s16*)(a0 + 0x1E) = 0; *(s16*)(a0 + 0x2C) = 0; } extern void func_8001CF48(s32 a0); void func_8001CD50(s32 a0, s32 a1) { s32 s0 = a0; s32 s1 = a1; func_8001CF48(s0); *(s16 *)((u8 *)s0 + 0x2) = 5; *(s32 *)((u8 *)s0 + 0x20) = s1; *(s16 *)((u8 *)s0 + 0xE) = 0; *(s16 *)((u8 *)s0 + 0x1E) = 0; *(s16 *)((u8 *)s0 + 0x2C) = 0; } extern void func_8001CF48(s32 a0); void func_8001CD9C(s32 a0, s32 a1) { func_8001CF48(a0); *(s16 *)(a0 + 0x2) = 6; *(s32 *)(a0 + 0x20) = a1; *(s16 *)(a0 + 0xE) = 0; *(s16 *)(a0 + 0x1E) = 0; *(s16 *)(a0 + 0x2C) = 0; } extern void func_8001CF48(s32 a0); void func_8001CDE8(s32 a0, s32 a1) { func_8001CF48(a0); *(s16 *)(a0 + 0x2) = 0x7; *(s32 *)(a0 + 0x20) = a1; } extern void func_8001CF48(s32 a0); void func_8001CE28(s32 a0, s32 a1) { func_8001CF48(a0); *(s16 *)(a0 + 0x2) = 0x8; *(s32 *)(a0 + 0x20) = a1; } extern void func_8001CF48(s32 a0); void func_8001CE68(s32 a0, s32 a1) { func_8001CF48(a0); *(s16 *)(a0 + 0x2) = 0x8; *(u8 *)(a0 + 0x27) = 0xBA; *(s32 *)(a0 + 0x4) |= 0x400000; *(s32 *)(a0 + 0x20) = a1; } extern void func_8001CF48(s32 a0); void func_8001CEC0(s32 a0, s32 a1) { func_8001CF48(a0); *(s16 *)(a0 + 0x2) = 0xA; *(s32 *)(a0 + 0x20) = a1; } extern void func_8001CF48(s32 a0); void func_8001CF00(s32 a0) { s32 s0 = a0; func_8001CF48(a0); *(s16 *)(s0 + 0x2) = 0xB; } void func_8001CF30(s32 *a0) { a0[1] = (a0[1] & ~0x40) | 0x1000; } void func_8001CF48(s32 a0) { *(s16 *)a0 = 2; *(s16 *)(a0 + 0x1C) = 0x1000; *(s16 *)(a0 + 0x1A) = 0x1000; *(s16 *)(a0 + 0x18) = 0x1000; *(s16 *)(a0 + 0x32) = 0; *(s16 *)(a0 + 0x30) = 0; *(s16 *)(a0 + 0x2E) = 0; *(s16 *)(a0 + 0x2C) = 0; *(u8 *)(a0 + 0x27) = 0; *(s16 *)(a0 + 0x1E) = 0; *(s16 *)(a0 + 0x14) = 0; *(s16 *)(a0 + 0x12) = 0; *(s16 *)(a0 + 0x10) = 0; *(s16 *)(a0 + 0x0E) = 0; *(s16 *)(a0 + 0x0C) = 0; *(s16 *)(a0 + 0x0A) = 0; *(s16 *)(a0 + 0x08) = 0; *(s32 *)(a0 + 0x04) = 0; *(s32 *)(a0 + 0x20) = 0; *(u8 *)(a0 + 0x26) = 0x80; *(u8 *)(a0 + 0x25) = 0x80; *(u8 *)(a0 + 0x24) = 0x80; *(s32 *)(a0 + 0x34) = 0; *(s16 *)(a0 + 0x2C) = 0; } extern void func_8001CFDC(s32 a0, s32 a1); void func_8001CFB8(void) { func_8001CFDC(0, 4); } extern u8 D_800AF630[]; void *func_8001CFDC_impl(s32 start, s32 end) __asm__("func_8001CFDC"); void *func_8001CFDC_impl(s32 start, s32 end) { u8 *base = D_800AF630; u8 *p; s32 off; s32 i; if ((u32)start >= 0xC0) return 0; if ((u32)end >= 0xC1) return 0; if (start < end) { p = base + start * 0x84; off = start * 0x84; for (i = start; i < end; i++) { if (*(u16 *)(p + 0x2A8) == 0) return base + (off + 0x2A8); p += 0x84; off += 0x84; } } return 0; } extern void func_8001D074(s32 a, s32 b); void func_8001D050(void) { func_8001D074(0, 4); } extern u8 D_800AF630[]; s32 func_8001D074_impl(s32 arg0, s32 arg1) __asm__("func_8001D074"); s32 func_8001D074_impl(s32 arg0, s32 arg1) { u8* base = D_800AF630; s32 off; u8* ptr; if ((u32)arg0 >= 0x100U) { goto ret0; } if ((u32)arg1 >= 0x101U) { return 0; } if (arg0 >= arg1) { goto ret0; } ptr = base + arg0 * 56; off = arg0 * 56; do { if (*(u16*)(ptr + 0x65A8) == 0) { return base + (off + 0x65A8); } ptr += 0x38; arg0 += 1; off += 0x38; } while (arg0 < arg1); ret0: return 0; } void func_8001D0E8(s32 arg0, s32 arg1, s32 arg2) { *(s16 *)(arg0 + 0x28) = arg1; *(s16 *)(arg0 + 0x2A) = arg2; } void func_8001D0F4(s32 *a0) { while (*a0 != 0) { *a0 &= 0x80FFFFFF; a0 += 1; } } void func_8001D130(s32 arg0, s32 arg1) { *(s32 *)(arg0 + arg1 * 4) &= 0x80FFFFFF; } s32 func_8001D150(s32 *arg0, s32 arg1) { arg1 *= 4; arg1 += (s32) arg0; *(s32 *) arg1 |= 0x1000000; return *(s32 *) arg1; } void func_8001D16C(s32 *arg0, s32 *arg1) { s32 *a2; s32 v0; a2 = arg0 + 0x20; v0 = *a2 & 0x80FFFFFF; if (v0 != 0) { do { a2 = (s32 *)((s32)a2 & 0x80FFFFFF); v0 = *a2 & 0x80FFFFFF; } while (v0 != 0); } *a2 = ((s32)arg1 & 0x80FFFFFF) | 0x01000000; *arg1 = 0; } extern u8 D_800B9A11; extern u8 D_800B5CB8; extern u8 D_800B5D28; extern u8 D_800B60A8; extern u8 D_800B6968; extern u8 D_800B7768; void func_8001D388(); void func_8001D1C4(void) { u8 *base; u8 *p; s32 sp10; u8 flags; base = &D_800B9A11; sp10 = 0; flags = *base; p = base - 0x3E39; if (p < base - 0x3D59) { do { func_8001D388(p, &sp10); p += 0x38; } while (p < base - 0x3D59); } if (!(flags & 4)) { p = &D_800B5CB8; if (p < &D_800B5CB8 + 0x70) { do { func_8001D388(p, &sp10); p += 0x38; } while (p < &D_800B5CB8 + 0x70); } } if (!(flags & 0x10)) { p = &D_800B5D28; if (p < &D_800B5D28 + 0x380) { do { func_8001D388(p, &sp10); p += 0x38; } while (p < &D_800B5D28 + 0x380); } } if (!(flags & 0x20)) { p = &D_800B60A8; if (p < &D_800B60A8 + 0x8C0) { do { func_8001D388(p, &sp10); p += 0x38; } while (p < &D_800B60A8 + 0x8C0); } } if (!(flags & 0x40)) { p = &D_800B6968; if (p < &D_800B6968 + 0xE00) { do { func_8001D388(p, &sp10); p += 0x38; } while (p < &D_800B6968 + 0xE00); } } if (!(flags & 0x80)) { p = &D_800B7768; if (p < &D_800B7768 + 0x1C70) { do { func_8001D388(p, &sp10); p += 0x38; } while (p < &D_800B7768 + 0x1C70); } } } extern void (*D_8006351C[])(void *); void func_8001D388(void *param_1) { if ((*(s32 *)((char *)param_1 + 4) >= 0) && (*(s32 *)((char *)param_1 + 0x20) != 0) && (*(u16 *)param_1 == 2) && (*(u16 *)((char *)param_1 + 2) < 11)) { D_8006351C[*(u16 *)((char *)param_1 + 2)](param_1); } } /* POLY_FT4 sprite-strip builder: walks a 12-byte record list at arg0+0x20, pulls * 0x28-byte prims off the D_800A5E60 bump allocator, builds tpage/clut/UVs with * X/Y flip, calls func_8001D8C4, and addPrim's each prim into the OT slot * &((PTag *)(D_800A6610 + (D_800B9A02 << 14)))[pri]. * * Five levers were needed (see cookbook notes in the wave report): * 1. libgpu addPrim bitfield `struct { u32 addr:24; u32 len:8; }` -> hoists 0x00FFFFFF into $s7. * 2. ONE record pointer (not two): gcc's giv combination emits the `addiu $s2,$s3,3` * preheader init and the -1/0/1/3 offsets off it. * 3. Chained store `*(u8 *)(cur+0x14) = *(u8 *)(cur+0x24) = u;` -> the SI->QI * `addu $v0,$a0,$zero` before each sb pair, one copy shared across both flip arms. * 4. `ut` is a SINGLE-SET temp: birthing-boost (sched.c adjust_priority / * birthing_insn_p, REG_N_SETS(dest)==1) sinks the sllv chain BELOW the * `cur[7] |= abe` block, which is what the target does. Folding ut into ub * (ub = expr; u = ub; ub = ub + w - 1) gives ub 3 sets, kills the boost, and * schedules the whole u-chain before the abe block instead (closeness 31). * 5. `u`/`ub` are u16: the mode-changing copy `u = ut` is NOT a plain reg-reg copy, * so cse/coalescing cannot propagate `ut` into u's later uses and delete it. * That single instruction (`addu $a0,$v1,$zero`) is what fixes the whole * $a0<->$v1 allocation of both the U and the V half. Same mechanism keeps the * V-flip diamond's else arm (`addu $a0,$v1,$zero`) alive and shares `(u16)vy` * across both arms. * 6. The explicit intermediate `tt` makes the U-half accumulator reuse $v1 * (`addu $v1,$v1,$v0`) instead of the dying rec[2] register $v0. */ typedef struct { u32 addr : 24; u32 len : 8; } PTag_8001D3FC; extern u8 D_800A6610[]; extern u16 D_800B9A02; extern u8 *D_800A5E60; extern void func_8001D70C(s32 arg0); extern void func_8001D8C4(s32 arg0, u8 *rec, u8 *prim); void func_8001D3FC(s32 arg0) { u32 pri; u32 attr; u8 *rec; PTag_8001D3FC *slot; u32 sc; s32 mode; s32 shift; u8 *cur; u16 id; u32 sx, sy; s32 xs; u16 vx, vy; s32 tpage; u16 u; u16 ub; s32 ut, vt; s32 tt; s32 clut; pri = *(u16 *)(arg0 + 0x2C) & 0x3FFF; if (pri < 0x1000) { attr = *(u32 *)(arg0 + 4); rec = *(u8 **)(arg0 + 0x20); sc = (u32)(u16)D_800B9A02; mode = (attr >> 24) & 3; shift = 2 - mode; slot = &((PTag_8001D3FC *)(D_800A6610 + (sc << 14)))[pri]; func_8001D70C(arg0); do { cur = D_800A5E60; id = *(u16 *)rec; xs = *(s16 *)(rec + 4); sx = *(u16 *)(arg0 + 0x28) + (xs >> shift); vx = sx; sy = *(u16 *)(arg0 + 0x2A) + *(u16 *)(rec + 6); vy = sy; D_800A5E60 = cur + 0x28; *(u8 *)(cur + 3) = 9; *(u8 *)(cur + 7) = 0x2C; if (attr & 0x40000000) { *(u8 *)(cur + 7) = 0x2E; tpage = (mode << 7) | (((attr >> 28) & 3) << 5) | ((sy & 0x100) >> 4) | ((sx & 0x3C0) >> 6) | ((sy & 0x200) << 2); } else { tpage = (mode << 7) | (1 << 5) | ((sy & 0x100) >> 4) | ((sx & 0x3C0) >> 6) | ((sy & 0x200) << 2); } *(s16 *)(cur + 0x16) = tpage; *(u8 *)(cur + 7) |= (attr & 0x40) >> 6; ut = (vx - ((tpage & 0xF) << 6)) << shift; u = ut; tt = ut + *(u8 *)(rec + 2); ub = tt - 1; if ((u16)ub >= 0x100) { ub = 0xFF; } if (id & 0x100) { *(u8 *)(cur + 0x14) = *(u8 *)(cur + 0x24) = u; *(u8 *)(cur + 0x0C) = *(u8 *)(cur + 0x1C) = ub; } else { *(u8 *)(cur + 0x0C) = *(u8 *)(cur + 0x1C) = u; *(u8 *)(cur + 0x14) = *(u8 *)(cur + 0x24) = ub; } vt = vy; if (tpage & 0x10) { u = vt - 0x100; } else { u = vt; } ub = u + *(u8 *)(rec + 3) - 1; if ((u16)ub >= 0x100) { ub = 0xFF; } if (id & 0x200) { *(u8 *)(cur + 0x1D) = *(u8 *)(cur + 0x25) = u; *(u8 *)(cur + 0x0D) = *(u8 *)(cur + 0x15) = ub; } else { *(u8 *)(cur + 0x0D) = *(u8 *)(cur + 0x15) = u; *(u8 *)(cur + 0x1D) = *(u8 *)(cur + 0x25) = ub; } clut = ((*(u8 *)(arg0 + 0x27) + 0x100) << 6) | ((*(u8 *)(arg0 + 0x27) < 0xE0) ? 0x16 : 0x10); *(s16 *)(cur + 0xE) = clut; *(u8 *)(cur + 4) = *(u8 *)(arg0 + 0x24); *(u8 *)(cur + 5) = *(u8 *)(arg0 + 0x25); *(u8 *)(cur + 6) = *(u8 *)(arg0 + 0x26); func_8001D8C4(arg0, rec, cur); ((PTag_8001D3FC *)cur)->addr = slot->addr; slot->addr = (u32)cur; rec += 0xC; } while ((id & 0xFF) != 0xFF); } } void func_8001D70C(s32 param_1) { s32 matrix[8]; /* sp+0x10 */ s16 sc[20]; /* sp+0x30 */ func_8004978C((s16 *)(param_1 + 0x10), (void *)matrix); /* build the diagonal scale matrix at sp+0x30 */ sc[0] = *(u16 *)(param_1 + 0x18); sc[1] = 0; sc[2] = 0; sc[3] = 0; sc[4] = *(u16 *)(param_1 + 0x1A); sc[5] = 0; sc[6] = 0; sc[7] = 0; sc[8] = 0x1000; __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;" "addiu $2, $sp, 0x30;" "lhu $12, 0($2);" "lhu $13, 6($2);" "lhu $14, 12($2);" "mtc2 $12, $9;" "mtc2 $13, $10;" "mtc2 $14, $11;" "nop;" "nop;" "mvmva 1, 0, 3, 3, 0;" "mfc2 $12, $9;" "mfc2 $13, $10;" "mfc2 $14, $11;" "sh $12, 0(%0);" "sh $13, 6(%0);" "sh $14, 12(%0);" "addiu $2, $sp, 0x32;" "lhu $12, 0($2);" "lhu $13, 6($2);" "lhu $14, 12($2);" "mtc2 $12, $9;" "mtc2 $13, $10;" "mtc2 $14, $11;" "nop;" "nop;" "mvmva 1, 0, 3, 3, 0;" "addiu $2, $sp, 0x12;" "mfc2 $12, $9;" "mfc2 $13, $10;" "mfc2 $14, $11;" "sh $12, 0($2);" "sh $13, 6($2);" "sh $14, 12($2);" "addiu $2, $sp, 0x34;" "lhu $12, 0($2);" "lhu $13, 6($2);" "lhu $14, 12($2);" "mtc2 $12, $9;" "mtc2 $13, $10;" "mtc2 $14, $11;" "nop;" "nop;" "mvmva 1, 0, 3, 3, 0;" "addiu $2, $sp, 0x14;" "mfc2 $12, $9;" "mfc2 $13, $10;" "mfc2 $14, $11;" "sh $12, 0($2);" "sh $13, 6($2);" "sh $14, 12($2);" : : "r"((s32)matrix) : "$12", "$13", "$14", "$2", "memory"); *(s32 *)((u8 *)matrix + 0x14) = *(s16 *)(param_1 + 8); *(s32 *)((u8 *)matrix + 0x18) = *(s16 *)(param_1 + 10); *(s32 *)((u8 *)matrix + 0x1C) = *(s16 *)(param_1 + 12); __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;" "lw $12, 20(%0);" "lw $13, 24(%0);" "ctc2 $12, $5;" "lw $14, 28(%0);" "ctc2 $13, $6;" "ctc2 $14, $7" : : "r"((s32)matrix) : "$12", "$13", "$14", "$2"); } /* ---- PsyQ GTE inline macros (same spelling as func_8001F730 / ov_SC03_* / 800.c gte_ldv0) ---- */ #define LDV0_8001D8C4(r0) __asm__ volatile ( \ "lwc2 $0, 0( %0 );" \ "lwc2 $1, 4( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define MVMVA_8001D8C4() __asm__ volatile ( \ "nop;" \ "nop;" \ "mvmva 1, 0, 0, 0, 0" \ : : : "memory" ) #define STMAC123_8001D8C4(r0) __asm__ volatile ( \ "swc2 $25, 0( %0 );" \ "swc2 $26, 4( %0 );" \ "swc2 $27, 8( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) void func_8001D8C4(s32 a0, u8 * a1_p, u8 * a2_p) { s32 a1 = (s32)a1_p; s32 a2 = (s32)a2_p; s16 sv[4][4]; /* 4 SVECTORs: sp+0x00..0x1F */ s32 out[4]; /* MAC1/2/3 scratch: sp+0x20..0x2F */ s32 cx, cy, dx, dy, z; cx = *(u16 *)(a0 + 0x2E) + *(u16 *)(a1 + 0x08); cy = *(u16 *)(a0 + 0x30) + *(u16 *)(a1 + 0x0A); sv[0][0] = sv[2][0] = cx; sv[0][1] = sv[1][1] = cy; dx = *(u8 *)(a1 + 0x02) + cx; sv[3][0] = dx; sv[1][0] = dx; dy = *(u8 *)(a1 + 0x03) + cy; sv[3][1] = dy; sv[2][1] = dy; z = *(u16 *)(a0 + 0x32); sv[3][2] = z; sv[2][2] = z; sv[1][2] = z; sv[0][2] = z; LDV0_8001D8C4(sv[0]); MVMVA_8001D8C4(); STMAC123_8001D8C4(out); *(s16 *)(a2 + 0x08) = out[0]; *(s16 *)(a2 + 0x0A) = out[1]; LDV0_8001D8C4(sv[1]); MVMVA_8001D8C4(); STMAC123_8001D8C4(out); *(s16 *)(a2 + 0x10) = out[0]; *(s16 *)(a2 + 0x12) = out[1]; LDV0_8001D8C4(sv[2]); MVMVA_8001D8C4(); STMAC123_8001D8C4(out); *(s16 *)(a2 + 0x18) = out[0]; *(s16 *)(a2 + 0x1A) = out[1]; LDV0_8001D8C4(sv[3]); MVMVA_8001D8C4(); STMAC123_8001D8C4(out); *(s16 *)(a2 + 0x20) = out[0]; *(s16 *)(a2 + 0x22) = out[1]; } #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; void func_8001E094(s32 arg0) { s16 sc[16]; s16 res[16]; s32 g; s32 t; sc[0] = *(u16 *)(arg0 + 0x18); sc[1] = 0; sc[2] = 0; sc[3] = 0; sc[4] = *(u16 *)(arg0 + 0x1A); sc[5] = 0; sc[6] = 0; sc[7] = 0; sc[8] = *(u16 *)(arg0 + 0x1C); g = (s32)&D_800AF648; /* gte_SetRotMatrix(obj->mat) */ __asm__ __volatile__( "lw $12, 0(%0)\n" "lw $13, 4(%0)\n" "ctc2 $12, $0\n" "ctc2 $13, $1\n" "lw $12, 8(%0)\n" "lw $13, 12(%0)\n" "lw $14, 16(%0)\n" "ctc2 $12, $2\n" "ctc2 $13, $3\n" "ctc2 $14, $4\n" : : "r"(*(s32 *)(arg0 + 0x34)) : "$12", "$13", "$14", "memory"); /* res = obj->mat * sc, column by column */ __asm__ __volatile__( "lhu $12, 0(%0)\n" "lhu $13, 6(%0)\n" "lhu $14, 12(%0)\n" "mtc2 $12, $9\n" "mtc2 $13, $10\n" "mtc2 $14, $11\n" "nop\n" "nop\n" "mvmva 1, 0, 3, 3, 0\n" : : "r"((s32)sc) : "$12", "$13", "$14", "memory"); __asm__ __volatile__( "mfc2 $12, $9\n" "mfc2 $13, $10\n" "mfc2 $14, $11\n" "sh $12, 0(%0)\n" "sh $13, 6(%0)\n" "sh $14, 12(%0)\n" : : "r"((s32)res) : "$12", "$13", "$14", "memory"); __asm__ __volatile__( "lhu $12, 0(%0)\n" "lhu $13, 6(%0)\n" "lhu $14, 12(%0)\n" "mtc2 $12, $9\n" "mtc2 $13, $10\n" "mtc2 $14, $11\n" "nop\n" "nop\n" "mvmva 1, 0, 3, 3, 0\n" : : "r"((s32)sc + 2) : "$12", "$13", "$14", "memory"); __asm__ __volatile__( "mfc2 $12, $9\n" "mfc2 $13, $10\n" "mfc2 $14, $11\n" "sh $12, 0(%0)\n" "sh $13, 6(%0)\n" "sh $14, 12(%0)\n" : : "r"((s32)res + 2) : "$12", "$13", "$14", "memory"); __asm__ __volatile__( "lhu $12, 0(%0)\n" "lhu $13, 6(%0)\n" "lhu $14, 12(%0)\n" "mtc2 $12, $9\n" "mtc2 $13, $10\n" "mtc2 $14, $11\n" "nop\n" "nop\n" "mvmva 1, 0, 3, 3, 0\n" : : "r"((s32)sc + 4) : "$12", "$13", "$14", "memory"); __asm__ __volatile__( "mfc2 $12, $9\n" "mfc2 $13, $10\n" "mfc2 $14, $11\n" "sh $12, 0(%0)\n" "sh $13, 6(%0)\n" "sh $14, 12(%0)\n" : : "r"((s32)res + 4) : "$12", "$13", "$14", "memory"); *(s32 *)((u8 *)res + 0x14) = *(s32 *)(*(s32 *)(arg0 + 0x34) + 0x14); *(s32 *)((u8 *)res + 0x18) = *(s32 *)(*(s32 *)(arg0 + 0x34) + 0x18); *(s32 *)((u8 *)res + 0x1C) = *(s32 *)(*(s32 *)(arg0 + 0x34) + 0x1C); /* gte_SetRotMatrix(&D_800AF648) */ __asm__ __volatile__( "lw $12, 0(%0)\n" "lw $13, 4(%0)\n" "ctc2 $12, $0\n" "ctc2 $13, $1\n" "lw $12, 8(%0)\n" "lw $13, 12(%0)\n" "lw $14, 16(%0)\n" "ctc2 $12, $2\n" "ctc2 $13, $3\n" "ctc2 $14, $4\n" : : "r"(g) : "$12", "$13", "$14", "memory"); __asm__ __volatile__( "lhu $12, 0(%0)\n" "lhu $13, 6(%0)\n" "lhu $14, 12(%0)\n" "mtc2 $12, $9\n" "mtc2 $13, $10\n" "mtc2 $14, $11\n" "nop\n" "nop\n" "mvmva 1, 0, 3, 3, 0\n" : : "r"((s32)res) : "$12", "$13", "$14", "memory"); __asm__ __volatile__( "mfc2 $12, $9\n" "mfc2 $13, $10\n" "mfc2 $14, $11\n" "sh $12, 0(%0)\n" "sh $13, 6(%0)\n" "sh $14, 12(%0)\n" : : "r"((s32)res) : "$12", "$13", "$14", "memory"); __asm__ __volatile__( "lhu $12, 0(%0)\n" "lhu $13, 6(%0)\n" "lhu $14, 12(%0)\n" "mtc2 $12, $9\n" "mtc2 $13, $10\n" "mtc2 $14, $11\n" "nop\n" "nop\n" "mvmva 1, 0, 3, 3, 0\n" : : "r"((s32)res + 2) : "$12", "$13", "$14", "memory"); __asm__ __volatile__( "mfc2 $12, $9\n" "mfc2 $13, $10\n" "mfc2 $14, $11\n" "sh $12, 0(%0)\n" "sh $13, 6(%0)\n" "sh $14, 12(%0)\n" : : "r"((s32)res + 2) : "$12", "$13", "$14", "memory"); __asm__ __volatile__( "lhu $12, 0(%0)\n" "lhu $13, 6(%0)\n" "lhu $14, 12(%0)\n" "mtc2 $12, $9\n" "mtc2 $13, $10\n" "mtc2 $14, $11\n" "nop\n" "nop\n" "mvmva 1, 0, 3, 3, 0\n" : : "r"((s32)res + 4) : "$12", "$13", "$14", "memory"); __asm__ __volatile__( "mfc2 $12, $9\n" "mfc2 $13, $10\n" "mfc2 $14, $11\n" "sh $12, 0(%0)\n" "sh $13, 6(%0)\n" "sh $14, 12(%0)\n" : : "r"((s32)res + 4) : "$12", "$13", "$14", "memory"); /* gte_SetTransMatrix(&D_800AF648) */ __asm__ __volatile__( "lw $12, 20(%0)\n" "lw $13, 24(%0)\n" "ctc2 $12, $5\n" "lw $14, 28(%0)\n" "ctc2 $13, $6\n" "ctc2 $14, $7\n" : : "r"(g) : "$12", "$13", "$14", "memory"); t = (s32)res + 0x14; __asm__ __volatile__( "lhu $13, 4(%0)\n" "lhu $12, 0(%0)\n" "sll $13, $13, 16\n" "or $12, $12, $13\n" "mtc2 $12, $0\n" "lwc2 $1, 8(%0)\n" "nop\n" "nop\n" "mvmva 1, 0, 0, 0, 0\n" : : "r"(t) : "$12", "$13", "memory"); __asm__ __volatile__( "swc2 $25, 0(%0)\n" "swc2 $26, 4(%0)\n" "swc2 $27, 8(%0)\n" : : "r"(t) : "memory"); /* gte_SetRotMatrix(res) ; gte_SetTransMatrix(res) */ __asm__ __volatile__( "lw $12, 0(%0)\n" "lw $13, 4(%0)\n" "ctc2 $12, $0\n" "ctc2 $13, $1\n" "lw $12, 8(%0)\n" "lw $13, 12(%0)\n" "lw $14, 16(%0)\n" "ctc2 $12, $2\n" "ctc2 $13, $3\n" "ctc2 $14, $4\n" : : "r"((s32)res) : "$12", "$13", "$14", "memory"); __asm__ __volatile__( "lw $12, 20(%0)\n" "lw $13, 24(%0)\n" "ctc2 $12, $5\n" "lw $14, 28(%0)\n" "ctc2 $13, $6\n" "ctc2 $14, $7\n" : : "r"((s32)res) : "$12", "$13", "$14", "memory"); } extern void func_8004978C(s16 *a0, void *a1); extern u8 D_800AF648; void func_8001E378(s32 param_1) { __asm__ __volatile__( ".set\tnoreorder\n" "addiu $29, $29, -96\n" "sw $16, 80($29)\n" "addu $16, $4, $0\n" "sw $17, 84($29)\n" "addiu $17, $29, 48\n" "addiu $4, $16, 16\n" "sw $31, 88($29)\n" "jal func_8004978C\n" "addu $5, $17, $0\n" "lhu $2, 24($16)\n" "sh $0, 18($29)\n" "sh $0, 20($29)\n" "sh $0, 22($29)\n" "sh $2, 16($29)\n" "lhu $2, 26($16)\n" "sh $0, 26($29)\n" "sh $0, 28($29)\n" "sh $0, 30($29)\n" "sh $2, 24($29)\n" "lhu $2, 28($16)\n" "lui $5, %hi(D_800AF648)\n" "addiu $5, $5, %lo(D_800AF648)\n" "sh $2, 32($29)\n" "lw $12, 0($17)\n" "lw $13, 4($17)\n" "ctc2 $12, $0\n" "ctc2 $13, $1\n" "lw $12, 8($17)\n" "lw $13, 12($17)\n" "lw $14, 16($17)\n" "ctc2 $12, $2\n" "ctc2 $13, $3\n" "ctc2 $14, $4\n" "addiu $2, $29, 16\n" "lhu $12, 0($2)\n" "lhu $13, 6($2)\n" "lhu $14, 12($2)\n" "mtc2 $12, $9\n" "mtc2 $13, $10\n" "mtc2 $14, $11\n" "nop\n" "nop\n" "mvmva 1, 0, 3, 3, 0\n" "mfc2 $12, $9\n" "mfc2 $13, $10\n" "mfc2 $14, $11\n" "sh $12, 0($17)\n" "sh $13, 6($17)\n" "sh $14, 12($17)\n" "addiu $2, $29, 18\n" "lhu $12, 0($2)\n" "lhu $13, 6($2)\n" "lhu $14, 12($2)\n" "mtc2 $12, $9\n" "mtc2 $13, $10\n" "mtc2 $14, $11\n" "nop\n" "nop\n" "mvmva 1, 0, 3, 3, 0\n" "addiu $4, $29, 50\n" "mfc2 $12, $9\n" "mfc2 $13, $10\n" "mfc2 $14, $11\n" "sh $12, 0($4)\n" "sh $13, 6($4)\n" "sh $14, 12($4)\n" "addiu $2, $29, 20\n" "lhu $12, 0($2)\n" "lhu $13, 6($2)\n" "lhu $14, 12($2)\n" "mtc2 $12, $9\n" "mtc2 $13, $10\n" "mtc2 $14, $11\n" "nop\n" "nop\n" "mvmva 1, 0, 3, 3, 0\n" "addiu $3, $29, 52\n" "mfc2 $12, $9\n" "mfc2 $13, $10\n" "mfc2 $14, $11\n" "sh $12, 0($3)\n" "sh $13, 6($3)\n" "sh $14, 12($3)\n" "lh $2, 8($16)\n" "nop\n" "sw $2, 68($29)\n" "lh $2, 10($16)\n" "nop\n" "sw $2, 72($29)\n" "lh $2, 12($16)\n" "nop\n" "sw $2, 76($29)\n" "lw $12, 0($5)\n" "lw $13, 4($5)\n" "ctc2 $12, $0\n" "ctc2 $13, $1\n" "lw $12, 8($5)\n" "lw $13, 12($5)\n" "lw $14, 16($5)\n" "ctc2 $12, $2\n" "ctc2 $13, $3\n" "ctc2 $14, $4\n" "lhu $12, 0($17)\n" "lhu $13, 6($17)\n" "lhu $14, 12($17)\n" "mtc2 $12, $9\n" "mtc2 $13, $10\n" "mtc2 $14, $11\n" "nop\n" "nop\n" "mvmva 1, 0, 3, 3, 0\n" "mfc2 $12, $9\n" "mfc2 $13, $10\n" "mfc2 $14, $11\n" "sh $12, 0($17)\n" "sh $13, 6($17)\n" "sh $14, 12($17)\n" "lhu $12, 0($4)\n" "lhu $13, 6($4)\n" "lhu $14, 12($4)\n" "mtc2 $12, $9\n" "mtc2 $13, $10\n" "mtc2 $14, $11\n" "nop\n" "nop\n" "mvmva 1, 0, 3, 3, 0\n" "mfc2 $12, $9\n" "mfc2 $13, $10\n" "mfc2 $14, $11\n" "sh $12, 0($4)\n" "sh $13, 6($4)\n" "sh $14, 12($4)\n" "lhu $12, 0($3)\n" "lhu $13, 6($3)\n" "lhu $14, 12($3)\n" "mtc2 $12, $9\n" "mtc2 $13, $10\n" "mtc2 $14, $11\n" "nop\n" "nop\n" "mvmva 1, 0, 3, 3, 0\n" "mfc2 $12, $9\n" "mfc2 $13, $10\n" "mfc2 $14, $11\n" "sh $12, 0($3)\n" "sh $13, 6($3)\n" "sh $14, 12($3)\n" "lw $12, 20($5)\n" "lw $13, 24($5)\n" "ctc2 $12, $5\n" "lw $14, 28($5)\n" "ctc2 $13, $6\n" "ctc2 $14, $7\n" "addiu $2, $29, 68\n" "lhu $13, 4($2)\n" "lhu $12, 0($2)\n" "sll $13, $13, 16\n" "or $12, $12, $13\n" "mtc2 $12, $0\n" "lwc2 $1, 8($2)\n" "nop\n" "nop\n" "mvmva 1, 0, 0, 0, 0\n" "swc2 $25, 0($2)\n" "swc2 $26, 4($2)\n" "swc2 $27, 8($2)\n" "lw $12, 0($17)\n" "lw $13, 4($17)\n" "ctc2 $12, $0\n" "ctc2 $13, $1\n" "lw $12, 8($17)\n" "lw $13, 12($17)\n" "lw $14, 16($17)\n" "ctc2 $12, $2\n" "ctc2 $13, $3\n" "ctc2 $14, $4\n" "lw $12, 20($17)\n" "lw $13, 24($17)\n" "ctc2 $12, $5\n" "lw $14, 28($17)\n" "ctc2 $13, $6\n" "ctc2 $14, $7\n" "lw $31, 88($29)\n" "lw $17, 84($29)\n" "lw $16, 80($29)\n" "addiu $29, $29, 96\n" ".set\treorder\n" ); } /* ---- PsyQ GTE inline macros (same spelling as func_80016F18, this TU) ---- */ #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_ldv0(r0) __asm__ volatile ( \ "lwc2 $0, 0( %0 );" \ "lwc2 $1, 4( %0 )" \ : \ : "r"( r0 ) ) #define gte_rtpt() __asm__ volatile ("nop;nop;rtpt") #define gte_rtps() __asm__ volatile ("nop;nop;rtps") #define gte_avsz4() __asm__ volatile ("nop;nop;avsz4") #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_stsxy(r0) __asm__ volatile ( \ "swc2 $14, 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_stotz(r0) __asm__ volatile ( \ "swc2 $7, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) /* GTE input vertex: {vx, vy, vz, pad} */ typedef struct { /* 0x0 */ s16 vx; /* 0x2 */ s16 vy; /* 0x4 */ s16 vz; /* 0x6 */ s16 pad; } GTESVec; /* 8 bytes */ /* GTE screen-space output slot: {xy; z; pad} */ typedef struct { /* 0x0 */ u32 xy; /* 0x4 */ s16 z; /* 0x6 */ s16 pad; } GTEOutV; /* 8 bytes */ s32 func_8001E668(s32 a0, s32 a1, GTEOutV *a2, s32 *a3) { GTESVec v[4]; s32 flag2; s32 otz; v[0].vx = *(u16 *)(a0 + 0x2E) + *(u16 *)(a1 + 0x8); v[0].vy = *(u16 *)(a0 + 0x30) + *(u16 *)(a1 + 0xA); v[1].vx = *(u16 *)(a0 + 0x2E) + *(u16 *)(a1 + 0x8) + *(u8 *)(a1 + 0x2); v[1].vy = *(u16 *)(a0 + 0x30) + *(u16 *)(a1 + 0xA); v[2].vx = *(u16 *)(a0 + 0x2E) + *(u16 *)(a1 + 0x8); v[2].vy = *(u16 *)(a0 + 0x30) + *(u16 *)(a1 + 0xA) + *(u8 *)(a1 + 0x3); v[3].vx = *(u16 *)(a0 + 0x2E) + *(u16 *)(a1 + 0x8) + *(u8 *)(a1 + 0x2); v[3].vy = *(u16 *)(a0 + 0x30) + *(u16 *)(a1 + 0xA) + *(u8 *)(a1 + 0x3); v[0].vz = v[1].vz = v[2].vz = v[3].vz = *(u16 *)(a0 + 0x32); gte_ldv3(&v[0], &v[1], &v[2]); gte_rtpt(); gte_stflg(a3); gte_stsxy3(&a2[1].xy, &a2[2].xy, &a2[3].xy); gte_ldv0(&v[3]); gte_rtps(); gte_stflg(&flag2); *a3 |= flag2; a2 += 4; gte_stsxy(&a2[0].xy); gte_avsz4(); gte_stotz(&otz); return otz; } extern u16 D_800B9A02; extern u8 D_800A6518[]; extern s32 func_80052460(s32 a0, s32 a1, s32 a2); void func_8001E7E0(u8 *arg0) { s32 buf[9]; u16 ox, oy; u8 *otp; u8 *p; u32 attr; u32 pri; s32 shift; s32 mode; u32 sx, sy; u16 vx, vy; s32 vv; u16 tv; s32 tpage; u16 id; pri = *(u16 *)(arg0 + 0x2C) & 0x3FFF; if (pri < 0x1000) { p = *(u8 **)(arg0 + 0x20); ox = *(u16 *)(arg0 + 0x8); oy = *(u16 *)(arg0 + 0xA); attr = *(u32 *)(arg0 + 0x4); mode = (attr >> 24) & 3; shift = 2 - mode; otp = &D_800A6518[(u16)D_800B9A02 * 20]; do { id = *(u16 *)p; sx = *(u16 *)(arg0 + 0x28) + (*(s16 *)(p + 4) >> shift); vx = sx; sy = *(u16 *)(arg0 + 0x2A) + *(u16 *)(p + 6); vy = sy; if (attr & 0x40000000) { tpage = (mode << 7) | (((attr >> 28) & 3) << 5) | ((sy & 0x100) >> 4) | ((sx & 0x3C0) >> 6) | ((sy & 0x200) << 2); } else { tpage = (mode << 7) | (1 << 5) | ((sy & 0x100) >> 4) | ((sx & 0x3C0) >> 6) | ((sy & 0x200) << 2); } buf[0] = attr; *(s16 *)((u8 *)buf + 0x04) = *(u16 *)(p + 8) + (ox + *(u16 *)(arg0 + 0x2E)); *(s16 *)((u8 *)buf + 0x06) = *(u16 *)(p + 10) + (oy + *(u16 *)(arg0 + 0x30)); *(s16 *)((u8 *)buf + 0x08) = *(u8 *)(p + 2); *(s16 *)((u8 *)buf + 0x0A) = *(u8 *)(p + 3); *(s16 *)((u8 *)buf + 0x0C) = tpage; *(u8 *)((u8 *)buf + 0x0E) = (vx - ((tpage & 0xF) << 6)) << shift; vv = vy; if (tpage & 0x10) { tv = vv - 0x100; *(u8 *)((u8 *)buf + 0x0F) = tv; } else { tv = vv; *(u8 *)((u8 *)buf + 0x0F) = tv; } *(s16 *)((u8 *)buf + 0x10) = (*(u8 *)(arg0 + 0x27) < 0xE0) ? 0x160 : 0x100; p += 12; *(s16 *)((u8 *)buf + 0x12) = *(u8 *)(arg0 + 0x27) + 0x100; *(u8 *)((u8 *)buf + 0x16) = 0x80; *(u8 *)((u8 *)buf + 0x15) = 0x80; *(u8 *)((u8 *)buf + 0x14) = 0x80; func_80052460((s32)buf, (s32)otp, (u16)pri); } while ((id & 0xFF) != 0xFF); } } /* func_8001EA14 - src/800.c (main, -O2), 371 ins. MATCH (match_one closeness 0). * * Sprite/billboard emitter: RTPS the entity origin through the global matrix D_800AF648, * build a per-entity MATRIX from unk18/unk1A, then walk a 12-byte cell list (stride 0xC, * terminated by a 0xFF low byte; cells whose u16 type == 8 are skipped) emitting one * POLY_FT4 (0x28 bytes) per cell out of the D_800A5E60 bump allocator and linking it into * the OT at D_800A6610 + (D_800B9A02 << 14). * * ============================================================================ * THE SIX LEVERS THAT CLOSED THIS (prior best was close=89; all byte-measured) * ============================================================================ * The four levers inherited from the close=89 draft still hold and are NOT repeated here: * (1) loop.c must hoist EXACTLY ONE address (a 2-operand gte_ldv3 + gte_rt with the * output as operand 0 -- see S148-A / S193-F); * (2) prim fields via PLAIN CASTS (MEM_IN_STRUCT_P = 0) to keep the target's nops; * (3) the COND_EXPR singleton fold must be escaped by making the arms different TREES; * (4) spill slots follow DECLARATION order (`mode` before `base`). * * NEW, and between them worth 89 -> 0: * * L5. **STATEMENT ORDER IS THE ALIAS ORDER: put a struct load BEFORE the plain stores that * it must be hoisted over.** gcc-2.7.2 `true_dependence` only exempts a load/store pair * when one side is MEM_IN_STRUCT_P *and* varying and the other is neither. A local * `MATRIX m` (mem/s, frame address) vs `p->unk18` (mem/s, varying) is therefore * DEPENDENT -- so `m.m[0][0] = p->unk18;` can never float above an earlier `m.* = 0`. * The target's schedule (`lhu; sh 0; sh 0; sh 0; sh`) is only reachable if the LOAD's * statement comes FIRST in the source. Writing the matrix init in NATURAL OFFSET ORDER * -- m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], * t[] -- closed the entire 45-instruction init block in one edit. (The `m.m[0][1]=0`- * first order the earlier drafts used is what forced the load down and cost the block.) * COROLLARY, worth another 20 in the loop: the four cell/entity loads only fill the * `lw $s0, D_800A5E60` load-delay if `sx`/`sy` are computed BEFORE the bump-pointer * store and `pk[3] = 9` -- the same law, one scope down. Do NOT reach for the alias * dial (rewriting `m` as a non-struct frame buffer DOES delete the dependence, measured, * but then nothing constrains the block and the schedule is worse: 89 -> 90). * * L6. **AN INLINE-ASM "r" OPERAND THAT IS A BARE `symbol_ref` IS ALLOCATED BY *RELOAD*, NOT * BY THE REGISTER ALLOCATOR.** `gte_SetRotMatrix(&D_800AF648)` leaves * `(asm_operands ... (symbol_ref "D_800AF648"))` in the RTL -- there is no pseudo at * all -- so reload materialises it into a scratch register and picks `$t0`. Assigning * it to a local pointer first (`mp = &D_800AF648; gte_SetRotMatrix(mp);`) creates a real * pseudo, local-alloc gives it `$v0`, and all TEN mismatched instructions vanish * (the `lui/addiu` pair plus the eight `lw $tN, k($v0)` inside the two macros). * TELL: a residual that is "same instructions, one register wrong, and the register is a * high `$tN`" on an asm operand => check whether the operand is a constant address. * * L7. **LET loop.c HOIST THE OT ADDRESS: a loop-invariant computed INSIDE the loop lands at * `loop_start` with a LOWER LUID than one written before the loop.** `addiu $s6,$sp,0x48` * (the &vo hoist, also emitted at loop_start) has to be emitted BEFORE `sll $v0,$a1,2` * / `addu $s4,$v0,$a2`. While `ot` was a pre-loop statement its insns had the lower * LUID and sched2's `rank_for_schedule` tiebreak put `$s6` last. Writing * `ot = (u32 *)((z << 2) + (s32)ob);` as the FIRST statement of the OT-link block inside * the loop makes loop.c hoist it after &vo, which is exactly the target's order. This * does not violate lever (1): the address is a giv-free invariant, the movable count is * unchanged for the &vo decision. * * L8. **THE `?:` NEEDS *BOTH* HALVES: a different TREE to escape the expand_expr fold AND a * register pin to escape jump2's noop-move deletion.** `uyx` must be materialised before * the branch (it is what fills the branch's delay slot -- `andi $v1,$a1,0xFFFF`), the arms * must be `(uyx - 0x100)` / `uyx`, and the else arm must survive as `move $v0,$v1`. * With plain locals gcc coalesces `vv` with `uyx`, the copy becomes a no-op, and jump2 * deletes BOTH it and the `j` (371 -> 369 ins, measured on 5 spellings incl. if/else and * a second temp). One pin -- `register s32 vv __asm__("$2")` -- forces the interference * and restores the copy. `uyx` needs NO pin ($v1 falls out). There is no call between * the pin's def and its last use, so S175's caller-saved-pin hazard does not apply, and * the byte-equality proves the emitted code is the target's. * * L9. **A COMMUTATIVE `+` KEEPS TREE OPERAND ORDER, BUT SWAPPING IT IN SOURCE ALSO SWAPS THE * OPERAND EVALUATION ORDER (and hence the register assignment).** The target wants * `addu $a0,$a0,$v1` = plus(p->unk28, e->x>>shift) with the loads still emitted * e->x-first. Writing `p->unk28 + (e->x >> shift)` gets the plus right but emits the * `lhu` first and costs 10 instructions of register churn. Splitting the shift into its * own statement -- `xs = e->x >> shift; sx = p->unk28 + xs;` -- fixes the plus order while * leaving the load order alone. Last instruction, 1 -> 0. * * METHOD NOTE (reusable): the whole close was driven off `cc1 -dS -dR` (sched1/sched2 ready * lists + priorities) plus a 0.13s `match_one` loop, and a scripted 858-candidate sweep that * PROVED statement placement of `mode`/`rot`/`shift` is completely inert here (all 858 == 90). * When a source-order sweep is flat, the residual is a DAG fact (L5) or an allocation fact * (L6/L8), not a LUID tiebreak. */ #define gte_SetRotMatrix_EA14(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_EA14(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_EA14(r0) __asm__ volatile ( \ "lwc2 $0, 0( %0 );" \ "lwc2 $1, 4( %0 )" \ : \ : "r"( r0 ) ) #define gte_ldv3_EA14(r0, r1) __asm__ volatile ( \ "lwc2 $0, 0( %0 );" \ "lwc2 $1, 4( %0 );" \ "lwc2 $2, 0( %1 );" \ "lwc2 $3, 4( %1 );" \ "lwc2 $4, 0( %1 );" \ "lwc2 $5, 4( %1 )" \ : \ : "r"( r0 ), "r"( r1 ) ) #define gte_rtps_EA14() __asm__ volatile ("nop;nop;rtps") #define gte_rtpt_EA14() __asm__ volatile ("nop;nop;rtpt") #define gte_stsxy_EA14(r0) __asm__ volatile ( \ "swc2 $14, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stsxy0_EA14(r0) __asm__ volatile ( \ "swc2 $12, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stsxy1_EA14(r0) __asm__ volatile ( \ "swc2 $13, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stflg_EA14(r0) __asm__ volatile ( \ "cfc2 $12, $31;" \ "nop;" \ "sw $12, 0( %0 )" \ : \ : "r"( r0 ) \ : "$12", "memory" ) #define gte_stsz_EA14(r0) __asm__ volatile ( \ "swc2 $19, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stszotz_EA14(r0) __asm__ volatile ( \ "mfc2 $12, $19;" \ "nop;" \ "sra $12, $12, 2;" \ "sw $12, 0( %0 )" \ : \ : "r"( r0 ) \ : "$12", "memory" ) #define gte_rt_EA14(r1, r0) __asm__ volatile ( \ "lwc2 $0, 0( %1 );" \ "lwc2 $1, 4( %1 );" \ "nop;" \ "nop;" \ "mvmva 1, 0, 0, 3, 0;" \ "swc2 $25, 0( %0 );" \ "swc2 $26, 4( %0 );" \ "swc2 $27, 8( %0 )" \ : \ : "r"( r1 ), "r"( r0 ) \ : "memory" ) typedef struct { u16 vx, vy; } DV_EA14; /* 0x04 */ typedef struct { u16 vx, vy, vz, pad; } SV_EA14; /* 0x08 */ typedef struct { s32 vx, vy, vz, pad; } VC_EA14; /* 0x10 */ typedef struct { s16 m[3][3]; s32 t[3]; } MT_EA14; /* 0x20 */ typedef struct { /* 0x00 */ u32 tag; /* 0x04 */ u8 r0, g0, b0, code; /* 0x08 */ u16 x0, y0; /* 0x0C */ u8 u0, v0; /* 0x0E */ u16 clut; /* 0x10 */ u16 x1, y1; /* 0x14 */ u8 u1, v1; /* 0x16 */ u16 tpage; /* 0x18 */ u16 x2, y2; /* 0x1C */ u8 u2, v2; /* 0x1E */ u16 pad1E; /* 0x20 */ u16 x3, y3; /* 0x24 */ u8 u3, v3; /* 0x26 */ u16 pad26; } Ft4_EA14; /* 0x28 */ typedef struct { /* 0x00 */ u16 unk00; /* 0x02 */ u16 unk02; /* 0x04 */ s32 unk04; /* 0x08 */ SV_EA14 unk08; /* 0x10 */ u8 unk10[8]; /* 0x18 */ u16 unk18; /* 0x1A */ u16 unk1A; /* 0x1C */ u8 unk1C[4]; /* 0x20 */ u8 *unk20; /* 0x24 */ u8 unk24; /* 0x25 */ u8 unk25; /* 0x26 */ u8 unk26; /* 0x27 */ u8 unk27; /* 0x28 */ u16 unk28; /* 0x2A */ u16 unk2A; /* 0x2C */ u16 unk2C; } Ent_EA14; typedef struct { /* 0x00 */ u16 type; /* 0x02 */ u8 w; /* 0x03 */ u8 h; /* 0x04 */ s16 x; /* 0x06 */ u16 y; /* 0x08 */ u16 u; /* 0x0A */ u16 v; } Cell_EA14; /* 0x0C */ extern u8 D_800AF648; extern u16 D_800B9A02; extern u8 D_800A6610[]; extern u8 *D_800A5E60; extern void func_8001F730(s32, void *, void *); #define B(o) (*(u8 *)(pk + (o))) #define H(o) (*(u16 *)(pk + (o))) #define W(o) (*(u32 *)(pk + (o))) void func_8001EA14(Ent_EA14 *p) { MT_EA14 m; /* 0x10 */ SV_EA14 sxy; /* 0x30 */ SV_EA14 v[2]; /* 0x38 */ VC_EA14 vo; /* 0x48 */ s32 flag; /* 0x58 */ s32 sz; /* 0x5C */ s32 otz; /* 0x60 */ u32 sc0; /* 0x64 */ u32 sc1; /* 0x68 */ Cell_EA14 *e; u32 fl; s32 z; s32 rot; s32 shift; s32 fpv; u8 mode; s32 base; u8 *ob; u32 *ot; u8 *pk; u8 *mp; u32 sx, sy; u16 ux, uy; s32 cl; s32 xs; register s32 vv __asm__("$2"); u32 uyx; u32 t; u8 c8; e = (Cell_EA14 *)p->unk20; fl = p->unk04; mp = &D_800AF648; gte_SetRotMatrix_EA14(mp); gte_SetTransMatrix_EA14(mp); gte_ldv0_EA14(&p->unk08); gte_rtps_EA14(); gte_stsxy_EA14(&sxy); gte_stflg_EA14(&flag); gte_stsz_EA14(&sz); gte_stszotz_EA14(&otz); if ((flag & ~0x1000) != 0) { return; } z = otz + 1; ob = (D_800B9A02 << 14) + D_800A6610; if ((p->unk2C & 0xC000) != 0) { if ((p->unk2C & 0xC000) == 0xC000) { z -= (p->unk2C & 0xFFF); if (z < 0) { z = 0; } } else { z += (p->unk2C & 0xFFF); } } if ((u32)z >= 0x1000) { return; } m.m[0][0] = p->unk18; m.m[0][1] = 0; m.m[0][2] = 0; m.m[1][0] = 0; mode = (fl >> 28) & 3; m.m[1][1] = p->unk1A; m.m[1][2] = 0; m.m[2][0] = 0; m.m[2][1] = 0; m.m[2][2] = 0x1000; m.t[0] = m.t[1] = m.t[2] = 0; rot = (fl >> 24) & 3; shift = 2 - rot; gte_SetRotMatrix_EA14(&m); gte_SetTransMatrix_EA14(&m); fpv = rot << 7; base = fpv | 0x20; do { t = e->type; if (t != 8) { pk = D_800A5E60; xs = e->x >> shift; sx = p->unk28 + xs; ux = sx; sy = p->unk2A + e->y; uy = sy; D_800A5E60 = pk + 0x28; *(u8 *)(pk + 3) = 9; B(7) = 0x2C; if ((fl & 0x40000000) != 0) { B(7) = 0x2E; H(0x16) = (fpv | (mode << 5)) | ((sy & 0x100) >> 4) | ((sx & 0x3C0) >> 6) | ((sy & 0x200) << 2); } else { H(0x16) = base | ((sy & 0x100) >> 4) | ((sx & 0x3C0) >> 6) | ((sy & 0x200) << 2); } B(7) |= (fl & 0x40) >> 6; B(0xC) = (ux - ((H(0x16) & 0xF) << 6)) << shift; uyx = uy; vv = (H(0x16) & 0x10) ? (uyx - 0x100) : uyx; B(0xD) = vv; B(0x14) = B(0xC) + e->w - 1; B(0x15) = B(0xD); B(0x1C) = B(0xC); B(0x1D) = B(0xD) + e->h - 1; B(0x24) = B(0x14); B(0x25) = B(0x1D); cl = (p->unk27 + 0x100) << 6; H(0xE) = (p->unk27 < 0xE0) ? (cl | 0x16) : (cl | 0x10); B(4) = p->unk24; B(5) = p->unk25; B(6) = p->unk26; v[0].vx = e->u; v[0].vy = e->v; v[0].vz = sz; v[1].vx = e->w; v[1].vy = e->h; v[1].vz = sz; if (p->unk02 == 3) { gte_ldv3_EA14(&v[0], &v[1]); gte_rtpt_EA14(); gte_stsxy0_EA14(&sc0); gte_stsxy1_EA14(&sc1); H(8) = sxy.vx + ((u16 *)&sc0)[0]; H(0xA) = sxy.vy + ((u16 *)&sc0)[1]; H(0x10) = H(8) + ((u16 *)&sc1)[0]; H(0x1A) = H(0xA) + ((u16 *)&sc1)[1]; } else { gte_rt_EA14(&vo, &v[0]); H(8) = sxy.vx + vo.vx; H(0xA) = sxy.vy + vo.vy; gte_rt_EA14(&vo, &v[1]); H(0x10) = H(8) + vo.vx; H(0x1A) = H(0xA) + vo.vy; } H(0x12) = H(0xA); H(0x18) = H(8); H(0x20) = H(0x10); H(0x22) = H(0x1A); if ((p->unk04 & 0x8000000) != 0) { func_8001F730((s32)p + 0x10, &sxy, pk); } ot = (u32 *)((z << 2) + (s32)ob); W(0) = (W(0) & 0xFF000000) | (*ot & 0xFFFFFF); *ot = (*ot & 0xFF000000) | ((u32)pk & 0xFFFFFF); } c8 = *(u8 *)e; e++; } while (c8 != 0xFF); } /* func_8001EFE0 - src/800.c (main, -O2), 468 ins. MATCH (match_one closeness 0, * rtu_match.py --tu src/800.c MATCH). * * Sprite/billboard emitter, the direct sibling of func_8001EA14 (which sits * immediately above it in the TU and is already banked): build a GsCOORDINATE2 * on the stack (flg@0x58, coord@0x5C, workm@0x7C, rotate@0x9C, super@0xA0) with * RotMatrixZYX/ScaleMatrix/GsGetLs (func_80049CAC / func_8004901C / * func_800547D8), write coord+workm back to arg0+0x34/+0x54, RTPS the origin, * then walk a 12-byte cell list (stride 0xC, low byte 8 = skip, 0xFF = end) * emitting one POLY_FT4 (0x28) per cell out of the D_800A5E60 bump allocator and * addPrim()ing it onto D_800A6610 + (*(u16 *)(D_800AF630+0xA3D2) << 14). * * SYMBOL AUDIT (SYS law 1c, done after MATCH): the 9 external symbols in this * file are exactly the 9 in asm/nonmatchings/800/func_8001EFE0.s, in the same * order -- %hi/%lo(D_800AF630), jal func_80049CAC, jal func_8004901C, * jal func_800547D8, jal func_80021174, %hi/%lo(D_800A6610), * %hi/%lo(D_800A5E60) x2, %hi/%lo(D_80072784) x2, jal func_8001F730. * * =========================================================================== * HISTORY: three earlier agents left this at 172, then 89, then 14, and the * closeness-14 header (preserved below) declared all three remaining clusters * "[permuter]-class, not reachable from C". ALL THREE WERE REACHABLE. The * unlock was reading the already-MATCHED same-TU siblings func_8001DA34 and * func_8001EA14 instead of sweeping spellings: every lever below is a spelling * one of those two already uses. 14 -> 0 in three edits. * =========================================================================== * * N1. THE addPrim TAIL: SPLIT THE OT ADDRESS SO ONE HALF SITS BETWEEN THE TWO * PINNED MASK CONSTANTS (14 -> 12; target order is `lui a1;ori a1; * sll a0,s4,2;lui a2,0xff00`, every draft emitted the two `lui`s adjacent). * A pinned hard-reg SET is placed FIRST in its block (cookbook 194-A), so * with `m24`/`mFF` both pinned NO source order of the two assignments can * separate them -- moving `mFF = 0xFF000000;` after the `ot = ...` * statement is completely inert, and a scheduling fence there (the * func_8001DA34 spelling) is WORSE (+3) because this target wants * `lui a2` BEFORE the `lw`/`addu` half, not after it. The lever is to give * `ot` TWO assignments and let the second mask land between them: * m24 = 0xFFFFFF; * ot = (u32 *)(d * 4); * mFF = 0xFF000000; * ot = (u32 *)((s32)ot + (s32)otbase); * GENERAL: when two pinned constants must be separated by a third insn, * do not move the constants -- SPLIT the insn that has to go between them. * * N2. A SCHEDULING FENCE AFTER THE tpage STORE (12 -> 9). The target issues * `sh $v0,0x16($s0)` BEFORE the `andi $a0,$s6,0x40` / `lbu $v1,7($s0)` pair; * every draft sank it 3 slots because gcc hoists the `q[7]` read-modify-write * over it. One `__asm__ __volatile__("");` between * *(u16 *)(q + 0x16) = tpage; * q[7] |= (attr & 0x40) >> 6; * pins the store above the pair. (func_8001DA34 has the identical statement * pair and needs NO fence -- there `tpage` stays in a register and the store * has no reload after it, so its priority is low; here the very next * statement re-reads `*(u16 *)(q + 0x16)` from memory, which is what makes * the store's position observable at all.) A fence one statement LATER, or * moving the `|=` after the q[0xC] statement, both explode (+19 / +158). * * N3. THE TWO-SVECTOR FILL AT sp+0x48/0x50 IS A LOCAL-ALLOC DENSITY PROBLEM, * NOT A STATEMENT-ORDER ONE (9 -> 0). This is the block three agents * filed as unreachable after sweeping all 720 statement orders; a re-sweep * of all 720 x {2-operand,3-operand ldv3} x {memory clobber on/off} on the * fixed base confirms the order floor is 9 and the macro arity is inert. * The target holds FIVE values live here (e->u $v0, sz $v1, e->h $a0, * e->v $a1, e->w $v0-after-e->u-dies) and stores e->h and e->v LAST; every * C spelling coalesced e->h into $v0 and only ever kept FOUR. The lever is * cookbook 419 -- buy references instead of a register -- with ONE zero-byte * asm after the group: * v0.vx = *(u16 *)(p + 8); * vv2 = *(u16 *)(p + 0xA); v0.vy = vv2; * v0.vz = sz; * vw = rec >> 16; v1.vx = vw & 0xFF; * vh = rec >> 24; v1.vy = vh; * v1.vz = sz; * __asm__("" : : "r"(vh), "r"(vv2), "r"(vw)); * Three separate facts, each measured by removing it: * (a) holding e->h and e->v alone (`"r"(vh), "r"(vv2)`) is worth 9 -> 2: * it buys them $a0/$a1 and the whole register assignment becomes the * target's. Holding only one of the two is worth nothing (11 / 12), * and two SEPARATE one-operand asms cost 99 (each is an independent * volatile barrier and they serialise the block). * (b) adding `"r"(vw)` to the SAME asm lengthens the `(rec>>16)` chain by * one and flips `srl $v0,$s1,16` ahead of `lhu $a1,0($s3)` -- but then * the `andi` rides up with the `srl` and lands one slot too early. * (c) SPLITTING the shift from its mask -- `vw = rec >> 16;` held by the * asm, `v1.vx = vw & 0xFF;` as its own statement -- separates their * priorities so the `lhu` fits between them. That is the last 2. * The statement ORDER that works is the plain ascending one (vx,vy,vz then * vx,vy,vz), which the 720-sweep had scored 12 and thereby hidden: the * sweep was over the wrong axis, so its flat plateau proved only that the * residual was an ALLOCATION fact (func_8001EA14's own METHOD NOTE says * exactly this). Placement of the `vw`/`vh` statements inside the group is * inert once (a)-(c) hold (6 placements measured, all 0 or all 2). * * =========================================================================== * LEVERS INHERITED FROM THE 89 -> 14 PASS (all re-measured, all still load-bearing) * =========================================================================== * - POLY_FT4 vertex stores go per-VERTEX (x1=x0+w; y1=y0; x2=x0; y2=y0+h), * not in copy groups: that is the -1 length drift and the 42-ins tail. * - `u16 tpage` (NOT u32): a single-bit mask-shift `(sy & 0x100) >> 4` is * fixed by the DESTINATION'S WIDTH, not by the expression. Ten spellings * (incl. 432's shift-pair) all converge on srl;andi with a 32-bit temp; * narrowing the accumulator carries a 0xFFFF mask into the IOR tree and the * single-bit term stops folding. This is why two agents filed it [permuter]. * - UV copies in ascending offset order (0x14,0x15,0x1C,0x1D,0x24,0x25). * - SetRotMatrix/SetTransMatrix as TWO asm blocks clobbering only $12/$13/$14 * (a phantom "$2" clobber evicts `abr` from $v0). * - The sp+0x10 unit matrix initialised in plain field order. * - tpBb,tpAb,tpA,tpB DECLARED in that order but ASSIGNED tpA..tpBb: the * global-alloc tie between four equal allocnos breaks on pseudo number, and * it decides whether jump2 cross-jumps the two tails. * - `t14` as an s32 temp so convert_to_integer does not shorten * q[0x14] = q[0x0C] + ((rec>>16)&0xFF) into QImode and drop its `andi`. * - ot pinned to $4 (invariant_p refuses to hoist &otbase[d] out of a loop with * a call while the address holds a call-used hard reg), m24 to $5, mFF to $6. * Unpinning any of the three costs 200+ instructions (re-measured: mFF alone * -> 238, m24 alone -> 245, both -> 242, ot alone -> 15). * - A 4-byte struct local is BLKmode, so assign_stack_local gives it 8-byte * BIGGEST_ALIGNMENT: xy0/xy1 must be s32 or the frame goes 0x130 -> 0x138. * - fold reassociates `(x<<9)|CONST|var`, so the four getTPage bases must be * explicit pre-loop vars or loop.c hoists only the shift. * - `(u8)` casts on the s32 mode/abr keep the two `andi 0xFF` zero-extends. * * BANK NOTE: src/800.c's neighbouring func_8001EA14 already defines its own * suffixed SV_/MT_/Ft4_ typedefs and gte_* macros, so the _8001EFE0-suffixed * ones below do not collide; rtu_match.py --tu src/800.c compiles clean. */ /* ---- local PsyQ-shaped types (suffixed: src/800.c has no file-scope MATRIX) ---- */ typedef struct { s16 m[3][3]; s32 t[3]; } MATRIX_8001EFE0; /* 0x20 */ typedef struct { s16 vx, vy, vz, pad; } SVEC_8001EFE0; /* 0x08 */ typedef struct { s32 vx, vy, vz, pad; } VEC_8001EFE0; /* 0x10 */ typedef struct { /* 0x50 */ u32 flg; MATRIX_8001EFE0 coord; MATRIX_8001EFE0 workm; void *rotate; void *super; void *sub; } COORD2_8001EFE0; typedef struct { u16 x, y, z, w; } COL_8001EFE0; /* align 2 -> lwl/lwr (§48-C2) */ typedef struct { u16 vx, vy; } XY_8001EFE0; /* 0x04 */ /* ---- GTE inline macros (house spelling, suffixed to avoid TU collisions) ---- */ #define SRM_8001EFE0(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 STM_8001EFE0(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 ldv0_8001EFE0(r0) __asm__ volatile ( \ "lwc2 $0, 0( %0 );" \ "lwc2 $1, 4( %0 )" \ : : "r"( r0 ) ) #define ldv3_8001EFE0(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 rtps_8001EFE0() __asm__ volatile ("nop;nop;rtps") #define rtpt_8001EFE0() __asm__ volatile ("nop;nop;rtpt") #define stsxy_8001EFE0(r0) __asm__ volatile ( \ "swc2 $14, 0( %0 )" \ : : "r"( r0 ) : "memory" ) #define stsxy0_8001EFE0(r0) __asm__ volatile ( \ "swc2 $12, 0( %0 )" \ : : "r"( r0 ) : "memory" ) #define stsxy1_8001EFE0(r0) __asm__ volatile ( \ "swc2 $13, 0( %0 )" \ : : "r"( r0 ) : "memory" ) #define stflg_8001EFE0(r0) __asm__ volatile ( \ "cfc2 $12, $31;" \ "nop;" \ "sw $12, 0( %0 )" \ : : "r"( r0 ) : "$12", "memory" ) #define stsz_8001EFE0(r0) __asm__ volatile ( \ "swc2 $19, 0( %0 )" \ : : "r"( r0 ) : "memory" ) #define stszotz_8001EFE0(r0) __asm__ volatile ( \ "mfc2 $12, $19;" \ "nop;" \ "sra $12, $12, 2;" \ "sw $12, 0( %0 )" \ : : "r"( r0 ) : "$12", "memory" ) extern u8 D_800AF630[]; extern u8 D_800A6610[]; extern u8 *D_800A5E60; extern u32 D_80072784; 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_8001F730(s32 a0, void *a1, void *a2); void func_8001EFE0(s32 arg0) { MATRIX_8001EFE0 um; /* sp+0x10 */ SVEC_8001EFE0 sv; /* sp+0x30 */ VEC_8001EFE0 scale; /* sp+0x38 */ SVEC_8001EFE0 v0; /* sp+0x48 */ SVEC_8001EFE0 v1; /* sp+0x50 */ COORD2_8001EFE0 c2; /* sp+0x58 */ MATRIX_8001EFE0 ls; /* sp+0xA8 */ COL_8001EFE0 col; /* sp+0xC8 */ s32 flag; /* sp+0xD0 */ s32 sz; /* sp+0xD4 */ s32 otz; /* sp+0xD8 */ MATRIX_8001EFE0 *mp; s32 xy0; /* sp+0xDC */ s32 xy1; /* sp+0xE0 */ u32 attr; u8 *base; u8 *p; u8 *q; s32 shift; u32 *otbase; u32 tpBb; u32 tpAb; u32 tpA; u32 tpB; s32 d; u32 pri; s32 mode; s32 abr; u32 rec; u16 sx, sy; u16 tpage; s32 vv; u16 tv; u32 cy, cbase; u32 tag; s32 t14; u16 vv2; s32 vh; s32 vw; register u32 *ot __asm__("$4"); register u32 m24 __asm__("$5"); register u32 mFF __asm__("$6"); attr = *(u32 *)(arg0 + 4); c2.super = *(void **)(arg0 + 0x78); c2.flg = 0; base = D_800AF630; if (*(u16 *)(arg0 + 0x2C) & 1) { *(u16 *)(arg0 + 0x2C) &= 0xFFFE; c2.coord = *(MATRIX_8001EFE0 *)(arg0 + 0x34); } else { func_80049CAC(arg0 + 0x10, (s32)&c2.coord); c2.coord.t[0] = *(s16 *)(arg0 + 8); c2.coord.t[1] = *(s16 *)(arg0 + 0xA); c2.coord.t[2] = *(s16 *)(arg0 + 0xC); } scale.vx = *(s16 *)(arg0 + 0x18); scale.vy = *(s16 *)(arg0 + 0x1A); scale.vz = *(s16 *)(arg0 + 0x1C); func_8004901C(&c2.coord, &scale); mp = &ls; func_800547D8((s32)&c2, mp); *(MATRIX_8001EFE0 *)(arg0 + 0x34) = c2.coord; *(MATRIX_8001EFE0 *)(arg0 + 0x54) = c2.workm; p = *(u8 **)(arg0 + 0x20); if (p != NULL && func_80021174(*(s32 *)(arg0 + 0x28), (s32)&c2.workm.t[0]) != 0) { SRM_8001EFE0(mp); STM_8001EFE0(mp); sv.vx = sv.vy = sv.vz = 0; ldv0_8001EFE0(&sv); rtps_8001EFE0(); stsxy_8001EFE0(&sv); stflg_8001EFE0(&flag); stsz_8001EFE0(&sz); stszotz_8001EFE0(&otz); if ((flag & ~0x1000) == 0) { d = otz + 1; otbase = (u32 *)&D_800A6610[*(u16 *)(base + 0xA3D2) << 14]; pri = *(u16 *)(arg0 + 0x2E); if (pri & 0xC000) { if ((pri & 0xC000) == 0xC000) { d -= (pri & 0xFFF); if (d < 0) { d = 0; } } else { d += (pri & 0xFFF); } } if ((u32)d < 0x1000) { if (*(u16 *)(arg0 + 0x2C) & 0x80) { col = *(COL_8001EFE0 *)(*(u8 **)(arg0 + 0x80)); } else { col.x = col.y = col.z = 0x80; } mode = (attr >> 24) & 3; um.m[0][0] = *(u16 *)(arg0 + 0x18); um.m[0][1] = 0; um.m[0][2] = 0; um.m[1][0] = 0; um.m[1][1] = *(u16 *)(arg0 + 0x1A); um.m[1][2] = 0; um.m[2][0] = 0; um.m[2][1] = 0; um.m[2][2] = 0x1000; um.t[2] = 0; um.t[1] = 0; um.t[0] = 0; shift = 2 - mode; abr = (attr >> 28) & 3; SRM_8001EFE0(&um); STM_8001EFE0(&um); tpA = ((u8)mode << 9) | ((u8)abr << 7); tpB = ((u8)mode << 7) | ((u8)abr << 5); tpAb = ((u8)mode << 9) | 0x80; tpBb = ((u8)mode << 7) | 0x20; do { rec = *(u32 *)p; if ((rec & 0xFF) != 8) { q = D_800A5E60; sx = *(u16 *)(p + 4); sy = *(u16 *)(p + 6); D_800A5E60 = q + 0x28; q[3] = 9; q[7] = 0x2C; if (attr & 0x40000000) { q[7] = 0x2E; if ((u32)(*(u8 *)(D_80072784 + 4) - 1) < 2) { tpage = (tpA | ((sy & 0x300) >> 3)) | ((sx & 0x3C0) >> 6); } else { tpage = (tpB | ((sy & 0x100) >> 4)) | ((sx & 0x3C0) >> 6) | ((sy & 0x200) << 2); } } else { if ((u32)(*(u8 *)(D_80072784 + 4) - 1) < 2) { tpage = (tpAb | ((sy & 0x300) >> 3)) | ((sx & 0x3C0) >> 6); } else { tpage = (tpBb | ((sy & 0x100) >> 4)) | ((sx & 0x3C0) >> 6) | ((sy & 0x200) << 2); } } *(u16 *)(q + 0x16) = tpage; __asm__ __volatile__(""); q[7] |= (attr & 0x40) >> 6; q[0x0C] = (sx - ((*(u16 *)(q + 0x16) & 0xF) << 6)) << shift; vv = sy; if (*(u16 *)(q + 0x16) & 0x10) { tv = vv - 0x100; } else { tv = vv; } q[0x0D] = tv; t14 = q[0x0C] + ((rec >> 16) & 0xFF); q[0x14] = t14; q[0x15] = q[0x0D]; q[0x1C] = q[0x0C]; q[0x1D] = q[0x0D] + (rec >> 24); q[0x24] = q[0x14]; q[0x25] = q[0x1D]; cy = (rec >> 8) & 0xFF; cbase = (cy + 0x100) << 6; if (cy < 0xE0) { *(u16 *)(q + 0xE) = cbase | 0x16; } else { *(u16 *)(q + 0xE) = cbase | 0x10; } q[4] = col.x; q[5] = col.y; q[6] = col.z; v0.vx = *(u16 *)(p + 8); vv2 = *(u16 *)(p + 0xA); v0.vy = vv2; v0.vz = sz; vw = rec >> 16; v1.vx = vw & 0xFF; vh = rec >> 24; v1.vy = vh; v1.vz = sz; __asm__("" : : "r"(vh), "r"(vv2), "r"(vw)); ldv3_8001EFE0(&v0, &v1, &v1); rtpt_8001EFE0(); stsxy0_8001EFE0(&xy0); stsxy1_8001EFE0(&xy1); *(u16 *)(q + 8) = *(u16 *)&xy0 + sv.vx; *(u16 *)(q + 0xA) = *((u16 *)&xy0 + 1) + sv.vy; *(u16 *)(q + 0x10) = *(u16 *)(q + 8) + *(u16 *)&xy1; *(u16 *)(q + 0x12) = *(u16 *)(q + 0xA); *(u16 *)(q + 0x18) = *(u16 *)(q + 8); *(u16 *)(q + 0x1A) = *(u16 *)(q + 0xA) + *((u16 *)&xy1 + 1); *(u16 *)(q + 0x20) = *(u16 *)(q + 0x10); *(u16 *)(q + 0x22) = *(u16 *)(q + 0x1A); if (*(u32 *)(arg0 + 4) & 0x8000000) { func_8001F730(arg0 + 0x10, &sv, q); } m24 = 0xFFFFFF; ot = (u32 *)(d * 4); mFF = 0xFF000000; ot = (u32 *)((s32)ot + (s32)otbase); tag = *(u32 *)q; *(u32 *)q = (tag & mFF) | (*ot & m24); *ot = (*ot & mFF) | ((u32)q & m24); } p += 12; } while ((rec & 0xFF) != 0xFF); } } } } /* func_8001F730 — rotate the four corners of a POLY_FT4-shaped primitive * (x/y pairs at +0x08, +0x10, +0x18, +0x20) about a u16 centre point (a1) * using the rotation matrix built by func_80020DA4(a0, &m). * Fully unrolled in the source: gcc-2.7.2 -O2 does not unroll loops. */ extern void func_80020DA4(s32 a0, s32 a1); #define SRM_8001F730(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 LDV0_8001F730(r0) __asm__ volatile ( \ "lwc2 $0, 0( %0 );" \ "lwc2 $1, 4( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define RTV0_8001F730() __asm__ volatile ( \ "nop;" \ "nop;" \ "mvmva 1, 0, 0, 3, 0" \ : : : "memory" ) #define STLVNL_8001F730(r0) __asm__ volatile ( \ "swc2 $25, 0( %0 );" \ "swc2 $26, 4( %0 );" \ "swc2 $27, 8( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) void func_8001F730(s32 a0, void *a1, void *a2) { s32 matrix[8]; /* sp+0x10 */ s16 sv[4][4]; /* sp+0x30 */ s32 out[4][4]; /* sp+0x50 */ u16 *c = (u16 *)a1; u8 *p = (u8 *)a2; func_80020DA4(a0, (s32)matrix); SRM_8001F730(matrix); sv[0][0] = *(u16 *)(p + 0x08) - c[0]; sv[0][1] = *(u16 *)(p + 0x0A) - c[1]; sv[0][2] = 0; sv[1][0] = *(u16 *)(p + 0x10) - c[0]; sv[1][1] = *(u16 *)(p + 0x12) - c[1]; sv[1][2] = 0; sv[2][0] = *(u16 *)(p + 0x18) - c[0]; sv[2][1] = *(u16 *)(p + 0x1A) - c[1]; sv[2][2] = 0; sv[3][0] = *(u16 *)(p + 0x20) - c[0]; sv[3][1] = *(u16 *)(p + 0x22) - c[1]; sv[3][2] = 0; LDV0_8001F730(sv[0]); RTV0_8001F730(); STLVNL_8001F730(out[0]); LDV0_8001F730(sv[1]); RTV0_8001F730(); STLVNL_8001F730(out[1]); LDV0_8001F730(sv[2]); RTV0_8001F730(); STLVNL_8001F730(out[2]); LDV0_8001F730(sv[3]); RTV0_8001F730(); STLVNL_8001F730(out[3]); *(u16 *)(p + 0x08) = c[0] + out[0][0]; *(u16 *)(p + 0x0A) = c[1] + out[0][1]; *(u16 *)(p + 0x10) = c[0] + out[1][0]; *(u16 *)(p + 0x12) = c[1] + out[1][1]; *(u16 *)(p + 0x18) = c[0] + out[2][0]; *(u16 *)(p + 0x1A) = c[1] + out[2][1]; *(u16 *)(p + 0x20) = c[0] + out[3][0]; *(u16 *)(p + 0x22) = c[1] + out[3][1]; } void func_8001F974(void) { } void func_8001F97C(void *a0) { extern void func_80020F34(s32 a0, s32 a1); s32 t1, t2, t3; func_80020DA4((s32)((u8 *)a0 + 0x10), (s32)((u8 *)a0 + 0x34)); if (*(u16 *)((u8 *)a0 + 0x2C) & 0x10) { func_80020F34((s32)((u8 *)a0 + 0x34), (s32)((u8 *)a0 + 0x18)); } t1 = *(s16 *)((u8 *)a0 + 0x08); t2 = *(s16 *)((u8 *)a0 + 0x0A); t3 = *(s16 *)((u8 *)a0 + 0x0C); *(s32 *)((u8 *)a0 + 0x48) = t1; *(s32 *)((u8 *)a0 + 0x4C) = t2; *(s32 *)((u8 *)a0 + 0x50) = t3; *(u16 *)((u8 *)a0 + 0x2C) |= 1; } extern u8 D_800AF8D8[]; extern u8 D_800AFB6C[]; extern u8 D_800AFBF0[]; extern u8 D_800B0430[]; extern u8 D_800B0C70[]; extern u8 D_800B3DF0[]; extern u8 D_800B9A11; extern void func_8001FB8C(s32* a0, s32* a1); void func_8001F9F8(void) { u8* p; u8 bits; s32 x; bits = D_800B9A11; { s32 i; i = 0; p = D_800AF8D8; x = 0; for (; i < 4; i++) { func_8001FB8C(p, &x); p += 0x84; } } if (!(bits & 2)) { s32 i; i = 0; p = D_800AF8D8 + 0x210; for (; i < 1; i++) { func_8001FB8C(p, &x); p += 0x84; } } if (!(bits & 8)) { s32 i; i = 0; p = D_800AFB6C; for (; i < 1; i++) { func_8001FB8C(p, &x); p += 0x84; } } if (!(bits & 0x10)) { s32 i; i = 0; p = D_800AFBF0; for (; i < 0x10; i++) { func_8001FB8C(p, &x); p += 0x84; } } if (!(bits & 0x20)) { s32 i; i = 0; p = D_800B0430; for (; i < 0x10; i++) { func_8001FB8C(p, &x); p += 0x84; } } if (!(bits & 0x40)) { s32 i; i = 0; p = D_800B0C70; for (; i < 0x60; i++) { func_8001FB8C(p, &x); p += 0x84; } } if (!(bits & 0x80)) { s32 i; i = 0; p = D_800B3DF0; for (; i < 0x3A; i++) { func_8001FB8C(p, &x); p += 0x84; } } } extern void (*D_80063500[])(); void func_8001FB8C(s32 *a0, s32 *a1) { if (a0[1] < 0) return; if (*(u16 *)a0 != 1) return; if (*(u16 *)((u8 *)a0 + 2) < 9) { D_80063500[*(u16 *)((u8 *)a0 + 2)](a0); a1[0]++; } } /* func_8001FC08 — the src/800.c model/actor DRAW dispatcher (400 ins, binary main). * * Builds the object's world matrix (func_80020DA4 + optional func_80020F34 scale), * copies it back into the object at +0x34, then walks the NULL-terminated primitive * list at +0x24 calling per-mode add-prim helpers. mode (+0x02) selects the emitter: * 0 -> func_80057094, 1 -> func_80027BF0 (first two only) / func_80055D40, * 5 -> func_800243EC, 6 -> func_800CAE80. * * match_one: MATCH, 400/400 ins, closeness 0. * * ---- INTEGRATION: RESOLVED (S77w) ------------------------------------------- * The previous MATCH of this function (S76, closeness 0) could not BANK. Cause, * now measured rather than guessed: it carried its own * `typedef struct {...} MTX_80020248;` plus `extern MTX_80020248 D_80074818[]/ * D_80075018[]` at FILE scope, and the TU declares those very names 15 lines * BELOW this function's INCLUDE_ASM (src/800.c:11213 / :11221-2). Splicing the * draft in and running the real cpp+cc1 gave THREE HARD ERRORS (cc1 rc=33 vs the * unmodified TU's rc=0): * conflicting types for `MTX_80020248' / `D_80074818' / `D_80075018' * Two same-TU anonymous struct typedefs are never compatible types in C89, so no * spelling of a duplicate file-scope typedef can work; the old note's "hoist the * TU's block above the INCLUDE_ASM" would edit shared carve state for one draft. * * THE FIX, and why it is sound: gcc-2.7.2 raises "conflicting types" (error) only * for a redeclaration in the SAME scope. Across scopes it degrades to * "type mismatch with previous external decl" (a WARNING — the same class the * unmodified TU already emits for func_80012E0C/func_80013028). So this draft * (a) names its own layout-identical struct `MTX_8001FC08`, never MTX_80020248, and * (b) declares `D_80074818` / `D_80075018` at BLOCK scope inside the function. * Measured after the change: cc1 rc=0, exactly 4 added warnings, 0 errors, and the * whole-TU .text is BYTE-IDENTICAL to the all-INCLUDE_ASM build once the linker * resolves D_1F800020 (= 0x1F800020, per build/us/SLUS_007.26.map:4858) — no other * function in src/800.c shifts by a byte. Nothing in src/ needs touching. * * The callee externs stay deliberately UNPROTOTYPED `f()` forms (C89-compatible * with every later prototype in the TU), which is what lets this function call * func_80021050 / func_80021120 with more arguments than their later same-TU * definitions declare — the target really does set those registers (lever 3). * * LAW 1c VERIFIED BY HAND (match_one masks relocations, so MATCH alone proves * nothing about symbol identity). Against this target's OWN relocation lines: * - 26 `jal` targets, identical in name AND order; * - 16 HI16/LO16 relocs, identical in name AND order (D_800B9A02, D_80062BA8, * D_800A6518, D_800A5BD0, D_80074818 x2, D_80075018 x2); * - all 16 internal `j` destinations decoded and equal (the §195-D blind spot, * which match_one, the permuter scorer and every similarity tier cannot see); * - the four words splat prints as %hi/%lo(D_1F800020) are NOT a real symbol — * they are `lui $s1,0x1F80` (the 0x1F800000 scratchpad literal) and the * `rm++` +0x20 pointer increment, which splat mis-paired into one fake * symbol whose value happens to agree. ROM 3C111F80 / 26310020 at words * 180/239/262/375; this draft emits those same four words directly. * ------------------------------------------------------------------------------ * * LEVERS THAT MOVED THE BYTES (each byte-measured; 1-3 took the standing best * from closeness 33 to 0, and are kept verbatim from the S76 attempt): * * 0. (carried from the previous attempt, all still required) * - the 0x18..0xC7 locals area is ONE address-taken aggregate; * - `s32 mode`, NOT `u16 mode` (as u16 it loses its global allocno); * - struct copies must be <= 32 B (`obj->mm.a = L.mm.a;` twice, never * `obj->mm = L.mm`, which becomes a block_move loop); * - `if (f() == 0) { main-RAM } else { scratchpad }` — the ZERO test is the * fall-through arm for BOTH func_80020248 and func_80020A28; * - `objs & 0xFEFFFFFF` must be a NAMED local computed BEFORE the * `if (flags & 0x40)` (gcc-2.7.2 has no PRE); * - the 0x7F000000 tag mask is computed BEFORE the 0x80FFFFFF mask. * * 1. *** THE 8-BYTE SPILL SLOT. sp+0xC8 and sp+0xD0 are NOT struct members — * they are two SPILLED PSEUDOS. *** `ot` (= D_80062BA8) and `spr` (= the * +0x80 handle) are ordinary long-lived locals; $s0-$s7 + $fp are already * taken by obj/om, objs/rm, mode, list, flags, sp5, otp, attr and fpc — nine * values — so these two are the 10th/11th and reload spills them. * The TELL, and the reason this is not guesswork: reload's `alter_reg` calls * `assign_stack_local (mode, size, -1)`, and align == -1 means * `alignment = BIGGEST_ALIGNMENT` (8 on MIPS) with `size = CEIL_ROUND(size,8)` * — so every 4-byte spill slot occupies EIGHT bytes. That is exactly why the * target's slots are 8 apart (0xC8, 0xD0) with 0xCC and 0xD4 never touched, * while the declared aggregate ends at 0xC8 (0x18 + 0xB0) and the saves start * at 0xD8: 0x18 args + 0xB0 aggregate + 0x10 spills + 0x28 saves = 0x100. * ANY 4-byte gap in a frame otherwise packed at 4 is a spill slot, not a pad. * A spilled pseudo also RE-LOADS at every use, which is what the previous * attempt was faking with a `volatile` struct member. * Worth 11 instructions on its own (33 -> 22): it takes both slots out of * local-alloc's pool, so reload picks their registers ($t0) instead of $v0. * * 2. *** THE PRE-PROLOGUE LOAD IS NOT A WALL — it is an $a0 ANTI-DEPENDENCE. *** * The target opens with `lui/lhu $v1, D_800B9A02` BEFORE `addiu $sp,$sp,-0x100`. * The previous attempt proved from the -dR sched2 dump that no statement order * reaches it and filed it as the §41b "global load above the RTL prologue" * wall (20 of its 33). That reading was wrong. sched2 weaves body insns into * the RTL prologue freely (S7); the load is one insn (the -G0 macro form, * length 2), it depends on nothing, and it floats to the very top of the block * — UNLESS it lands in $a0, because the param copy `addu $s0,$a0,$zero` READS * $a0 and pins it below. So the fix is two independent moves that must be * made TOGETHER: * (a) make `otp = &D_800A6518[D_800B9A02 * 20];` the FIRST statement (LUID), and * (b) get the value out of $a0 — which lever 1 does for free: once `ot` is a * spilled pseudo it leaves local-alloc's pool, so the index temp takes * $v1 instead and nothing anchors it. * Either alone is worthless (statement-first alone MEASURED 33 -> 50). * Together: 22 -> 4. The `sll/addu/sll` scaling chain stays behind at its own * priority (load-fed, pri 2), which is why the target shows the load at idx 0 * and its own multiply at idx 22. * * 3. *** `func_80021120(&L.cnt, L.lp)` — the guard value IS the second argument. *** * The last 4: the target reads sp+0xC0 into $a1 at the two func_80021120 guards * but into $v0 at the two func_8002109C guards. $a1 is unreachable by * local-alloc's scan-from-$v0 unless the pseudo carries a COPY SUGGESTION to a * hard reg (`qty_phys_copy_sugg`) — i.e. unless that very value is passed to the * call as arg 1. Passing it makes the test load land in $a1 and coalesces the * copy away at zero cost. func_8002109C's guard does NOT pass it, and stays $v0 * — which is the control that proves the mechanism rather than a coincidence. * (The same reading explains the extra argument at func_80021050/func_80021008.) * A `register s16 * __asm__("$5")` pin also reaches MATCH, but this is the form * the original had: no pin, no asm. 4 -> 0. */ typedef struct { s16 m[3][3]; s32 t[3]; } MTX_8001FC08; /* 0x20 */ typedef struct { MTX_8001FC08 a; MTX_8001FC08 b; } MTX2_8001FC08; /* 0x40 */ typedef struct { /* 0x00 */ u16 h00; /* 0x02 */ u16 mode; /* 0x04 */ u32 attr; /* 0x08 */ s16 x; /* 0x0A */ s16 y; /* 0x0C */ s16 z; /* 0x0E */ u16 h0E; /* 0x10 */ s16 rot[3]; /* 0x16 */ u16 h16; /* 0x18 */ s16 scale[3]; /* 0x1E */ u16 h1E; /* 0x20 */ u32 objs; /* 0x24 */ s32 *list; /* 0x28 */ s32 w28; /* 0x2C */ u16 flags; /* 0x2E */ u16 idx; /* 0x30 */ s32 w30; /* 0x34 */ MTX2_8001FC08 mm; /* 0x74 */ s32 w74; /* 0x78 */ s32 w78; /* 0x7C */ s32 w7C; /* 0x80 */ s32 w80; } OBJ_8001FC08; typedef struct { /* 0x00 */ s32 attr; /* 0x04 */ s32 u04; /* 0x08 */ s32 u08; /* 0x0C */ s32 u0C; /* 0x10 */ s32 u10; /* 0x14 */ s32 u14; /* 0x18 */ MTX_8001FC08 m0; /* 0x38 */ MTX_8001FC08 m1; /* 0x58 */ s32 f; /* 0x5C */ MTX2_8001FC08 mm; /* 0x9C */ s32 u9C; /* 0xA0 */ s32 uA0; /* 0xA4 */ s32 uA4; /* 0xA8 */ s16 *lp; /* 0xAC */ s32 cnt; } LOC_8001FC08; /* 0xB0 */ extern u8 D_800A6518[]; extern u8 D_800A5BD0[]; extern u16 D_800B9A02; extern s32 D_80062BA8; extern void func_80020DA4(); extern void func_80020F34(); extern s32 func_80020248(); extern void func_80020598(); extern s32 func_80020A28(); extern void func_80054AAC(); extern s32 func_80021174(); extern void func_80021008(); extern void func_80021050(); extern void func_8002109C(); extern void func_80021120(); extern void func_80056F18(); extern void func_80052F04(); extern void func_80052E38(); extern void func_80057094(); extern void func_80055D40(); extern void func_80027BF0(); extern void func_800243EC(); extern void func_800CAE80(); void func_8001FC08(OBJ_8001FC08 *obj) { LOC_8001FC08 L; extern MTX_8001FC08 D_80074818[]; extern MTX_8001FC08 D_80075018[]; MTX_8001FC08 *rm; MTX_8001FC08 *om; s32 *list; u8 *sp5; u8 *otp; u32 attrv; u32 objs; u32 w; u32 m; u32 tag; u32 n; s32 mode; u16 flags; u32 fpc; s32 ot; s32 spr; otp = &D_800A6518[D_800B9A02 * 20]; attrv = obj->attr; mode = obj->mode; flags = obj->flags; L.uA0 = obj->w78; ot = D_80062BA8; L.f = 0; if (flags & 1) { obj->flags &= ~1; L.mm.a = obj->mm.a; } else { func_80020DA4((s32)obj->rot, (s32)&L.mm.a); L.mm.a.t[0] = obj->x; L.mm.a.t[1] = obj->y; L.mm.a.t[2] = obj->z; } spr = 0; if (flags & 0x20) { spr = obj->w80; } if (flags & 0x80) { if (spr != 0) { L.lp = (s16 *)(obj->w80 + 0x30); } else { L.lp = (s16 *)obj->w80; } } else { L.lp = 0; } L.cnt = 0; fpc = 0; if (flags & 0x10) { func_80020F34((s32)&L.mm.a, (s32)obj->scale); } func_80054AAC((s32)&L.f, (s32)&L.m0, (s32)&L.m1); obj->mm.a = L.mm.a; obj->mm.b = L.mm.b; list = obj->list; if (list == 0) { return; } objs = obj->objs; if (objs == 0) { return; } if (func_80021174(obj->w28, (s32)&L.mm.b.t[0]) == 0) { return; } if (mode == 1 || mode == 6) { sp5 = &D_800A5BD0[obj->idx * 20]; L.attr = attrv; L.u10 = 0; } else { L.attr = attrv; } if (spr != 0) { func_80021008(spr); } if ((objs & 0x01000000) == 0 && (flags & 0x40) == 0) { if (func_80020248(list, objs, (s32)&L.m0, (s32)&L.m1, attrv & 0x40) == 0) { rm = D_80074818; om = D_80075018; } else { rm = (MTX_8001FC08 *)0x1F800000; om = (MTX_8001FC08 *)0x1F8000E0; } w = *list; if (w != 0) { do { tag = w & 0x7F000000; m = w & 0x80FFFFFF; if (tag != 0x01000000) { func_80056F18(m + 0xC, (s32)&L, 0); if ((attrv & 0x40) == 0) { func_80052F04(rm); } func_80052E38(om); if (L.lp != 0) { func_8002109C(L.cnt, &L.lp); } if (mode == 0) { func_80057094((s32)&L, (s32)otp, 0, ot); } else if (mode == 5) { func_800243EC((s32)&L, (s32)otp, 0); } } if (L.lp != 0) { func_80021120(&L.cnt, L.lp); } list++; rm++; om++; w = *list; } while (w != 0); } } else { n = objs & 0xFEFFFFFF; if (flags & 0x40) { func_80020598(list, n, (s32)&L.m0, (s32)&L.m1); rm = D_80074818; om = D_80075018; } else { if (func_80020A28(list, n, (s32)&L.m0, (s32)&L.m1, attrv & 0x40) == 0) { rm = D_80074818; om = D_80075018; } else { rm = (MTX_8001FC08 *)0x1F800000; om = (MTX_8001FC08 *)0x1F8000E0; } } w = *list; if (w != 0) { do { tag = w & 0x7F000000; m = w & 0x80FFFFFF; if (tag == 0x01000000) { if (mode == 1 || mode == 6) { sp5 += 0x14; } } else { if (mode == 0 || mode == 5) { func_80056F18(m + 0xC, (s32)&L, 0); } else if (mode == 1 || mode == 6) { L.u08 = *(s32 *)(sp5 + 8); L.u0C = *(s32 *)(sp5 + 0xC); sp5 += 0x14; } else { L.u08 = m; } if ((attrv & 0x40) == 0) { func_80052F04(rm); } func_80052E38(om); if ((flags & 0x40) == 0 && L.lp != 0) { func_8002109C(L.cnt, &L.lp); } if (mode == 0) { func_80057094((s32)&L, (s32)otp, 0, ot); } else if (mode == 1) { if (fpc < 2) { func_80027BF0((s32)&L, (s32)otp, 0); fpc++; } else { func_80055D40((s32)&L, (s32)otp, 0, ot); } } else if (mode == 6) { func_800CAE80((s32)&L, (s32)otp, 0); } else if (mode == 5) { func_800243EC((s32)&L, (s32)otp, 0); } } if ((flags & 0x40) == 0 && L.lp != 0) { func_80021120(&L.cnt, L.lp); } list++; rm++; om++; w = *list; } while (w != 0); } } if (spr != 0) { func_80021050(spr); } } /* func_80020248 — build the per-object rotation/translation matrices for a * NULL-terminated list of objects. If the list is short (< 8 entries) the two * output matrix arrays live in the scratchpad (0x1F800000 / 0x1F8000E0) and the * function returns 1; otherwise they go to the main-RAM buffers D_80074818 / * D_80075018 and it returns 0. * * All the GTE work is the usual libgte inline-asm block set (SetRotMatrix / * MulMatrix0 column loop / SetTransMatrix / RotTrans). */ typedef struct { /* 0x00 */ s16 m[3][3]; /* 0x14 */ s32 t[3]; } MTX_80020248; /* 0x20 */ typedef struct { /* 0x0 */ u32 xy; /* 0x4 */ s16 z; /* 0x6 */ s16 rot[3]; } OBJ_80020248; /* 0xC */ extern MTX_80020248 D_80074818[]; extern MTX_80020248 D_80075018[]; extern void func_80020DA4(s32 a0, s32 a1); #define SRM_80020248(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 STM_80020248(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 LDCLMV_80020248(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_80020248() __asm__ volatile ( \ "nop;" \ "nop;" \ "mvmva 1, 0, 3, 3, 0" \ : : : "memory" ) #define STCLMV_80020248(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 LDLVL_80020248(r0) __asm__ volatile ( \ "lhu $13, 4( %0 );" \ "lhu $12, 0( %0 );" \ "sll $13, $13, 16;" \ "or $12, $12, $13;" \ "mtc2 $12, $0;" \ "lwc2 $1, 8( %0 )" \ : \ : "r"( r0 ) \ : "$12", "$13" ) #define RT_80020248() __asm__ volatile ( \ "nop;" \ "nop;" \ "mvmva 1, 0, 0, 0, 0" \ : : : "memory" ) #define STLVNL_80020248(r0) __asm__ volatile ( \ "swc2 $25, 0( %0 );" \ "swc2 $26, 4( %0 );" \ "swc2 $27, 8( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) s32 func_80020248(s32 *a0, OBJ_80020248 *a1, MTX_80020248 *a2, MTX_80020248 *a3, s32 a4) { MTX_80020248 lm; /* sp+0x10 */ MTX_80020248 *rm; MTX_80020248 *om; u32 w; s32 n; s32 ret; ret = 0; n = 0; while (*a0++ != 0) { n++; } if (n >= 8) { rm = D_80074818; om = D_80075018; } else { rm = (MTX_80020248 *)0x1F800000; om = (MTX_80020248 *)0x1F8000E0; ret = 1; } while (--n != -1) { func_80020DA4((s32)a1->rot, (s32)&lm); w = a1->xy; lm.t[0] = w & 0xFFFF; lm.t[1] = w >> 16; lm.t[2] = a1->z; if (a4 == 0) { SRM_80020248(a2); LDCLMV_80020248(&lm.m[0][0]); RTIR_80020248(); STCLMV_80020248(&rm->m[0][0]); LDCLMV_80020248(&lm.m[0][1]); RTIR_80020248(); STCLMV_80020248(&rm->m[0][1]); LDCLMV_80020248(&lm.m[0][2]); RTIR_80020248(); STCLMV_80020248(&rm->m[0][2]); rm++; } SRM_80020248(a3); LDCLMV_80020248(&lm.m[0][0]); RTIR_80020248(); STCLMV_80020248(&om->m[0][0]); LDCLMV_80020248(&lm.m[0][1]); RTIR_80020248(); STCLMV_80020248(&om->m[0][1]); LDCLMV_80020248(&lm.m[0][2]); RTIR_80020248(); STCLMV_80020248(&om->m[0][2]); STM_80020248(a3); LDLVL_80020248(&lm.t[0]); RT_80020248(); STLVNL_80020248(&om->t[0]); om++; a1++; } return ret; } /* func_80020598 — per-object rotation/translation matrix builder, sibling of * func_80020248 (same TU, same libgte inline-asm block set). Walks a * NULL-terminated s32 list; for each entry it builds a local MATRIX from a * packed 12-bit rotation triple, then writes the concatenated matrix into * D_80074818[i] (via a2) and D_80074818[i + 0x40] == D_80075018[i] (via a3), * plus the RotTrans'd translation vector. * * Index 0 and 1 take their rotation from entry[3] (a1[3]) and their translation * from entry[0]'s first / second byte triple; index >= 2 takes both from the * entry the walking pointer is on. Indices 0x19 / 0x16 / 8 get an extra * uniform ScaleMatrix by D_80126C4E / D_80126D10 / D_80126D12. * * Levers this one needed (all byte-measured against asm/nonmatchings/800): * - §168/§194-E the SAME-TU neighbour func_80020248 supplies the eight * gte_* macro bodies verbatim; nothing else reproduces them. * - the 3-way index dispatch is a `switch`, NOT `if / else if`: the target * branches to all three arms (beqz / beq / j) instead of falling through * into arm 0. That alone was 30+ rows. * - only ONE pointer variable (a1) is written; `p = a1 + 5` is loop.c's own * combined address giv ($s0, preheader `addiu $s0,$a1,0x5`). Writing that * second pointer in C costs 8 instructions and a whole different regalloc. * - §3-T2 statement order: `i = 0;` must precede `rm = D_80074818;` or the * lui/addiu pair schedules ahead of the `lw $v0,0($s5)` guard and leaves a * load-delay nop (LENGTH-DRIFT +1, and it swaps the $s1/$s2 save order). * - §333/§334 frame arithmetic: .frame is 0x88 with only 0x28 of live locals. * The a2/a3 homes are 8-byte-aligned/8-byte-rounded slots at 0x38/0x40 and * 24 further bytes sit ABOVE them at 0x48-0x5F. A trailing dead aggregate * alone cannot reproduce that (declared locals are slotted BEFORE the * reload spills, so it pushes 0x38/0x40 to 0x50/0x58); modelling the two * pointer homes as declared 8-byte aggregates and putting the dead 24 bytes * after them lands every offset exactly. * - §17 register pin: the a3 home must be read once into $t0 and survive the * "memory"-clobbering RTIR/STCLMV blocks so STM can reuse it. Unpinned, * gcc picks $v1 (9 rows); `register ... __asm__("$8")` on one variable that * carries BOTH homes in turn closes them all. * * - the last instruction (default arm `lbu $v1,0x1($s3)` — biv-based while * arm 0's identical byte is giv-based) is NOT a combine_givs split: it is a * pass-ordering fact. Spell the byte as `(w >> 8) & 0xFF` off the bare word * load (the same-TU func_80020A28 precedent), NOT as a byte deref. A byte * deref is a DEST_ADDR giv that loop.c unconditionally folds onto the * reduced register; the shift-mask defers the load's BIRTH to the post-loop * combine pass, which narrows it into `lbu 1(base)` off the ORIGINAL base. * (Temp-pointer spellings cannot reach it: cse1's find_best_addr folds any * same-ebb temp back into (plus base k) before loop — the equal-ADDRESS_COST * tie-break prefers the complex address to free a register.) The narrowed * spelling shifts spill demand: pad[6] -> pad[4] keeps the frame at 0x88. * * MATCH: 292/292, closeness 0 (S69e1 fable escalation). Relocs walked: 3x * func_80020DA4, func_80012558, func_80020F34 (cross-jumped shared tail), * D_80074818, D_80126C4E/D10/D12 — all verified against the target's own lines. */ /* 0x20 */ typedef struct { MTX_80020248 *p; s32 unused; } MH; typedef struct { /* 0x0 */ u8 b0; /* 0x1 */ u8 b1; /* 0x2 */ u8 b2; /* 0x3 */ s8 t[3]; /* 0x6 */ u16 h; } OBJ598; /* 0x8 */ extern MTX_80020248 D_80074818[]; extern u16 D_80126C4E; extern u16 D_80126D10; extern u16 D_80126D12; extern void func_80020DA4(s32 a0, s32 a1); extern void func_80012558(s32 a0, s32 a1); extern void func_80020F34(s32 a0, s32 a1); #define SRM_80020598(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 STM_80020598(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 LDCLMV_80020598(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_80020598() __asm__ volatile ( \ "nop;" \ "nop;" \ "mvmva 1, 0, 3, 3, 0" \ : : : "memory" ) #define STCLMV_80020598(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 LDLVL_80020598(r0) __asm__ volatile ( \ "lhu $13, 4( %0 );" \ "lhu $12, 0( %0 );" \ "sll $13, $13, 16;" \ "or $12, $12, $13;" \ "mtc2 $12, $0;" \ "lwc2 $1, 8( %0 )" \ : \ : "r"( r0 ) \ : "$12", "$13" ) #define RT_80020598() __asm__ volatile ( \ "nop;" \ "nop;" \ "mvmva 1, 0, 0, 0, 0" \ : : : "memory" ) #define STLVNL_80020598(r0) __asm__ volatile ( \ "swc2 $25, 0( %0 );" \ "swc2 $26, 4( %0 );" \ "swc2 $27, 8( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) void func_80020598(s32 *a0, OBJ598 *a1, MTX_80020248 *a2, MTX_80020248 *a3) { MTX_80020248 lm; s16 local[3]; MTX_80020248 *rm; s32 i; u32 w; s16 v; MH h2; MH h3; register MTX_80020248 *m3 __asm__("$8"); s32 pad[4]; h2.p = a2; h3.p = a3; i = 0; rm = D_80074818; while (*a0 != 0) { switch (i) { case 0: local[0] = a1[3].h; w = *(u32 *)&a1[3]; local[1] = a1[3].b1 | ((w & 0xF) << 8); local[2] = ((w >> 16) & 0xFF) | ((w & 0xF0) << 4); func_80020DA4((s32)local, (s32)&lm); func_80012558((s32)&lm, 0x800); lm.t[0] = (s8)a1->b0; lm.t[1] = (s8)a1->b1; lm.t[2] = (s8)a1->b2; break; case 1: local[0] = a1[3].h; w = *(u32 *)&a1[3]; local[1] = a1[3].b1 | ((w & 0xF) << 8); local[2] = ((w >> 16) & 0xFF) | ((w & 0xF0) << 4); func_80020DA4((s32)local, (s32)&lm); lm.t[0] = a1->t[0]; lm.t[1] = a1->t[1]; lm.t[2] = a1->t[2]; a1++; break; default: local[0] = a1->h; w = *(u32 *)a1; local[1] = ((w >> 8) & 0xFF) | ((w & 0xF) << 8); local[2] = ((w >> 16) & 0xFF) | ((w & 0xF0) << 4); func_80020DA4((s32)local, (s32)&lm); lm.t[0] = a1->t[0]; lm.t[1] = a1->t[1]; lm.t[2] = a1->t[2]; a1++; if (i == 0x19) { v = D_80126C4E; local[2] = v; local[1] = v; local[0] = v; func_80020F34((s32)&lm, (s32)local); } else if (i == 0x16) { v = D_80126D10; local[2] = v; local[1] = v; local[0] = v; func_80020F34((s32)&lm, (s32)local); } else if (i == 8) { v = D_80126D12; local[2] = v; local[1] = v; local[0] = v; func_80020F34((s32)&lm, (s32)local); } break; } m3 = h2.p; SRM_80020598(m3); LDCLMV_80020598(&lm.m[0][0]); RTIR_80020598(); STCLMV_80020598(&rm->m[0][0]); LDCLMV_80020598(&lm.m[0][1]); RTIR_80020598(); STCLMV_80020598(&rm->m[0][1]); LDCLMV_80020598(&lm.m[0][2]); RTIR_80020598(); STCLMV_80020598(&rm->m[0][2]); m3 = h3.p; SRM_80020598(m3); LDCLMV_80020598(&lm.m[0][0]); RTIR_80020598(); STCLMV_80020598(&rm[0x40].m[0][0]); LDCLMV_80020598(&lm.m[0][1]); RTIR_80020598(); STCLMV_80020598(&rm[0x40].m[0][1]); LDCLMV_80020598(&lm.m[0][2]); RTIR_80020598(); STCLMV_80020598(&rm[0x40].m[0][2]); STM_80020598(m3); LDLVL_80020598(&lm.t[0]); RT_80020598(); STLVNL_80020598(&rm[0x40].t[0]); rm++; a0++; i++; } } /* func_80020A28 -- the 12-bit-packed-euler sibling of func_80020248 (same 5-arg shape). * Walks a NULL-terminated object list, builds each object's rotation matrix with * func_80020DA4 and multiplies it into two output matrix arrays (scratchpad when the * list is short, main RAM otherwise). a4 != 0 skips the a2 (rm) array. * * LEVERS THAT MADE IT MATCH (cookbook): * - the 5th parameter `a4` is never register-allocated, so every use reloads it from * its incoming stack home -> the in-loop `lw $t0, 0x88($sp)`; do NOT hand-roll that * load with inline asm (the warm start did, and it cost 8 bytes of frame). * - `c` is written INSIDE the loop (`(u8 *)a1 + 3`) so loop.c strength-reduces it into * a giv whose init `addiu $s3,$s4,3` is emitted AFTER move_movables' three hoisted * `addiu $sN,$sp,0x1x` column pointers (loop.md L1 rule 6 / §358 family). Declaring * it before the loop makes it a BIV, whose init then lands BEFORE the hoists and in * the `beq` delay slot. * - byte 1 of the packed word is `(w >> 8) & 0xFF`, NOT `((u8 *)a1)[1]`. gcc-2.7.2 * narrows the shift+mask back into `lbu $v1, 1($s4)` on its own, while the explicit * byte deref creates a THIRD address giv that combine_givs folds into `c` as * `lbu -2($s3)` -- which also re-ranks the allocnos ($s1/$s2/$s3 permute) and drops * two reload slots (frame -0x70 instead of -0x78). One spelling, three symptoms. */ /* §321 dial 2 -- src/800.c already carries this typedef and these three declarations at * FILE scope (lines 10200-10211), immediately above this function's slot. They are * emitted only for the standalone `match_one` probe; SRM_80020248 is defined by the TU * just above us, so the real TU sees none of this and there is no `conflicting types`. */ #ifndef SRM_80020248 /* 0x20 */ extern MTX_80020248 D_80074818[]; extern MTX_80020248 D_80075018[]; extern void func_80020DA4(s32 a0, s32 a1); #endif typedef struct { /* 0x0 */ u32 xy; /* 0x4 */ u16 unk4; /* 0x6 */ u16 unk6; } OBJ_80020A28; /* 0x8 */ #define SRM_80020A28(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 STM_80020A28(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 LDCLMV_80020A28(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_80020A28() __asm__ volatile ( \ "nop;" \ "nop;" \ "mvmva 1, 0, 3, 3, 0" \ : : : "memory" ) #define STCLMV_80020A28(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 LDLVL_80020A28(r0) __asm__ volatile ( \ "lb $13, 1( %0 );" \ "lb $12, 0( %0 );" \ "sll $13, $13, 16;" \ "sll $12, $12, 16;" \ "srl $12, $12, 16;" \ "or $12, $12, $13;" \ "lb $13, 2( %0 );" \ "mtc2 $12, $0;" \ "mtc2 $13, $1" \ : \ : "r"( r0 ) \ : "$12", "$13" ) #define RT_80020A28() __asm__ volatile ( \ "nop;" \ "nop;" \ "mvmva 1, 0, 0, 0, 0" \ : : : "memory" ) #define STLVNL_80020A28(r0) __asm__ volatile ( \ "swc2 $25, 0( %0 );" \ "swc2 $26, 4( %0 );" \ "swc2 $27, 8( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) s32 func_80020A28(s32 *a0, OBJ_80020A28 *a1, MTX_80020248 *a2, MTX_80020248 *a3, s32 a4) { MTX_80020248 lm; /* sp+0x10 */ u16 src[3]; /* sp+0x30 */ MTX_80020248 *rm; MTX_80020248 *om; u8 *c; u32 w; s32 n; s32 ret; ret = 0; n = 0; while (*a0++ != 0) { n++; } if (n >= 8) { rm = D_80074818; om = D_80075018; } else { rm = (MTX_80020248 *)0x1F800000; om = (MTX_80020248 *)0x1F8000E0; ret = 1; } while (--n != -1) { src[0] = *(u16 *)((u8 *)a1 + 6); w = a1->xy; src[1] = ((w >> 8) & 0xFF) | ((w & 0xF) << 8); src[2] = ((w >> 16) & 0xFF) | ((w & 0xF0) << 4); func_80020DA4((s32)src, (s32)&lm); if (a4 == 0) { SRM_80020A28(a2); LDCLMV_80020A28(&lm.m[0][0]); RTIR_80020A28(); STCLMV_80020A28(&rm->m[0][0]); LDCLMV_80020A28(&lm.m[0][1]); RTIR_80020A28(); STCLMV_80020A28(&rm->m[0][1]); LDCLMV_80020A28(&lm.m[0][2]); RTIR_80020A28(); STCLMV_80020A28(&rm->m[0][2]); rm++; } SRM_80020A28(a3); LDCLMV_80020A28(&lm.m[0][0]); RTIR_80020A28(); STCLMV_80020A28(&om->m[0][0]); LDCLMV_80020A28(&lm.m[0][1]); RTIR_80020A28(); STCLMV_80020A28(&om->m[0][1]); LDCLMV_80020A28(&lm.m[0][2]); RTIR_80020A28(); STCLMV_80020A28(&om->m[0][2]); STM_80020A28(a3); c = (u8 *)a1 + 3; LDLVL_80020A28(c); RT_80020A28(); STLVNL_80020A28(&om->t[0]); om++; a1++; } return ret; } /* func_80020DA4 (0x80020DA4): builds a 3x3 Q12 rotation matrix from three angle-table lookups. * decl_prior (src/800.c:12196): extern void func_80020DA4(s32 a0, s32 a1); -- callers cast pointers to s32. * a0 -> u16[3] angle indices (a0[0],a0[1],a0[2], each &0xFFF); a1 -> MTX_80020248-shaped s16[9] * output (m[0][0..2],m[1][0..2],m[2][0..2], offsets 0x0-0x10). D_800636D4 is a GLOBAL POINTER * (loaded via lw, not lui/addiu) to a table of packed {lo:s16, hi:s16} Q12 entries, 4B each. * Semantics hand-verified against every mult/mflo/sra/store in the target .s (identical Euler * composition to the banked twin ov_SC02_011/func_8018B76C, just Q12 not Q15 and a packed-word * table instead of two lookup calls). * * MATCH STATUS: MATCH (match_one 100/100 AND rtu_match MATCH in the real TU src/800.c, P32 T5x, * 2026-09-05, Fable). Sixth-generation draft: 51 -> 20 -> 14 -> 8 -> 2 -> 0. The levers the earlier * sessions found load-bearing are all kept EXCEPT the very first one, which turned out to be the * cause of the final residual: * 1. (REMOVED) `register s32 e0 __asm__("$6")` pin on the idxA table word (51 -> 20 in S76). The pin * put e0 in $a2 the way the target has it, but an explicit hard register is copied into * reload1.c's `regs_explicitly_used` (reload1.c:486) -> `bad_spill_regs` (reload1.c:3651-3660) * -> `forbidden_regs` (reload1.c:709), and that set is the `losers` argument of every * `retry_global_alloc` (reload1.c:3497 -> global.c find_reg). The mult results here are GLOBAL * allocnos (their preferred class is LO_REG, the `=l` output of mulsi3_internal, mips.md:848); * global.c first parks m3/m8/m13 in LO itself, reload then spills LO ("Spilling reg 65") and * RETRIES them -- and the retried m13 (pseudo 102) could not take the freshly-dead $a2 because * $a2 was forbidden by the pin. That is the whole `mflo $t0` vs `mflo $a2` residual (idx 91/92). * Unpinned, m13's retry lands in $a2 ("Register 102 now in 6" in the -dg dump) exactly as the * target's does. * 2. Address computation as plain integer arithmetic `(idx << 2) + tbl` (tbl a plain `s32`, NOT * `s32 *`) instead of `&tbl[idx]` pointer-index sugar (20 -> 14). * 3. Do not give a once-read shifted product (m7) its own named temp; inline it at its single use * (14 -> 8). * 4. `s32 frame_pad[3]; (void)&frame_pad;` -- an address-taken-but-never-stored-through local * array reproduces the target's 16-byte phantom frame (8 -> 2), zero emitted code. * 5. (NEW) Two zero-byte launders `__asm__("" : "=r"(x) : "0"(x))` that steer LOCAL-alloc's * qty_compare priorities (local-alloc.c:1579) so that unpinned first-fit lands e0 in $a2: * - `e1` laundered immediately before `dst[6] = -e1`. Unpinned, e0's tied quantity {e0, e0<<16, * lo0} (10 refs / 38 insns, pri 7894) out-ranked e1's tied quantity {e1, -e1} (5 refs / 15, * pri 6666), so e0 was allocated first and took the free $v1; the target needs e1's quantity * allocated FIRST (it then holds $v1 across e0's range, and e0's first fit falls through * $v0/$v1/$a0/$a1 to $a2). The launder adds two refs to e1's quantity (7 refs / 16 -> 8750) * and sched1 places it at position 40, AFTER lo1's death (the m12 mult), which matters: placed * right after e1's load (pos 27) it lengthens lo1 by one insn and flips the exact * qty_compare tie lo1 (4/32) vs p_ab (3/12) = 2500 both, handing $a0 to p_ab (closeness 15). * - `p1` laundered between its `& 0xFFF` and its `<< 2`. The e1 launder at pos 40 lengthens the * global allocnos m6/m7/m12 (95/96/101: 23 -> 24 insns, allocno pri 869 -> 833) but not m1 * (90: 24 insns, 833), collapsing the order 95 96 101 90 into an allocno-number tie won by 90 * (closeness 6: the $t8/$t6/$t7 mflo rotation). A second zero-byte insn at sched1 pos 20 -- * inside m1's range [15,38], outside lo1's [23,39] and before m2's birth -- restores * len(90) > len(95/96/101) while leaving every local qty_compare order unchanged (hand-checked * for all 30 local quantities; equivalently, laundering `addr1` right after its addu also * matches byte-for-byte). * The order of allocations is exactly the target's now: global "92 97 99 100 102 91 93 94 95 96 * 101 90 98"; local e1 {81,124} -> $v1 before e0 {80,120,85} -> $a2, tbl -> $a3, lo1 -> $a0, p_ab/p1v * -> $a3, lo2/hi0/hi2 -> $t0/$t1/$t2, p_ac -> $t3; reload retries m3 -> $a3, m8 -> $s1, m13 -> $a2. * Evidence and the full -dl/-dg readings: .run/P32/t5x/reports/func_80020DA4.md; cookbook §47/§500-G * are the priority-slider precedents, §500-H the launder-of-a-pinned-value precedent. */ extern s32 *D_800636D4; void func_80020DA4(s32 a0, s32 a1) { u16 *src = (u16 *)a0; s16 *dst = (s16 *)a1; s32 tbl = (s32)D_800636D4; s32 p0, p1, p2; s32 e0; s32 e1; s32 addr1, addr2; s32 hi0, lo0, hi1, lo1, hi2, lo2; s32 m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13; s32 p_ab, p_ac, p1v; s32 frame_pad[3]; (void)&frame_pad; p0 = src[0] & 0xFFF; e0 = *(s32 *)((p0 << 2) + tbl); p2 = src[2] & 0xFFF; addr2 = (p2 << 2) + tbl; hi0 = e0 >> 16; lo2 = *(s16 *)addr2; m1 = hi0 * lo2; hi2 = *(s16 *)(addr2 + 2); m2 = hi0 * hi2; p1 = src[1] & 0xFFF; __asm__("" : "=r"(p1) : "0"(p1)); addr1 = (p1 << 2) + tbl; lo0 = (s16)e0; lo1 = *(s16 *)addr1; m3 = lo0 * lo1; e1 = *(s32 *)addr1; hi1 = e1 >> 16; m4 = hi2 * hi1; p_ab = m3 >> 12; m5 = p_ab * hi2; p_ac = m2 >> 12; m6 = p_ac * lo1; m7 = lo0 * lo2; m8 = lo2 * hi1; m9 = p_ab * lo2; m10 = hi1 * lo0; m11 = hi1 * hi0; p1v = m1 >> 12; m12 = p1v * lo1; __asm__("" : "=r"(e1) : "0"(e1)); dst[6] = -e1; dst[0] = m4 >> 12; dst[3] = m8 >> 12; dst[7] = m10 >> 12; m13 = lo0 * hi2; dst[8] = m11 >> 12; dst[1] = (m5 >> 12) - p1v; dst[2] = (m6 >> 12) + (m7 >> 12); dst[4] = (m9 >> 12) + p_ac; dst[5] = (m12 >> 12) - (m13 >> 12); } /* func_80020F34 — handwritten GTE routine (PSYQ libgte-class ScaleMatrix-style op). * a0 = MATRIX* (3x3 short matrix, rows at +0x0/+0x6/+0xC/... , 3 columns per row), * a1 = SVECTOR-like short[3] scale vector. * Column-by-column: IR0=a1[col], IR1..3 = a0 column; GPF sf=1 computes * (IR0*IR1..3)>>12, results written back into the same column of a0. * Software-pipelined by hand: each column's input loads are issued right after the * previous column's GPF (to hide its latency) before that column's result is read back. */ void func_80020F34(s32 a0, s32 a1) { __asm__ __volatile__( "lh $8, 0(%1)\n" "lh $9, 0(%0)\n" "lh $10, 6(%0)\n" "lh $11, 12(%0)\n" "mtc2 $8, $8\n" "mtc2 $9, $9\n" "mtc2 $10, $10\n" "mtc2 $11, $11\n" "nop\n" "nop\n" "gpf 1\n" "lh $8, 2(%1)\n" "lh $9, 2(%0)\n" "lh $10, 8(%0)\n" "lh $11, 14(%0)\n" "mfc2 $12, $9\n" "mfc2 $13, $10\n" "mfc2 $14, $11\n" "sh $12, 0(%0)\n" "sh $13, 6(%0)\n" "sh $14, 12(%0)\n" "mtc2 $8, $8\n" "mtc2 $9, $9\n" "mtc2 $10, $10\n" "mtc2 $11, $11\n" "nop\n" "nop\n" "gpf 1\n" "lh $8, 4(%1)\n" "lh $9, 4(%0)\n" "lh $10, 10(%0)\n" "lh $11, 16(%0)\n" "mfc2 $12, $9\n" "mfc2 $13, $10\n" "mfc2 $14, $11\n" "sh $12, 2(%0)\n" "sh $13, 8(%0)\n" "sh $14, 14(%0)\n" "mtc2 $8, $8\n" "mtc2 $9, $9\n" "mtc2 $10, $10\n" "mtc2 $11, $11\n" "nop\n" "nop\n" "gpf 1\n" "mfc2 $12, $9\n" "mfc2 $13, $10\n" "mfc2 $14, $11\n" "sh $12, 4(%0)\n" "sh $13, 10(%0)\n" "sh $14, 16(%0)\n" : : "r"(a0), "r"(a1) : "$8", "$9", "$10", "$11", "$12", "$13", "$14", "memory"); } extern void func_80028620(s32 a0, void *a1); void func_80021008(void *a0) { void *s0 = a0; func_80028620(0, s0); func_80028620(1, (u8 *)s0 + 0x10); func_80028620(2, (u8 *)s0 + 0x20); } extern void func_80028620(s32 a0, void *a1); extern u8 D_800A5E88; void func_80021050(void) { u8 *s0 = &D_800A5E88; func_80028620(0, s0); func_80028620(1, s0 + 0x10); func_80028620(2, s0 + 0x20); } extern void func_800491FC(s32 a0, s32 a1, s32 a2); void func_8002109C(s32 a0, s16 **a1) { s16 *v1 = *a1; if (v1[3] == -2) { func_800491FC(v1[0], v1[1], v1[2]); return; } if (a0 == v1[3]) { func_800491FC(v1[0], v1[1], v1[2]); *a1 += 4; } } extern s16 D_800AE7E0; extern s16 D_800AE7E2; extern s16 D_800AE7E4; extern void func_80053AF8(s32 a0, s32 a1, s32 a2); void func_80021120(s32 *arg0) { func_80053AF8(D_800AE7E0, D_800AE7E2, D_800AE7E4); *arg0 = *arg0 + 1; } typedef struct { s16 vx; s16 vy; s16 vz; s16 pad; } SV_80021174; extern s32 D_800AE688[]; s32 func_80021174(s32 a0, s32 a1) { volatile s32 sp[4]; s32 lim; register s32 *p_a3 __asm__("$7"); register s32 ret __asm__("$2"); if (a0 == 0x7FFF7FFF) { return 1; } p_a3 = D_800AE688; __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;" "lw $12, 20( %0 );" "lw $13, 24( %0 );" "ctc2 $12, $5;" "lw $14, 28( %0 );" "ctc2 $13, $6;" "ctc2 $14, $7" : : "r"( p_a3 ) : "$12", "$13", "$14", "memory" ); __asm__ volatile ( "lhu $13, 4( %0 );" "lhu $12, 0( %0 );" "sll $13, $13, 16;" "or $12, $12, $13;" "mtc2 $12, $0;" "lwc2 $1, 8( %0 )" : : "r"( (SV_80021174 *)a1 ) : "$12", "$13", "memory" ); __asm__ volatile ( "nop;" "nop;" "rtps" : : : "memory" ); __asm__ volatile ( "swc2 $14, 0( %0 )" : : "r"( sp ) : "memory" ); __asm__ volatile ( "cfc2 $12, $31;" "nop;" "sw $12, 0( %0 )" : : "r"( &sp[1] ) : "$12", "memory" ); __asm__ volatile ( "mfc2 $12, $19;" "nop;" "sra $12, $12, 2;" "sw $12, 0( %0 )" : : "r"( &sp[2] ) : "$12", "memory" ); if (sp[1] < 0) { return 0; } ret = 0; lim = (s16) a0; ; if ((*((s16 *) sp)) <= (-lim)) { return ret; } if (lim < (*((s16 *) sp))) { return ret; } a0 = a0 >> 16; a1 = *((s16 *) (((char *) sp) + 2)); if (a1 <= (-a0)) { return ret; } ret = !(a0 < a1); return ret; } /* func_80021284 — POLY_G4 fan builder (12-spoke rotated fan + optional DR_MODE tail). * * Shape: rotate arg0+0x2E..0x32 (SVECTOR) by the current RT matrix into out[0], then 12 more * rotations of (base + rec[0x10+4i], base + rec[0x12+4i], base.vz) into out[1..12]; then 6 * POLY_G4s (0x24 each, 0xD8 total off the D_800A5E60 bump allocator) fanned around out[0], * addPrim'd into &((PTag *)(D_800A6610 + (D_800B9A02 << 14)))[pri]; then the last prim's * (x3,y3) is patched to out[1] to close the fan; then, when attr & 0x40000000, one extra * 8-byte DR_MODE (0xE1000000 | ((attr >> 23) & 0x60)) is bumped and addPrim'd. * * FOUR levers were needed (all four are scheduling/loop-shape, not arithmetic): * * 1. MEM_IN_STRUCT_P alias lever (THE unlock — took the diff from 7 to 0). gcc-2.7.2 * `true_dependence` drops a dependence when one MEM is in a struct with a VARYING address * (mode != QImode) and the other is a non-struct MEM with a CONSTANT address. `rec`/`attr`/ * `pri` written as raw `*(u32 *)(arg0 + N)` derefs are NOT in-struct, so they conflicted with * the `sw D_800A5E60` (constant address) and the scheduler could not hoist them across it — * pinning `lw $s1`/`lw $s4` five slots late no matter how the statements were permuted (a * 120-order sweep plateaued at closeness 7). Reading them through a struct pointer * (Ent_80021284) sets MEM_IN_STRUCT_P, kills the dependence, and the whole prologue falls out. * * 2. The first loop indexes `rec + i * 4 + 0x10/0x12`; it must NOT be a self-incremented `q += 4` * pointer. A separate pointer biv makes gcc bias the giv base to rec+0x12 (`addiu $a1,$s1,18` * with -2/0 offsets); the index form gives the giv base `rec` itself (`addu $a1,$s1,$zero`) * with the target's 0x10/0x12 offsets. * * 3. The preheader copies must be EXPLICIT variables (ptmp/pout/pflag), not loop-invariant * motion. Hoisted invariants are emitted at loop_start, i.e. AFTER ordinary preheader code, * which put `li $a3,0x10` (the `off` init) first instead of last. Writing `i = 0; ptmp; * pout; pflag; off = 0x10;` as plain statements before a do/while reproduces the target's * `move $a2,$zero / addiu $t2 / move $t1 / move $t0 / li $a3,0x10` order exactly. * `off` must stay a plain biv: `(i + 1) * 16` gets CSE'd against the `i++` increment and * destroys both this loop's givs, and `&out[i + 1]` fully reduces to one pointer (-2 ins). * * 4. OT address association: `((pri & 0xFFF) << 2) + (u32)D_800A6610` must be its OWN statement, * with `(u32)D_800B9A02 << 14` in another, or fold reassociates A + (B + C) into (A + C) + B * (the address constant migrates to the shift) and both addu operand orders flip. * * addPrim is the libgpu `struct { u32 addr:24; u32 len:8; }` bitfield (cookbook: func_8001D3FC * lever 1, same TU) — that is what hoists 0x00FFFFFF / 0xFF000000 into $a3 / $t2. */ #include "common.h" typedef struct { u32 addr : 24; u32 len : 8; } PTag_80021284; typedef struct { /* 0x00 */ u32 unk00; /* 0x04 */ u32 attr; /* 0x08 */ u8 pad08[0x18]; /* 0x20 */ u8 *rec; /* 0x24 */ u8 pad24[8]; /* 0x2C */ u16 pri; } Ent_80021284; typedef struct { /* 0x00 */ u32 addr : 24; /* 0x03 */ u32 len : 8; /* 0x04 */ u8 r0, g0, b0, code; /* 0x08 */ s16 x0, y0; /* 0x0C */ u8 r1, g1, b1, pad1; /* 0x10 */ s16 x1, y1; /* 0x14 */ u8 r2, g2, b2, pad2; /* 0x18 */ s16 x2, y2; /* 0x1C */ u8 r3, g3, b3, pad3; /* 0x20 */ s16 x3, y3; } PolyG4_80021284; typedef struct { u32 addr : 24; u32 len : 8; u32 code0; } DrMode_80021284; typedef struct { u16 vx, vy, vz, pad; } SV_80021284; typedef struct { s32 vx, vy, vz, pad; } VEC_80021284; extern u8 D_800A6610[]; extern u16 D_800B9A02; extern u8 *D_800A5E60; extern void func_8001D70C(s32 arg0); #define gte_ldv0(r0) __asm__ __volatile__( \ "lwc2 $0, 0(%0)\n" \ "lwc2 $1, 4(%0)\n" \ : : "r"(r0) : "memory") #define gte_rt() __asm__ __volatile__( \ "nop\n" \ "nop\n" \ "mvmva 1, 0, 0, 0, 0\n" \ : : : "memory") #define gte_stlvnl(r0) __asm__ __volatile__( \ "swc2 $25, 0(%0)\n" \ "swc2 $26, 4(%0)\n" \ "swc2 $27, 8(%0)\n" \ : : "r"(r0) : "memory") #define gte_stflg(r0) __asm__ __volatile__( \ "cfc2 $12, $31\n" \ "nop\n" \ "sw $12, 0(%0)\n" \ : : "r"(r0) : "$12", "memory") void func_80021284(s32 arg0) { SV_80021284 base; SV_80021284 tmp; VEC_80021284 out[13]; s32 flag; PolyG4_80021284 *p; PTag_80021284 *ot; u8 *rec; u32 attr; s32 i; s32 off; SV_80021284 *ptmp; u8 *pout; s32 *pflag; u32 slot; u32 sc; u8 code; p = (PolyG4_80021284 *)D_800A5E60; sc = (u32)D_800B9A02 << 14; rec = ((Ent_80021284 *)arg0)->rec; attr = ((Ent_80021284 *)arg0)->attr; D_800A5E60 = (u8 *)p + 0xD8; slot = (((u32)((Ent_80021284 *)arg0)->pri & 0xFFF) << 2) + (u32)D_800A6610; ot = (PTag_80021284 *)(sc + slot); func_8001D70C(arg0); base.vx = *(u16 *)(arg0 + 0x2E); base.vy = *(u16 *)(arg0 + 0x30); base.vz = *(u16 *)(arg0 + 0x32); gte_ldv0(&base); gte_rt(); gte_stlvnl(&out[0]); gte_stflg(&flag); i = 0; ptmp = &tmp; pout = (u8 *)&out[0]; pflag = &flag; off = 0x10; do { tmp.vx = base.vx + *(u16 *)(rec + i * 4 + 0x10); tmp.vy = base.vy + *(u16 *)(rec + i * 4 + 0x12); tmp.vz = base.vz; gte_ldv0(ptmp); gte_rt(); gte_stlvnl((VEC_80021284 *)(pout + off)); gte_stflg(pflag); off += 0x10; i++; } while (i < 12); code = 0x38; if (attr & 0x40000000) { code = 0x3A; } i = 1; do { p->len = 8; p->code = code; p->r0 = rec[4]; p->g0 = rec[5]; p->b0 = rec[6]; p->r1 = rec[4]; p->g1 = rec[5]; p->b1 = rec[6]; p->r2 = rec[0]; p->g2 = rec[1]; p->b2 = rec[2]; p->r3 = rec[4]; p->g3 = rec[5]; p->b3 = rec[6]; p->x0 = out[i].vx; p->y0 = out[i].vy; p->x1 = out[i + 1].vx; p->y1 = out[i + 1].vy; p->x2 = out[0].vx; p->y2 = out[0].vy; p->x3 = out[i + 2].vx; p->y3 = out[i + 2].vy; ((PTag_80021284 *)p)->addr = ot->addr; ot->addr = (u32)p; i += 2; p++; } while (i < 13); p[-1].x3 = out[1].vx; p[-1].y3 = out[1].vy; if (attr & 0x40000000) { DrMode_80021284 *dm = (DrMode_80021284 *)p; dm->code0 = ((attr >> 23) & 0x60) | 0xE1000000; D_800A5E60 = D_800A5E60 + 8; dm->len = 1; ((PTag_80021284 *)dm)->addr = ot->addr; ot->addr = (u32)dm; } } /* ---- 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 * 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. * * Projects a 12-point silhouette for the object at arg0 and hands the screen-XY * array to func_800226C0 with the OT slot picked by the projected depth. * GsCOORDINATE2 at sp+0x88 (super is at +0x48, so `rotate` must be declared), * MATRIX out at sp+0xD8 -> $s0, the u16 game-state word is read off the * D_800AF630 base at +0xA3D2 (the house idiom, cf. func_80015144 in this TU). * * LEVERS THAT MATTERED (all four were needed; each was worth 3-160 instructions): * 1. FRAME LAYOUT = declaration order of the AGGREGATES. expand_decl slots every * BLKmode local with align 0 -> BIGGEST_ALIGNMENT (8), so each aggregate starts * 8-byte aligned in declaration order: sv0/sv1/sv2 @0x10/0x18/0x20, sxy[13] * @0x28 (ends 0x5B -> rounds to 0x60), the 4-byte AGGREGATE sxy2 @0x60 (a * scalar there would sit at 0x5C and pull m2 to 0x64), m2 @0x68, cd @0x88, * m @0xD8, vv @0xF8. The address-taken SCALARS follow in declaration order * at 0x108/0x10C/0x110, giving frame 0x130. (§333/§358 family.) * 2. NO STRENGTH REDUCTION. The target recomputes every index with sll; a * `i += 3;` placed BEFORE the last sxy[i] pair let loop.c combine_givs merge * the address givs and reduce them into 4 extra induction registers (-8 ins, * closeness 166). Writing `sxy[i + 3]` first and `i += 3;` LAST keeps every * giv bare (benefit 2 - 2*biv_count = 0 => "not worth while", loop.md §L0), * and cse then rewrites `i += 3` as a COPY of the already-computed i+3 * (`addu $t0,$a1,$zero`). §344's counter pin also kills LSR, but a hard-reg * counter lets local-alloc coalesce i+3 back in place -- it plateaus at 41. * 3. `i = 0;` goes AFTER the `if (o->unk74)` block, not before: dbr fills the * beqz slot with a copy of the join head, so the insn appears TWICE. * 4. `o->unk2E & 0xFFF` INLINE in both arms, not via a `d` local -- the cse temp * takes $v0 (the dead 0xC000 constant's register); a named local takes $v1. */ typedef struct { s16 vx, vy, vz, pad; } SV221A8; typedef struct { s16 m[3][3]; s32 t[3]; } MX221A8; /* 0x20 */ typedef struct { s32 vx, vy, vz, pad; } VC221A8; /* 0x10 */ typedef struct { u16 x, y; } XY221A8; /* 0x04 */ typedef struct { /* 0x00 */ s32 flg; /* 0x04 */ MX221A8 coord; /* 0x24 */ MX221A8 workm; /* 0x44 */ void *rotate; /* 0x48 */ void *super; /* 0x4C */ void *sub; } CD221A8; /* 0x50 */ typedef struct { /* 0x00 */ u8 pad00[0x10]; /* 0x10 */ XY221A8 pt[1]; } Src221A8; typedef struct { /* 0x00 */ u8 pad00[8]; /* 0x08 */ s16 unk08; /* 0x0A */ s16 unk0A; /* 0x0C */ s16 unk0C; /* 0x0E */ u8 pad0E[2]; /* 0x10 */ s16 unk10[4]; /* 0x18 */ s16 unk18; /* 0x1A */ s16 unk1A; /* 0x1C */ u8 pad1C[4]; /* 0x20 */ Src221A8 *unk20; /* 0x24 */ u8 pad24[4]; /* 0x28 */ s32 unk28; /* 0x2C */ u16 unk2C; /* 0x2E */ u16 unk2E; /* 0x30 */ u8 pad30[4]; /* 0x34 */ MX221A8 unk34; /* 0x54 */ MX221A8 unk54; /* 0x74 */ s32 unk74; /* 0x78 */ void *unk78; } Obj221A8; #define SRM221A8(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 STM221A8(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 LDV0221A8(r0) __asm__ volatile ( \ "lwc2 $0, 0( %0 );" "lwc2 $1, 4( %0 )" : : "r"( r0 ) ) #define LDV3221A8(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 RTPS221A8() __asm__ volatile ("nop;nop;rtps") #define RTPT221A8() __asm__ volatile ("nop;nop;rtpt") #define STSXY221A8(r0) __asm__ volatile ( \ "swc2 $14, 0( %0 )" : : "r"( r0 ) : "memory" ) #define STSXY3221A8(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 STFLG221A8(r0) __asm__ volatile ( \ "cfc2 $12, $31;nop;sw $12, 0( %0 )" \ : : "r"( r0 ) : "$12", "memory" ) #define STSZOTZ221A8(r0) __asm__ volatile ( \ "mfc2 $12, $19;nop;sra $12, $12, 2;sw $12, 0( %0 )" \ : : "r"( r0 ) : "$12", "memory" ) #define STSZ221A8(r0) __asm__ volatile ( \ "swc2 $19, 0( %0 )" : : "r"( r0 ) : "memory" ) extern u8 D_800AF630[]; extern u8 D_800A6610[]; 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(); void func_800221A8(Obj221A8 *o) { SV221A8 sv0; SV221A8 sv1; SV221A8 sv2; XY221A8 sxy[13]; XY221A8 sxy2[1]; MX221A8 m2; CD221A8 cd; MX221A8 m; VC221A8 vv; s32 flag; s32 otz; s32 sz; u8 *p; u8 *ot; Src221A8 *s; MX221A8 *mp; u32 z; s32 i; cd.super = o->unk78; cd.flg = 0; p = D_800AF630; if (o->unk2C & 1) { o->unk2C &= 0xFFFE; cd.coord = o->unk34; } else { func_80049CAC((s32)o->unk10, (s32)&cd.coord); cd.coord.t[0] = o->unk08; cd.coord.t[1] = o->unk0A; cd.coord.t[2] = o->unk0C; } vv.vx = o->unk18; vv.vy = o->unk1A; vv.vz = 0x1000; func_8004901C(&cd.coord, &vv); mp = &m; func_800547D8((s32)&cd, mp); o->unk34 = cd.coord; o->unk54 = cd.workm; s = o->unk20; if (s != 0 && func_80021174(o->unk28, (s32)cd.workm.t) != 0) { ot = (u8 *)((*(u16 *)(p + 0xA3D2) << 14) + (u32)D_800A6610); SRM221A8(mp); STM221A8(mp); sv0.vz = 0; sv0.vy = 0; sv0.vx = 0; LDV0221A8(&sv0); RTPS221A8(); STSXY221A8(&sxy[0]); STFLG221A8(&flag); STSZOTZ221A8(&otz); if ((flag & ~0x1000) == 0) { STSZ221A8(&sz); z = otz + 1; if (o->unk2E & 0xC000) { if ((o->unk2E & 0xC000) == 0xC000) { z -= (o->unk2E & 0xFFF); if ((s32)z < 0) { z = 0; } } else { z += (o->unk2E & 0xFFF); } } if (z < 0x1000) { m2.m[0][0] = o->unk18; m2.m[0][1] = 0; m2.m[0][2] = 0; m2.m[1][0] = 0; m2.m[1][1] = o->unk1A; m2.m[1][2] = 0; m2.m[2][0] = 0; m2.m[2][1] = 0; m2.m[2][2] = 0x1000; m2.t[2] = 0; m2.t[1] = 0; m2.t[0] = 0; SRM221A8(&m2); STM221A8(&m2); if (o->unk74 != 0) { sv0.vx = *(u16 *)&o->unk74; sv0.vy = *((u16 *)&o->unk74 + 1); sv0.vz = sz; LDV0221A8(&sv0); RTPS221A8(); STSXY221A8(&sxy2[0]); sxy[0].x += sxy2[0].x; sxy[0].y += sxy2[0].y; } i = 0; do { sv0.vx = s->pt[i].x; sv0.vy = s->pt[i].y; sv0.vz = sz; sv1.vx = s->pt[i + 1].x; sv1.vy = s->pt[i + 1].y; sv1.vz = sz; sv2.vx = s->pt[i + 2].x; sv2.vy = s->pt[i + 2].y; sv2.vz = sz; LDV3221A8(&sv0, &sv1, &sv2); RTPT221A8(); STSXY3221A8(&sxy[i + 1], &sxy[i + 2], &sxy[i + 3]); sxy[i + 1].x += sxy[0].x; sxy[i + 1].y += sxy[0].y; sxy[i + 2].x += sxy[0].x; sxy[i + 2].y += sxy[0].y; sxy[i + 3].x += sxy[0].x; sxy[i + 3].y += sxy[0].y; i += 3; } while (i < 0xC); func_800226C0((s32)o, (s32)s, (s32)&sxy[0], (s32)(ot + z * 4)); } } } } /* 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; void func_80023138(s32 *arg0, u8 *col, u8 *pts, u32 *ot) { s32 frame_pad[12]; u32 flags; u8 *p; s32 i; u32 maskAddr; u32 maskLen; u8 code; register u32 x __asm__("$3"); u32 y; s32 h; register s32 *zp __asm__("$4"); (void)&frame_pad; p = D_800A5E60; flags = arg0[1]; D_800A5E60 = p + 0x1B0; code = (flags & 0x40000000) ? 0x3A : 0x38; i = 0; maskAddr = 0x00FFFFFF; maskLen = 0xFF000000; do { p[0x03] = 8; p[0x07] = code; p[0x04] = col[4]; p[0x05] = col[5]; p[0x06] = col[6]; p[0x0C] = col[4]; p[0x0D] = col[5]; p[0x0E] = col[6]; p[0x14] = col[0]; p[0x15] = col[1]; p[0x16] = col[2]; p[0x1C] = col[0]; p[0x1D] = col[1]; p[0x1E] = col[2]; *(u16 *)(p + 0x08) = *(u16 *)(pts + i * 4 + 0x4C); *(u16 *)(p + 0x0A) = *(u16 *)(pts + i * 4 + 0x4E); *(u16 *)(p + 0x10) = *(u16 *)(pts + i * 4 + 0x50); *(u16 *)(p + 0x12) = *(u16 *)(pts + i * 4 + 0x52); *(u16 *)(p + 0x18) = *(u16 *)(pts + i * 4 + 0x04); *(u16 *)(p + 0x1A) = *(u16 *)(pts + i * 4 + 0x06); *(u16 *)(p + 0x20) = *(u16 *)(pts + i * 4 + 0x08); *(u16 *)(p + 0x22) = *(u16 *)(pts + i * 4 + 0x0A); h = i >> 1; i++; zp = (s32 *)((h << 2) + (u32)pts); x = *(u32 *)p; y = ot[zp[13]]; x &= maskLen; y &= maskAddr; x |= y; *(u32 *)p = x; ot[zp[13]] = (ot[zp[13]] & maskLen) | ((u32)p & maskAddr); p += 0x24; } while (i < 12); *(u16 *)(p - 0x14) = *(u16 *)(pts + 0x4C); *(u16 *)(p - 0x12) = *(u16 *)(pts + 0x4E); *(u16 *)(p - 0x04) = *(u16 *)(pts + 0x04); *(u16 *)(p - 0x02) = *(u16 *)(pts + 0x06); if (flags & 0x40000000) { u8 *r; register s32 j __asm__("$11"); u32 e1; register u32 mA __asm__("$9"); register u32 mL __asm__("$10"); u8 one; r = p; j = 0; one = 1; e1 = ((flags >> 23) & 0x60) | 0xE1000000; mA = 0x00FFFFFF; mL = 0xFF000000; D_800A5E60 = D_800A5E60 + 0x60; do { h = j >> 1; j++; zp = (s32 *)((h << 2) + (u32)pts); r[0x03] = one; *(u32 *)(r + 4) = e1; x = *(u32 *)r; y = ot[zp[13]]; x &= mL; y &= mA; x |= y; *(u32 *)r = x; ot[zp[13]] = (ot[zp[13]] & mL) | ((u32)r & mA); r += 8; } while (j < 12); } } extern s32 func_8004787C(s32 a0); /* rcos-like: angle (0..0xFFF) -> 1.12 fixed */ extern s32 func_80047948(s32 a0); /* rsin-like: angle (0..0xFFF) -> 1.12 fixed */ typedef struct { u16 x; u16 y; } Poly12Pt; typedef struct { u8 pad_00[0x10]; Poly12Pt pts[12]; } Poly12Obj; /* Builds a 12-gon of radius a1 into the u16{x,y} table at obj+0x10: * quarter arc (4 pts, angle step 0x155), mirrored in y, then in x. */ void func_800233CC(Poly12Obj *a0, u16 a1) { s32 i = 0; s32 radius = a1; /* `i * 0x155` must stay a DERIVED induction variable (no explicit `ang` * local): loop_strength_reduce then emits its preheader init alongside the * hoisted &pts[0] copy, giving the target's s2-before-s1 init order and the * s1-before-s0 increment order. An explicit pre-loop `ang = 0` statement * sits ahead of the LSR-inserted init instead -> 6-instruction residual. */ for (; i < 4; i++) { a0->pts[i].x = (func_8004787C(i * 0x155) * radius) >> 12; a0->pts[i].y = (func_80047948(i * 0x155) * radius) >> 12; } for (i = 4; i < 7; i++) { a0->pts[i].x = a0->pts[6 - i].x; a0->pts[i].y = -a0->pts[6 - i].y; } for (i = 7; i < 12; i++) { a0->pts[i].x = -a0->pts[12 - i].x; a0->pts[i].y = a0->pts[12 - i].y; } } extern s32 func_8004787C(s32 a0); extern s32 func_80047948(s32 a0); void func_800234E4(void *a0, u16 a1, u16 a2) { s32 k = 0; s32 rad = a1; /* Struct-free spelling of the func_800233CC sibling: the only dependencies * are common.h types and the two callee externs, so the body survives any * splice model (whole-snippet or function-only extraction). Addresses are * written as a0 + k*4 + 0x10/0x12 so LSR keeps the incoming pointer as the * IV base (addu s1,a0,zero) and folds the member offset into the sh * displacements, exactly as the struct form did. */ while (k < 12) { *(u16 *)((u8 *)a0 + k * 4 + 0x10) = (func_8004787C(a2 + k * 0x155) * rad) >> 12; *(u16 *)((u8 *)a0 + k * 4 + 0x12) = (func_80047948(a2 + k * 0x155) * rad) >> 12; k++; } } typedef struct { u32 addr : 24; u32 len : 8; } PTag_80023570; extern u8 D_800A6610[]; extern u16 D_800B9A02; extern u8 *D_800A5E60; extern s32 GetTPage(s32, s32, s32, s32); extern s32 func_8005A600(s32, s32, s32, s32, s32); void func_80023570(s32 arg0) { u32 *ot; register u32 flags __asm__("$5"); u16 idx; PTag_80023570 *slot2; u8 *cur; u8 *nxt; { register u8 *rec0 __asm__("$8"); register u8 *p __asm__("$7"); register u16 addX __asm__("$14"); register u16 addY __asm__("$13"); register u8 code __asm__("$11"); register u32 tagconst __asm__("$15"); register u32 *slot __asm__("$10"); register u32 maskAddr __asm__("$9"); register u32 maskLen __asm__("$12"); register u8 *rec3 __asm__("$6"); register u8 *q __asm__("$4"); register u32 sc __asm__("$2"); u8 last; u32 x, y; u16 t16; rec0 = *(u8 **)(arg0 + 0x20); sc = (u32)(u16)D_800B9A02; p = D_800A5E60; flags = *(u32 *)(arg0 + 4); addX = *(u16 *)(arg0 + 8); addY = *(u16 *)(arg0 + 0xA); idx = *(u16 *)(arg0 + 0x2C); ot = (u32 *)(D_800A6610 + (sc << 14)); code = (flags & 0x40000000) ? 0x62 : 0x60; tagconst = 0x03000000; sc = idx << 2; slot = (u32 *)(sc + (u32)ot); maskAddr = 0x00FFFFFF; maskLen = 0xFF000000; rec3 = rec0 + 3; q = p + 0xE; do { q[-7] = code; *(u32 *)p = tagconst; q[-0xA] = rec0[0]; q[-9] = rec3[-2]; q[-8] = rec3[-1]; *(u16 *)(q - 6) = addX + *(u16 *)(rec3 + 1); cur = D_800A5E60; *(u16 *)(q - 4) = addY + *(u16 *)(rec3 + 3); t16 = *(u16 *)(rec3 + 5); rec0 += 0xC; *(u16 *)(q - 2) = t16; t16 = *(u16 *)(rec3 + 7); nxt = cur + 0x10; D_800A5E60 = nxt; *(u16 *)(q) = t16; q += 0x10; x = *(u32 *)p; y = *slot; x = x & maskLen; y = y & maskAddr; x = x | y; *(u32 *)p = x; x = (u32)p & maskAddr; p += 0x10; y = *slot; y = y & maskLen; y = y | x; *slot = y; last = rec3[0]; rec3 += 0xC; } while (last == 0); } if (flags & 0x40000000) { s32 tp; D_800A5E60 = cur + 0x1C; tp = GetTPage(2, (flags >> 28) & 3, 0x280, 0); func_8005A600((s32)nxt, 0, 0, (u16)tp, 0); slot2 = (PTag_80023570 *)(((u32)idx << 2) + (u32)ot); ((PTag_80023570 *)nxt)->addr = slot2->addr; slot2->addr = (u32)nxt; } } /* func_8002374C (297 ins, main, src/800.c) -- sprite/primitive list walker. * * Shape: for each 0x1C-byte source record, build ONE OT packet whose single tag * covers [tag][DR_MODE code x2][GPU prim][DR_MODE code x2], then link it into * ot[idx]. The two SetDrawMode (func_8005A600) calls write their DR_MODE tag * words at p+0 and at (new D_800A5E60 - 0xC); both tag slots are deliberately * overwritten afterwards (p[0] by the real 0x07/0x0A/0x0C000000 tag, the second * by the last vertex), which is why only the code[] halves survive. * case 0x60 = TILE (0x20 block, tag 0x07000000) * case 0x38 = POLY_G4(0x34 block, tag 0x0C000000) -- x,y,w,h -> 4 vertices * case 0x30 = POLY_G3(0x2C block, tag 0x0A000000) * Case bodies are emitted in source order 0x60,0x38,0x30; the compare tree is * moved ahead of them by expand_end_case's reorder_insns, and 0x38's tail * cross-jumps into 0x30's `p[6] = t` suffix (both break paths then share the * `lui $a1,0xFF000000` at .L80023B64). * * LEVERS (this went 291 -> 280 -> 164 -> 74 -> 27 -> 17 -> 9 -> 3 -> MATCH): * 1. TWO pseudos for the loop word. `tag` is the loaded value shifted IN PLACE * (`sll $s3,$s3,24`) and `code` is a copy of it (`addu $s1,$s3,$zero`). One * variable gives `sll $s3,$s1,24` and is 2 insns short. `code = tag` must * sit at the BOTTOM of the loop body (with the duplicate in the entry block) * so the loop-back branch takes it as its delay slot and `addiu $s2,$s2,0x1C` * fills the `lw $s3` load delay; putting it at the loop TOP swaps those two * and costs a nop. * 2. The x/y transform is a THREE-statement read-modify-write (cookbook S219). * The arithmetic must be its own statement so it is emitted BEFORE the mask, * and the mask must be a compound `&=` so it lands in place on `t`'s own * register: * q = w0 + t; t &= 0xFFFF0000; t |= (q & 0xFFFF); * Writing it as one expression `t = (t & 0xFFFF0000) | ((w0 + t) & 0xFFFF)` * emits `and` first, keeps `t` live, forces a scratch register, and also * steals two `lw`/`lhu` load-delay nops (-2 ins). `q` must be declared in a * PER-SITE nested block: one function-wide temp gets a fragmented live range * and the allocator stops tying `andi $v0,$v0,0xFFFF` in place. * 3. Entry block: sched2 will not group the four `lhu` ahead of the two `subu` * from any statement order (5 permutations, all identical). Pinning the * four halfwords ($5/$3/$4/$2) forces all four live at once, which also * pushes the arg0 copy off $a1 onto $a2 (17 -> 9), and pinning the OT base * pair ($3 = &D_800A6610, $2 = D_800B9A02) fixes the v0/v1 swap (9 -> 3). * 4. The last 3 were the zero-byte sched1 fence of cookbook S194-A, placed * AFTER the w0/h0 block so `subu $s5` emits ahead of `lui %hi(D_800A6610)`. * Everything above it (lw src + 4 lhu + 2 subu) is exactly what the target * emits first, which is the rule's precondition. * * Declarations copied verbatim from src/800.c (S376): D_800B9A02 u16, * D_800A5E60 u8*, D_800A6610 u8[], GetTPage/func_8005A600 as the TU spells them. */ extern u16 D_800B9A02; extern u8 *D_800A5E60; extern u8 D_800A6610[]; extern s32 GetTPage(s32, s32, s32, s32); extern s32 func_8005A600(s32, s32, s32, s32, s32); void func_8002374C(s32 arg0) { u32 *src; u32 *p; u32 *ot; u32 *otp; u32 code; u32 tag; s32 t; s32 w0; s32 h0; u16 idx; if (*(s32 *)(arg0 + 4) < 0) { return; } src = *(u32 **)(arg0 + 0x20); { register s32 x0 __asm__("$5") = *(u16 *)(arg0 + 8); register s32 x1 __asm__("$3") = *(u16 *)(arg0 + 0x2E); register s32 y0 __asm__("$4") = *(u16 *)(arg0 + 0xA); register s32 y1 __asm__("$2") = *(u16 *)(arg0 + 0x30); w0 = x0 - x1; h0 = y0 - y1; } __asm__ __volatile__(""); { register s32 obase __asm__("$3") = (s32)D_800A6610; register u32 oshf __asm__("$2") = D_800B9A02; ot = (u32 *)((oshf << 14) + obase); } idx = *(u16 *)(arg0 + 0x2C); tag = *src; code = tag; while (tag != 0) { tag = tag << 24; if (code & 0x40000000) { tag |= 0x02000000; } p = (u32 *)D_800A5E60; if (code & 0x800000) { func_8005A600((s32)p, 0, 1, (u16)GetTPage(2, (code >> 28) & 3, 0x280, 0), 0); } else { func_8005A600((s32)p, 0, 0, (u16)GetTPage(2, (code >> 28) & 3, 0x280, 0), 0); } switch (code & 0xFF) { case 0x60: D_800A5E60 += 0x20; func_8005A600((s32)(D_800A5E60 - 0xC), 0, 0, (u16)GetTPage(2, (code >> 28) & 3, 0x280, 0), 0); p[0] = 0x07000000; p += 2; p[1] = tag | (src[3] & 0xFFFFFF); t = src[1]; { s32 q = w0 + t; t &= 0xFFFF0000; t |= (q & 0xFFFF); } { s32 q = t >> 16; t &= 0xFFFF; t |= (h0 + q) << 16; } p[2] = t; p[3] = src[2]; break; case 0x38: D_800A5E60 += 0x34; func_8005A600((s32)(D_800A5E60 - 0xC), 0, 0, (u16)GetTPage(2, (code >> 28) & 3, 0x280, 0), 0); p[0] = 0x0C000000; p += 2; p[1] = tag | (src[3] & 0xFFFFFF); p[3] = src[4]; p[5] = src[5]; p[7] = src[6]; t = src[1]; { s32 q = w0 + t; t &= 0xFFFF0000; t |= (q & 0xFFFF); } { s32 q = t >> 16; t &= 0xFFFF; t |= (h0 + q) << 16; } p[2] = t; { s32 q = ((u16 *)src)[4] + t; t &= 0xFFFF0000; t |= (q & 0xFFFF); } p[4] = t; { s32 q = t >> 16; t &= 0xFFFF; t |= (((u16 *)src)[5] + q) << 16; } p[8] = t; { s32 q = t - ((u16 *)src)[4]; t &= 0xFFFF0000; t |= (q & 0xFFFF); } p[6] = t; break; case 0x30: D_800A5E60 += 0x2C; func_8005A600((s32)(D_800A5E60 - 0xC), 0, 0, (u16)GetTPage(2, (code >> 28) & 3, 0x280, 0), 0); p[0] = 0x0A000000; p += 2; p[1] = tag | (src[4] & 0xFFFFFF); p[3] = src[5]; p[5] = src[6]; t = src[1]; { s32 q = w0 + t; t &= 0xFFFF0000; t |= (q & 0xFFFF); } { s32 q = t >> 16; t &= 0xFFFF; t |= (h0 + q) << 16; } p[2] = t; t = src[2]; { s32 q = w0 + t; t &= 0xFFFF0000; t |= (q & 0xFFFF); } { s32 q = t >> 16; t &= 0xFFFF; t |= (h0 + q) << 16; } p[4] = t; t = src[3]; { s32 q = w0 + t; t &= 0xFFFF0000; t |= (q & 0xFFFF); } { s32 q = t >> 16; t &= 0xFFFF; t |= (h0 + q) << 16; } p[6] = t; break; } otp = (u32 *)(idx * 4 + (u32)ot); p[-2] = (p[-2] & 0xFF000000) | (*otp & 0xFFFFFF); *otp = (*otp & 0xFF000000) | ((u32)(p - 2) & 0xFFFFFF); src += 7; tag = *src; code = tag; } } /* 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) { extern u16 D_800636D8[]; extern u16 D_80063818[]; extern u16 D_80063770[]; s32 var_a3; u8 c; u16 val; const u16 *tbl_81; const u16 *tbl_82lo; const u16 *tbl_83; var_a3 = 0; c = *arg0; arg0++; if (c != 0) { tbl_81 = D_80063818; tbl_82lo = D_800636D8; tbl_83 = D_80063770; do { switch (c) { case 0x81: { s32 idx = (*arg0) - 0x40; __asm__ __volatile__("" : "=r"(idx) : "0"(idx)); val = tbl_81[idx]; __asm__ __volatile__(""); break; } case 0x82: { u8 b = *arg0; if (b < 0x9F) { s32 idx = b - 0x4F; __asm__ __volatile__("" : "=r"(idx) : "0"(idx)); val = tbl_82lo[idx]; } else { s32 idx = b - 0x9F; __asm__ __volatile__("" : "=r"(idx) : "0"(idx)); val = tbl_83[idx]; } break; } case 0x83: { u8 b = *arg0; s16 off; if (b < 0x80) { off = b - 0x40; } else { off = b - 0x41; } val = tbl_83[off] + 0x2000; break; } default: *arg1 = 0xFFFF; return (s16) var_a3; } switch (val & 0xC000) { case 0x8000: *arg1 = 0x1858; arg1++; *arg1 = val & 0x3FFF; arg1++; var_a3 += 4; break; case 0xC000: *arg1 = 0x1850; arg1++; *arg1 = val & 0x3FFF; arg1++; var_a3 += 4; break; default: *arg1 = val; arg1++; break; } var_a3 -= 4; arg0++; c = *arg0; arg0++; } while (c != 0); } *arg1 = 0xFFFF; return (s16) var_a3; } extern u16 D_800636D8[]; extern u16 D_80063818[]; extern s16 D_80063870[]; void func_800241C0(s32 arg0, u16 *arg1, s32 arg2) { s32 pad[2]; s32 rem; register s32 i __asm__("$7"); s32 cnt; register s32 d __asm__("$4"); register s32 q __asm__("$6"); register s32 zr __asm__("$0"); register s32 dd __asm__("$3"); register s32 srem __asm__("$3"); s32 i2; u16 *pA; u16 *pB; s16 *pC; rem = arg0; cnt = 0; arg2--; i = arg2 + zr; if ((arg2 << 16) < 0) goto tail; pA = D_80063818; pB = D_800636D8; pC = D_80063870; do { if ((i << 16) != 0) { *arg1 = pA[0]; } else { *arg1 = pB[0]; } srem = (s16)rem; d = pC[(s16)i]; q = srem / d; dd = d + zr; if ((s16)q != 0) { *arg1 = pB[(s16)q]; cnt++; rem -= q * dd; } else if (cnt != 0) { *arg1 = pB[0]; } i2 = i - 1; i = i2 + zr; arg1++; } while ((i2 << 16) >= 0); tail: *(s16 *)arg1 = -1; } extern u16 D_80063858; extern u16 D_8006386C; /* SAME spelling as the already-banked func_800241C0 uses at file scope: gcc-2.7.2 * rejects a conflicting redeclaration at file scope AND at block scope, so match it. * Only the ADDRESS is taken here (t4 is a `register s16 *`), so the element type of * the decl does not reach codegen. */ extern s16 D_80063870[]; void func_800242D0(s32 arg0, u16* arg1, s32 arg2) { s32 frame_pad[1]; register s32 t0 __asm__("$8"); register s32 t1 __asm__("$9"); s32 v0; register s32 v1 __asm__("$3"); register s32 a3 __asm__("$7"); register s16* t4 __asm__("$12"); register u16* t2 __asm__("$10"); register s32 t3 __asm__("$11"); register s32 divisor __asm__("$4"); (void)&frame_pad; t0 = arg2 & 0xFFAF; arg2 = arg2 & 0x80; t1 = arg0; if (arg2 != 0) { t0 = 4; } v0 = t0 - 1; t0 = v0; __asm__ __volatile__("" : "=r"(t0) : "0"(t0)); if ((v0 << 16) >= 0) { t4 = D_80063870; t2 = &D_80063858; t3 = 2; v1 = t1 << 16; do { v0 = (t0 << 16) >> 15; divisor = *(s16*)(v0 + (s32)t4); v1 = v1 >> 16; v0 = v1 / divisor; a3 = v0; if (((a3 << 16) >> 16) != 0) { v1 = divisor; __asm__ __volatile__("" : "=r"(v1) : "0"(v1)); *(u16*)arg1 = *(u16*)((((a3 << 16) >> 16) << 1) + (s32)t2); t1 -= a3 * v1; } else { *(u16*)arg1 = *(u16*)t2; } arg1 += 1; if (arg2 != 0) { v0 = (t0 << 16) >> 16; if (v0 == t3) { *(u16*)arg1 = D_8006386C; arg1 += 1; } } v0 = t0 - 1; t0 = v0; __asm__ __volatile__("" : "=r"(t0) : "0"(t0)); v1 = t1 << 16; } while ((v0 << 16) >= 0); } *(s16*)arg1 = -1; } extern s32 D_80078D88[]; extern s32 D_800A2B78; extern void func_80026D64(); extern void func_80024448(); void func_800243EC(s32 *a0) { s32 v = *a0; D_80078D88[0] = v; D_800A2B78 = (v >> 28) & 7; if (v & 0x40) { func_80026D64(); } else { func_80024448(); } } /* func_80024448 (main / src/800.c) -- TMD mesh dispatcher: walks the primitive list at * hdr->prims (hdr = *(a0+8): [0]=verts [2]=norms [4]=prims [5]=count) and, per run of * primitives, dispatches on (word>>24)&0xFC (0x20..0x3C, jump table jtbl_80072B88) and the * gradation bit (word>>16)&4 to the matching emitter, which bumps the D_800A5E60 packet * pointer. Each arm then subtracts the run length (u16 at +0) from the remaining count and * advances by run_len * stride (16/20/24/28/32/36 bytes). The D_80078D88 tests select the * handwritten shaded variants (func_80026514 / func_800268D0) with an 8th argument. * [MATCH 362/362, S72m_1] Shape facts: every arm carries its OWN `count -= n; p += n*stride` * tail (gcc-2.7.2 cross-jumps only the suffix shared with the fall-through predecessor, so the * three-way D_80078D88 chains keep three copies); case bodies are in source order * 0x20,0x28,0x24,0x2C,0x30,0x38,0x34,0x3C to reproduce the target's body layout while the * jump table indexes by value. */ extern u8 *D_800A5E60; extern s32 D_80078D88[]; /* Callees: every one is DEFINED later in src/800.c; these are spelled to be compatible with * those definitions (verbatim where the types are spellable here, no-proto otherwise). */ extern long *func_800249F0(char *f, s32 verts, s32 norms, long *prim, s32 count, s32 shift, s32 ot); extern u8 *func_80024DE8(); extern u8 *func_8002528C(); extern void *func_80025504(); extern u8 *func_80025A30(u8 *prim, u8 *vtx, u8 *nrm, u8 *pkt, s32 n, s32 shift, u32 *ot); extern void *func_80025CBC(void *prims, void *scr, void *cols, void *packets, s32 count, s32 otshift, u32 *ot); extern u32 *func_80025EB8(); /* Handwritten-asm stubs, defined `void f()` in the TU; the packet pointer they leave in $v0 * is consumed here through a no-proto function-pointer cast (§378: folds to a direct jal). */ extern void func_80026514(); extern void func_800268D0(); #define CNT (*(u16 *)s0) #define OT (*(u32 **)(param_2 + 4)) void func_80024448(int param_1, int param_2, u32 param_3) { u32 *s0; u32 *v0; s32 iVar; u32 flag; u32 vtx; u32 nrm; /* These five return TU-local untagged struct type names (POLY_F4, PG4, PG3, PktGT3, * PktGT4) that src/800.c introduces only AFTER this function, so no file-scope forward * decl can spell them (a file-scope `extern u8 *f();` is a hard "conflicting types" error * at the later definition). Block-scope externs live in gcc-2.7.2's limbo instead: the * later definition draws only a "type mismatch with previous external decl" warning, * and the emitted bytes are identical (measured on a synthetic TU, S72m_1). */ extern u8 *func_80024BC0(); extern u8 *func_80025000(); extern u8 *func_80025818(); extern u8 *func_80026128(); extern u8 *func_800262D8(); v0 = *(u32 **)(param_1 + 8); s0 = (u32 *)v0[4]; iVar = v0[5]; vtx = v0[0]; nrm = v0[2]; if (iVar == 0) return; do { flag = (*s0 >> 16) & 4; switch ((*s0 >> 24) & 0xFC) { case 0x20: if (flag) { D_800A5E60 = (u8 *)func_80024DE8(s0, vtx, nrm, D_800A5E60, CNT, param_3, OT); iVar -= CNT; s0 = (u32 *)((u8 *)s0 + CNT * 24); } else { D_800A5E60 = (u8 *)func_800249F0((char *)s0, vtx, nrm, (long *)D_800A5E60, CNT, param_3, (s32)OT); iVar -= CNT; s0 = (u32 *)((u8 *)s0 + CNT * 16); } break; case 0x28: if (flag) { D_800A5E60 = (u8 *)func_80025000(s0, vtx, nrm, D_800A5E60, CNT, param_3, OT); iVar -= CNT; s0 = (u32 *)((u8 *)s0 + CNT * 32); } else { D_800A5E60 = (u8 *)func_80024BC0(s0, vtx, nrm, D_800A5E60, CNT, param_3, OT); iVar -= CNT; s0 = (u32 *)((u8 *)s0 + CNT * 20); } break; case 0x24: D_800A5E60 = (u8 *)func_80025CBC(s0, (void *)vtx, (void *)nrm, D_800A5E60, CNT, param_3, OT); iVar -= CNT; s0 = (u32 *)((u8 *)s0 + CNT * 24); break; case 0x2C: D_800A5E60 = (u8 *)func_80025EB8(s0, vtx, nrm, D_800A5E60, CNT, param_3, OT); iVar -= CNT; s0 = (u32 *)((u8 *)s0 + CNT * 32); break; case 0x30: if (flag) { D_800A5E60 = (u8 *)func_80025818(s0, vtx, nrm, D_800A5E60, CNT, param_3, OT); iVar -= CNT; s0 = (u32 *)((u8 *)s0 + CNT * 28); } else { if (D_80078D88[0] & 0x10000) { D_800A5E60 = ((u8 *(*)())func_80026514)(s0, vtx, nrm, D_800A5E60, CNT, param_3, OT, 0); iVar -= CNT; s0 = (u32 *)((u8 *)s0 + CNT * 20); } else if (D_80078D88[0] & 0x20000) { D_800A5E60 = ((u8 *(*)())func_80026514)(s0, vtx, nrm, D_800A5E60, CNT, param_3, OT, 1); iVar -= CNT; s0 = (u32 *)((u8 *)s0 + CNT * 20); } else { D_800A5E60 = (u8 *)func_8002528C(s0, vtx, nrm, D_800A5E60, CNT, param_3, OT); iVar -= CNT; s0 = (u32 *)((u8 *)s0 + CNT * 20); } } break; case 0x38: if (flag) { D_800A5E60 = (u8 *)func_80025A30((u8 *)s0, (u8 *)vtx, (u8 *)nrm, D_800A5E60, CNT, param_3, OT); iVar -= CNT; s0 = (u32 *)((u8 *)s0 + CNT * 36); } else if (D_80078D88[0] & 0x10000) { D_800A5E60 = ((u8 *(*)())func_800268D0)(s0, vtx, nrm, D_800A5E60, CNT, param_3, OT, 0); iVar -= CNT; s0 = (u32 *)((u8 *)s0 + CNT * 24); } else if (D_80078D88[0] & 0x20000) { D_800A5E60 = ((u8 *(*)())func_800268D0)(s0, vtx, nrm, D_800A5E60, CNT, param_3, OT, 1); iVar -= CNT; s0 = (u32 *)((u8 *)s0 + CNT * 24); } else { D_800A5E60 = (u8 *)func_80025504(s0, vtx, nrm, D_800A5E60, CNT, param_3, OT); iVar -= CNT; s0 = (u32 *)((u8 *)s0 + CNT * 24); } break; case 0x34: D_800A5E60 = (u8 *)func_80026128(s0, vtx, nrm, D_800A5E60, CNT, param_3, OT); iVar -= CNT; s0 = (u32 *)((u8 *)s0 + CNT * 28); break; case 0x3C: D_800A5E60 = (u8 *)func_800262D8(s0, vtx, nrm, D_800A5E60, CNT, param_3, OT); iVar -= CNT; s0 = (u32 *)((u8 *)s0 + CNT * 36); break; } } while (iVar != 0); } extern s32 D_800A2B78; /* func_800249F0 — HANDWRITTEN assembly (splat marks it "Handwritten function"). * Loop-carried state lives in $t1/$t2/$t5/$t6/$t8/$t9 with hand-placed latency * nops -- not reachable from gcc-2.7.2 codegen -- so the body is reproduced * verbatim as one .set noreorder asm block (same idiom as func_80027200 above); * gcc supplies only jr $ra + delay nop. * * Semantics: TMD F3 mesh emitter. Per face: RTPT 3 verts -> GTE flag cull -> * NCLIP cull -> store SXY0..2 -> AVSZ3 depth -> NCCS colour from normal p[0] * -> OT insert (tag code 0x04) -> if D_800A2B78 != 0 and the rgb word just * written has bit 1 set, append a 2-word semi-transparency primitive * (0xE100000A | ((D_800A2B78 & 3) << 5)) into the OT (tag code 0x01). * Returns the advanced packet pointer. */ long *func_800249F0(char *f, s32 verts, s32 norms, long *prim, s32 count, s32 shift, s32 ot) { __asm__ __volatile__( ".set\tnoreorder\n" "addiu $sp, $sp, -8\n" "lw $t5, 24($sp)\n" "lw $t9, 28($sp)\n" "lw $t8, 32($sp)\n" "beq $t5, $zero, 3f\n" "addu $t6, $a0, $zero\n" "addiu $t7, $sp, 4\n" "lui $t3, 0xFF\n" "ori $t3, $t3, 0xFFFF\n" "addiu $t2, $a0, 8\n" "addiu $t1, $a3, 4\n" "1:\n" "lhu $a0, 2($t2)\n" "lhu $v1, 4($t2)\n" "lhu $v0, 6($t2)\n" "sll $a0, $a0, 3\n" "addu $a0, $a1, $a0\n" "sll $v1, $v1, 3\n" "addu $v1, $a1, $v1\n" "sll $v0, $v0, 3\n" "addu $v0, $a1, $v0\n" "lwc2 $0, 0($a0)\n" "lwc2 $1, 4($a0)\n" "lwc2 $2, 0($v1)\n" "lwc2 $3, 4($v1)\n" "lwc2 $4, 0($v0)\n" "lwc2 $5, 4($v0)\n" "nop\n" "nop\n" "rtpt\n" "cfc2 $t4, $31\n" "nop\n" "sw $t4, 0($sp)\n" "lw $v0, 0($sp)\n" "addiu $v1, $zero, -0x1001\n" "and $v0, $v0, $v1\n" "bne $v0, $zero, 2f\n" "nop\n" "nop\n" "nop\n" "nclip\n" "swc2 $24, 0($t7)\n" "lw $v0, 4($sp)\n" "nop\n" "blez $v0, 2f\n" "nop\n" "swc2 $12, 8($a3)\n" "swc2 $13, 12($a3)\n" "swc2 $14, 16($a3)\n" "nop\n" "nop\n" "avsz3\n" "swc2 $7, 0($t7)\n" "addiu $v0, $t6, 4\n" "lwc2 $6, 0($v0)\n" "lhu $v0, 0($t2)\n" "nop\n" "sll $v0, $v0, 3\n" "addu $v0, $a2, $v0\n" "lwc2 $0, 0($v0)\n" "lwc2 $1, 4($v0)\n" "nop\n" "nop\n" "nccs\n" "swc2 $22, 0($t1)\n" "lw $v0, 4($sp)\n" "addiu $t1, $t1, 20\n" "srav $v0, $v0, $t9\n" "sll $v0, $v0, 2\n" "addu $t0, $t8, $v0\n" "lw $v1, 0($t0)\n" "lui $v0, 0x400\n" "and $v1, $v1, $t3\n" "or $v1, $v1, $v0\n" "and $v0, $a3, $t3\n" "sw $v1, 0($a3)\n" "sw $v0, 0($t0)\n" "lui $v0, %hi(D_800A2B78)\n" "lw $v0, %lo(D_800A2B78)($v0)\n" "nop\n" "beq $v0, $zero, 2f\n" "addiu $a3, $a3, 20\n" "lbu $v0, -17($t1)\n" "nop\n" "andi $v0, $v0, 0x2\n" "beq $v0, $zero, 2f\n" "lui $a0, 0xE100\n" "ori $a0, $a0, 0xA\n" "lw $v1, 4($sp)\n" "addiu $v0, $zero, 1\n" "sb $v0, -1($t1)\n" "lui $v0, %hi(D_800A2B78)\n" "lw $v0, %lo(D_800A2B78)($v0)\n" "srav $v1, $v1, $t9\n" "sll $v1, $v1, 2\n" "addu $t0, $t8, $v1\n" "andi $v0, $v0, 0x3\n" "sll $v0, $v0, 5\n" "or $v0, $v0, $a0\n" "sw $v0, 0($t1)\n" "addiu $t1, $t1, 8\n" "lw $v1, 0($t0)\n" "lui $v0, 0x100\n" "and $v1, $v1, $t3\n" "or $v1, $v1, $v0\n" "and $v0, $a3, $t3\n" "sw $v1, 0($a3)\n" "addiu $a3, $a3, 8\n" "sw $v0, 0($t0)\n" "2:\n" "addiu $t5, $t5, -1\n" "addiu $t2, $t2, 16\n" "bne $t5, $zero, 1b\n" "addiu $t6, $t6, 16\n" "3:\n" "addu $v0, $a3, $zero\n" "addiu $sp, $sp, 8\n" ".set\treorder\n" ); } /* ---- PsyQ GTE inline macros (the subset this routine uses) --------------- */ #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_ldrgb(r0) __asm__ volatile ( \ "lwc2 $6, 0( %0 )" \ : \ : "r"( r0 ) ) #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_avsz4() __asm__ volatile ("nop;nop;avsz4") #define gte_nccs() __asm__ volatile ("nop;nop;nccs") #define gte_stsxy3_f4(r0) __asm__ volatile ( \ "swc2 $12, 8( %0 );" \ "swc2 $13, 12( %0 );" \ "swc2 $14, 16( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stsxy(r0) __asm__ volatile ( \ "swc2 $14, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_strgb(r0) __asm__ volatile ( \ "swc2 $22, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stopz(r0) __asm__ volatile ( \ "swc2 $24, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stotz(r0) __asm__ volatile ( \ "swc2 $7, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stflg(r0) __asm__ volatile ( \ "cfc2 $12, $31;" \ "nop;" \ "sw $12, 0( %0 )" \ : \ : "r"( r0 ) \ : "$12", "memory" ) /* ---- types --------------------------------------------------------------- */ typedef struct { s16 vx, vy, vz, pad; } SVECTOR_80024BC0; typedef struct { u32 tag; /* 0x00 */ u8 r0, g0, b0, code; /* 0x04 */ s16 x0, y0; /* 0x08 */ s16 x1, y1; /* 0x0C */ s16 x2, y2; /* 0x10 */ s16 x3, y3; /* 0x14 */ } POLY_F4; /* 0x18 */ typedef struct { u32 tag; /* 0x00 */ u32 code0; /* 0x04 */ } DR_MODE; /* 0x08 */ typedef struct { s32 unk00; /* 0x00 */ u32 rgb; /* 0x04 */ u16 n0; /* 0x08 */ u16 v0; /* 0x0A */ u16 v1; /* 0x0C */ u16 v2; /* 0x0E */ u16 v3; /* 0x10 */ u16 pad; /* 0x12 */ } Face; /* 0x14 */ extern s32 D_800A2B78; POLY_F4 *func_80024BC0(Face *f, SVECTOR_80024BC0 *sv, SVECTOR_80024BC0 *nv, POLY_F4 *poly0, s32 n, s32 shift, u32 *ot) { POLY_F4 *poly = poly0; s32 flag; s32 otz; u32 *p; if (n != 0) { do { gte_ldv3(&sv[f->v0], &sv[f->v1], &sv[f->v2]); gte_rtpt(); gte_stflg(&flag); if ((flag & 0xFFFFEFFF) == 0) { gte_nclip(); gte_stopz(&otz); if (otz > 0) { gte_stsxy3_f4(poly); gte_ldv0(&sv[f->v3]); gte_rtps(); gte_stflg(&flag); if ((flag & 0xFFFFEFFF) == 0) { gte_stsxy(&poly->x3); gte_avsz4(); gte_stotz(&otz); gte_ldrgb(&f->rgb); gte_ldv0(&nv[f->n0]); gte_nccs(); gte_strgb(&poly->r0); p = &ot[otz >> shift]; poly->tag = (*p & 0x00FFFFFF) | 0x05000000; *p = (u32)poly & 0x00FFFFFF; poly++; if (D_800A2B78 != 0 && (poly[-1].code & 2) != 0) { s32 z = otz; *((u8 *)poly + 3) = 1; p = &ot[z >> shift]; *(u32 *)((u8 *)poly + 4) = 0xE100000A | ((D_800A2B78 & 3) << 5); poly->tag = (*p & 0x00FFFFFF) | 0x01000000; *p = (u32)poly & 0x00FFFFFF; poly = (POLY_F4 *)((u8 *)poly + 8); } } } } n--; f++; } while (n != 0); } return poly; } /* ---- PsyQ inline_c.h GTE macros ---- */ #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_ldv0(r0) __asm__ volatile ( \ "lwc2 $0, 0( %0 );" \ "lwc2 $1, 4( %0 )" \ : \ : "r"( r0 ) ) #define gte_ldrgb(r0) __asm__ volatile ( \ "lwc2 $6, 0( %0 )" \ : \ : "r"( r0 ) ) #define gte_rtpt() __asm__ volatile ("nop;nop;rtpt") #define gte_nclip() __asm__ volatile ("nop;nop;nclip") #define gte_avsz3() __asm__ volatile ("nop;nop;avsz3") #define gte_nccs() __asm__ volatile ("nop;nop;nccs") #define gte_stflg(r0) __asm__ volatile ( \ "cfc2 $12, $31;" \ "nop;" \ "sw $12, 0( %0 )" \ : \ : "r"( r0 ) \ : "$12", "memory" ) #define gte_stopz(r0) __asm__ volatile ( \ "swc2 $24, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stotz(r0) __asm__ volatile ( \ "swc2 $7, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_strgb(r0) __asm__ volatile ( \ "swc2 $22, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stsxy3_g3(r0) __asm__ volatile ( \ "swc2 $12, 8( %0 );" \ "swc2 $13, 16( %0 );" \ "swc2 $14, 24( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) extern s32 D_800A2B78; u8 *func_80024DE8(TmdG3 *prim, u8 *vtx, u8 *nrm, u8 *pkt, s32 n, s32 shift, u32 *ot) { s32 flag; s32 z; u32 *otp; if (n != 0) { /* Identity no-op (emits nothing). It must sit in the loop PREHEADER (inside the `if (n != 0)`), not before the guard branch. Why: loop.c `record_initial`/`valid_initial_value_p` otherwise take the biv's initial value to be the raw incoming hard reg $a3, so the `pkt + 4` giv is emitted as `addiu giv,$a3,4`. That keeps $a3 live past the parameter copy, so the pkt pseudo CONFLICTS with $a3 and can never live there (costing an extra `move`). An asm_operands src makes valid_initial_value_p reject it, the giv is computed from the pseudo, and pkt keeps $a3. In the preheader it also stays clear of reorg's backward delay-slot scan, which breaks on any asm and would otherwise lose `addu $t5,$a0,$zero` from the guard's delay slot. */ __asm__("" : "=r"(pkt) : "0"(pkt)); do { gte_ldv3(vtx + prim->v0 * 8, vtx + prim->v1 * 8, vtx + prim->v2 * 8); gte_rtpt(); gte_stflg(&flag); if ((flag & ~0x1000) == 0) { gte_nclip(); gte_stopz(&z); if (z > 0) { gte_stsxy3_g3(pkt); gte_avsz3(); gte_stotz(&z); gte_ldv0(nrm + prim->n0 * 8); gte_ldrgb(&prim->rgb0); gte_nccs(); gte_strgb(pkt + 4); gte_ldrgb(&prim->rgb1); gte_nccs(); gte_strgb(pkt + 0xC); gte_ldrgb(&prim->rgb2); gte_nccs(); gte_strgb(pkt + 0x14); pkt[7] = (pkt[7] & 2) | 0x30; otp = ot + (z >> shift); *(u32 *)pkt = (*otp & 0xFFFFFF) | 0x6000000; *otp = (u32)pkt & 0xFFFFFF; pkt += 0x1C; if (D_800A2B78 != 0) { if (pkt[7 - 0x1C] & 2) { otp = ot + (z >> shift); pkt[3] = 1; *(u32 *)(pkt + 4) = ((D_800A2B78 & 3) << 5) | 0xE100000A; *(u32 *)pkt = (*otp & 0xFFFFFF) | 0x1000000; *otp = (u32)pkt & 0xFFFFFF; pkt += 8; } } } } n--; prim++; } while (n != 0); } return pkt; } /* ------------------------------------------------------------------ * * func_80025000 — gouraud-quad (POLY_G4) transform + light + OT link. * GTE inline-asm dialect copied from the proven ov_SC03_099 TUs. * ------------------------------------------------------------------ */ #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_ldrgb(r0) __asm__ volatile ( \ "lwc2 $6, 0( %0 )" \ : \ : "r"( r0 ) ) #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_avsz4() __asm__ volatile ("nop;nop;avsz4") #define gte_nccs() __asm__ volatile ("nop;nop;nccs") #define gte_stflg(r0) __asm__ volatile ( \ "cfc2 $12, $31;" \ "nop;" \ "sw $12, 0( %0 )" \ : \ : "r"( r0 ) \ : "$12", "memory" ) #define gte_stopz(r0) __asm__ volatile ( \ "swc2 $24, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stotz(r0) __asm__ volatile ( \ "swc2 $7, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stsxy(r0) __asm__ volatile ( \ "swc2 $14, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stsxy3_g4(r0) __asm__ volatile ( \ "swc2 $12, 8( %0 );" \ "swc2 $13, 16( %0 );" \ "swc2 $14, 24( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_strgb(r0) __asm__ volatile ( \ "swc2 $22, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) typedef struct { s16 vx, vy, vz, pad; } SVEC_80025000; /* 0x08 */ typedef struct { u8 r, g, b, code; } RGBC; /* 0x04 */ typedef struct { /* 0x20 — source quad description */ u32 unk0; RGBC c0; RGBC c1; RGBC c2; RGBC c3; u16 n; u16 v0; u16 v1; u16 v2; u16 v3; u16 unk1E; } SRCQ; typedef struct { /* 0x24 — POLY_G4 */ u32 tag; RGBC c0; u32 xy0; RGBC c1; u32 xy1; RGBC c2; u32 xy2; RGBC c3; u32 xy3; } PG4; typedef struct { u32 tag; u32 code0; } DRM; /* 0x08 */ extern s32 D_800A2B78; PG4 *func_80025000(SRCQ *p, SVEC_80025000 *verts, SVEC_80025000 *norms, PG4 *poly0, s32 n, s32 shift, u32 *ot) { struct { s32 flag; s32 z; } g; u32 *otp; PG4 *prim; PG4 *poly = poly0; while (n != 0) { gte_ldv3(&verts[p->v0], &verts[p->v1], &verts[p->v2]); gte_rtpt(); gte_stflg(&g.flag); if ((g.flag & 0xFFFFEFFF) == 0) { gte_nclip(); gte_stopz(&g.z); if (g.z > 0) { gte_stsxy3_g4(poly); gte_ldv0(&verts[p->v3]); gte_rtps(); gte_stflg(&g.flag); if ((g.flag & 0xFFFFEFFF) == 0) { gte_stsxy(&poly->xy3); gte_avsz4(); gte_stotz(&g.z); gte_ldv0(&norms[p->n]); gte_ldrgb(&p->c0); gte_nccs(); gte_strgb(&poly->c0); gte_ldrgb(&p->c1); gte_nccs(); gte_strgb(&poly->c1); gte_ldrgb(&p->c2); gte_nccs(); gte_strgb(&poly->c2); gte_ldrgb(&p->c3); gte_nccs(); gte_strgb(&poly->c3); poly->c0.code = (poly->c0.code & 2) | 0x38; otp = &ot[g.z >> shift]; poly->tag = (*otp & 0xFFFFFF) | 0x08000000; *otp = (u32)poly & 0xFFFFFF; prim = poly; poly = (PG4 *)((u8 *)poly + 0x24); if (D_800A2B78 != 0) { if ((prim->c0.code & 2) != 0) { otp = &ot[g.z >> shift]; ((u8 *)poly)[3] = 1; ((DRM *)poly)->code0 = 0xE100000A | ((D_800A2B78 & 3) << 5); ((DRM *)poly)->tag = (*otp & 0xFFFFFF) | 0x01000000; *otp = (u32)poly & 0xFFFFFF; poly = (PG4 *)((u8 *)poly + 8); } } } } } n--; p++; } return poly; } /* --- PsyQ inline_c.h GTE macros (verbatim bodies, project spelling) --- */ #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_ldrgb(r0) __asm__ volatile ( \ "lwc2 $6, 0( %0 )" \ : \ : "r"( r0 ) ) #define gte_rtpt() __asm__ volatile ("nop;nop;rtpt") #define gte_nclip() __asm__ volatile ("nop;nop;nclip") #define gte_avsz3() __asm__ volatile ("nop;nop;avsz3") #define gte_ncct() __asm__ volatile ("nop;nop;ncct") #define gte_stflg(r0) __asm__ volatile ( \ "cfc2 $12, $31;" \ "nop;" \ "sw $12, 0( %0 )" \ : \ : "r"( r0 ) \ : "$12", "memory" ) #define gte_stopz(r0) __asm__ volatile ( \ "swc2 $24, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stotz(r0) __asm__ volatile ( \ "swc2 $7, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stsz3(r0, r1, r2) __asm__ volatile ( \ "swc2 $17, 0( %0 );" \ "swc2 $18, 0( %1 );" \ "swc2 $19, 0( %2 )" \ : \ : "r"( r0 ), "r"( r1 ), "r"( r2 ) \ : "memory" ) #define gte_stsxy3_g3(r0) __asm__ volatile ( \ "swc2 $12, 8( %0 );" \ "swc2 $13, 16( %0 );" \ "swc2 $14, 24( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_strgb3_g3(r0) __asm__ volatile ( \ "swc2 $20, 4( %0 );" \ "swc2 $21, 12( %0 );" \ "swc2 $22, 20( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) extern s32 D_80078D88[]; extern s32 D_800A2B78; typedef struct { s16 vx, vy, vz, pad; } Vec8; typedef struct { u32 unk0; u32 rgbc; u16 n0, v0, n1, v1, n2, v2; } Prim; u8 *func_8002528C(Prim *pr, Vec8 *vtx, Vec8 *nrm, void *arg3, s32 n, s32 shift, u32 *ot) { struct { s32 flag, opz, sz1, sz2, sz3; } g; u8 *pk = (u8 *)arg3; u32 *otp; s32 z; if (n != 0) { do { gte_ldv3(&vtx[pr->v0], &vtx[pr->v1], &vtx[pr->v2]); gte_rtpt(); gte_stflg(&g.flag); if (!(g.flag & ~0x1000)) { gte_nclip(); gte_stopz(&g.opz); if (g.opz > 0) { gte_stsxy3_g3(pk); if (D_80078D88[0] & 0x8000) { gte_stsz3(&g.sz1, &g.sz2, &g.sz3); if (g.sz1 > g.sz2) { z = g.sz1; if (z < g.sz3) { z = g.sz3; } } else { z = g.sz2; if (z < g.sz3) { z = g.sz3; } } g.opz = z >> 2; } else { gte_avsz3(); gte_stotz(&g.opz); } gte_ldrgb(&pr->rgbc); gte_ldv3(&nrm[pr->n0], &nrm[pr->n1], &nrm[pr->n2]); gte_ncct(); gte_strgb3_g3(pk); otp = &ot[g.opz >> shift]; *(u32 *)pk = (*otp & 0xFFFFFF) | 0x6000000; *otp = (u32)pk & 0xFFFFFF; pk += 0x1C; if (D_800A2B78 != 0 && (pk[-0x15] & 2)) { otp = &ot[g.opz >> shift]; pk[3] = 1; *(u32 *)(pk + 4) = 0xE100000A | ((D_800A2B78 & 3) << 5); *(u32 *)pk = (*otp & 0xFFFFFF) | 0x1000000; *otp = (u32)pk & 0xFFFFFF; pk += 8; } } } n--; pr++; } while (n != 0); } return pk; } /* func_80025504 - POLY_G4 mesh transform + OT insert (GTE, PsyQ inline macros). * * Two non-obvious constructs are REQUIRED for the byte match (both emit ZERO * instructions); do not "clean them up": * * 1) __asm__("" : "=r"(poly) : "0"(poly)); * loop.c:3509 valid_initial_value_p() accepts a HARD reg as a biv's initial * value when no call intervenes, so gcc would emit the poly+4 giv init as * `addiu giv,$a3,4` reading the INCOMING $a3. That keeps hard $a3 live past * the param copy, so the poly pseudo CONFLICTS with $7 (see .greg * "75 conflicts: ... 7"), loses the copy-preference, and gcc emits a spurious * `move t1,$a3`. Re-tying poly through an empty asm makes the biv init_set * src an ASM_OPERANDS -> valid_initial_value_p() fails -> the pseudo is used, * $a3 dies at the param copy, poly gets $a3, the copy folds away. * * 2) register GFace *p0 __asm__("$4") = prim; + fp = p0; * prim needs the OPPOSITE treatment: its giv init must read the hard $a0 * (`addiu $t1,$a0,0x14`), but its copy `addu $t6,$a0,$zero` must schedule * AFTER the three stack-arg loads. Assigning through an $a0-pinned local * gives the copy a late LUID while keeping (reg 4) as the biv's initial value. */ /* ---- PsyQ-style inline GTE macros (inline_c.h shapes) ---------------------- */ #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_ldrgb(r0) __asm__ volatile("lwc2 $6, 0(%0)" : : "r"(r0)) #define gte_rtpt() __asm__ volatile("nop;nop;cop2 0x0280030") #define gte_rtps() __asm__ volatile("nop;nop;cop2 0x0180001") #define gte_nclip() __asm__ volatile("nop;nop;cop2 0x1400006") #define gte_avsz4() __asm__ volatile("nop;nop;cop2 0x168002e") #define gte_ncct() __asm__ volatile("nop;nop;cop2 0x118043f") #define gte_nccs() __asm__ volatile("nop;nop;cop2 0x108041b") #define gte_stflg(r0) __asm__ volatile("cfc2 $12, $31;nop;sw $12, 0(%0)" : : "r"(r0) : "$12", "memory") #define gte_stopz(r0) __asm__ volatile("swc2 $24, 0(%0)" : : "r"(r0) : "memory") #define gte_stotz(r0) __asm__ volatile("swc2 $7, 0(%0)" : : "r"(r0) : "memory") #define gte_stsxy2(r0) __asm__ volatile("swc2 $14, 0(%0)" : : "r"(r0) : "memory") #define gte_strgb2(r0) __asm__ volatile("swc2 $22, 0(%0)" : : "r"(r0) : "memory") #define gte_stsxy3_g4(r0) __asm__ volatile( \ "swc2 $12, 8(%0);swc2 $13, 16(%0);swc2 $14, 24(%0)" : : "r"(r0) : "memory") #define gte_strgb3_g4(r0) __asm__ volatile( \ "swc2 $20, 4(%0);swc2 $21, 12(%0);swc2 $22, 20(%0)" : : "r"(r0) : "memory") #define gte_stsz4(r0, r1, r2, r3) __asm__ volatile( \ "swc2 $16, 0(%0);swc2 $17, 0(%1);swc2 $18, 0(%2);swc2 $19, 0(%3)" \ : : "r"(r0), "r"(r1), "r"(r2), "r"(r3) : "memory") /* ---- local types ----------------------------------------------------------- */ typedef struct { s16 vx, vy, vz, pad; } GVec8; /* 0x08 */ typedef struct { s32 unk0; u32 rgb; u16 n0, v0; u16 n1, v1; u16 n2, v2; u16 n3, v3; } GFace; /* 0x18 */ typedef struct { u32 tag; u8 r0, g0, b0, code; u32 xy0; u32 rgb1; u32 xy1; u32 rgb2; u32 xy2; u32 rgb3; u32 xy3; } GPolyG4; /* 0x24 */ typedef struct { u8 addr0, addr1, addr2, len; u32 code0; } GDrMode; /* 0x08 */ extern s32 D_80078D88[]; extern s32 D_800A2B78; void *func_80025504(GFace *prim, GVec8 *vtx, GVec8 *nrm, GPolyG4 *poly, s32 n, s32 shift, u32 *ot) { s32 flag; s32 p; s32 sz0, sz1, sz2, sz3; s32 zmax, ztmp; u32 *op; GDrMode *dm; GFace *fp; register GFace *p0 __asm__("$4") = prim; __asm__("" : "=r"(poly) : "0"(poly)); fp = p0; for (; n != 0; n--, fp++) { gte_ldv3(&vtx[fp->v0], &vtx[fp->v1], &vtx[fp->v2]); gte_rtpt(); gte_stflg(&flag); if (flag & 0xffffefff) { continue; } gte_nclip(); gte_stopz(&p); if (p <= 0) { continue; } gte_stsxy3_g4(poly); gte_ldv0(&vtx[fp->v3]); gte_rtps(); gte_stflg(&flag); if (flag & 0xffffefff) { continue; } gte_stsxy2(&poly->xy3); if (D_80078D88[0] & 0x8000) { gte_stsz4(&sz0, &sz1, &sz2, &sz3); zmax = sz2; if (zmax < sz3) { zmax = sz3; } ztmp = sz0; if (ztmp < sz1) { ztmp = sz1; } if (ztmp < zmax) { ztmp = zmax; } p = ztmp >> 2; } else { gte_avsz4(); gte_stotz(&p); } gte_ldrgb(&fp->rgb); gte_ldv3(&nrm[fp->n0], &nrm[fp->n1], &nrm[fp->n2]); gte_ncct(); gte_strgb3_g4(poly); gte_ldv0(&nrm[fp->n3]); gte_nccs(); gte_strgb2(&poly->rgb3); op = &ot[p >> shift]; poly->tag = (*op & 0xffffff) | 0x08000000; *op = (u32)poly & 0xffffff; poly++; if (D_800A2B78 != 0) { if (poly[-1].code & 2) { dm = (GDrMode *)poly; op = &ot[p >> shift]; dm->len = 1; dm->code0 = 0xe100000a | ((D_800A2B78 & 3) << 5); *(u32 *)dm = (*op & 0xffffff) | 0x01000000; *op = (u32)dm & 0xffffff; poly = (GPolyG4 *)((u8 *)dm + 8); } } } return poly; } /* ---- PsyQ inline GTE macros ------------------------------------------------ * Raw `.word` forms: include/gte_macros.inc is not on the standalone match_one * assembler path, so the cop2 opcodes are spelled as their encoded words, taken * verbatim from the target .s: * rtpt 0x4A280030 · nclip 0x4B400006 · avsz3 0x4B58002D · nccs 0x4B08041B * Everything else is the stock PSY-Q 4.7 inline_c.h text (gte_stflg's $12 scratch * is exactly the target's `cfc2 $t4,$31`). * --------------------------------------------------------------------------- */ #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_ldrgb(r0) __asm__ volatile ( \ "lwc2 $6, 0( %0 )" \ : \ : "r"( r0 ) ) #define gte_rtpt() __asm__ volatile ("nop;nop;.word 0x4A280030") #define gte_nclip() __asm__ volatile ("nop;nop;.word 0x4B400006") #define gte_avsz3() __asm__ volatile ("nop;nop;.word 0x4B58002D") #define gte_nccs() __asm__ volatile ("nop;nop;.word 0x4B08041B") #define gte_stflg(r0) __asm__ volatile ( \ "cfc2 $12, $31;" \ "nop;" \ "sw $12, 0( %0 )" \ : \ : "r"( r0 ) \ : "$12", "memory" ) #define gte_stopz(r0) __asm__ volatile ( \ "swc2 $24, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stotz(r0) __asm__ volatile ( \ "swc2 $7, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_strgb(r0) __asm__ volatile ( \ "swc2 $22, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stsxy3_g3(r0) __asm__ volatile ( \ "swc2 $12, 8( %0 );" \ "swc2 $13, 16( %0 );" \ "swc2 $14, 24( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) /* 8-byte SVECTOR: vertex / normal table entry (index scaled by 8 => sll 3). */ typedef struct { s16 vx, vy, vz, pad; } SVEC8_80025818; /* 0x1C POLY_G3 packet (tag + 6 words => tag length nibble 0x06). */ typedef struct { u32 tag; u8 r0, g0, b0, code; s16 x0, y0; u8 r1, g1, b1, pad1; s16 x1, y1; u8 r2, g2, b2, pad2; s16 x2, y2; } PG3; /* 0x1C TMD gouraud-3 (3 colours, no texture) primitive record. */ typedef struct { u32 hdr; u32 rgb0; u32 rgb1; u32 rgb2; u16 n0, v0; u16 n1, v1; u16 n2, v2; } TPRIM_80025818; extern s32 D_800A2B78; PG3 *func_80025818(TPRIM_80025818 *prim, SVEC8_80025818 *vtx, SVEC8_80025818 *nrm, PG3 *poly, s32 n, s32 shift, u32 *ot) { s32 otz; s32 flag; u32 *otp; if (n != 0) { /* Zero-instruction launder of the `poly` biv's initial value. * Without it, loop.c's record_initial (loop.c:3454-3511) takes the biv's * initial value straight from the parameter copy `poly = $a3`, so the GIV * preheader init is emitted as `giv = (hard $a3) + 4`. That keeps hard * $a3 live to the preheader, which makes the `poly` pseudo CONFLICT with * $a3 (dump: `75 conflicts: ... 7`), so $a3 goes to the GIV and `poly` * needs an extra `move`. An asm_operands SET_SRC fails * valid_initial_value_p, so bl->initial_value stays the pseudo and $a3 * stays free for `poly` -- byte-exact, 135 -> 134 instructions. * It MUST sit below the `n != 0` guard: above it the #APP/#NO_APP pair * blocks the `prim` parameter copy from hopping into the beqz delay slot * (cookbook RC-11). */ __asm__ ("" : "=r"(poly) : "0"(poly)); do { gte_ldv3(&vtx[prim->v0], &vtx[prim->v1], &vtx[prim->v2]); gte_rtpt(); gte_stflg(&flag); if ((flag & ~0x1000) == 0) { gte_nclip(); gte_stopz(&otz); if (otz > 0) { gte_stsxy3_g3(poly); gte_avsz3(); gte_stotz(&otz); gte_ldv0(&nrm[prim->n0]); gte_ldrgb(&prim->rgb0); gte_nccs(); gte_strgb(&poly->r0); gte_ldrgb(&prim->rgb1); gte_nccs(); gte_strgb(&poly->r1); gte_ldrgb(&prim->rgb2); gte_nccs(); gte_strgb(&poly->r2); poly->code = (poly->code & 2) | 0x30; otp = ot + (otz >> shift); *(u32 *)poly = (*otp & 0xFFFFFF) | 0x06000000; *otp = (u32)poly & 0xFFFFFF; poly++; if (D_800A2B78 != 0) { if (poly[-1].code & 2) { /* 2-word DR_MODE packet chained in front of the G3 */ otp = ot + (otz >> shift); *((u8 *)poly + 3) = 1; *(u32 *)((u8 *)poly + 4) = 0xE100000A | ((D_800A2B78 & 3) << 5); *(u32 *)poly = (*otp & 0xFFFFFF) | 0x01000000; *otp = (u32)poly & 0xFFFFFF; poly = (PG3 *)((u8 *)poly + 8); } } } } n--; prim++; } while (n != 0); } return poly; } /* func_80025A30 (main / src/800.c) -- TMD "quad, flat normal, gradation" emitter. * Per prim (stride 0x24): RTPT 3 verts -> flag/nclip cull -> RTPS 4th vert -> AVSZ4 -> 4x NCCS * (one shared normal, 4 base colours) -> POLY_G4 (code 0x38) -> OT insert (len 8). * If D_800A2B78 != 0 and the poly kept its bit-1 flag, a second 2-word 0xE100000A draw-mode * packet (len 1) is appended and linked into the same OT slot. * * MATCH 163/163. Two non-obvious levers (both were byte-decisive): * 1. ONE `pkt` pointer, not pkt+q. loop.c strength-reduces it into biv $a3 + giv($a3+4)=$t1; * that is where the -0x21 / -0x1D style offsets come from. Writing an explicit `q = pkt+4` * variable instead makes gcc build a THIRD register (giv q+3) -> +4 insns. Same for `prim`: * one pointer -> biv $t3 + giv($a0+0x10)=$t2. (cookbook loop.md L1) * 2. The zero-byte `__asm__("" : "=r"(pkt) : "0"(pkt))` in the preheader. Without it loop.c's * record_initial/valid_initial_value_p takes the biv's initial value from the HARD reg $a3 * (`addiu giv, a3, 4` reads hard a3 in the preheader), which keeps hard $a3 live past the * param copy -> the pkt pseudo CONFLICTS with $a3 and can never be granted it: gcc then * emits biv=$t1 / giv=$a3 plus an extra `move t1,a3` (164 ins, whole-loop register swap). * A set whose SET_SRC is an asm_operands makes the initial value "complex" (loop.c:3509), * so the giv init reads the PSEUDO, hard $a3 dies at the copy, and the biv wins $a3. * It must sit INSIDE `if (n != 0)` so it lands in the preheader: an early `return` instead * plants a CODE_LABEL that halts record_initial's backward scan for EVERY biv, which also * robs `prim` of its hard-$a0 initial value (`addiu t2,t3,16` != `addiu t2,a0,0x10`). * 3. `o = &ot[z >> shift];` must precede `pkt[3] = 1;` -- the scheduler cannot hoist the * `lw z` above an aliasing `sb`, so source order decides those 4 instructions (§175 lever A). * * PsyQ inline_c.h GTE macro bodies (same spelling as the matched * ov_SC01_000 renderer in src/ov_SC03_099/ov_SC03_099_jr_8017BEBC.c). */ #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_ldrgb(r0) __asm__ volatile ( \ "lwc2 $6, 0( %0 )" \ : \ : "r"( r0 ) ) #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_avsz4() __asm__ volatile ("nop;nop;avsz4") #define gte_nccs() __asm__ volatile ("nop;nop;nccs") #define gte_stflg(r0) __asm__ volatile ( \ "cfc2 $12, $31;" \ "nop;" \ "sw $12, 0( %0 )" \ : \ : "r"( r0 ) \ : "$12", "memory" ) #define gte_stopz(r0) __asm__ volatile ( \ "swc2 $24, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stotz(r0) __asm__ volatile ( \ "swc2 $7, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stsxy(r0) __asm__ volatile ( \ "swc2 $14, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_strgb(r0) __asm__ volatile ( \ "swc2 $22, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stsxy3_g4(r0) __asm__ volatile ( \ "swc2 $12, 8( %0 );" \ "swc2 $13, 16( %0 );" \ "swc2 $14, 24( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) extern s32 D_800A2B78; u8 *func_80025A30(u8 *prim, u8 *vtx, u8 *nrm, u8 *pkt, s32 n, s32 shift, u32 *ot) { long flag; long z; u32 *o; if (n != 0) { __asm__ volatile ("" : "=r"(pkt) : "0"(pkt)); for (; n != 0; n--, prim += 0x24) { gte_ldv3(vtx + (*(u16 *)(prim + 0x16) << 3), vtx + (*(u16 *)(prim + 0x1A) << 3), vtx + (*(u16 *)(prim + 0x1E) << 3)); gte_rtpt(); gte_stflg(&flag); if (flag & 0xFFFFEFFF) { continue; } gte_nclip(); gte_stopz(&z); if (z <= 0) { continue; } gte_stsxy3_g4(pkt); gte_ldv0(vtx + (*(u16 *)(prim + 0x22) << 3)); gte_rtps(); gte_stflg(&flag); if (flag & 0xFFFFEFFF) { continue; } gte_stsxy(pkt + 0x20); gte_avsz4(); gte_stotz(&z); gte_ldv0(nrm + (*(u16 *)(prim + 0x14) << 3)); gte_ldrgb(prim + 0x4); gte_nccs(); gte_strgb(pkt + 0x4); gte_ldrgb(prim + 0x8); gte_nccs(); gte_strgb(pkt + 0xC); gte_ldrgb(prim + 0xC); gte_nccs(); gte_strgb(pkt + 0x14); gte_ldrgb(prim + 0x10); gte_nccs(); gte_strgb(pkt + 0x1C); pkt[7] = (pkt[7] & 2) | 0x38; o = &ot[z >> shift]; *(u32 *)pkt = (*o & 0xFFFFFF) | 0x08000000; *o = (u32)pkt & 0xFFFFFF; pkt += 0x24; if (D_800A2B78 == 0) { continue; } if ((pkt[-0x1D] & 2) == 0) { continue; } o = &ot[z >> shift]; pkt[3] = 1; *(u32 *)(pkt + 4) = ((D_800A2B78 & 3) << 5) | 0xE100000A; *(u32 *)pkt = (*o & 0xFFFFFF) | 0x01000000; *o = (u32)pkt & 0xFFFFFF; pkt += 8; } } return pkt; } /* Handwritten GTE polygon-transform loop (cookbook: handwritten inline-asm wrapper form). * The whole body is written in asm because the original is hand-written assembly: the * loop-carried values live in $t1/$t2/$t3/$t5/$t6/$t7/$t8/$t9 (gcc-2.7.2 would never * allocate caller-saved temporaries that way), and the two `lw 0x10($sp)` copies plus the * duplicated `sra $v0,$v1,2` on both arms of the min() are hand-scheduled. * * maspsx notes: `.set noreorder` must be the FIRST line so maspsx stops auto-filling * branch-delay slots / load-delay nops (we write every nop ourselves, exactly as in the * target), and `.set reorder` must be the LAST so the compiler-emitted `j $31` still gets * its delay-slot nop. All memory offsets are DECIMAL: maspsx int()s them. */ extern u32 D_8006387C; extern s32 D_80078D88[]; void *func_80025CBC(void *prims, void *scr, void *cols, void *packets, s32 count, s32 otshift, u32 *ot) { __asm__ __volatile__( ".set\tnoreorder\n" "addiu $sp, $sp, -32\n" "lw $11, 48($sp)\n" "sw $17, 28($sp)\n" "lui $17, %hi(D_8006387C)\n" "addiu $17, $17, %lo(D_8006387C)\n" "sw $16, 24($sp)\n" "lw $16, 52($sp)\n" "lw $25, 56($sp)\n" "lwc2 $6, 0($17)\n" "beq $11, $0, .LfnEnd\n" "addu $2, $7, $0\n" "addiu $14, $sp, 4\n" "addiu $24, $sp, 8\n" "addiu $15, $sp, 12\n" "lui $13, 255\n" "ori $13, $13, 65535\n" "addiu $9, $4, 16\n" "addiu $10, $7, 4\n" ".LfnLoop:\n" "lhu $4, 2($9)\n" "lhu $3, 4($9)\n" "lhu $2, 6($9)\n" "sll $4, $4, 3\n" "addu $4, $5, $4\n" "sll $3, $3, 3\n" "addu $3, $5, $3\n" "sll $2, $2, 3\n" "addu $2, $5, $2\n" "lwc2 $0, 0($4)\n" "lwc2 $1, 4($4)\n" "lwc2 $2, 0($3)\n" "lwc2 $3, 4($3)\n" "lwc2 $4, 0($2)\n" "lwc2 $5, 4($2)\n" "nop\n" "nop\n" "rtpt\n" "lw $2, -12($9)\n" "nop\n" "sw $2, 8($10)\n" "lw $2, -8($9)\n" "nop\n" "sw $2, 16($10)\n" "lw $2, -4($9)\n" "nop\n" "sw $2, 24($10)\n" "cfc2 $12, $31\n" "nop\n" "sw $12, 0($sp)\n" "lw $2, 0($sp)\n" "addiu $3, $0, -4097\n" "and $2, $2, $3\n" "bne $2, $0, .LfnNext\n" "nop\n" "nop\n" "nop\n" "nclip\n" "swc2 $24, 0($14)\n" "lw $2, 4($sp)\n" "nop\n" "blez $2, .LfnNext\n" "nop\n" "swc2 $12, 8($7)\n" "swc2 $13, 16($7)\n" "swc2 $14, 24($7)\n" "lui $2, %hi(D_80078D88)\n" "lw $2, %lo(D_80078D88)($2)\n" "nop\n" "andi $2, $2, 32768\n" "beq $2, $0, .LfnAvsz\n" "addiu $2, $sp, 16\n" "swc2 $17, 0($24)\n" "swc2 $18, 0($15)\n" "swc2 $19, 0($2)\n" "lw $8, 8($sp)\n" "lw $3, 12($sp)\n" "nop\n" "slt $2, $3, $8\n" "beq $2, $0, .LfnMinB\n" "nop\n" "lw $4, 16($sp)\n" "j .LfnMinJ\n" "addu $3, $8, $0\n" ".LfnMinB:\n" "lw $4, 16($sp)\n" ".LfnMinJ:\n" "nop\n" "slt $2, $3, $4\n" "beq $2, $0, .LfnMinC\n" "sra $2, $3, 2\n" "addu $3, $4, $0\n" "sra $2, $3, 2\n" ".LfnMinC:\n" "j .LfnJoin\n" "sw $2, 4($sp)\n" ".LfnAvsz:\n" "nop\n" "nop\n" "avsz3\n" "swc2 $7, 0($14)\n" ".LfnJoin:\n" "lhu $2, 0($9)\n" "nop\n" "sll $2, $2, 3\n" "addu $2, $6, $2\n" "lwc2 $0, 0($2)\n" "lwc2 $1, 4($2)\n" "nop\n" "nop\n" "nccs\n" "swc2 $22, 0($10)\n" "lw $4, 4($sp)\n" "addiu $10, $10, 32\n" "srav $4, $4, $16\n" "sll $4, $4, 2\n" "addu $4, $25, $4\n" "lw $3, 0($4)\n" "lui $2, 1792\n" "and $3, $3, $13\n" "or $3, $3, $2\n" "and $2, $7, $13\n" "sw $3, 0($7)\n" "addiu $7, $7, 32\n" "sw $2, 0($4)\n" ".LfnNext:\n" "addiu $11, $11, -1\n" "bne $11, $0, .LfnLoop\n" "addiu $9, $9, 24\n" "addu $2, $7, $0\n" ".LfnEnd:\n" "lw $17, 28($sp)\n" "lw $16, 24($sp)\n" "addiu $sp, $sp, 32\n" ".set\treorder\n" ); } /* func_80025EB8 — POLY_FT4 (0x28) mesh emitter, main / src/800.c [MATCH 156/156] * * Per prim (0x20 bytes): RTPT the first 3 verts -> GTE flag cull -> NCLIP cull -> * RTPS the 4th vert -> flag cull -> depth (max(sz0..sz3)>>2 when D_80078D88 & 0x8000, * else AVSZ4) -> NCCS colour from the prim's normal -> OT insert (tag code 0x09). * Returns the advanced packet pointer. * * GTE ops are verbatim PsyQ inline_c.h macro bodies, spelled with the project gas * macros from include/gte_macros.inc (pulled in by common.h) — same idiom as the * already-matched func_8017BEBC family in the overlays. * * TWO NON-OBVIOUS LEVERS (both cost zero bytes, both were the whole residual): * * 1. `D_80078D88` is declared as an ARRAY, not a scalar. gcc-2.7.2 alias.c refuses * to reorder two memory refs only if they can conflict; its rule exempts a * MEM_IN_STRUCT_P ref with a varying address from a NON-struct ref with a constant * address. With `extern s32 D_80078D88;` the global load is a plain scalar at a * symbol_ref, so sched1 hoisted `lui/lw D_80078D88` above the `pkt->uv3` store and * swallowed both load-delay nops (-2 insns). An ARRAY_REF sets MEM_IN_STRUCT_P on * the load, the exemption no longer applies, the pair is ordered, and the two nops * come back exactly as in the target. * * 2. The packet cursor is a LOCAL (`pkt`) seeded from the parameter (`pkt0`), not the * parameter itself. loop.c's record_initial() takes the biv's initial_value from * its last pre-loop set: with the parameter used directly that set is * `(set P (reg:SI 7 a3))`, so the +4 giv's preheader init is emitted against the * HARD register — which keeps $a3 live into the preheader and makes it CONFLICT * with P (`;; 75 conflicts: ... 7` in .greg). P then can never be granted $a3, so * it took $t2, the giv took $a3, and the dead param copy `move t2,a3` survived * (+1 insn, and every packet reference register-swapped). Seeding a local makes * initial_value a pseudo, $a3 dies at the param copy, the conflict disappears and * the copy preference puts the cursor back in $a3. */ #define gte_ldrgb(r0) __asm__ volatile ( \ "lwc2 $6, 0( %0 )" \ : \ : "r"( r0 ) ) #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_avsz4() __asm__ volatile ("nop;nop;avsz4") #define gte_nccs() __asm__ volatile ("nop;nop;nccs") #define gte_stflg(r0) __asm__ volatile ( \ "cfc2 $12, $31;" \ "nop;" \ "sw $12, 0( %0 )" \ : \ : "r"( r0 ) \ : "$12", "memory" ) #define gte_stopz(r0) __asm__ volatile ( \ "swc2 $24, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stotz(r0) __asm__ volatile ( \ "swc2 $7, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stsxy(r0) __asm__ volatile ( \ "swc2 $14, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stsxy3_ft4(r0) __asm__ volatile ( \ "swc2 $12, 8( %0 );" \ "swc2 $13, 16( %0 );" \ "swc2 $14, 24( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stsz4(r0, r1, r2, r3) __asm__ volatile ( \ "swc2 $16, 0( %0 );" \ "swc2 $17, 0( %1 );" \ "swc2 $18, 0( %2 );" \ "swc2 $19, 0( %3 )" \ : \ : "r"( r0 ), "r"( r1 ), "r"( r2 ), "r"( r3 ) ) #define gte_strgb2(r0) __asm__ volatile ( \ "swc2 $22, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) typedef struct { s16 vx, vy, vz, pad; } SVEC8_80025EB8; typedef struct { u32 pad0; /* 0x00 */ u32 uv0; /* 0x04 */ u32 uv1; /* 0x08 */ u32 uv2; /* 0x0C */ u32 uv3; /* 0x10 */ u16 n0; /* 0x14 */ u16 v0; /* 0x16 */ u16 v1; /* 0x18 */ u16 v2; /* 0x1A */ u16 v3; /* 0x1C */ u16 pad1; /* 0x1E */ } Prim20; typedef struct { /* libgpu POLY_FT4, 0x28 bytes */ u32 tag; /* 0x00 */ u32 rgb; /* 0x04 */ u32 xy0; /* 0x08 */ u32 uv0; /* 0x0C */ u32 xy1; /* 0x10 */ u32 uv1; /* 0x14 */ u32 xy2; /* 0x18 */ u32 uv2; /* 0x1C */ u32 xy3; /* 0x20 */ u32 uv3; /* 0x24 */ } Ft4; extern u32 D_80063880; extern s32 D_80078D88[]; u32 *func_80025EB8(Prim20 *prim, SVEC8_80025EB8 *verts, SVEC8_80025EB8 *norms, Ft4 *pkt0, s32 n, s32 shift, u32 *ot) { struct { long flag, otz, sz0, sz1, sz2, sz3; } g; s32 a, b; u32 *otp; Ft4 *pkt; pkt = pkt0; gte_ldrgb(&D_80063880); while (n != 0) { gte_ldv3(&verts[prim->v0], &verts[prim->v1], &verts[prim->v2]); gte_rtpt(); pkt->uv0 = prim->uv0; pkt->uv1 = prim->uv1; pkt->uv2 = prim->uv2; gte_stflg(&g.flag); if (!(g.flag & 0xFFFFEFFF)) { gte_nclip(); gte_stopz(&g.otz); if (g.otz > 0) { gte_stsxy3_ft4(pkt); gte_ldv0(&verts[prim->v3]); gte_rtps(); gte_stflg(&g.flag); if (!(g.flag & 0xFFFFEFFF)) { gte_stsxy(&pkt->xy3); pkt->uv3 = prim->uv3; if (D_80078D88[0] & 0x8000) { gte_stsz4(&g.sz0, &g.sz1, &g.sz2, &g.sz3); a = g.sz2; if (a < g.sz3) a = g.sz3; b = g.sz0; if (b < g.sz1) b = g.sz1; if (b < a) b = a; g.otz = b >> 2; } else { gte_avsz4(); gte_stotz(&g.otz); } gte_ldv0(&norms[prim->n0]); gte_nccs(); gte_strgb2(&pkt->rgb); otp = &ot[g.otz >> shift]; pkt->tag = (*otp & 0xFFFFFF) | 0x09000000; *otp = (u32)pkt & 0xFFFFFF; pkt++; } } } n--; prim++; } return (u32 *)pkt; } typedef struct { s16 vx, vy, vz, pad; } SVEC8_80026128; typedef struct { /* 0x1C */ u32 unk00; u32 uv0; u32 uv1; u32 uv2; u16 n0, v0; u16 n1, v1; u16 n2, v2; } FaceGT3; typedef struct { /* 0x28 */ u32 tag; u32 rgb0, xy0, uv0; u32 rgb1, xy1, uv1; u32 rgb2, xy2, uv2; } PktGT3; extern u32 D_80063884; #define gte_ldrgb(r0) __asm__ volatile ( \ "lwc2 $6, 0( %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_rtpt() __asm__ volatile ("nop;nop;rtpt") #define gte_nclip() __asm__ volatile ("nop;nop;nclip") #define gte_avsz3() __asm__ volatile ("nop;nop;avsz3") #define gte_ncct() __asm__ volatile ("nop;nop;ncct") #define gte_stflg(r0) __asm__ volatile ( \ "cfc2 $12, $31;" \ "nop;" \ "sw $12, 0( %0 )" \ : \ : "r"( r0 ) \ : "$12", "memory" ) #define gte_stopz(r0) __asm__ volatile ( \ "swc2 $24, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stotz(r0) __asm__ volatile ( \ "swc2 $7, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_strgb3_gt3(r0, r1, r2) __asm__ volatile ( \ "swc2 $20, 0( %0 );" \ "swc2 $21, 0( %1 );" \ "swc2 $22, 0( %2 )" \ : \ : "r"( r0 ), "r"( r1 ), "r"( r2 ) \ : "memory" ) /* func_80026128 — main/800 — textured-GT3 (POLY_GT3) triangle emitter with per-vertex * normal-colour lighting. Sibling of the matched func_800262D8 in this TU (same loop * shape, same pkt->uvN = face->uvN copy); NOT a twin of the Gouraud-only func_800CBC2C. * Vertex type carries the function suffix per TU convention (cf. SVEC8_800262D8) since * gcc-2.7.2 rejects typedef redefinition and SVEC2 already exists at line 93. The SXY * store trio is written inline rather than as gte_stsxy3_gt3 because the TU already * binds that name to the 8/16/24 G3-shape macro further down (func_800277DC block); * offsets here are the true GT3 slot xy words 8/20/32. Splice: replace INCLUDE_ASM at * src/800.c:8983. Base macros above are byte-identical to the in-scope func_8002528C * block (lines 8001-8063); drop this duplicate set when pasting into the TU. * * NOTE: the target .s carries splat's "/* Handwritten function * /" marker (and marks * the bare `cfc2 $t4,$31` as a handwritten instruction). That label is a spimdisasm * heuristic firing on the raw cop2 discipline — the pinned cc1 REACHES all 108 words * from this C (oracle MATCH, repeatedly reproduced), unlike func_80027200 where the * same marker was genuine. If the whole-binary gate still refuses this byte-perfect * body, suspect the extract/prune layer special-casing Handwritten-marked .s files * (duplicate-symbol link), not this function's codegen. */ PktGT3 *func_80026128(FaceGT3 *face, SVEC8_80026128 *vtx, SVEC8_80026128 *nrm, PktGT3 *arg3, s32 count, s32 shift, u32 *ot) { long flag; long otz; u32 *otp; PktGT3 *pkt = arg3; gte_ldrgb(&D_80063884); while (count != 0) { gte_ldv3(&vtx[face->v0], &vtx[face->v1], &vtx[face->v2]); gte_rtpt(); pkt->uv0 = face->uv0; pkt->uv1 = face->uv1; pkt->uv2 = face->uv2; gte_stflg(&flag); if (!(flag & ~0x1000)) { gte_nclip(); gte_stopz(&otz); if (otz > 0) { __asm__ volatile ( "swc2 $12, 8( %0 );" "swc2 $13, 20( %0 );" "swc2 $14, 32( %0 )" : : "r"( pkt ) : "memory" ); gte_avsz3(); gte_stotz(&otz); gte_ldv3(&nrm[face->n0], &nrm[face->n1], &nrm[face->n2]); gte_ncct(); otp = &ot[otz >> shift]; pkt->tag = (*otp & 0x00FFFFFF) | 0x09000000; gte_strgb3_gt3(&pkt->rgb0, &pkt->rgb1, &pkt->rgb2); *otp = (u32)pkt & 0x00FFFFFF; pkt++; } } face++; count--; } return pkt; } /* func_800262D8 — main/800 — GT4 quad emitter with per-vertex normal-colour lighting. * GTE ops spelled as verbatim PsyQ inline_c.h macro bodies (project gas macros from * include/gte_macros.inc, pulled in via common.h -> include_asm.h -> macro.inc). */ #define gte_ldrgb(r0) __asm__ volatile ( \ "lwc2 $6, 0( %0 )" \ : \ : "r"( r0 ) ) #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_avsz4() __asm__ volatile ("nop;nop;avsz4") #define gte_ncct() __asm__ volatile ("nop;nop;ncct") #define gte_nccs() __asm__ volatile ("nop;nop;nccs") #define gte_stflg(r0) __asm__ volatile ( \ "cfc2 $12, $31;" \ "nop;" \ "sw $12, 0( %0 )" \ : \ : "r"( r0 ) \ : "$12", "memory" ) #define gte_stopz(r0) __asm__ volatile ( \ "swc2 $24, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stotz(r0) __asm__ volatile ( \ "swc2 $7, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stsxy(r0) __asm__ volatile ( \ "swc2 $14, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stsxy3_gt4(r0) __asm__ volatile ( \ "swc2 $12, 8( %0 );" \ "swc2 $13, 20( %0 );" \ "swc2 $14, 32( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_strgb(r0) __asm__ volatile ( \ "swc2 $22, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_strgb3(r0, r1, r2) __asm__ volatile ( \ "swc2 $20, 0( %0 );" \ "swc2 $21, 0( %1 );" \ "swc2 $22, 0( %2 )" \ : \ : "r"( r0 ), "r"( r1 ), "r"( r2 ) \ : "memory" ) typedef struct { s16 vx, vy, vz, pad; } SVEC8_800262D8; /* 0x08 */ typedef struct { /* 0x24 */ u32 unk00; u32 uv0; u32 uv1; u32 uv2; u32 uv3; u16 n0, v0; u16 n1, v1; u16 n2, v2; u16 n3, v3; } FaceGT4; typedef struct { /* 0x34 */ u32 tag; u32 rgb0, xy0, uv0; u32 rgb1, xy1, uv1; u32 rgb2, xy2, uv2; u32 rgb3, xy3, uv3; } PktGT4; extern u32 D_80063888; /* NOTE (§RC-10 preference cascade): `pkt` MUST be a body-local copy of the 4th * parameter. If the loop's biv IS the parameter pseudo, loop.c's record_initial * takes the biv's initial value straight from the incoming HARD reg, so the * preheader giv is emitted as `(set giv (plus (reg $a3) 0x28))` — that keeps $a3 * live to the preheader (biv pseudo CONFLICTS with $7) and hands the giv the $7 * set_preference. Result: giv->$a3, pkt->$t1 + a spurious `move t1,a3` (144 ins). * With the local copy the initial value is a PSEUDO: $a3 dies at the param copy, * pkt takes $a3 by preference, the giv falls to $t1, and the move disappears. */ PktGT4 *func_800262D8(FaceGT4 *face, SVEC8_800262D8 *vtx, SVEC8_800262D8 *nrm, PktGT4 *arg3, s32 count, s32 shift, u32 *ot) { long flag; long otz; u32 *otp; PktGT4 *pkt = arg3; gte_ldrgb(&D_80063888); while (count != 0) { gte_ldv3(&vtx[face->v0], &vtx[face->v1], &vtx[face->v2]); gte_rtpt(); pkt->uv0 = face->uv0; pkt->uv1 = face->uv1; pkt->uv2 = face->uv2; gte_stflg(&flag); if (!(flag & ~0x1000)) { gte_nclip(); gte_stopz(&otz); if (otz > 0) { gte_stsxy3_gt4(pkt); gte_ldv0(&vtx[face->v3]); gte_rtps(); pkt->uv3 = face->uv3; gte_stflg(&flag); if (!(flag & ~0x1000)) { gte_stsxy(&pkt->xy3); gte_avsz4(); gte_stotz(&otz); gte_ldv3(&nrm[face->n0], &nrm[face->n1], &nrm[face->n2]); gte_ncct(); otp = &ot[otz >> shift]; pkt->tag = (*otp & 0x00FFFFFF) | 0x0C000000; gte_strgb3(&pkt->rgb0, &pkt->rgb1, &pkt->rgb2); gte_ldv0(&nrm[face->n3]); gte_nccs(); gte_strgb(&pkt->rgb3); *otp = (u32)pkt & 0x00FFFFFF; pkt++; } } } face++; count--; } return pkt; } /* func_80026514 — main/800 — HANDWRITTEN assembly (splat marks it * "Handwritten function"). Textured GT3 triangle emitter, the GT3 sibling of the * neighbouring handwritten func_800268D0 (GT4) — same prologue, same D_8006388C / * D_8006389C table walk, same D_800A2B78 semi-transparency patch. * * Not reachable from gcc-2.7.2 codegen: the D_800B99DA fetch is a gas macro * expansion of `lbu $v1, 41898($t2)` (lui $at,1 / addu $at,$t2,$at / * lbu $v1,-23638($at)) — the compiler never materialises $at, and it would fold * symbol+constant into a single %hi/%lo pair instead. Add the hand-placed cop2 * latency nops and the $t0-$t9 + $s0-$s7 loop-carried discipline and the body is * reproduced verbatim as one .set noreorder asm block (same idiom as * func_800249F0 / func_80027200 / func_800268D0 in this TU); gcc supplies only * the jr $ra + delay nop. * * Symbols referenced by the block (D_800AF630, D_8006388C, D_8006389C, * D_800A2B78, D_80063884) are resolved by gas/ld directly — deliberately NO new * C declarations, so this cannot conflict with the TU's existing ones (§376/§378). */ void func_80026514() { __asm__ __volatile__( ".set\tnoreorder\n" "addiu $sp, $sp, -0x30\n" "lw $t6, 64($sp)\n" "lw $t0, 76($sp)\n" "addu $t1, $a0, $zero\n" "sw $s0, 16($sp)\n" "addu $s0, $a1, $zero\n" "sw $s6, 40($sp)\n" "lw $s6, 68($sp)\n" "lui $t2, %hi(D_800AF630)\n" "addiu $t2, $t2, %lo(D_800AF630)\n" "sw $s5, 36($sp)\n" "lw $s5, 72($sp)\n" "lui $v0, %hi(D_8006388C)\n" "addiu $v0, $v0, %lo(D_8006388C)\n" "sw $s7, 44($sp)\n" "sw $s4, 32($sp)\n" "sw $s3, 28($sp)\n" "sw $s2, 24($sp)\n" "sw $s1, 20($sp)\n" "sll $a0, $t0, 3\n" "lui $at, %hi(D_8006389C)\n" "addu $at, $at, $t0\n" "lbu $v1, %lo(D_8006389C)($at)\n" "addu $t9, $a0, $v0\n" "addiu $v0, $v1, 0x100\n" "sltiu $v1, $v1, 0xE0\n" "beqz $v1, 1f\n" "sll $v0, $v0, 6\n" "j 2f\n" "ori $s3, $v0, 0x16\n" "1:\n" "ori $s3, $v0, 0x10\n" "2:\n" "lui $a0, %hi(D_800A2B78)\n" "lw $a0, %lo(D_800A2B78)($a0)\n" "lhu $a1, 2($t9)\n" "lhu $v0, 0($t9)\n" "andi $a0, $a0, 0x3\n" "sll $a0, $a0, 5\n" "srl $v1, $a1, 4\n" "andi $v1, $v1, 0x10\n" "or $a0, $a0, $v1\n" "andi $v0, $v0, 0x3FF\n" "srl $v0, $v0, 6\n" "or $a0, $a0, $v0\n" "andi $a1, $a1, 0x200\n" "sll $a1, $a1, 2\n" "lui $at, 0x1\n" "addu $at, $t2, $at\n" "lbu $v1, -23638($at)\n" "or $s4, $a0, $a1\n" "bnez $t0, 3f\n" "addu $a0, $v1, $zero\n" "lw $v0, 4($t1)\n" "nop\n" "sw $v0, 0($sp)\n" "addiu $v0, $zero, 0x34\n" "sb $v0, 3($sp)\n" "lwc2 $6, 0($sp)\n" "j 4f\n" "nop\n" "3:\n" "lui $s7, %hi(D_80063884)\n" "addiu $s7, $s7, %lo(D_80063884)\n" "lwc2 $6, 0($s7)\n" "4:\n" "beqz $t6, 5f\n" "addu $v0, $a3, $zero\n" "addiu $s2, $sp, 0xC\n" "andi $t8, $a0, 0xFF\n" "andi $t7, $v1, 0xFF\n" "lui $s1, 0xFF\n" "ori $s1, $s1, 0xFFFF\n" "addiu $t1, $t1, 0x10\n" "addiu $t0, $a3, 0x25\n" "6:\n" "lhu $a0, -6($t1)\n" "lhu $v1, -2($t1)\n" "lhu $v0, 2($t1)\n" "sll $a0, $a0, 3\n" "addu $a0, $s0, $a0\n" "sll $v1, $v1, 3\n" "addu $v1, $s0, $v1\n" "sll $v0, $v0, 3\n" "addu $v0, $s0, $v0\n" "lwc2 $0, 0($a0)\n" "lwc2 $1, 4($a0)\n" "lwc2 $2, 0($v1)\n" "lwc2 $3, 4($v1)\n" "lwc2 $4, 0($v0)\n" "lwc2 $5, 4($v0)\n" "nop\n" "nop\n" "rtpt\n" "addiu $v0, $sp, 0x8\n" "cfc2 $t4, $31\n" "nop\n" "sw $t4, 0($v0)\n" "lw $v0, 8($sp)\n" "addiu $v1, $zero, -0x1001\n" "and $v0, $v0, $v1\n" "bnez $v0, 7f\n" "nop\n" "nop\n" "nop\n" "nclip\n" "swc2 $24, 0($s2)\n" "lw $v0, 12($sp)\n" "nop\n" "blez $v0, 7f\n" "nop\n" "swc2 $12, 8($a3)\n" "swc2 $13, 20($a3)\n" "swc2 $14, 32($a3)\n" "nop\n" "nop\n" "avsz3\n" "swc2 $7, 0($s2)\n" "lhu $a0, -8($t1)\n" "lhu $v1, -4($t1)\n" "lhu $v0, 0($t1)\n" "sll $a0, $a0, 3\n" "addu $a0, $a2, $a0\n" "sll $v1, $v1, 3\n" "addu $v1, $a2, $v1\n" "sll $v0, $v0, 3\n" "addu $v0, $a2, $v0\n" "lwc2 $0, 0($a0)\n" "lwc2 $1, 4($a0)\n" "lwc2 $2, 0($v1)\n" "lwc2 $3, 4($v1)\n" "lwc2 $4, 0($v0)\n" "lwc2 $5, 4($v0)\n" "nop\n" "nop\n" "ncct\n" "addiu $a0, $a3, 0x4\n" "addiu $v1, $a3, 0x10\n" "addiu $v0, $a3, 0x1C\n" "swc2 $20, 0($a0)\n" "swc2 $21, 0($v1)\n" "swc2 $22, 0($v0)\n" "lhu $v0, -29($t0)\n" "lbu $a1, 0($t9)\n" "lhu $v1, -27($t0)\n" "sll $v0, $v0, 16\n" "sra $a0, $v0, 16\n" "srl $v0, $v0, 31\n" "addu $a0, $a0, $v0\n" "sra $a0, $a0, 1\n" "addu $a0, $t8, $a0\n" "andi $a0, $a0, 0x1F\n" "sll $a1, $a1, 2\n" "andi $a1, $a1, 0xFF\n" "addu $t5, $a0, $a1\n" "sll $v1, $v1, 16\n" "sra $v0, $v1, 16\n" "srl $v1, $v1, 31\n" "addu $v0, $v0, $v1\n" "sra $v0, $v0, 1\n" "addu $v0, $t7, $v0\n" "andi $v0, $v0, 0x1F\n" "lhu $a0, 2($t9)\n" "lhu $v1, -17($t0)\n" "addu $t4, $a0, $v0\n" "sll $v1, $v1, 16\n" "sra $v0, $v1, 16\n" "srl $v1, $v1, 31\n" "addu $v0, $v0, $v1\n" "sra $v0, $v0, 1\n" "addu $v0, $t8, $v0\n" "andi $v0, $v0, 0x1F\n" "lhu $v1, -15($t0)\n" "addu $t3, $v0, $a1\n" "sll $v1, $v1, 16\n" "sra $v0, $v1, 16\n" "srl $v1, $v1, 31\n" "addu $v0, $v0, $v1\n" "sra $v0, $v0, 1\n" "addu $v0, $t7, $v0\n" "andi $v0, $v0, 0x1F\n" "lhu $v1, -5($t0)\n" "addu $t2, $a0, $v0\n" "sll $v1, $v1, 16\n" "sra $v0, $v1, 16\n" "srl $v1, $v1, 31\n" "addu $v0, $v0, $v1\n" "sra $v0, $v0, 1\n" "addu $v0, $t8, $v0\n" "andi $v0, $v0, 0x1F\n" "lhu $v1, -3($t0)\n" "addu $a1, $v0, $a1\n" "sll $v1, $v1, 16\n" "sra $v0, $v1, 16\n" "srl $v1, $v1, 31\n" "addu $v0, $v0, $v1\n" "sra $v0, $v0, 1\n" "addu $v0, $t7, $v0\n" "andi $v0, $v0, 0x1F\n" "lui $v1, %hi(D_800A2B78)\n" "lw $v1, %lo(D_800A2B78)($v1)\n" "nop\n" "beqz $v1, 8f\n" "addu $a0, $a0, $v0\n" "lbu $v0, -30($t0)\n" "nop\n" "ori $v0, $v0, 0x2\n" "sb $v0, -30($t0)\n" "8:\n" "sb $t5, -25($t0)\n" "sb $t4, -24($t0)\n" "sb $t3, -13($t0)\n" "sb $t2, -12($t0)\n" "sb $a1, -1($t0)\n" "sb $a0, 0($t0)\n" "lw $a0, 12($sp)\n" "sh $s3, -23($t0)\n" "sh $s4, -11($t0)\n" "addiu $t0, $t0, 0x28\n" "srav $a0, $a0, $s6\n" "sll $a0, $a0, 2\n" "addu $a0, $s5, $a0\n" "lw $v1, 0($a0)\n" "lui $v0, 0x900\n" "and $v1, $v1, $s1\n" "or $v1, $v1, $v0\n" "and $v0, $a3, $s1\n" "sw $v1, 0($a3)\n" "addiu $a3, $a3, 0x28\n" "sw $v0, 0($a0)\n" "7:\n" "addiu $t6, $t6, -0x1\n" "bnez $t6, 6b\n" "addiu $t1, $t1, 0x14\n" "addu $v0, $a3, $zero\n" "5:\n" "lw $s7, 44($sp)\n" "lw $s6, 40($sp)\n" "lw $s5, 36($sp)\n" "lw $s4, 32($sp)\n" "lw $s3, 28($sp)\n" "lw $s2, 24($sp)\n" "lw $s1, 20($sp)\n" "lw $s0, 16($sp)\n" "addiu $sp, $sp, 0x30\n" ".set\treorder\n" ); } void func_800268D0() { __asm__ __volatile__( ".set\tnoreorder\n" "addiu $sp, $sp, -64\n" "addu $t1, $a0, $zero\n" "addu $t9, $a1, $zero\n" "lui $t2, %hi(D_800AF630)\n" "addiu $t2, $t2, %lo(D_800AF630)\n" "sw $s1, 28($sp)\n" "lw $s1, 80($sp)\n" "lw $t0, 92($sp)\n" "lui $v0, %hi(D_8006388C)\n" "addiu $v0, $v0, %lo(D_8006388C)\n" "sw $fp, 56($sp)\n" "sw $s7, 52($sp)\n" "sw $s6, 48($sp)\n" "sw $s5, 44($sp)\n" "sw $s4, 40($sp)\n" "sw $s3, 36($sp)\n" "sw $s2, 32($sp)\n" "sw $s0, 24($sp)\n" "sll $a0, $t0, 3\n" "lui $at, %hi(D_8006389C)\n" "addu $at, $at, $t0\n" "lbu $v1, %lo(D_8006389C)($at)\n" "addu $s2, $a0, $v0\n" "addiu $v0, $v1, 256\n" "sltiu $v1, $v1, 224\n" "beqz $v1, 1f\n" "sll $v0, $v0, 6\n" "j 2f\n" "ori $s6, $v0, 22\n" "1:\n" "ori $s6, $v0, 16\n" "2:\n" "lui $a0, %hi(D_800A2B78)\n" "lw $a0, %lo(D_800A2B78)($a0)\n" "lhu $a1, 2($s2)\n" "lhu $v0, 0($s2)\n" "andi $a0, $a0, 3\n" "sll $a0, $a0, 5\n" "srl $v1, $a1, 4\n" "andi $v1, $v1, 16\n" "or $a0, $a0, $v1\n" "andi $v0, $v0, 1023\n" "srl $v0, $v0, 6\n" "or $a0, $a0, $v0\n" "andi $a1, $a1, 512\n" "sll $a1, $a1, 2\n" "lui $at, 1\n" "addu $at, $t2, $at\n" "lbu $v1, -23638($at)\n" "or $fp, $a0, $a1\n" "bnez $t0, 3f\n" "addu $a0, $v1, $zero\n" "lw $v0, 4($t1)\n" "nop\n" "sw $v0, 0($sp)\n" "addiu $v0, $zero, 60\n" "sb $v0, 3($sp)\n" "lwc2 $6, 0($sp)\n" "j 4f\n" "nop\n" "3:\n" "lui $t3, %hi(D_80063888)\n" "addiu $t3, $t3, %lo(D_80063888)\n" "lwc2 $6, 0($t3)\n" "4:\n" "beqz $s1, 5f\n" "addu $v0, $a3, $zero\n" "addiu $s5, $sp, 8\n" "addiu $s7, $zero, -4097\n" "addiu $s4, $sp, 12\n" "andi $t6, $a0, 255\n" "andi $t5, $v1, 255\n" "lui $s3, 255\n" "ori $s3, $s3, 65535\n" "addiu $t1, $t1, 20\n" "addiu $t0, $a3, 49\n" "6:\n" "lhu $a0, -10($t1)\n" "lhu $v1, -6($t1)\n" "lhu $v0, -2($t1)\n" "sll $a0, $a0, 3\n" "addu $a0, $t9, $a0\n" "sll $v1, $v1, 3\n" "addu $v1, $t9, $v1\n" "sll $v0, $v0, 3\n" "addu $v0, $t9, $v0\n" "lwc2 $0, 0($a0)\n" "lwc2 $1, 4($a0)\n" "lwc2 $2, 0($v1)\n" "lwc2 $3, 4($v1)\n" "lwc2 $4, 0($v0)\n" "lwc2 $5, 4($v0)\n" "nop\n" "nop\n" "rtpt\n" "cfc2 $t4, $31\n" "nop\n" "sw $t4, 0($s5)\n" "lw $v0, 8($sp)\n" "nop\n" "and $v0, $v0, $s7\n" "bnez $v0, 7f\n" "nop\n" "nop\n" "nop\n" "nclip\n" "swc2 $24, 0($s4)\n" "lw $v0, 12($sp)\n" "nop\n" "blez $v0, 7f\n" "nop\n" "swc2 $12, 8($a3)\n" "swc2 $13, 20($a3)\n" "swc2 $14, 32($a3)\n" "lhu $v0, 2($t1)\n" "nop\n" "sll $v0, $v0, 3\n" "addu $v0, $t9, $v0\n" "lwc2 $0, 0($v0)\n" "lwc2 $1, 4($v0)\n" "nop\n" "nop\n" "rtps\n" "cfc2 $t4, $31\n" "nop\n" "sw $t4, 0($s5)\n" "lw $v0, 8($sp)\n" "nop\n" "and $v0, $v0, $s7\n" "bnez $v0, 7f\n" "addiu $v0, $a3, 44\n" "swc2 $14, 0($v0)\n" "nop\n" "nop\n" "avsz4\n" "swc2 $7, 0($s4)\n" "lhu $a0, -12($t1)\n" "lhu $v1, -8($t1)\n" "lhu $v0, -4($t1)\n" "sll $a0, $a0, 3\n" "addu $a0, $a2, $a0\n" "sll $v1, $v1, 3\n" "addu $v1, $a2, $v1\n" "sll $v0, $v0, 3\n" "addu $v0, $a2, $v0\n" "lwc2 $0, 0($a0)\n" "lwc2 $1, 4($a0)\n" "lwc2 $2, 0($v1)\n" "lwc2 $3, 4($v1)\n" "lwc2 $4, 0($v0)\n" "lwc2 $5, 4($v0)\n" "nop\n" "nop\n" "ncct\n" "addiu $a0, $a3, 4\n" "addiu $v1, $a3, 16\n" "addiu $v0, $a3, 28\n" "swc2 $20, 0($a0)\n" "swc2 $21, 0($v1)\n" "swc2 $22, 0($v0)\n" "lhu $v0, 0($t1)\n" "nop\n" "sll $v0, $v0, 3\n" "addu $v0, $a2, $v0\n" "lwc2 $0, 0($v0)\n" "lwc2 $1, 4($v0)\n" "nop\n" "nop\n" "nccs\n" "addiu $v0, $a3, 40\n" "swc2 $22, 0($v0)\n" "lhu $v0, -41($t0)\n" "lbu $a1, 0($s2)\n" "lhu $v1, -39($t0)\n" "sll $v0, $v0, 16\n" "sra $a0, $v0, 16\n" "srl $v0, $v0, 31\n" "addu $a0, $a0, $v0\n" "sra $a0, $a0, 1\n" "addu $a0, $t6, $a0\n" "andi $a0, $a0, 31\n" "sll $a1, $a1, 2\n" "andi $a1, $a1, 255\n" "addu $s0, $a0, $a1\n" "sll $v1, $v1, 16\n" "sra $v0, $v1, 16\n" "srl $v1, $v1, 31\n" "addu $v0, $v0, $v1\n" "sra $v0, $v0, 1\n" "addu $v0, $t5, $v0\n" "andi $v0, $v0, 31\n" "lhu $a0, 2($s2)\n" "lhu $v1, -29($t0)\n" "addu $t8, $a0, $v0\n" "sll $v1, $v1, 16\n" "sra $v0, $v1, 16\n" "srl $v1, $v1, 31\n" "addu $v0, $v0, $v1\n" "sra $v0, $v0, 1\n" "addu $v0, $t6, $v0\n" "andi $v0, $v0, 31\n" "lhu $v1, -27($t0)\n" "addu $t7, $v0, $a1\n" "sll $v1, $v1, 16\n" "sra $v0, $v1, 16\n" "srl $v1, $v1, 31\n" "addu $v0, $v0, $v1\n" "sra $v0, $v0, 1\n" "addu $v0, $t5, $v0\n" "andi $v0, $v0, 31\n" "lhu $v1, -17($t0)\n" "addu $t4, $a0, $v0\n" "sll $v1, $v1, 16\n" "sra $v0, $v1, 16\n" "srl $v1, $v1, 31\n" "addu $v0, $v0, $v1\n" "sra $v0, $v0, 1\n" "addu $v0, $t6, $v0\n" "andi $v0, $v0, 31\n" "lhu $v1, -15($t0)\n" "addu $v0, $v0, $a1\n" "sh $v0, 16($sp)\n" "sll $v1, $v1, 16\n" "sra $v0, $v1, 16\n" "srl $v1, $v1, 31\n" "addu $v0, $v0, $v1\n" "sra $v0, $v0, 1\n" "addu $v0, $t5, $v0\n" "andi $v0, $v0, 31\n" "lhu $v1, -5($t0)\n" "addu $t2, $a0, $v0\n" "sll $v1, $v1, 16\n" "sra $v0, $v1, 16\n" "srl $v1, $v1, 31\n" "addu $v0, $v0, $v1\n" "sra $v0, $v0, 1\n" "addu $v0, $t6, $v0\n" "andi $v0, $v0, 31\n" "lhu $v1, -3($t0)\n" "addu $a1, $v0, $a1\n" "sll $v1, $v1, 16\n" "sra $v0, $v1, 16\n" "srl $v1, $v1, 31\n" "addu $v0, $v0, $v1\n" "sra $v0, $v0, 1\n" "addu $v0, $t5, $v0\n" "andi $v0, $v0, 31\n" "lui $v1, %hi(D_800A2B78)\n" "lw $v1, %lo(D_800A2B78)($v1)\n" "nop\n" "beqz $v1, 8f\n" "addu $a0, $a0, $v0\n" "lbu $v0, -42($t0)\n" "nop\n" "ori $v0, $v0, 2\n" "sb $v0, -42($t0)\n" "8:\n" "sb $s0, -37($t0)\n" "sb $t8, -36($t0)\n" "sb $t7, -25($t0)\n" "sb $t4, -24($t0)\n" "lbu $t3, 16($sp)\n" "sb $t2, -12($t0)\n" "sb $a1, -1($t0)\n" "sb $a0, 0($t0)\n" "sb $t3, -13($t0)\n" "lw $a0, 12($sp)\n" "sh $s6, -35($t0)\n" "sh $fp, -23($t0)\n" "lw $t3, 84($sp)\n" "lui $v0, 3072\n" "srav $a0, $a0, $t3\n" "lw $t3, 88($sp)\n" "sll $a0, $a0, 2\n" "addu $a0, $t3, $a0\n" "lw $v1, 0($a0)\n" "addiu $t0, $t0, 52\n" "and $v1, $v1, $s3\n" "or $v1, $v1, $v0\n" "and $v0, $a3, $s3\n" "sw $v1, 0($a3)\n" "addiu $a3, $a3, 52\n" "sw $v0, 0($a0)\n" "7:\n" "addiu $s1, $s1, -1\n" "bnez $s1, 6b\n" "addiu $t1, $t1, 24\n" "addu $v0, $a3, $zero\n" "5:\n" "lw $fp, 56($sp)\n" "lw $s7, 52($sp)\n" "lw $s6, 48($sp)\n" "lw $s5, 44($sp)\n" "lw $s4, 40($sp)\n" "lw $s3, 36($sp)\n" "lw $s2, 32($sp)\n" "lw $s1, 28($sp)\n" "lw $s0, 24($sp)\n" "addiu $sp, $sp, 64\n" ".set\treorder\n" ); } extern u8 *D_800A5E60; /* The five packet emitters live LATER in this TU with return types this function * cannot spell here (func_80027058/func_80027200 are `void` asm-block wrappers and * func_800279AC returns `LineG4 *`, a typedef declared below the INCLUDE_ASM site). * The §183 __asm__-label alias binds a locally-typed name to the real symbol, so the * call site keeps its `u8 *` result with no declaration conflict at the definitions. */ extern u8 *func_800273F4(); extern void *func_800275BC(); extern void *func_800277DC(); extern u8 *aF80027058() __asm__("func_80027058"); extern u8 *aF80027200() __asm__("func_80027200"); extern u8 *aF800279AC() __asm__("func_800279AC"); /* The per-case `p += count * K` (rather than one shared `adv` temp assigned in every * arm) is LOAD-BEARING, not style. With a shared temp the switch end-label is a real * source label, so jump.c:1993's jump_chain path (`find_cross_jump(..., minimum=2)`) * pairs every `j` to it with every other and tail-merges five block groups that the * target keeps separate (-23 instructions). Advancing `p` inside each arm makes the * shared `addu $s0,$s0,$v0` a CROSS-JUMP-CREATED label (`get_label_before`), whose * INSN_UID is >= max_uid — and jump.c:1988 guards the jump-to-jump search with * `INSN_UID (JUMP_LABEL (insn)) < max_uid`, so it never runs. Only the minimum=1 * "code before my own target label" path survives, which is exactly the one merge the * target does have: .L8002700C, the 0x28-flag arm folded into the 0x2D/0x2C tail. */ void func_80026D64(s32 arg0, s32 arg1, s32 arg2) { u32 *hdr; u8 *p; s32 n; u8 *vtx; u8 *nrm; u32 w; s32 flg; s32 code; hdr = *(u32 **)(arg0 + 8); p = (u8 *)hdr[4]; n = hdr[5]; vtx = (u8 *)hdr[0]; nrm = (u8 *)hdr[2]; if (n == 0) { return; } do { w = *(u32 *)p; flg = (w >> 16) & 4; code = (w >> 24) & 0xFD; switch (code) { case 0x20: if (flg != 0) { D_800A5E60 = func_800273F4(p, vtx, nrm, D_800A5E60, *(u16 *)p, arg2, *(u32 **)(arg1 + 4)); n -= *(u16 *)p; p += *(u16 *)p * 0x18; } else { D_800A5E60 = aF80027058(p, vtx, nrm, D_800A5E60, *(u16 *)p, arg2, *(u32 **)(arg1 + 4)); n -= *(u16 *)p; p += *(u16 *)p * 0x10; } break; case 0x28: if (flg != 0) { D_800A5E60 = (u8 *)func_800275BC(p, vtx, nrm, D_800A5E60, *(u16 *)p, arg2, *(u32 **)(arg1 + 4)); n -= *(u16 *)p; p += *(u16 *)p * 0x20; } else { D_800A5E60 = aF80027200(p, vtx, nrm, D_800A5E60, *(u16 *)p, arg2, *(u32 **)(arg1 + 4)); n -= *(u16 *)p; p += *(u16 *)p * 0x14; } break; case 0x24: n -= *(u16 *)p; p += *(u16 *)p * 0x18; break; case 0x30: n -= *(u16 *)p; p += *(u16 *)p * 0x14; break; case 0x38: n -= *(u16 *)p; p += *(u16 *)p * 0x18; break; case 0x34: n -= *(u16 *)p; p += *(u16 *)p * 0x1C; break; case 0x3C: n -= *(u16 *)p; p += *(u16 *)p * 0x24; break; case 0x25: D_800A5E60 = (u8 *)func_800277DC(p, vtx, nrm, D_800A5E60, *(u16 *)p, arg2, *(u32 **)(arg1 + 4)); n -= *(u16 *)p; p += *(u16 *)p * 0x1C; break; case 0x2D: D_800A5E60 = aF800279AC(p, vtx, nrm, D_800A5E60, *(u16 *)p, arg2, *(u32 **)(arg1 + 4)); n -= *(u16 *)p; p += *(u16 *)p * 0x20; break; case 0x2C: n -= *(u16 *)p; p += *(u16 *)p * 0x20; break; } } while (n != 0); } extern s32 D_800A2B78; void func_80027058() { __asm__ __volatile__( ".set\tnoreorder\n" "addiu $29, $29, -8\n" "lw $11, 24($29)\n" "lw $15, 28($29)\n" "lw $14, 32($29)\n" "beqz $11, 100f\n" "addu $2, $7, $0\n" "addiu $13, $29, 4\n" "lui $10, 0xFF\n" "ori $10, $10, 0xFFFF\n" "addiu $9, $4, 4\n" "addiu $8, $7, 4\n" "98:\n" "lhu $4, 6($9)\n" "lhu $3, 8($9)\n" "lhu $2, 10($9)\n" "sll $4, $4, 3\n" "addu $4, $5, $4\n" "sll $3, $3, 3\n" "addu $3, $5, $3\n" "sll $2, $2, 3\n" "addu $2, $5, $2\n" "lwc2 $0, 0($4)\n" "lwc2 $1, 4($4)\n" "lwc2 $2, 0($3)\n" "lwc2 $3, 4($3)\n" "lwc2 $4, 0($2)\n" "lwc2 $5, 4($2)\n" "nop\n" "nop\n" "rtpt\n" "cfc2 $12, $31\n" "nop\n" "sw $12, 0($29)\n" "lw $2, 0($29)\n" "addiu $3, $0, -4097\n" "and $2, $2, $3\n" "bnez $2, 99f\n" "nop\n" "nop\n" "nop\n" "nclip\n" "swc2 $24, 0($13)\n" "lw $2, 4($29)\n" "nop\n" "blez $2, 99f\n" "nop\n" "swc2 $12, 8($7)\n" "swc2 $13, 12($7)\n" "swc2 $14, 16($7)\n" "nop\n" "nop\n" "avsz3\n" "swc2 $7, 0($13)\n" "lw $2, 0($9)\n" "nop\n" "sw $2, 0($8)\n" "lw $2, 4($29)\n" "addiu $8, $8, 20\n" "srav $2, $2, $15\n" "sll $2, $2, 2\n" "addu $6, $14, $2\n" "lw $3, 0($6)\n" "lui $2, 0x400\n" "and $3, $3, $10\n" "or $3, $3, $2\n" "and $2, $7, $10\n" "sw $3, 0($7)\n" "sw $2, 0($6)\n" "lui $2, %hi(D_800A2B78)\n" "lw $2, %lo(D_800A2B78)($2)\n" "nop\n" "beqz $2, 99f\n" "addiu $7, $7, 20\n" "lbu $2, -17($8)\n" "nop\n" "andi $2, $2, 0x2\n" "beqz $2, 99f\n" "lui $4, 0xE100\n" "ori $4, $4, 0xA\n" "lw $3, 4($29)\n" "addiu $2, $0, 1\n" "sb $2, -1($8)\n" "lui $2, %hi(D_800A2B78)\n" "lw $2, %lo(D_800A2B78)($2)\n" "srav $3, $3, $15\n" "sll $3, $3, 2\n" "addu $6, $14, $3\n" "andi $2, $2, 0x3\n" "sll $2, $2, 5\n" "or $2, $2, $4\n" "sw $2, 0($8)\n" "addiu $8, $8, 8\n" "lw $3, 0($6)\n" "lui $2, 0x100\n" "and $3, $3, $10\n" "or $3, $3, $2\n" "and $2, $7, $10\n" "sw $3, 0($7)\n" "addiu $7, $7, 8\n" "sw $2, 0($6)\n" "99:\n" "addiu $11, $11, -1\n" "bnez $11, 98b\n" "addiu $9, $9, 16\n" "addu $2, $7, $0\n" "100:\n" "addiu $29, $29, 8\n" ".set\treorder\n" ); } /* func_80027200 — HANDWRITTEN assembly (splat marks it "Handwritten function"). * The register discipline (loop counter in $t3, live args in $a0/$a1/$a3, no * callee-saved registers at all, raw GTE cop2 ops with hand-placed latency nops) * is not reachable from gcc-2.7.2 codegen, so the body is reproduced verbatim as * a single .set noreorder asm block. gcc supplies only the `jr $ra` + delay nop. */ extern s32 D_800A2B78; void func_80027200(void) { __asm__ __volatile__( ".set\tnoreorder\n" "addiu $sp, $sp, -0x8\n" "lw $t3, 24($sp)\n" "lw $t8, 28($sp)\n" "lw $t7, 32($sp)\n" "beq $t3, $zero, 3f\n" "addu $v0, $a3, $zero\n" "addiu $t6, $zero, -0x1001\n" "addiu $t5, $sp, 0x4\n" "lui $t2, 0xFF\n" "ori $t2, $t2, 0xFFFF\n" "addiu $t1, $a0, 0x4\n" "addiu $t0, $a3, 0x4\n" "1:\n" "lhu $a0, 6($t1)\n" "lhu $v1, 8($t1)\n" "lhu $v0, 10($t1)\n" "sll $a0, $a0, 3\n" "addu $a0, $a1, $a0\n" "sll $v1, $v1, 3\n" "addu $v1, $a1, $v1\n" "sll $v0, $v0, 3\n" "addu $v0, $a1, $v0\n" "lwc2 $0, 0($a0)\n" "lwc2 $1, 4($a0)\n" "lwc2 $2, 0($v1)\n" "lwc2 $3, 4($v1)\n" "lwc2 $4, 0($v0)\n" "lwc2 $5, 4($v0)\n" "nop\n" "nop\n" "rtpt\n" "cfc2 $t4, $31\n" "nop\n" "sw $t4, 0($sp)\n" "lw $v0, 0($sp)\n" "nop\n" "and $v0, $v0, $t6\n" "bne $v0, $zero, 2f\n" "nop\n" "nop\n" "nop\n" "nclip\n" "swc2 $24, 0($t5)\n" "lw $v0, 4($sp)\n" "nop\n" "blez $v0, 2f\n" "nop\n" "swc2 $12, 8($a3)\n" "swc2 $13, 12($a3)\n" "swc2 $14, 16($a3)\n" "lhu $v0, 12($t1)\n" "nop\n" "sll $v0, $v0, 3\n" "addu $v0, $a1, $v0\n" "lwc2 $0, 0($v0)\n" "lwc2 $1, 4($v0)\n" "nop\n" "nop\n" "rtps\n" "cfc2 $t4, $31\n" "nop\n" "sw $t4, 0($sp)\n" "lw $v0, 0($sp)\n" "nop\n" "and $v0, $v0, $t6\n" "bne $v0, $zero, 2f\n" "addiu $v0, $a3, 0x14\n" "swc2 $14, 0($v0)\n" "nop\n" "nop\n" "avsz4\n" "swc2 $7, 0($t5)\n" "lw $v0, 0($t1)\n" "nop\n" "sw $v0, 0($t0)\n" "lw $v0, 4($sp)\n" "addiu $t0, $t0, 0x18\n" "srav $v0, $v0, $t8\n" "sll $v0, $v0, 2\n" "addu $a2, $t7, $v0\n" "lw $v1, 0($a2)\n" "lui $v0, 0x500\n" "and $v1, $v1, $t2\n" "or $v1, $v1, $v0\n" "and $v0, $a3, $t2\n" "sw $v1, 0($a3)\n" "sw $v0, 0($a2)\n" "lui $v0, %hi(D_800A2B78)\n" "lw $v0, %lo(D_800A2B78)($v0)\n" "nop\n" "beq $v0, $zero, 2f\n" "addiu $a3, $a3, 0x18\n" "lbu $v0, -21($t0)\n" "nop\n" "andi $v0, $v0, 0x2\n" "beq $v0, $zero, 2f\n" "lui $a0, 0xE100\n" "ori $a0, $a0, 0xA\n" "lw $v1, 4($sp)\n" "addiu $v0, $zero, 0x1\n" "sb $v0, -1($t0)\n" "lui $v0, %hi(D_800A2B78)\n" "lw $v0, %lo(D_800A2B78)($v0)\n" "srav $v1, $v1, $t8\n" "sll $v1, $v1, 2\n" "addu $a2, $t7, $v1\n" "andi $v0, $v0, 0x3\n" "sll $v0, $v0, 5\n" "or $v0, $v0, $a0\n" "sw $v0, 0($t0)\n" "addiu $t0, $t0, 0x8\n" "lw $v1, 0($a2)\n" "lui $v0, 0x100\n" "and $v1, $v1, $t2\n" "or $v1, $v1, $v0\n" "and $v0, $a3, $t2\n" "sw $v1, 0($a3)\n" "addiu $a3, $a3, 0x8\n" "sw $v0, 0($a2)\n" "2:\n" "addiu $t3, $t3, -0x1\n" "bne $t3, $zero, 1b\n" "addiu $t1, $t1, 0x14\n" "addu $v0, $a3, $zero\n" "3:\n" "addiu $sp, $sp, 0x8\n" ".set\treorder\n" ); } /* ---- PsyQ inline_c.h GTE macros ---- */ #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_rtpt() __asm__ volatile ("nop;nop;rtpt") #define gte_nclip() __asm__ volatile ("nop;nop;nclip") #define gte_avsz3() __asm__ volatile ("nop;nop;avsz3") #define gte_stflg(r0) __asm__ volatile ( \ "cfc2 $12, $31;" \ "nop;" \ "sw $12, 0( %0 )" \ : \ : "r"( r0 ) \ : "$12", "memory" ) #define gte_stopz(r0) __asm__ volatile ( \ "swc2 $24, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stotz(r0) __asm__ volatile ( \ "swc2 $7, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stsxy3_g3(r0) __asm__ volatile ( \ "swc2 $12, 8( %0 );" \ "swc2 $13, 16( %0 );" \ "swc2 $14, 24( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) extern s32 D_800A2B78; u8 *func_800273F4(TmdG3 *f, u8 *vtx, u8 *nrm, u8 *pkt, s32 n, s32 shift, u32 *ot) { s32 flag; s32 z; u32 *otp; if (n != 0) { __asm__("" : "=r"(pkt) : "0"(pkt)); do { gte_ldv3(vtx + f->v0 * 8, vtx + f->v1 * 8, vtx + f->v2 * 8); gte_rtpt(); gte_stflg(&flag); if ((flag & ~0x1000) == 0) { gte_nclip(); gte_stopz(&z); if (z > 0) { gte_stsxy3_g3(pkt); gte_avsz3(); gte_stotz(&z); *(u32 *)(pkt + 4) = f->rgb0; *(u32 *)(pkt + 0xC) = f->rgb1; *(u32 *)(pkt + 0x14) = f->rgb2; pkt[7] = (pkt[7] & 2) | 0x30; otp = ot + (z >> shift); *(u32 *)pkt = (*otp & 0xFFFFFF) | 0x6000000; *otp = (u32)pkt & 0xFFFFFF; pkt += 0x1C; if (D_800A2B78 != 0) { if (pkt[7 - 0x1C] & 2) { otp = ot + (z >> shift); pkt[3] = 1; *(u32 *)(pkt + 4) = ((D_800A2B78 & 3) << 5) | 0xE100000A; *(u32 *)pkt = (*otp & 0xFFFFFF) | 0x1000000; *otp = (u32)pkt & 0xFFFFFF; pkt += 8; } } } } n--; f++; } while (n != 0); } return pkt; } /* ---- PsyQ inline GTE macros (inline_c.h forms) ------------------------- */ #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;.word 0x4a180001") #define gte_rtpt() __asm__ volatile ("nop;nop;.word 0x4a280030") #define gte_nclip() __asm__ volatile ("nop;nop;.word 0x4b400006") #define gte_avsz4() __asm__ volatile ("nop;nop;.word 0x4b68002e") #define gte_stflg(r0) __asm__ volatile ( \ "cfc2 $12, $31;" \ "nop;" \ "sw $12, 0( %0 )" \ :: "r"(r0) : "$12", "memory") #define gte_stsxy2(r0) __asm__ volatile ( \ "swc2 $14, 0( %0 )" \ :: "r"(r0) : "memory") #define gte_stsxy3_g4(r0) __asm__ volatile ( \ "swc2 $12, 8( %0 );" \ "swc2 $13, 16( %0 );" \ "swc2 $14, 24( %0 )" \ :: "r"(r0) : "memory") #define gte_stotz(r0) __asm__ volatile ( \ "swc2 $7, 0( %0 )" \ :: "r"(r0) : "memory") #define gte_stopz(r0) __asm__ volatile ( \ "swc2 $24, 0( %0 )" \ :: "r"(r0) : "memory") /* ---- data ------------------------------------------------------------- */ extern s32 D_800A2B78; typedef struct { s16 vx, vy, vz, pad; } SVEC_800275BC; /* 8 bytes */ typedef struct { /* TMD G4 flat-normal primitive, 0x20 bytes */ u32 tag; /* 0x00 */ u32 c0; /* 0x04 */ u32 c1; /* 0x08 */ u32 c2; /* 0x0C */ u32 c3; /* 0x10 */ u16 n0; /* 0x14 */ u16 v0; /* 0x16 */ u16 v1; /* 0x18 */ u16 v2; /* 0x1A */ u16 v3; /* 0x1C */ u16 pad; /* 0x1E */ } TPRIM_800275BC; void *func_800275BC(TPRIM_800275BC *prim, SVEC_800275BC *vp, void *nrm, u8 *pk, s32 n, s32 shift, u32 *ot) { s32 flag; s32 otz; u32 *op; if (n == 0) { return pk; } do { gte_ldv3(&vp[prim->v0], &vp[prim->v1], &vp[prim->v2]); gte_rtpt(); gte_stflg(&flag); if ((flag & 0xFFFFEFFF) == 0) { gte_nclip(); gte_stopz(&otz); if (otz > 0) { gte_stsxy3_g4(pk); gte_ldv0(&vp[prim->v3]); gte_rtps(); gte_stflg(&flag); if ((flag & 0xFFFFEFFF) == 0) { gte_stsxy2(pk + 0x20); gte_avsz4(); gte_stotz(&otz); *(u32 *)(pk + 0x04) = prim->c0; *(u32 *)(pk + 0x0C) = prim->c1; *(u32 *)(pk + 0x14) = prim->c2; *(u32 *)(pk + 0x1C) = prim->c3; *(u8 *)(pk + 7) = (*(u8 *)(pk + 7) & 2) | 0x38; op = &ot[otz >> shift]; *(u32 *)pk = (*op & 0xFFFFFF) | 0x08000000; *op = (u32)pk & 0xFFFFFF; pk += 0x24; if (D_800A2B78 != 0) { if (*(u8 *)(pk - 0x1D) & 2) { op = &ot[otz >> shift]; *(u8 *)(pk + 3) = 1; *(u32 *)(pk + 4) = ((D_800A2B78 & 3) << 5) | 0xE100000A; *(u32 *)pk = (*op & 0xFFFFFF) | 0x01000000; *op = (u32)pk & 0xFFFFFF; pk += 8; } } } } } prim++; } while (--n != 0); return pk; } /* GTE inline-asm idioms, verbatim PsyQ inline_c.h macro bodies (see cookbook §-GTE and e.g. * src/ov_SC01_000/ov_SC01_000_jr_8017BEBC.c for the established shape of this idiom family). * NOTE gte_stsxy3_gt3: the target stores SXY0/1/2 at 8/16/24 off ONE base register ($a3, the * POLY_GT3 being built), not off three separate pointers -- that is the PsyQ `_gt3` variant. * Using the 3-pointer gte_stsxy3 costs three extra `addiu` and was the first agent's length drift. */ #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_rtpt() __asm__ volatile ("nop;nop;rtpt") #define gte_nclip() __asm__ volatile ("nop;nop;nclip") #define gte_stflg(r0) __asm__ volatile ( \ "cfc2 $12, $31;" \ "nop;" \ "sw $12, 0( %0 )" \ : \ : "r"( r0 ) \ : "$12", "memory" ) #define gte_stopz(r0) __asm__ volatile ( \ "swc2 $24, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stsxy3_gt3(r0) __asm__ volatile ( \ "swc2 $12, 8( %0 );" \ "swc2 $13, 16( %0 );" \ "swc2 $14, 24( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stsz3(r0, r1, r2) __asm__ volatile ( \ "swc2 $17, 0( %0 );" \ "swc2 $18, 0( %1 );" \ "swc2 $19, 0( %2 )" \ : \ : "r"( r0 ), "r"( r1 ), "r"( r2 ) \ : "memory" ) #define gte_avsz3() __asm__ volatile ("nop;nop;avsz3") #define gte_stotz(r0) __asm__ volatile ( \ "swc2 $7, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) extern s32 D_80078D88[]; typedef struct { s16 vx, vy, vz, pad; } SVECTOR800277DC; void *func_800277DC(void *a0, SVECTOR800277DC *a1, void *a2, u8 *a3, s32 count, s32 shift, s32 *ot) { /* The five GTE scratch slots MUST be ONE array, not five scalars. gcc-2.7.2 assigns a * stack slot at the point each addressable local's address is first taken, NOT in * declaration order; hoisting &otz/&sz0/&sz1 into pre-loop pointer temps (needed to put * their three `addiu $sp` ahead of the mask/t0/t1 setup, see below) therefore stole slot 0 * from `flag` and shifted the whole frame. One array pins the layout: * st[0]=flag @sp+0 st[1]=otz @sp+4 st[2]=sz0 @sp+8 st[3]=sz1 @sp+0xC st[4]=sz2 @sp+0x10 * &st[0] folds to plain $sp, which is why `flag` is touched as 0($sp) with no address reg. */ s32 st[5]; /* t0/t1 pins ARE load-bearing (unpinning them re-drifts to 124 ins): they reproduce the * hand-budgeted src/dst induction-variable allocation so every field offset stays a direct * displacement off $t0 / $t1. cnt/mask/shiftAmt/otp need no pin -- gcc lands them on * $t2/$t3/$t9/$t8 on its own, and pinning `otp` to $24 actively BREAKS the match (below). */ register u8 *t0 __asm__("$8"); register u8 *t1 __asm__("$9"); s32 cnt; s32 mask; s32 shiftAmt; s32 *otp; s32 *potz, *psz0, *psz1; s32 idx0, idx1, idx2; s32 idx; s32 m; cnt = count; shiftAmt = shift; otp = ot; if (cnt == 0) { return a3; } /* Explicit pre-loop pointer temps, ordered ahead of mask/t0/t1: loop-invariant motion * APPENDS hoisted insns after the preheader's own statements, so leaving these implicit * emitted them last (`lui/ori, addiu t0, addiu t1, addiu sp+4/8/0xC`) -- the target is the * reverse order. */ potz = &st[1]; psz0 = &st[2]; psz1 = &st[3]; mask = 0xFFFFFF; t0 = (u8 *)a0 + 3; t1 = a3 + 4; do { idx0 = *(u16 *)(t0 + 0x11); idx1 = *(u16 *)(t0 + 0x13); idx2 = *(u16 *)(t0 + 0x15); gte_ldv3(&a1[idx0], &a1[idx1], &a1[idx2]); gte_rtpt(); *(u32 *)(t1 + 8) = *(u32 *)(t0 + 1); *(u32 *)(t1 + 0x10) = *(u32 *)(t0 + 5); *(u32 *)(t1 + 0x18) = *(u32 *)(t0 + 9); gte_stflg(&st[0]); if (!(st[0] & ~0x1000)) { gte_nclip(); gte_stopz(potz); if (st[1] > 0) { gte_stsxy3_gt3(a3); if (D_80078D88[0] & 0x8000) { gte_stsz3(psz0, psz1, &st[4]); /* `m` must NOT be the address-taken otz slot: assigning otz in each arm * makes gcc spill to sp+4 three times. The target keeps the running max * in $v1 and stores ONCE, so the max needs a plain (non-addressable) temp. * `st[2] > st[3]` (not `st[3] < st[2]`) is what loads sz0 first and emits * `slt $v0, $v1, $a2`. */ if (st[2] > st[3]) { m = st[2]; if (m < st[4]) m = st[4]; } else { m = st[3]; if (m < st[4]) m = st[4]; } st[1] = m >> 2; } else { gte_avsz3(); gte_stotz(potz); } *(u32 *)t1 = (*(u32 *)(t0 + 0xD) & mask) | ((u32)t0[0] << 24); t1 += 0x20; idx = st[1] >> shiftAmt; { u32 old = (u32)a3 & mask; /* `s32 *p = otp + idx;` -- writing `otp[idx]` inline emits the address * plus with the SCALED INDEX first (`addu $a0,$a0,$t8`); an explicit * pointer temp emits it base-first (`addu $a0,$t8,$a0`) like the target. * The second half of that fix is that `otp` must be an ordinary pseudo: * with `register s32 *otp __asm__("$24")` local-alloc will not tie the * dying index register to the address destination and the chain lands in * $v0 instead of $a0. Base-first + unpinned base = the last instruction. */ s32 *p = otp + idx; *(u32 *)a3 = (*p & mask) | 0x7000000; a3 += 0x20; *p = old; } } } t0 += 0x1C; } while (--cnt); return a3; } /* ---- PsyQ inline GTE macros (inline_c.h style) ---- */ #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_ldv0(r0) __asm__ __volatile__ ( \ "lwc2 $0, 0( %0 );" \ "lwc2 $1, 4( %0 )" \ : \ : "r"( r0 ) ) #define gte_rtpt() __asm__ __volatile__ ("nop;nop;rtpt") #define gte_rtps() __asm__ __volatile__ ("nop;nop;rtps") #define gte_nclip() __asm__ __volatile__ ("nop;nop;nclip") #define gte_avsz4() __asm__ __volatile__ ("nop;nop;avsz4") #define gte_stflg(r0) __asm__ __volatile__ ( \ "cfc2 $12, $31;" \ "nop;" \ "sw $12, 0( %0 )" \ : \ : "r"( r0 ) \ : "$12", "memory" ) #define gte_stopz(r0) __asm__ __volatile__ ( \ "swc2 $24, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stotz(r0) __asm__ __volatile__ ( \ "swc2 $7, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stsxy(r0) __asm__ __volatile__ ( \ "swc2 $14, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stsxy3_g4(r0) __asm__ __volatile__ ( \ "swc2 $12, 8( %0 );" \ "swc2 $13, 16( %0 );" \ "swc2 $14, 24( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stsz4(r0, r1, r2, r3) __asm__ __volatile__ ( \ "swc2 $16, 0( %0 );" \ "swc2 $17, 0( %1 );" \ "swc2 $18, 0( %2 );" \ "swc2 $19, 0( %3 )" \ : \ : "r"( r0 ), "r"( r1 ), "r"( r2 ), "r"( r3 ) \ : "memory" ) typedef struct { s16 vx, vy, vz, pad; } Vtx8; typedef struct { u8 pad0; u8 pad1; u8 pad2; u8 code; /* +0x03 */ u32 rgb1; /* +0x04 */ u32 rgb2; /* +0x08 */ u32 rgb3; /* +0x0C */ u32 term; /* +0x10 */ u32 rgb0; /* +0x14 */ u16 i0; /* +0x18 */ u16 i1; /* +0x1A */ u16 i2; /* +0x1C */ u16 i3; /* +0x1E */ } SrcQ; typedef struct { u32 tag; /* +0x00 */ u32 rgb0; /* +0x04 */ u32 xy0; /* +0x08 */ u32 rgb1; /* +0x0C */ u32 xy1; /* +0x10 */ u32 rgb2; /* +0x14 */ u32 xy2; /* +0x18 */ u32 rgb3; /* +0x1C */ u32 xy3; /* +0x20 */ u32 term; /* +0x24 */ } LineG4; extern s32 D_80078D88[]; LineG4 *func_800279AC(SrcQ *src, Vtx8 *vb, s32 unused, LineG4 *prim, s32 n, s32 shift, u32 *ot) { s32 flag; s32 otz; /* cookbook §21 zero-byte re-tie: without it loop.c's record_initial takes the biv's * initial value straight from the parameter COPY (set prim (reg $a3)), so the +4 giv is * emitted as (plus (reg $a3) 4) -- $a3 then stays live past the copy, conflicts with the * prim pseudo, and prim/giv land in $t1/$a3 (target: $a3/$t1). The re-tie makes the * pre-loop set non-invariant so the giv is derived from the PSEUDO and prim coalesces * into $a3. Emits nothing. */ __asm__ ("" : "=r"(prim) : "0"(prim)); for (; n != 0; n--, src++) { gte_ldv3(&vb[src->i0], &vb[src->i1], &vb[src->i2]); gte_rtpt(); prim->rgb1 = src->rgb1; prim->rgb2 = src->rgb2; prim->rgb3 = src->rgb3; gte_stflg(&flag); if (flag & 0xFFFFEFFF) { continue; } gte_nclip(); gte_stopz(&otz); if (otz <= 0) { continue; } gte_stsxy3_g4(prim); gte_ldv0(&vb[src->i3]); gte_rtps(); gte_stflg(&flag); if (flag & 0xFFFFEFFF) { continue; } gte_stsxy(&prim->xy3); prim->term = src->term; /* §5a zero-byte barrier: else sched2 hoists the D_80078D88 load above this store and * eats the two load-delay nops the target keeps (the -2 length drift). */ __asm__ __volatile__ ("" : : : "memory"); if (D_80078D88[0] & 0x8000) { s32 sz[4]; s32 m, k; gte_stsz4(&sz[0], &sz[1], &sz[2], &sz[3]); m = sz[2]; if (m < sz[3]) { m = sz[3]; } k = sz[0]; if (k < sz[1]) { k = sz[1]; } if (k < m) { k = m; } otz = k >> 2; } else { gte_avsz4(); gte_stotz(&otz); } prim->rgb0 = (src->rgb0 & 0xFFFFFF) | ((u32)src->code << 24); /* §5a zero-byte barrier: keeps the rgb0 store ahead of the OT-link block instead of * letting sched2 interleave the `otz` reload into the rgb0 chain. */ __asm__ __volatile__ ("" : : : "memory"); { u32 *p = &ot[otz >> shift]; prim->tag = (*p & 0xFFFFFF) | 0x09000000; *p = (u32)prim & 0xFFFFFF; } prim++; } return prim; } void func_80027BF0(void) { func_80027C10(); } extern G3P *func_80027D20(Face2 *f, SVEC2 *verts, SVEC2 *norms, G3P *prims, s32 count, s32 shift, s32 ot); extern G4P *func_80027F4C(Face3 *f, SVEC2 *verts, SVEC2 *norms, G4P *prims, s32 count, s32 shift, s32 ot); void func_80027C10(void *a0, void *a1, s32 a2) { void *geo; SVEC2 *verts; SVEC2 *norms; Face2 *f; G3P *p; u32 w0; s32 cnt; u32 opc; u32 masked; geo = *(void **)((s32)a0 + 0x8); p = *(G3P **)((s32)a0 + 0xC); verts = *(SVEC2 **)((s32)geo + 0x0); norms = *(SVEC2 **)((s32)geo + 0x8); f = *(Face2 **)((s32)geo + 0x10); do { w0 = p->tag; cnt = p->rgb0; p = (G3P *)((s32)p + 8); opc = (w0 >> 24) & 0xFC; switch (opc) { case 0x30: func_80027D20(f, verts, norms, p, cnt, a2, *(s32 *)((s32)a1 + 4)); f = (Face2 *)((s32)f + cnt * 20); break; case 0x38: func_80027F4C((Face3 *)f, verts, norms, (G4P *)p, cnt, a2, *(s32 *)((s32)a1 + 4)); f = (Face2 *)((s32)f + cnt * 24); break; } masked = w0 & 0xFFFFFF; p = (G3P *)(masked | 0x80000000); } while (masked != 0); } /* ---- PsyQ inline GTE macros (same spellings as the matched ov_SC03_006 body) ---- */ #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_ldrgb(r0) __asm__ volatile ( \ "lwc2 $6, 0( %0 )" \ : \ : "r"( r0 ) ) #define gte_rtpt() __asm__ volatile ("nop;nop;rtpt") #define gte_nclip() __asm__ volatile ("nop;nop;nclip") #define gte_ncct() __asm__ volatile ("nop;nop;ncct") #define gte_stflg(r0) __asm__ volatile ( \ "cfc2 $12, $31;" \ "nop;" \ "sw $12, 0( %0 )" \ : \ : "r"( r0 ) \ : "$12", "memory" ) #define gte_stopz(r0) __asm__ volatile ( \ "swc2 $24, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stsz3(r0, r1, r2) __asm__ volatile ( \ "swc2 $17, 0( %0 );" \ "swc2 $18, 0( %1 );" \ "swc2 $19, 0( %2 )" \ : \ : "r"( r0 ), "r"( r1 ), "r"( r2 ) \ : "memory" ) #define gte_stsxy3_g3(r0) __asm__ volatile ( \ "swc2 $12, 8( %0 );" \ "swc2 $13, 16( %0 );" \ "swc2 $14, 24( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_strgb3_g3(r0) __asm__ volatile ( \ "swc2 $20, 4( %0 );" \ "swc2 $21, 12( %0 );" \ "swc2 $22, 20( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) extern s16 D_800C7C74; extern void func_80028200(s32 a0, s32 a1, s32 a2, s32 a3, s32 stack_arg); G3P *func_80027D20(Face2 *f, SVEC2 *verts, SVEC2 *norms, G3P *prims, s32 count, s32 shift, s32 ot) { struct { s32 flag, opz, sz0, sz1, sz2; } g; G3P *prim; s32 z; s32 w; s32 i; prim = &prims[D_800C7C74]; for (i = count; i != 0; i--, f++, prim += 2) { gte_ldv3(&verts[f->v0], &verts[f->v1], &verts[f->v2]); gte_rtpt(); gte_stflg(&g.flag); if (!(g.flag & ~0x1000)) { gte_nclip(); gte_stopz(&g.opz); if (g.opz > 0) { gte_stsxy3_g3(prim); gte_stsz3(&g.sz0, &g.sz1, &g.sz2); if (g.sz0 < g.sz1) { w = g.sz2; z = g.sz0; } else { w = g.sz2; z = g.sz1; } if (w < z) z = w; g.opz = (z >> 2) - 1; if (g.opz < 0) { g.opz = 0; } gte_ldrgb(&f->rgb); gte_ldv3(&norms[f->n0], &norms[f->n1], &norms[f->n2]); gte_ncct(); gte_strgb3_g3(prim); func_80028200(ot, g.opz, shift, (s32)prim, 0x06000000); } } } return prim; } /* ---- PsyQ inline GTE macros (same spellings as the matched func_80027D20 / func_800279AC) ---- */ #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_ldrgb(r0) __asm__ volatile ( \ "lwc2 $6, 0( %0 )" \ : \ : "r"( r0 ) ) #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_ncct() __asm__ volatile ("nop;nop;ncct") #define gte_nccs() __asm__ volatile ("nop;nop;nccs") #define gte_stflg(r0) __asm__ volatile ( \ "cfc2 $12, $31;" \ "nop;" \ "sw $12, 0( %0 )" \ : \ : "r"( r0 ) \ : "$12", "memory" ) #define gte_stopz(r0) __asm__ volatile ( \ "swc2 $24, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stsxy(r0) __asm__ volatile ( \ "swc2 $14, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_strgb(r0) __asm__ volatile ( \ "swc2 $22, 0( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_stsz4(r0, r1, r2, r3) __asm__ volatile ( \ "swc2 $16, 0( %0 );" \ "swc2 $17, 0( %1 );" \ "swc2 $18, 0( %2 );" \ "swc2 $19, 0( %3 )" \ : \ : "r"( r0 ), "r"( r1 ), "r"( r2 ), "r"( r3 ) \ : "memory" ) #define gte_stsxy3_g4(r0) __asm__ volatile ( \ "swc2 $12, 8( %0 );" \ "swc2 $13, 16( %0 );" \ "swc2 $14, 24( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) #define gte_strgb3_g4(r0) __asm__ volatile ( \ "swc2 $20, 4( %0 );" \ "swc2 $21, 12( %0 );" \ "swc2 $22, 20( %0 )" \ : \ : "r"( r0 ) \ : "memory" ) extern s16 D_800C7C74; extern void func_80028200(s32 a0, s32 a1, s32 a2, s32 a3, s32 stack_arg); G4P *func_80027F4C(Face3 *f, SVEC2 *verts, SVEC2 *norms, G4P *prims, s32 count, s32 shift, s32 ot) { struct { s32 flag, opz, sz0, sz1, sz2, sz3; } g; G4P *prim; s32 z; s32 w; s32 i; prim = &prims[D_800C7C74]; for (i = count; i != 0; i--, f++, prim += 2) { gte_ldv3(&verts[f->v0], &verts[f->v1], &verts[f->v2]); gte_rtpt(); gte_stflg(&g.flag); if (!(g.flag & ~0x1000)) { gte_nclip(); gte_stopz(&g.opz); if (g.opz > 0) { gte_stsxy3_g4(prim); gte_ldv0(&verts[f->v3]); gte_rtps(); gte_stflg(&g.flag); if (!(g.flag & ~0x1000)) { gte_stsxy(&prim->x3); gte_stsz4(&g.sz0, &g.sz1, &g.sz2, &g.sz3); w = g.sz2; if (g.sz3 < w) { w = g.sz3; } z = g.sz0; if (g.sz1 < z) { z = g.sz1; } if (w < z) { z = w; } g.opz = (z >> 2) - 1; if (g.opz < 0) { g.opz = 0; } gte_ldrgb(&f->rgb); gte_ldv3(&norms[f->n0], &norms[f->n1], &norms[f->n2]); gte_ncct(); gte_strgb3_g4(prim); gte_ldv0(&norms[f->n3]); gte_nccs(); gte_strgb(&prim->rgb3); func_80028200(ot, g.opz, shift, (s32)prim, 0x08000000); } } } } return prim; } void func_80028200(s32 a0, s32 a1, s32 a2, s32 a3, s32 stack_arg) { s32 mask; s32 shifted_index; s32 *array_ptr; s32 loaded; s32 masked_a3; mask = 0xFFFFFF; shifted_index = (a1 >> a2) << 2; array_ptr = (s32 *)(a0 + shifted_index); loaded = *array_ptr; loaded = (loaded & mask) | stack_arg; *(s32 *)a3 = loaded; masked_a3 = (a3 & mask); *array_ptr = masked_a3; } void func_80028234(void) { } extern u8 D_800A5E88; extern s32 D_800A5E8C; extern s32 D_800A5E90; extern u8 D_800A5E94; extern u8 D_800A5E95; extern u8 D_800A5E96; extern void func_80028620(s32 a0, void *a1); void func_8002823C(void) { s32 i; s32 a; s32 b; u8 *p; s32 off; i = 0; a = 0x800; b = 0x20; p = &D_800A5E88; off = 0; do { *(s32 *)((s32)&D_800A5E88 + off) = a; *(s32 *)((s32)&D_800A5E8C + off) = a; *(s32 *)((s32)&D_800A5E90 + off) = 0; *(u8 *)((s32)&D_800A5E94 + off) = b; *(u8 *)((s32)&D_800A5E95 + off) = b; *(u8 *)((s32)&D_800A5E96 + off) = b; func_80028620(i, p); p += 0x10; i++; off += 0x10; } while (i < 3); } void func_800282FC(void) { } extern u8 D_800A5E88; extern s32 D_800A5E8C; extern s32 D_800A5E90; extern u8 D_800A5E94; extern u8 D_800A5E95; extern u8 D_800A5E96; extern void func_80028620(s32 a0, void *a1); void func_80028304(s32 a0, s32 a1, s32 a2, s32 a3, u8 a4, u8 a5, u8 a6) { s32 offset; a0 &= 0xFFFF; if ((u16)a0 >= 3) { return; } offset = a0 << 4; *(s32 *)((s32)&D_800A5E88 + offset) = a1; *(s32 *)((s32)&D_800A5E8C + offset) = a2; *(s32 *)((s32)&D_800A5E90 + offset) = a3; *(u8 *)((s32)&D_800A5E94 + offset) = a4; *(u8 *)((s32)&D_800A5E95 + offset) = a5; *(u8 *)((s32)&D_800A5E96 + offset) = a6; func_80028620(a0, (void *)((s32)&D_800A5E88 + offset)); } extern u8 D_800A5E88; extern s32 D_800A5E8C; extern s32 D_800A5E90; extern void func_80028620(s32, void *); void func_80028394(s32 a0, s32 a1, s32 a2, s32 a3) { s32 offset; a0 &= 0xFFFF; if ((u16)a0 >= 3) { return; } offset = a0 << 4; *(s32 *)((s32)&D_800A5E88 + offset) += a1; *(s32 *)((s32)&D_800A5E8C + offset) += a2; *(s32 *)((s32)&D_800A5E90 + offset) += a3; func_80028620(a0, (void *)((s32)&D_800A5E88 + offset)); } extern void func_80028620(s32, void *); extern u8 D_800A5E94; extern u8 D_800A5E95; extern u8 D_800A5E96; extern u8 D_800A5E88; void func_80028428(s32 a0, s32 a1, s32 a2, s32 a3) { s32 offset; a0 &= 0xFFFF; if ((u16)a0 >= 3) { return; } offset = a0 << 4; *(u8 *)((s32)&D_800A5E94 + offset) = (u8)a1; *(u8 *)((s32)&D_800A5E95 + offset) = (u8)a2; *(u8 *)((s32)&D_800A5E96 + offset) = (u8)a3; func_80028620(a0, (void *)((s32)&D_800A5E88 + offset)); } extern u8 D_800A5E88; extern s32 D_800A5E8C; extern s32 D_800A5E90; extern u8 D_800A5E94; extern u8 D_800A5E95; extern u8 D_800A5E96; extern void func_80028620(s32 a0, void *a1); void func_80028488(s32 a0) { s32 offset; a0 &= 0xFFFF; if ((u16)a0 >= 3) { return; } offset = a0 << 4; *(s32 *)((s32)&D_800A5E90 + offset) = 100; *(s32 *)((s32)&D_800A5E88 + offset) = 0; *(s32 *)((s32)&D_800A5E8C + offset) = 0; *(u8 *)((s32)&D_800A5E94 + offset) = 0; *(u8 *)((s32)&D_800A5E95 + offset) = 0; *(u8 *)((s32)&D_800A5E96 + offset) = 0; func_80028620(a0, (void *)((s32)&D_800A5E88 + offset)); } extern s16 D_800AE7E0; extern s16 D_800AE7E2; extern s16 D_800AE7E4; extern void func_80053AF8(s32 a0, s32 a1, s32 a2); void func_8002850C(a0, a1, a2) s16 a0, a1, a2; { D_800AE7E0 = a0; D_800AE7E2 = a1; D_800AE7E4 = a2; func_80053AF8(a0, a1, a2); } extern u8 D_800A5E94; extern u8 D_800A5E95; extern u8 D_800A5E96; extern u8 D_800A5E88; extern void func_80028620(s32, void *); void func_80028558(s32 a0, s32 a1, s32 a2, s32 a3) { s32 v0; a0 &= 0xFFFF; v0 = a0 << 4; *(u8 *)((u32)&D_800A5E94 + v0) = a1; *(u8 *)((u32)&D_800A5E95 + v0) = a2; *(u8 *)((u32)&D_800A5E96 + v0) = a3; func_80028620(a0, (u8 *)&D_800A5E88 + v0); } extern void func_800538EC(s32 param); void SsUtReverbOff(void) { func_800538EC(0); } extern s16 D_800C6D18; extern s32 D_800C6D1C; extern s8 D_800C6D20; extern s8 D_800C6D22; extern void GsSetFogParam(s32 *param); extern void func_800538EC(s32 param); void func_800285CC(void) { s32 *fog = (s32 *)&D_800C6D18; *(s16 *)fog = -0x106; D_800C6D1C = 0x1F40000; D_800C6D20 = 0; D_800C6D22 = 0; GsSetFogParam(fog); func_800538EC(1); } void func_80028620(s32 a0, void *a1) { struct { u32 a, b, c, d; } buf, *src = (void *)a1; buf = *src; if ((buf.a | buf.b | buf.c) == 0) { buf.a = 0x1000; ((u8 *)&buf)[14] = 0; ((u8 *)&buf)[13] = 0; ((u8 *)&buf)[12] = 0; } func_80053328(a0, &buf); } extern u8 D_800B9A15; extern s32 D_80075818; extern s32 D_8007581C; extern u16 D_80075820[]; extern u16 D_80075824[]; extern u16 D_80075828[]; extern u16 D_8007582C[]; extern u16 D_80075830[]; extern u16 D_80075834[]; extern u8 D_800AF737; extern u8 D_800AF6DB; extern u8 D_800AF67F; extern u16 D_800AF77C[]; extern u16 D_800AF780[]; extern u16 D_800AF790[]; extern u16 D_800AF794[]; extern u16 D_800AF7A4[]; extern u16 D_800AF7A8[]; extern u8 D_800AF630[]; void func_80028694(void) { u8 *s0 = D_800AF630; u32 m; if (D_800B9A15 != 0) { return; } if ((s16)func_80014C54(1, 0, 2) != 0) { D_800AF737 = 1; D_800AF6DB = 1; D_800AF67F = 1; D_8007581C = 0; D_80075818 = 0; D_80075820[0] = D_800AF77C[0]; D_80075820[1] = D_800AF77C[1]; D_80075824[0] = D_800AF780[0]; D_80075824[1] = D_800AF780[1]; D_80075828[0] = D_800AF790[0]; D_80075828[1] = D_800AF790[1]; D_8007582C[0] = D_800AF794[0]; D_8007582C[1] = D_800AF794[1]; D_80075830[0] = D_800AF7A4[0]; D_80075830[1] = D_800AF7A4[1]; D_80075834[0] = D_800AF7A8[0]; D_80075834[1] = D_800AF7A8[1]; } else { if (((func_80014BFC(1) & 2) != 0) && ((func_80014B10(1) & 2) == 0)) { D_800AF737 = 0; D_800AF6DB = 0; D_800AF67F = 0; D_800AF77C[0] = D_80075820[0]; D_800AF77C[1] = D_80075820[1]; D_800AF780[0] = D_80075824[0]; D_800AF780[1] = D_80075824[1]; D_800AF790[0] = D_80075828[0]; D_800AF790[1] = D_80075828[1]; D_800AF794[0] = D_8007582C[0]; D_800AF794[1] = D_8007582C[1]; D_800AF7A4[0] = D_80075830[0]; D_800AF7A4[1] = D_80075830[1]; D_800AF7A8[0] = D_80075834[0]; D_800AF7A8[1] = D_80075834[1]; } } if ((s16)func_80014C54(1, 1, 2) == 0) { return; } m = func_80014B10(1) & 0x5000; if (m == 0x1000) goto minus1; if (m == 0x4000) goto plus1; goto skip1; minus1: D_8007581C = D_8007581C - *(s32 *)(s0 + 0xA3E8) * 2; goto store1; plus1: D_8007581C = D_8007581C + *(s32 *)(s0 + 0xA3E8) * 2; store1: skip1: m = func_80014B10(1) & 0xA000; if (m == 0x2000) goto plus2; if (m != 0x8000) goto skip2; D_80075818 = D_80075818 - *(s32 *)(s0 + 0xA3E8) * 2; goto store2; plus2: D_80075818 = D_80075818 + *(s32 *)(s0 + 0xA3E8) * 2; store2: skip2: if ((s16)func_80014C54(1, 1, 8) != 0) { SetDefDispEnv(s0 + (*(u16 *)(s0 + 0x188) * 20 + 0x14C), D_80075818, D_8007581C, 0x140, 0xF0); } else { SetDefDispEnv(s0 + (*(u16 *)(s0 + 0x188) * 20 + 0x14C), D_80075818, D_8007581C, 0x280, 0x1E0); } } void func_80028A30(void) { } void func_80028A38(void) { } void func_80028A40(void) { } typedef void (*GsTMDfastFn)(void); extern void GsTMDfastF3L(void); extern void GsTMDfastF3NL(void); extern void GsTMDfastNF3(void); extern void GsTMDfastG3L(void); extern void GsTMDfastG3NL(void); extern void GsTMDfastNG3(void); extern void GsTMDfastTF3L(void); extern void GsTMDfastTF3NL(void); extern void GsTMDfastTNF3(void); extern void GsTMDfastTG3L(void); extern void GsTMDfastTG3NL(void); extern void GsTMDfastTNG3(void); extern void GsTMDfastF4L(void); extern void GsTMDfastF4NL(void); extern void GsTMDfastNF4(void); extern void GsTMDfastG4L(void); extern void GsTMDfastG4NL(void); extern void GsTMDfastNG4(void); extern void GsTMDfastTF4L(void); extern void GsTMDfastTF4NL(void); extern void GsTMDfastTNF4(void); extern void GsTMDfastTG4L(void); extern void GsTMDfastTG4NL(void); extern void GsTMDfastTNG4(void); extern void GsTMDfastF3GL(void); extern void GsTMDfastF3GNL(void); extern void GsTMDfastG3GL(void); extern void GsTMDfastG3GNL(void); extern void GsTMDfastF4GL(void); extern void GsTMDfastF4GNL(void); extern void GsTMDfastG4GL(void); extern void GsTMDfastG4GNL(void); extern GsTMDfastFn D_800A2BC0; extern GsTMDfastFn D_800A2BC8; extern GsTMDfastFn D_800A2BD8; extern GsTMDfastFn D_800A2BE0; extern GsTMDfastFn D_800A2BE8; extern GsTMDfastFn D_800A2BF8; extern GsTMDfastFn D_800A2C00; extern GsTMDfastFn D_800A2C08; extern GsTMDfastFn D_800A2C18; extern GsTMDfastFn D_800A2C20; extern GsTMDfastFn D_800A2C28; extern GsTMDfastFn D_800A2C38; extern GsTMDfastFn D_800A2C40; extern GsTMDfastFn D_800A2C48; extern GsTMDfastFn D_800A2C58; extern GsTMDfastFn D_800A2C60; extern GsTMDfastFn D_800A2C68; extern GsTMDfastFn D_800A2C78; extern GsTMDfastFn D_800A2C80; extern GsTMDfastFn D_800A2C88; extern GsTMDfastFn D_800A2C98; extern GsTMDfastFn D_800A2CA0; extern GsTMDfastFn D_800A2CA8; extern GsTMDfastFn D_800A2CB8; extern GsTMDfastFn D_800A2CC0; extern GsTMDfastFn D_800A2CC8; extern GsTMDfastFn D_800A2CCC; extern GsTMDfastFn D_800A2CD4; extern GsTMDfastFn D_800A2CD8; extern GsTMDfastFn D_800A2CE0; extern GsTMDfastFn D_800A2CE4; extern GsTMDfastFn D_800A2CEC; void func_80028A48(void) { D_800A2BC0 = GsTMDfastF3L; D_800A2BC8 = GsTMDfastF3NL; D_800A2BD8 = GsTMDfastNF3; D_800A2BE0 = GsTMDfastG3L; D_800A2BE8 = GsTMDfastG3NL; D_800A2BF8 = GsTMDfastNG3; D_800A2C00 = GsTMDfastTF3L; D_800A2C08 = GsTMDfastTF3NL; D_800A2C18 = GsTMDfastTNF3; D_800A2C20 = GsTMDfastTG3L; D_800A2C28 = GsTMDfastTG3NL; D_800A2C38 = GsTMDfastTNG3; D_800A2C40 = GsTMDfastF4L; D_800A2C48 = GsTMDfastF4NL; D_800A2C58 = GsTMDfastNF4; D_800A2C60 = GsTMDfastG4L; D_800A2C68 = GsTMDfastG4NL; D_800A2C78 = GsTMDfastNG4; D_800A2C80 = GsTMDfastTF4L; D_800A2C88 = GsTMDfastTF4NL; D_800A2C98 = GsTMDfastTNF4; D_800A2CA0 = GsTMDfastTG4L; D_800A2CA8 = GsTMDfastTG4NL; D_800A2CB8 = GsTMDfastTNG4; D_800A2CC0 = GsTMDfastF3GL; D_800A2CC8 = GsTMDfastF3GNL; D_800A2CCC = GsTMDfastG3GL; D_800A2CD4 = GsTMDfastG3GNL; D_800A2CD8 = GsTMDfastF4GL; D_800A2CE0 = GsTMDfastF4GNL; D_800A2CE4 = GsTMDfastG4GL; D_800A2CEC = GsTMDfastG4GNL; } extern void *GsPrstF3L(); extern void *GsPrstF3NL(); extern void *GsPrstG3L(); extern void *GsPrstG3NL(); extern void *GsPrstTF3L(); extern void *GsPrstTF3NL(); extern void *GsPrstTG3L(); extern void *GsPrstTG3NL(); extern void *GsPrstF4L(); extern void *GsPrstF4NL(); extern void *GsPrstG4L(); extern void *GsPrstG4NL(); extern void *GsPrstTF4L(); extern void *GsPrstTF4NL(); extern void *GsPrstTG4L(); extern void *GsPrstTG4NL(); extern void *D_800A2CF0; extern void *D_800A2CF8; extern void *D_800A2D10; extern void *D_800A2D18; extern void *D_800A2D30; extern void *D_800A2D38; extern void *D_800A2D50; extern void *D_800A2D58; extern void *D_800A2D70; extern void *D_800A2D78; extern void *D_800A2D90; extern void *D_800A2D98; extern void *D_800A2DB0; extern void *D_800A2DB8; extern void *D_800A2DD0; extern void *D_800A2DD8; void func_80028C50(void) { D_800A2CF0 = GsPrstF3L; D_800A2CF8 = GsPrstF3NL; D_800A2D10 = GsPrstG3L; D_800A2D18 = GsPrstG3NL; D_800A2D30 = GsPrstTF3L; D_800A2D38 = GsPrstTF3NL; D_800A2D50 = GsPrstTG3L; D_800A2D58 = GsPrstTG3NL; D_800A2D70 = GsPrstF4L; D_800A2D78 = GsPrstF4NL; D_800A2D90 = GsPrstG4L; D_800A2D98 = GsPrstG4NL; D_800A2DB0 = GsPrstTF4L; D_800A2DB8 = GsPrstTF4NL; D_800A2DD0 = GsPrstTG4L; D_800A2DD8 = GsPrstTG4NL; } extern s32 func_80029178(); s32 func_80028D58(void) { return func_80029178(0xB) & 0xFF; } extern void func_80029124(s32, s32); void func_80028D78(s32 a0) { func_80029124(0xB, a0 & 0xFF); } extern s32 func_80029178(); u8 func_80028D9C(void) { return func_80029178(0xC); } extern void func_80029124(s32, s32); void func_80028DBC(s32 a0) { func_80029124(0xC, a0 & 0xFF); } extern s32 func_80029178(); s32 func_80028DE0(void) { return func_80029178(0xD) & 0xFF; } extern void func_80029124(s32, s32); void func_80028E00(s32 a0) { func_80029124(0xD, a0 & 0xFF); } extern s32 func_80029178(); u8 func_80028E24(void) { return func_80029178(0xE); } extern void func_80029124(s32, s32); void func_80028E44(s32 a0) { func_80029124(0xE, a0 & 0xFF); } extern s32 func_80029178(); u8 func_80028E68(void) { return func_80029178(0xF); } extern void func_80029124(s32, s32); void func_80028E88(s32 a0) { func_80029124(0xF, a0 & 0xFF); } extern s32 func_80029178(); u8 func_80028EAC(void) { return func_80029178(0x10); } extern void func_80029124(s32, s32); void func_80028ECC(s32 a0) { func_80029124(0x10, a0 & 0xFF); } extern s32 func_80029178(); u8 func_80028EF0(void) { return func_80029178(0x11); } extern void func_80029124(s32, s32); void func_80028F10(s32 a0) { func_80029124(0x11, a0 & 0xFF); } extern s32 func_80029178(); u8 func_80028F34(void) { return func_80029178(0x12); } extern void func_80029124(s32, s32); void func_80028F54(s32 a0) { func_80029124(0x12, a0 & 0xFF); } extern s32 func_80029178(); u8 func_80028F78(void) { return func_80029178(0x13); } extern void func_80029124(s32, s32); void func_80028F98(s32 a0) { func_80029124(0x13, a0 & 0xFF); } extern s32 func_800291B4(s32); u8 func_80028FBC(void) { return func_800291B4(0x2C); } extern void func_800291A0(s32, s32); void func_80028FDC(s32 a0) { func_800291A0(0x2C, a0 & 0xFF); } extern s32 func_800291B4(s32); u8 func_80029000(void) { return func_800291B4(0x2D); } extern void func_800291A0(s32, s32); void func_80029020(s32 a0) { func_800291A0(0x2D, a0 & 0xFF); } extern u8 D_800AE648[]; void func_80029044(void) { register s32 v1 __asm__("$3"); v1 = 0; do { D_800AE648[v1] = 0; v1 += 1; } while ((u32)v1 < 0x40); } extern u8 D_800BA1B8[]; void func_8002906C(void) { register s32 v1 __asm__("$3"); v1 = 0; do { D_800BA1B8[v1] = 0; v1 += 1; } while ((u32)v1 < 0x100); } extern void func_80016714(void *a0, s32 a1); extern u8 D_800BA2B8[]; void func_80029094(void) { func_80016714(D_800BA2B8, 0x60); } extern void func_80029124(s32, s32); void func_800290BC(void) { func_80029124(0x7F, 1); } extern void func_80029124(s32, s32); void func_800290E0(void) { func_80029124(0x7F, 0); } extern s32 func_80029178(); u8 func_80029104(void) { return func_80029178(0x7F); } extern u8 D_800AE648[]; void func_80029124(s32 arg0, s32 arg1) { s32 idx = (u32)arg0 >> 3; s32 mask = 1 << (arg0 & 7); if ((arg1 & 0xFF) != 0) D_800AE648[idx] |= mask; else D_800AE648[idx] &= ~mask; } extern u8 D_800AE648[]; s32 func_80029178(u32 arg0) { u32 idx = arg0 >> 3; u32 bit = arg0 & 7; u32 m = 1 << bit; return (D_800AE648[idx] & m) != 0; } extern u8 D_800BA1B8[]; void func_800291A0(s32 arg0, s32 arg1) { D_800BA1B8[arg0] = arg1; } extern u8 D_800BA1B8[]; s32 func_800291B4(s32 arg0) { return D_800BA1B8[arg0]; } extern u8 D_800BA1B8[]; void func_800291C8(s32 arg0, s32 arg1) { *(u16 *) &D_800BA1B8[arg0] = arg1; } extern u8 D_800BA1B8[]; s32 func_800291DC(s32 index) { return *(s16 *)&D_800BA1B8[index]; } extern u8 D_800BA1B8[]; void func_800291F0(s32 a0, s32 a1) { *(s32 *)&D_800BA1B8[a0] = a1; } extern u8 D_800BA1B8[]; u32 func_80029204(s32 arg0) { return *(u32 *) &D_800BA1B8[arg0]; } extern s32 D_800AE6A8; extern void func_80016714(void *a0, s32 a1); void func_80029218(void) { func_80016714(&D_800AE6A8, 0x3C); } extern s32 D_80075838; void func_80029240(void) { D_80075838 = 1; } extern s32 D_80075838; void func_80029254(void) { D_80075838 = 0; } extern s32 D_80075838; s32 func_80029264(void) { return D_80075838; } extern Blk98_80029274 D_80072C84; extern Blk98_80029274 D_80078E78; void func_80029274(void) { Blk98_80029274 buffer; buffer = D_80072C84; D_80078E78 = buffer; } extern void func_80029344(void); extern s32 D_80078E90; void func_8002931C(s32 a0) { D_80078E90 = a0; func_80029344(); } extern u16 D_80078E92; extern u8 D_80078EAE; extern Blk98_80029274 D_80078E78; void func_80029344(void) { s32 val; u8* blk; val = D_80078E92; blk = (u8*)&D_80078E78; if ((u32)(val - 0x1A4) >= 0x2D0) { D_80078EAE = 0; } else { D_80078EAE = 1; } if ((s16)val < 0x528) { if ((s16)val >= 0x474) { blk[0x37] = 3; } else { if ((s16)val >= 0x3C0) { blk[0x37] = 2; } else { if ((s16)val >= 0x1A4) { blk[0x37] = 1; } else { if ((s16)val < 0x12C) { blk[0x37] = 4; } else { blk[0x37] = 0; } } } } } else { blk[0x37] = 4; } blk[0x39] = (s16)val / 60; blk[0x38] = (s16)val % 60; } extern void func_80016714(void *a0, s32 a1); extern u16 D_800A6588[]; void func_8002941C(void) { func_80016714(D_800A6588, 0x2); } extern u8 D_80078E7C __asm__("D_80078E7C"); extern u8 D_80078E7C_1 __asm__("D_80078E7C+1"); extern u8 D_80078E7C_2 __asm__("D_80078E7C+2"); extern u8 D_80078E7C_3 __asm__("D_80078E7C+3"); extern u32 D_80078E7C_w __asm__("D_80078E7C"); void func_80029444(void) { if ((u8)(++D_80078E7C) < 30) { return; } D_80078E7C = 0; if ((u8)(++D_80078E7C_1) < 60) { return; } D_80078E7C_1 = 0; if ((u8)(++D_80078E7C_2) < 60) { return; } D_80078E7C_2 = 0; if ((u8)(++D_80078E7C_3) < 100) { return; } D_80078E7C_w = 0x633B3B00; } extern s32 D_80078E80; s32 func_80029504(void) { return D_80078E80; } extern s32 D_80078E80; void func_80029514(s32 arg0) { D_80078E80 = arg0; } extern s32 D_80078E80; s32 func_80029524(void) { return (u32)(D_80078E80 - 200) < 400; } extern s32 D_80078E84; s32 func_8002953C(void) { return D_80078E84; } extern s32 D_80078E84; void func_8002954C(s32 arg0) { D_80078E84 = arg0; } extern s32 D_80078E84; void func_8002955C(void) { D_80078E84 += 1; } extern s32 D_80078E88; s32 func_8002957C(void) { return D_80078E88; } extern s32 D_80078E88; void func_8002958C(s32 arg0) { D_80078E88 = arg0; } extern s32 func_8002A544(s32); extern s32 D_80078E88; void func_8002959C(void) { func_8002A544(0x32); D_80078E88 = D_80078E88 + 1; } extern void func_80029124(s32, s32); extern s16 D_80078EB4; extern u16 D_80078EB2; extern s16 D_80078EB8; extern u16 D_80078EB6; extern u8 D_80078EC0; extern s32 D_80078E8C; void func_800295D4(void) { D_80078EB4 = 0xFA; D_80078EB2 = 0xFA; D_80078EB8 = 0xFA; D_80078EB6 = 0xFA; func_80029124(0x1E, 1); D_80078EC0 = 1; func_80029124(0x80, 1); D_80078E8C = 0x64; func_80029124(0x82, 1); func_80029124(0x83, 1); func_80029124(0x85, 1); } extern void func_80029344(void); extern s32 D_80078E90; void func_80029664(void) { D_80078E90 = 0x528; func_80029344(); } void func_80029690(void) { extern void func_80016714(void *a0, s32 a1); extern u8 D_80078F28[]; extern s32 D_800A5E58[]; s32 i; u8 *p; s32 *q; i = 0; p = D_80078F28; q = D_800A5E58; do { *q = 0; func_80016714(p, 0x5B8); p += 0x2DC; i++; q++; } while (i < 2); } extern void func_80029774(s32); extern Blk36_800296F8 D_80079204; extern s32 D_800A5E5C; s32 func_800296F8(s32 arg0) { func_80029774(1); D_80079204 = *(Blk36_800296F8 *)arg0; return D_800A5E5C = 1; } typedef struct { char c[4]; } Blk4_80029774; extern Blk98_80029274 D_80078E78; extern u8 D_80078F28[]; extern u16 D_800A6588[]; extern u8 D_800AE648[]; extern u8 D_800BA1B8[]; extern u8 D_800BA2B8[]; extern s32 D_800A5E58[]; void func_80029774(s32 a0) { u8 *dst; s32 i; dst = D_80078F28 + a0 * 0x2DC; *(Blk98_80029274 *)(dst + 0x24) = D_80078E78; for (i = 0; i < 0x40; i++) { *(u16 *)(dst + 0xBC + i * 2) = D_800A6588[i]; } for (i = 0; i < 0x40; i++) { *(u8 *)(i + dst + 0x13C) = D_800AE648[i]; } for (i = 0; i < 0x100; i++) { *(u8 *)(i + dst + 0x17C) = D_800BA1B8[i]; } for (i = 0; i < 0x18; i++) { *(Blk4_80029774 *)(dst + 0x27C + i * 4) = *(Blk4_80029774 *)(D_800BA2B8 + i * 4); } D_800A5E58[a0] = 1; } extern Blk36_800296F8 D_80079204; extern void func_8002992C(s32); void func_800298BC(Blk36_800296F8 *arg0) { func_8002992C(1); *arg0 = D_80079204; } typedef struct { u8 b[4]; } Blk4; extern Blk98_80029274 D_80078E78; extern u16 D_800A6588[]; extern u8 D_800BA2B8[]; extern u8 D_80078F28[]; extern u8 D_800AE648[]; extern u8 D_800BA1B8[]; void func_8002992C(s32 arg0) { u8* base; s32 i; base = D_80078F28 + arg0 * 0x2DC; D_80078E78 = *(Blk98_80029274*)(base + 0x24); for (i = 0; i < 0x40; i++) { D_800A6588[i] = *(u16*)(base + 0xBC + i * 2); } for (i = 0; i < 0x40; i++) { D_800AE648[i] = *(i + base + 0x13C); } for (i = 0; i < 0x100; i++) { D_800BA1B8[i] = *(i + base + 0x17C); } for (i = 0; i < 0x18; i++) { *(Blk4*)(D_800BA2B8 + i * 4) = *(Blk4*)(base + 0x27C + i * 4); } } extern u8 D_80078F28[]; void func_80029A58(s32 arg0) { u32 *p = (u32 *)(D_80078F28 + arg0 * 0x2DC); p[0xE] >>= 1; } extern s32 D_800A5E58[]; s32 func_80029A94(s32 arg0) { return D_800A5E58[arg0]; } s32 func_80029AAC(void) { extern s32 func_8002A2B0(void); s32 ret; ret = func_8002A2B0(); return ret * 75 / 100; } extern s32 func_80029E30(void); extern s32 func_80029CD4(s32); s32 func_80029AF4(void) { s32 s0; s0 = func_80029E30(); s0 = s0 * (func_80029CD4(0x28) + 0xC8); return s0 / 100; } extern s32 func_80029CD4(s32 a0); extern s32 func_80029DB4(void); s32 func_80029B4C(s16 a0, s16 a1) { s32 b; s32 d; b = func_80029DB4(); d = func_80029CD4(a1); return b * (a0 + d) / 100; } extern s32 func_80029E30(void); extern s32 func_80029CD4(s32); s32 func_80029BC8(s16 a0, s16 a1) { s32 c; s32 d; c = func_80029E30(); d = func_80029CD4(a1); return c * (a0 + d) / 100; } extern s32 func_80029DB4(void); s32 func_80029C44(s16 a0, s16 a1) { s32 b; s32 c; s32 d; b = func_80029DB4(); c = func_80029E30(); d = func_80029CD4(a1); return (b + c) * (a0 + d) / 100; } extern s32 rand(void); s32 func_80029CD4(s32 a0) { if (a0 == 0) return 0; return rand() % a0; } /* func_80029D3C — banked candidate, rounds 1-9. * Canonical s32(void) def per engine_core.h caller macros; incoming $a0 * bridged via register pins ($4 -> $16) because the prototype takes no * named parameters. Clamp MUST stay a ternary: an if/else spelling moves * ret into $a0 and breaks the early-exit delay-slot chain (verified R9). * Divisor 4800 forced by choose_multiplier arithmetic on M=0x1B4E81B5,l=9. */ extern s32 func_8002A4FC(s32 a0); s32 func_80029D3C(void) { register s32 n __asm__("$4"); register s32 s0 __asm__("$16"); s32 p; s32 q; s32 ret; __asm__("" : "=r"(n)); s0 = n; if (s0 <= 0) { ret = 0; } else { p = func_8002A4FC(s0) * 60; q = s0 - s0 * p / 4800; ret = q > 0 ? q : 1; } return ret; } extern s32 func_8002A9DC(void); extern s32 func_8002A2B0(void); s32 func_80029DB4(void) { s32 iVar4; s32 iVar2; s32 iVar3; iVar4 = 0x7D; if ((func_80029178(0x81) & 0xFF) != 0) { iVar4 = 0x96; } iVar2 = func_8002A9DC(); iVar3 = func_8002A2B0(); return (iVar2 + iVar3) * iVar4 / 100; } extern s32 func_8002A76C(void); extern s32 func_8002A2B0(void); s32 func_80029E30(void) { register s32 v1 __asm__("$3"); s32 a, b, sum, v0, sign; a = func_8002A76C(); b = func_8002A2B0(); // Load magic constant directly into v1 __asm__("lui %0, 0x51eb; ori %0, %0, 0x851f" : "=r"(v1)); sum = a + b; // Compute sum * 175 via shifts v0 = sum << 1; // sum * 2 v0 = v0 + sum; // sum * 3 v0 = v0 << 2; // sum * 12 v0 = v0 - sum; // sum * 11 v0 = v0 << 4; // sum * 176 v0 = v0 - sum; // sum * 175 // Multiply by magic constant __asm__ __volatile__("mult %0, %1" : : "r"(v0), "r"(v1) : "hi", "lo"); // Sign of v0 for division correction (happens in parallel) sign = v0 >> 31; // Get high word of multiplication into a hard-pinned $a0, scoped // narrowly so the pin doesn't bleed backward and swallow v1's // (magic-constant) live range into the same physical register. { register s32 a0 __asm__("$4"); __asm__ __volatile__("mfhi %0" : "=r"(a0) : : "hi", "lo"); // Final result: (high >> 5) - sign v1 = a0 >> 5; } return v1 - sign; } s32 func_80029E94(s32 arg0, s32 arg1) { s32 result = 0; s16 val; val = *(s16 *)(*(s32 *)(arg0 + 0x78) + 0x30); if (val != 0) { result = (val * arg1) >> 12; } return result; } s32 func_80029EC0(s32 a0) { return *(s16 *)(*(s32 *)(a0 + 0x78) + 0x2E); } extern s32 func_80029504(void); extern s32 D_80078F04; extern s32 D_80078F08; extern s32 D_80078F0C; extern s32 func_8002A2D4(s32); void func_80029ED4(s32 arg0) { s32 v0; if (func_80029504() < 10) { return; } D_80078F04 += arg0; if ((u32)D_80078F04 < 0x66) { return; } D_80078F04 = (u32)D_80078F04 % 102u; D_80078F08 += 1; if ((u32)D_80078F08 > 9999999u) { D_80078F08 = 9999999; } v0 = D_80078F08 - D_80078F0C; if (v0 < 10) { return; } D_80078F0C = D_80078F08; func_8002A2D4(v0 / 10); } extern s32 D_80078F08; s32 func_80029FD4(void) { return D_80078F08; } extern s32 func_8002A26C(void); extern s32 func_8002A4B8(void); extern s32 func_8002A728(void); extern s32 func_8002A998(void); s32 func_80029FE4(void) { s32 a; s32 b; s32 c; s32 d; a = func_8002A26C(); b = func_8002A4B8(); c = func_8002A728(); d = func_8002A998(); return (a + b + c + d) / 4; } extern s32 func_8002A088(s32); s32 func_8002A04C(s32 a0) { s32 v0 = *(s32 *)(a0 + 0x78); if (v0) { return func_8002A088(*(s16 *)(v0 + 0x2E)); } return 0; } extern s32 func_8002A108(); extern s32 func_8002A26C(void); extern void func_8016E918(s16 a0, s16 a1); extern void func_8002D4C8(s32 a0, s32 a1); extern void func_8002AAAC(void); extern u8 D_800638FA[]; s32 func_8002A088(s32 arg0) { s32 i; s32 off; if (func_8002A108() != 0) { i = func_8002A26C(); off = i * 16; i = (i - 1) * 16; func_8016E918(0, *(u16 *)(D_800638FA + off) - *(u16 *)(D_800638FA + i)); func_8002D4C8(0xB2C, 0); func_8002AAAC(); return 1; } return 0; } extern s32 func_8002A26C(void); extern s32 func_8002AA00(s32 a0); extern s32 func_8002A28C(void); extern s32 func_8002A200(void); extern void func_8002A234(void); extern s32 D_80078EE8; s32 func_8002A108(s32 arg0) { s32 checkA00; checkA00 = func_8002AA00(func_8002A26C() + 1); if (func_8002A28C() != 0) { if (checkA00 >= 0) { D_80078EE8 += arg0; if (func_8002A200() != 0) { if (checkA00 != 0) { D_80078EE8 = func_8002A28C(); return 0; } func_8002A234(); return 1; } } } return 0; } extern s32 func_8002A26C(void); extern s32 func_8002AA00(s32 a0); extern s32 func_8002A27C(void); extern s32 func_8002A28C(void); s32 func_8002A1B4(void) { s32 temp; s32 check; s32 val1; s32 val2; s32 s0_val; temp = func_8002A26C(); check = func_8002AA00(temp + 1); if (check == 0) { return 0; } val1 = func_8002A27C(); val2 = func_8002A28C(); s0_val = val1 ^ val2; if (s0_val == 0) { return 1; } return 0; } extern s32 func_8002A28C(void); extern s32 func_8002A27C(void); s32 func_8002A200(void) { s32 val_28C = func_8002A28C(); s32 val_27C = func_8002A27C(); return (val_27C >= val_28C) ? 1 : 0; } extern s32 D_80078EE4; extern s32 D_80078EE8; void func_8002A234(void) { s32 v1 = D_80078EE4 + 1; if (v1 >= 0x1F) { v1 = 0x1E; } D_80078EE4 = v1; D_80078EE8 = 0; } extern s32 D_80078EE4; s32 func_8002A26C(void) { return D_80078EE4; } extern s32 D_80078EE8; s32 func_8002A27C(void) { return D_80078EE8; } extern s32 D_80078EE4; extern u8 D_800638F8[]; s32 func_8002A28C(void) { return *(u16 *)(D_800638F8 + D_80078EE4 * 16); } extern s32 D_80078EE4; extern u8 D_800638FA[]; s32 func_8002A2B0(void) { return *(u16 *)(D_800638FA + D_80078EE4 * 16); } extern s32 func_8002A354(s32 arg0); extern s32 func_8002A4B8(void); extern void func_8016E918(s16 a0, s16 a1); extern void func_8002D4C8(s32 a0, s32 a1); extern void func_8002AAAC(void); extern u8 D_800638FE[]; s32 func_8002A2D4(s32 a0) { s32 i; s32 off; if (func_8002A354(a0) != 0) { i = func_8002A4B8(); off = i * 16; i = (i - 1) * 16; func_8016E918(1, *(u16 *)(D_800638FE + off) - *(u16 *)(D_800638FE + i)); func_8002D4C8(0xB2C, 0); func_8002AAAC(); return 1; } return 0; } extern s32 func_8002A4B8(void); extern s32 func_8002AA00(s32); extern s32 func_8002A4D8(void); extern s32 func_8002A44C(void); extern void func_8002A480(void); extern s32 D_80078EF0; s32 func_8002A354(s32 arg0) { s32 v0; s32 s0; v0 = func_8002A4B8(); s0 = func_8002AA00(v0 + 1); v0 = func_8002A4D8(); if (v0 != 0) { if (s0 < 0) { return 0; } D_80078EF0 += arg0; v0 = func_8002A44C(); if (v0 != 0) { if (s0 != 0) { D_80078EF0 = func_8002A4D8(); return 0; } func_8002A480(); return 1; } } return 0; } extern s32 func_8002AA00(s32); extern s32 func_8002A4D8(void); s32 func_8002A400(void) { s32 a, b; a = func_8002A4B8(); b = func_8002AA00(a + 1); if (!b) { return 0; } a = func_8002A4C8(); a ^= func_8002A4D8(); if (a == 0) { return 1; } return 0; } extern s32 func_8002A4D8(void); extern s32 func_8002A4C8(void); s32 func_8002A44C(void) { s32 result1 = func_8002A4D8(); s32 result2 = func_8002A4C8(); return !(result2 < result1); } extern s32 D_80078EEC; extern s32 D_80078EF0; void func_8002A480(void) { s32 v0 = D_80078EEC; s32 v1 = v0 + 1; if (v1 >= 0x1F) { v1 = 0x1E; } D_80078EEC = v1; D_80078EF0 = 0; } extern s32 D_80078EEC; s32 func_8002A4B8(void) { return D_80078EEC; } extern s32 D_80078EF0; s32 func_8002A4C8(void) { return D_80078EF0; } extern s32 D_80078EEC; extern u8 D_800638FC[]; s32 func_8002A4D8(void) { return *(u16 *)(D_800638FC + D_80078EEC * 16); } extern s32 D_80078EEC; extern u8 D_800638FE[]; s32 func_8002A4FC(s32 a0) { return *(u16 *)(D_800638FE + D_80078EEC * 16); } extern s32 func_8002A544(s32); void func_8002A520(s32 arg0) { func_8002A544(*(u8 *) (arg0 + 200)); } extern s32 func_8002A5C4(s32 a0); extern s32 func_8002A728(void); extern void func_8016E918(short param_1, short param_2); extern void func_8002D4C8(s32 a0, s32 a1); extern void func_8002AAAC(void); extern u16 D_80063902[][8]; s32 func_8002A544(s32 arg0) { s32 idx; if (func_8002A5C4(arg0) != 0) { idx = func_8002A728(); func_8016E918(3, (s16) (D_80063902[idx][0] - D_80063902[idx - 1][0])); func_8002D4C8(0xB2C, 0); func_8002AAAC(); return 1; } return 0; } extern s32 func_8002A6BC(void); extern void func_8002A6F0(void); extern s32 func_8002A728(void); extern s32 func_8002A748(void); extern s32 func_8002AA00(s32 a0); extern s32 D_80078F00; s32 func_8002A5C4(s32 param_1) { s32 s0, v0; s0 = func_8002AA00(func_8002A728() + 1); v0 = func_8002A748(); if (v0 != 0) { if (s0 < 0) { return 0; } D_80078F00 = D_80078F00 + param_1; if (func_8002A6BC() != 0) { if (s0 != 0) { D_80078F00 = func_8002A748(); return 0; } func_8002A6F0(); return 1; } } return 0; } extern s32 func_8002A728(void); extern s32 func_8002A738(void); extern s32 func_8002A748(void); extern s32 func_8002AA00(s32 a0); s32 func_8002A670(void) { s32 s0; if (func_8002AA00(func_8002A728() + 1) != 0) { s0 = func_8002A738(); return s0 == func_8002A748(); } else { return 0; } } extern s32 func_8002A748(void); s32 func_8002A6BC(void) { s32 a = func_8002A748(); s32 b = func_8002A738(); return !(b < a); } extern s32 D_80078EFC; extern s32 D_80078F00; void func_8002A6F0(void) { register s32 loaded asm("$2"); // $v0 register s32 result asm("$3"); // $v1 loaded = D_80078EFC; result = loaded + 1; if (result >= 0x1F) { result = 0x1E; } D_80078EFC = result; D_80078F00 = 0; } extern s32 D_80078EFC; s32 func_8002A728(void) { return D_80078EFC; } extern s32 D_80078F00; s32 func_8002A738(void) { return D_80078F00; } extern s32 D_80078EFC; extern u16 D_80063900[][8]; s32 func_8002A748(void) { return D_80063900[D_80078EFC][0]; } extern s32 D_80078EFC; extern u16 D_80063902[][8]; s32 func_8002A76C(void) { return D_80063902[D_80078EFC][0]; } extern s32 func_8002A7B4(s32); void func_8002A790(u8 *a0) { func_8002A7B4(a0[0xC9]); } extern s32 func_8002A834(s32 a0); extern s32 func_8002A998(void); extern void func_8016E918(s16 a0, s16 a1); extern void func_8002D4C8(s32 a0, s32 a1); extern void func_8002AAAC(void); extern T16 D_80063906[]; s32 func_8002A7B4(s32 a0) { s32 id; if (func_8002A834(a0) != 0) { id = func_8002A998(); func_8016E918(2, (s16) (D_80063906[id].unk00 - D_80063906[id - 1].unk00)); func_8002D4C8(0xB2C, 0); func_8002AAAC(); return 1; } return 0; } extern s32 D_80078EF8; s32 func_8002A834(s32 a0) { s32 s0; s0 = func_8002AA00(func_8002A998() + 1); if (func_8002A9B8() == 0) { goto ret0; } if (s0 < 0) { return 0; } D_80078EF8 = D_80078EF8 + a0; if (func_8002A92C() == 0) { goto ret0; } if (s0 != 0) { D_80078EF8 = func_8002A9B8(); return 0; } func_8002A960(); return 1; ret0: return 0; } extern s32 func_8002A998(void); extern s32 func_8002A9A8(void); extern s32 func_8002A9B8(void); extern s32 func_8002AA00(s32); s32 func_8002A8E0(void) { s32 s0, v0; v0 = func_8002AA00(func_8002A998() + 1); if (!v0) { return 0; } s0 = func_8002A9A8(); s0 = s0 ^ func_8002A9B8(); if (s0 == 0) { return 1; } return 0; } extern s32 func_8002A9B8(void); s32 func_8002A92C(void) { s32 val1 = func_8002A9B8(); s32 val2 = func_8002A9A8(); s32 cmp = (val2 < val1) ? 1 : 0; return cmp ^ 1; } extern s32 D_80078EF4; extern s32 D_80078EF8; void func_8002A960(void) { s32 v1 = D_80078EF4 + 1; if (v1 >= 0x1F) { v1 = 0x1E; } D_80078EF4 = v1; D_80078EF8 = 0; } extern s32 D_80078EF4; s32 func_8002A998(void) { return D_80078EF4; } extern s32 D_80078EF8; s32 func_8002A9A8(void) { return D_80078EF8; } extern s32 D_80078EF4; extern u16 D_80063904[][8]; s32 func_8002A9B8(void) { return D_80063904[D_80078EF4][0]; } extern T16 D_80063906[]; extern s32 D_80078EF4; s32 func_8002A9DC(void) { return D_80063906[D_80078EF4].unk00; } extern s32 func_8002AA3C(void); s32 func_8002AA00(s32 a0) { s32 ret; ret = func_8002AA3C(); if (ret != 0) { return !(a0 < ret); } return -1; } extern s32 func_80029504(void); s32 func_8002AA3C(void) { s32 v1 = func_80029504(); if (v1 >= 1200) { return 99; } if (v1 >= 900) { return 27; } if (v1 >= 600) { return 22; } if (v1 >= 200) { return 16; } if (v1 >= 10) { return 8; } return 0; } void func_8002AAAC(void) { } extern s32 func_8002A26C(void); extern s32 func_8002A4B8(void); extern s32 func_8002A728(void); extern s32 func_8002A998(void); extern s32 D_800638A0[]; extern s32 D_800638EC; s32 func_8002AAB4(void) { register s32 s0 __asm__("$16"); register s32 s1 __asm__("$17"); register s32 s2 __asm__("$18"); register s32 v0 __asm__("$2"); register s32 *v1 __asm__("$3"); register s32 a0; register s32 *a1; // Get values from the four functions s0 = func_8002A26C(); s2 = func_8002A4B8(); s1 = func_8002A728(); v0 = func_8002A998(); // Sum them: s0 = s0 + s2 + s1; v0 = s0 + v0 s0 = s0 + s2; s0 = s0 + s1; v0 = s0 + v0; // Rounding: if v0 < 0, add 3 before dividing by 4 if (v0 < 0) { v0 = v0 + 3; } // Divide by 4 using arithmetic right shift s0 = v0 >> 2; // Initialize pointer to table v1 = D_800638A0; a0 = v1[0]; // If the first threshold is 0, return the default if (a0 == 0) { return D_800638EC; } a1 = v1 + 1; // Search through the table while (1) { if (s0 >= a0) { return a1[0]; } v1 = v1 + 2; a0 = v1[0]; if (a0 == 0) { return D_800638EC; } a1 = a1 + 2; } } void func_8002AB64(void) { extern s32 func_800291B4(s32); extern void func_800291A0(s32, s32); extern void func_8002AC00(s32); u8 c; s16 i; c = func_800291B4(0x2E) + 1; func_800291A0(0x2E, c); if (c == 1) { for (i = 1; i < 7; i++) { func_8002AC00(i & 0xFF); } for (i = 0x3B; i < 0x40; i++) { func_800291A0(i, 3); } } } extern s32 func_800291B4(s32); extern void func_800291A0(s32, s32); extern u16 D_800A6588[]; extern u8 D_8010F46E[]; extern u8 D_8010F46C[]; void func_8002AC00(s32 a0) { register s32 s2 __asm__("$18"); /* $s2 */ register s32 s0 __asm__("$16"); /* $s0 */ register s32 s3 __asm__("$19"); /* $s3 */ register s32 v0 __asm__("$2"); register s32 v1 __asm__("$3"); register s32 zr __asm__("$0"); register s32 arg1 __asm__("$4"); /* $a0 */ s32 s1, tmp; v0 = a0; v1 = v0 + 0x62; s2 = v1 & 0xFF; __asm__("" : "=r"(s2) : "0"(s2)); arg1 = s2; s0 = v0 + zr; s3 = (s32)D_800A6588; __asm__("" : "=r"(s3) : "0"(s3)); v0 = func_800291B4(arg1); s0 = s0 & 0xFF; if (v0 & 0x80) { return; } s1 = s0 << 3; tmp = D_8010F46E[s1]; tmp = tmp | 0x80; func_800291A0(s2, tmp); s0 = s0 << 1; v0 = *(u16*)&D_8010F46C[s1]; s0 = s0 + s3; *(u16*)(s0 - 2) = (u16)v0; } extern s32 func_800291B4(s32); extern u16 D_800A6588[]; extern u8 D_8010F46C[]; extern u8 D_80078EBA; void func_8002AC98(void) { register u16 *base __asm__("$20"); s16 i; s16 val; s32 t; s32 r; s32 idx; s16 rc; s32 b; base = D_800A6588; if (D_80078EBA == 0) { for (i = 0; i < 0x40; i++) { t = func_800291B4(((s16) i) + 0x63) & 0xF; if (t != 0) { r = rand() & 0x1F; switch (t) { case 1: rc = r; if ((rc != 0) && (rc != 0xB)) { b = base[i] - 0xB; { s32 xt = r + b; val = xt; } } else { val = ((s16) base[i]) >> 1; } break; case 2: if (r != 0) { b = base[i] - 0xB; { s32 xt = r + b; val = xt; } } break; case 3: rc = r; if ((rc != 0) && (rc != 0xB)) { b = base[i] - 0xB; { s32 xt = r + b; val = xt; } } else { val = base[i] * 2; } break; } idx = i + 1; if (val < (*((s16 *) (&D_8010F46C[idx * 8])))) { base[i] = *((s16 *) (&D_8010F46C[idx * 8])); } else if (((*((s16 *) (&D_8010F46C[idx * 8]))) * 4) < val) { rc = 4; base[i] = (*((s16 *) (&D_8010F46C[idx * 8]))) * rc; } else { base[i] = val; } } } } } s32 func_8002AE60(void) { s16 i; s32 count; i = 0; count = 0; for (; i < 0x2B; i++) { if (func_800291B4((s16)i + 0x63) & 0x40) { count++; } } return (count << 16) > 0x2A0000; } extern s32 D_800760C8; void func_8002AED0(void) { D_800760C8 = 1; } extern s32 D_800760C8; void func_8002AEE4(void) { D_800760C8 = 2; } extern s32 D_800760C8; void func_8002AEF8(void) { D_800760C8 = 0; } s32 func_8002AF08(void) { return D_800760C8; } extern s32 D_800760AC; extern s32 D_8007609C; extern s32 D_800760A0; extern s32 D_800760A4; extern s32 D_800760A8; void func_8002AF18(void) { D_800760AC = 0; D_8007609C = 0; D_800760A0 = 0; D_800760A4 = 0; D_800760A8 = 0; } extern u8 D_80075A50[]; u8 *func_8002AF48(s32 a0) { return &D_80075A50[(a0 + 1) * 0x80]; } extern s32 D_80075A40; s32 func_8002AF60(void) { return D_80075A40; } extern void func_80016714(void *a0, s32 a1); extern Blk16 D_80075CC0[]; extern u32 D_80076040; void func_8002AF70(void *a0, u32 a1) { register Blk16 *src __asm__("$17") = (Blk16 *)a0; register u32 val __asm__("$18") = a1; register Blk16 *dest __asm__("$16"); Blk16 *end; __asm__( "lui %0, %%hi(D_80075CC0)\n\taddiu %0, %0, %%lo(D_80075CC0)" : "=r"(dest) : "r"(val) ); func_80016714(dest, 0x300); end = src + 0x2D0 / 0x10; do { *dest = *src; src++; dest++; } while (src != end); __asm__ volatile("" : "=r"(src) : "0"(src)); *(Blk12 *)dest = *(Blk12 *)src; D_80076040 = val; } extern Blk16 D_80075CC0[]; void func_8002B00C(void *dest) { Blk16 *d = (Blk16 *)dest; Blk16 *s = D_80075CC0; Blk16 *end = s + (0x2D0 / 0x10); do { *d = *s; s++; d++; } while (s != end); *(Blk12 *)d = *(Blk12 *)s; } u16 func_8002B064(u8 *a0) { u32 sum = 0; s32 a1 = (s32)a0 + 0x70; do { sum += *a0; a0++; } while ((s32)a0 < a1); return sum & 0xFFFF; } u16 func_8002B08C(s32 a0) { s32 v1 = 0; s32 a1 = a0 + 0x300; do { v1 += *(u8 *)a0; a0++; } while (a0 < a1); return v1 & 0xFFFF; }