#include "common.h" #include "psyq/libcd.h" #include "shared/clearTbl40.h" /* dedup group I0: func_80037004 / func_80037334 share one body */ /* hoisted by gate_main so drafts above can reuse them (§181) */ typedef struct { s32 a; s32 b[4]; } OtBlk_80016450; 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; typedef struct Ent30D80 { /* 0x00 */ s32 unk00; /* 0x04 */ u8 pad04[6]; /* 0x0A */ u16 unk0A; /* 0x0C */ u8 pad0C[0x34]; /* 0x40 */ void (*unk40)(s32, s32); /* 0x44 */ s32 unk44; /* 0x48 */ u8 pad48[6]; /* 0x4E */ u8 unk4E; /* 0x4F */ u8 pad4F; /* 0x50 */ u8 unk50; /* 0x51 */ u8 unk51; } Ent30D80; typedef struct Rec14 { /* 0x00 */ u16 unk00; /* 0x02 */ u16 unk02; /* 0x04 */ u32 unk04; /* 0x08 */ u32 unk08; /* 0x0C */ u16 unk0C; /* 0x0E */ u16 unk0E; /* 0x10 */ u32 unk10; } Rec14; /* 0x14 */ typedef struct Slot54 { /* 0x00 */ s16 unk00; /* 0x02 */ s16 unk02; /* 0x04 */ s16 unk04; /* 0x06 */ s16 unk06; /* 0x08 */ u16 unk08; /* 0x0A */ s8 unk0A; } Slot54; typedef struct Owner4EE8 { /* 0x00 */ u8 pad00[0x14]; /* 0x14 */ Rec14 **unk14; } Owner4EE8; /* 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; typedef struct { u16 f0; } H2; /* 0x2 */ typedef struct { s32 unk00; u8 unk04; u8 unk05; u8 unk06; u8 unk07; u8 unk08; u8 unk09; u8 unk0A; u8 unk0B; s32 unk0C; } Rsc16; /* 0x10 */ INCLUDE_ASM("asm/nonmatchings/800", func_800123F0); INCLUDE_ASM("asm/nonmatchings/800", func_80012558); INCLUDE_ASM("asm/nonmatchings/800", func_800126C4); INCLUDE_ASM("asm/nonmatchings/800", func_8001282C); 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); } INCLUDE_ASM("asm/nonmatchings/800", func_800128EC); 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; } } INCLUDE_ASM("asm/nonmatchings/800", func_80012A60); s32 func_80012AB0(s32 arg0, s32 arg1) { return (arg0 - arg1) & 0xFFF; } INCLUDE_ASM("asm/nonmatchings/800", func_80012ABC); INCLUDE_ASM("asm/nonmatchings/800", func_80012B04); INCLUDE_ASM("asm/nonmatchings/800", func_80012B58); INCLUDE_ASM("asm/nonmatchings/800", func_80012C6C); INCLUDE_ASM("asm/nonmatchings/800", func_80012CB8); INCLUDE_ASM("asm/nonmatchings/800", func_80012D0C); INCLUDE_ASM("asm/nonmatchings/800", func_80012DBC); INCLUDE_ASM("asm/nonmatchings/800", func_80012E0C); INCLUDE_ASM("asm/nonmatchings/800", func_80012E6C); INCLUDE_ASM("asm/nonmatchings/800", func_80012F74); INCLUDE_ASM("asm/nonmatchings/800", func_80012FC8); INCLUDE_ASM("asm/nonmatchings/800", func_80013028); INCLUDE_ASM("asm/nonmatchings/800", func_800130D0); INCLUDE_ASM("asm/nonmatchings/800", func_8001311C); INCLUDE_ASM("asm/nonmatchings/800", func_80013154); 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); } INCLUDE_ASM("asm/nonmatchings/800", func_80013478); INCLUDE_ASM("asm/nonmatchings/800", func_800134FC); INCLUDE_ASM("asm/nonmatchings/800", func_80013694); INCLUDE_ASM("asm/nonmatchings/800", func_8001382C); INCLUDE_ASM("asm/nonmatchings/800", func_800139C8); INCLUDE_ASM("asm/nonmatchings/800", func_80013B64); INCLUDE_ASM("asm/nonmatchings/800", func_80013CFC); INCLUDE_ASM("asm/nonmatchings/800", func_80013E94); INCLUDE_ASM("asm/nonmatchings/800", func_80013ED0); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_80013F98); INCLUDE_ASM("asm/nonmatchings/800", func_80013FBC); INCLUDE_ASM("asm/nonmatchings/800", func_80013FE0); INCLUDE_ASM("asm/nonmatchings/800", func_80014004); INCLUDE_ASM("asm/nonmatchings/800", func_80014028); INCLUDE_ASM("asm/nonmatchings/800", func_8001404C); INCLUDE_ASM("asm/nonmatchings/800", func_80014070); INCLUDE_ASM("asm/nonmatchings/800", func_80014094); INCLUDE_ASM("asm/nonmatchings/800", func_800140B8); INCLUDE_ASM("asm/nonmatchings/800", func_80014128); INCLUDE_ASM("asm/nonmatchings/800", func_80014148); 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); } INCLUDE_ASM("asm/nonmatchings/800", func_800141AC); 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(); } INCLUDE_ASM("asm/nonmatchings/800", func_80014238); 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(); } INCLUDE_ASM("asm/nonmatchings/800", func_80014390); 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); } INCLUDE_ASM("asm/nonmatchings/800", func_800144D4); void func_80014554(void) { } void func_8001455C(void) { } INCLUDE_ASM("asm/nonmatchings/800", func_80014564); INCLUDE_ASM("asm/nonmatchings/800", func_80014588); INCLUDE_ASM("asm/nonmatchings/800", func_800145EC); INCLUDE_ASM("asm/nonmatchings/800", func_800146B0); 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(); } INCLUDE_ASM("asm/nonmatchings/800", func_80014998); 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); } INCLUDE_ASM("asm/nonmatchings/800", func_80014C54); INCLUDE_ASM("asm/nonmatchings/800", func_80014CAC); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_80015118); 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(); } } INCLUDE_ASM("asm/nonmatchings/800", func_800152F4); INCLUDE_ASM("asm/nonmatchings/800", func_80015310); INCLUDE_ASM("asm/nonmatchings/800", func_8001534C); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_800153CC); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_8001544C); 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(s32, s32); 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: 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; } } } INCLUDE_ASM("asm/nonmatchings/800", func_80015608); INCLUDE_ASM("asm/nonmatchings/800", func_80015760); INCLUDE_ASM("asm/nonmatchings/800", func_80015908); INCLUDE_ASM("asm/nonmatchings/800", func_80015954); INCLUDE_ASM("asm/nonmatchings/800", func_80015978); INCLUDE_ASM("asm/nonmatchings/800", func_8001599C); INCLUDE_ASM("asm/nonmatchings/800", func_800159C0); INCLUDE_ASM("asm/nonmatchings/800", func_800159E4); INCLUDE_ASM("asm/nonmatchings/800", func_80015A08); INCLUDE_ASM("asm/nonmatchings/800", func_80015A2C); INCLUDE_ASM("asm/nonmatchings/800", func_80015A50); #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 INCLUDE_ASM("asm/nonmatchings/800", func_80015A74); #endif INCLUDE_ASM("asm/nonmatchings/800", func_80015AD0); INCLUDE_ASM("asm/nonmatchings/800", func_80015B6C); /* 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); } INCLUDE_ASM("asm/nonmatchings/800", func_80016224); /* 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); } INCLUDE_ASM("asm/nonmatchings/800", func_800167F0); extern u16 D_800AF7CE; void func_800168B4(void) { D_800AF7CE = 0; } INCLUDE_ASM("asm/nonmatchings/800", func_800168C4); 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); } INCLUDE_ASM("asm/nonmatchings/800", func_80016978); INCLUDE_ASM("asm/nonmatchings/800", func_80016A3C); INCLUDE_ASM("asm/nonmatchings/800", func_80016A5C); INCLUDE_ASM("asm/nonmatchings/800", func_80016A7C); 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); } } INCLUDE_ASM("asm/nonmatchings/800", func_80016EB0); INCLUDE_ASM("asm/nonmatchings/800", func_80016ED4); INCLUDE_ASM("asm/nonmatchings/800", func_80016EF8); /* ---- 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); } } INCLUDE_ASM("asm/nonmatchings/800", func_80017168); INCLUDE_ASM("asm/nonmatchings/800", func_80017254); INCLUDE_ASM("asm/nonmatchings/800", func_80017274); /* 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); } } INCLUDE_ASM("asm/nonmatchings/800", func_8001739C); INCLUDE_ASM("asm/nonmatchings/800", func_800173BC); INCLUDE_ASM("asm/nonmatchings/800", func_800173DC); INCLUDE_ASM("asm/nonmatchings/800", func_800174DC); INCLUDE_ASM("asm/nonmatchings/800", func_800174FC); INCLUDE_ASM("asm/nonmatchings/800", func_8001751C); INCLUDE_ASM("asm/nonmatchings/800", func_800176F0); INCLUDE_ASM("asm/nonmatchings/800", func_80017714); INCLUDE_ASM("asm/nonmatchings/800", func_80017738); INCLUDE_ASM("asm/nonmatchings/800", func_80017758); /* 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); } } } INCLUDE_ASM("asm/nonmatchings/800", func_800178C8); INCLUDE_ASM("asm/nonmatchings/800", func_800178EC); INCLUDE_ASM("asm/nonmatchings/800", func_80017910); /* 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; } INCLUDE_ASM("asm/nonmatchings/800", func_80017DC4); 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); } INCLUDE_ASM("asm/nonmatchings/800", func_80017E68); INCLUDE_ASM("asm/nonmatchings/800", func_80017E8C); INCLUDE_ASM("asm/nonmatchings/800", func_80017F14); 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); } } INCLUDE_ASM("asm/nonmatchings/800", func_80018194); INCLUDE_ASM("asm/nonmatchings/800", func_8001820C); INCLUDE_ASM("asm/nonmatchings/800", func_80018384); INCLUDE_ASM("asm/nonmatchings/800", func_800183E0); INCLUDE_ASM("asm/nonmatchings/800", func_80018450); typedef struct { u16 x; u16 y; u16 w; u16 h; } SpadRect_800184F0; 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 */ 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; 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; 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; } } INCLUDE_ASM("asm/nonmatchings/800", func_80018714); /* 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; } } } } INCLUDE_ASM("asm/nonmatchings/800", func_80018918); INCLUDE_ASM("asm/nonmatchings/800", func_800189A8); 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 func_8005D184(s32); extern s32 func_8005D244(s32, s32, s32); extern void func_8005D4B8(s32, void*); extern void func_8005D538(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 (func_8005D184(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 (func_8005D244(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(); } func_8005D538(chan, &D_80062BBC, 2); func_8005D4B8(chan, &D_80062BAC); return 1; case 6: if (arg0 != 0) { return 1; } D_800AE610 = 0; if (func_8005D244(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(); } func_8005D538(chan, &D_800747B8, 2); func_8005D4B8(chan, &D_80062BB4); return 1; } } INCLUDE_ASM("asm/nonmatchings/800", func_80018C64); 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); } INCLUDE_ASM("asm/nonmatchings/800", func_80018E78); INCLUDE_ASM("asm/nonmatchings/800", func_80018E9C); 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); } INCLUDE_ASM("asm/nonmatchings/800", func_80018F88); INCLUDE_ASM("asm/nonmatchings/800", func_80018FC8); extern s32 D_800747C8; void func_80019018(void) { D_800747C8 = 0; } INCLUDE_ASM("asm/nonmatchings/800", func_80019028); extern void func_80016714(void *a0, s32 a1); extern s32 D_800747C0; void func_8001903C(void) { func_80016714(&D_800747C0, 8); } INCLUDE_ASM("asm/nonmatchings/800", func_80019064); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_800191A8); INCLUDE_ASM("asm/nonmatchings/800", func_800191BC); INCLUDE_ASM("asm/nonmatchings/800", func_800191D4); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_8001931C); 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 INCLUDE_ASM("asm/nonmatchings/800", func_80019930); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_80019A10); 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(char); /* the CdlReadN data-ready callback @0x8001A338 */ 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 INCLUDE_ASM("asm/nonmatchings/800", CdReadStateMachine); #endif INCLUDE_ASM("asm/nonmatchings/800", func_8001A0FC); INCLUDE_ASM("asm/nonmatchings/800", func_8001A114); #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 INCLUDE_ASM("asm/nonmatchings/800", CdReadSectorReadyCB); #endif INCLUDE_ASM("asm/nonmatchings/800", func_8001A9D8); INCLUDE_ASM("asm/nonmatchings/800", func_8001A9F8); 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 void func_8001ABBC(s32 a0, s32 a1, s32 a2, s32 a3, s32 a4); void func_8001AAA0(s32 arg0) { func_8001ABBC(1, arg0, 0, 0, 0); } INCLUDE_ASM("asm/nonmatchings/800", func_8001AAD0); void func_8001ABB4(void) { } INCLUDE_ASM("asm/nonmatchings/800", func_8001ABBC); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_8001AE90); extern s32 D_800BA1B4; s32 func_8001AF04(void) { s32 value = D_800BA1B4; if (value == 0) { return 0; } if (value == 3) { return 1; } return 2; } INCLUDE_ASM("asm/nonmatchings/800", func_8001AF34); INCLUDE_ASM("asm/nonmatchings/800", func_8001B0D4); 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(int arg0); 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) { 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(int arg0); 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) { 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 INCLUDE_ASM("asm/nonmatchings/800", func_8001B710); 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); } INCLUDE_ASM("asm/nonmatchings/800", func_8001BADC); INCLUDE_ASM("asm/nonmatchings/800", func_8001BB60); /* 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; } INCLUDE_ASM("asm/nonmatchings/800", func_8001BC6C); INCLUDE_ASM("asm/nonmatchings/800", SsGetMute); INCLUDE_ASM("asm/nonmatchings/800", func_8001BDA0); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_8001BFB0); INCLUDE_ASM("asm/nonmatchings/800", func_8001BFD0); extern s32 D_800747D8; s32 func_8001BFE8(void) { return D_800747D8; } INCLUDE_ASM("asm/nonmatchings/800", func_8001BFF8); 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(); } } INCLUDE_ASM("asm/nonmatchings/800", func_8001C044); INCLUDE_ASM("asm/nonmatchings/800", func_8001C0C8); INCLUDE_ASM("asm/nonmatchings/800", func_8001C14C); 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); } INCLUDE_ASM("asm/nonmatchings/800", func_8001C214); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_8001C320); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_8001C4A4); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_8001C5B8); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_8001C810); 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); } } INCLUDE_ASM("asm/nonmatchings/800", func_8001C9D0); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_8001CA88); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_8001CB6C); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_8001CF30); INCLUDE_ASM("asm/nonmatchings/800", func_8001CF48); INCLUDE_ASM("asm/nonmatchings/800", func_8001CFB8); INCLUDE_ASM("asm/nonmatchings/800", func_8001CFDC); INCLUDE_ASM("asm/nonmatchings/800", func_8001D050); INCLUDE_ASM("asm/nonmatchings/800", func_8001D074); void func_8001D0E8(s32 arg0, s32 arg1, s32 arg2) { *(s16 *)(arg0 + 0x28) = arg1; *(s16 *)(arg0 + 0x2A) = arg2; } INCLUDE_ASM("asm/nonmatchings/800", func_8001D0F4); INCLUDE_ASM("asm/nonmatchings/800", func_8001D130); INCLUDE_ASM("asm/nonmatchings/800", func_8001D150); INCLUDE_ASM("asm/nonmatchings/800", func_8001D16C); INCLUDE_ASM("asm/nonmatchings/800", func_8001D1C4); INCLUDE_ASM("asm/nonmatchings/800", func_8001D388); /* 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); } } INCLUDE_ASM("asm/nonmatchings/800", func_8001D70C); /* ---- 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_ASM("asm/nonmatchings/800", func_8001DA34); 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"); } INCLUDE_ASM("asm/nonmatchings/800", func_8001E378); /* ---- 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); } } INCLUDE_ASM("asm/nonmatchings/800", func_8001EA14); INCLUDE_ASM("asm/nonmatchings/800", func_8001EFE0); /* 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) { } INCLUDE_ASM("asm/nonmatchings/800", func_8001F97C); INCLUDE_ASM("asm/nonmatchings/800", func_8001F9F8); INCLUDE_ASM("asm/nonmatchings/800", func_8001FB8C); INCLUDE_ASM("asm/nonmatchings/800", func_8001FC08); /* 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; } INCLUDE_ASM("asm/nonmatchings/800", func_80020598); INCLUDE_ASM("asm/nonmatchings/800", func_80020A28); INCLUDE_ASM("asm/nonmatchings/800", func_80020DA4); /* 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); } INCLUDE_ASM("asm/nonmatchings/800", func_8002109C); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_80021174); INCLUDE_ASM("asm/nonmatchings/800", func_80021284); INCLUDE_ASM("asm/nonmatchings/800", func_800215F4); INCLUDE_ASM("asm/nonmatchings/800", func_80021D38); INCLUDE_ASM("asm/nonmatchings/800", func_800221A8); INCLUDE_ASM("asm/nonmatchings/800", func_800226C0); 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; } } INCLUDE_ASM("asm/nonmatchings/800", func_800234E4); 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; } } INCLUDE_ASM("asm/nonmatchings/800", func_8002374C); INCLUDE_ASM("asm/nonmatchings/800", func_80023BF0); INCLUDE_ASM("asm/nonmatchings/800", func_80024054); INCLUDE_ASM("asm/nonmatchings/800", func_800241C0); INCLUDE_ASM("asm/nonmatchings/800", func_800242D0); INCLUDE_ASM("asm/nonmatchings/800", func_800243EC); INCLUDE_ASM("asm/nonmatchings/800", func_80024448); INCLUDE_ASM("asm/nonmatchings/800", func_800249F0); /* ---- 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" ) typedef struct { u32 pad0; u32 rgb0; u32 rgb1; u32 rgb2; u16 n0; u16 v0; u16 v1; u16 v2; } TmdG3; 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; } INCLUDE_ASM("asm/nonmatchings/800", func_80026128); /* 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; } INCLUDE_ASM("asm/nonmatchings/800", func_80026514); INCLUDE_ASM("asm/nonmatchings/800", func_800268D0); INCLUDE_ASM("asm/nonmatchings/800", func_80026D64); INCLUDE_ASM("asm/nonmatchings/800", func_80027058); /* 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" ); } INCLUDE_ASM("asm/nonmatchings/800", func_800273F4); /* ---- 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; } INCLUDE_ASM("asm/nonmatchings/800", func_80027BF0); 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) { } INCLUDE_ASM("asm/nonmatchings/800", func_8002823C); void func_800282FC(void) { } INCLUDE_ASM("asm/nonmatchings/800", func_80028304); 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)); } INCLUDE_ASM("asm/nonmatchings/800", func_80028488); INCLUDE_ASM("asm/nonmatchings/800", func_8002850C); 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); } INCLUDE_ASM("asm/nonmatchings/800", SsUtReverbOff); 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); } INCLUDE_ASM("asm/nonmatchings/800", func_80028620); INCLUDE_ASM("asm/nonmatchings/800", func_80028694); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_80028D58); INCLUDE_ASM("asm/nonmatchings/800", func_80028D78); INCLUDE_ASM("asm/nonmatchings/800", func_80028D9C); INCLUDE_ASM("asm/nonmatchings/800", func_80028DBC); INCLUDE_ASM("asm/nonmatchings/800", func_80028DE0); INCLUDE_ASM("asm/nonmatchings/800", func_80028E00); INCLUDE_ASM("asm/nonmatchings/800", func_80028E24); INCLUDE_ASM("asm/nonmatchings/800", func_80028E44); INCLUDE_ASM("asm/nonmatchings/800", func_80028E68); INCLUDE_ASM("asm/nonmatchings/800", func_80028E88); INCLUDE_ASM("asm/nonmatchings/800", func_80028EAC); INCLUDE_ASM("asm/nonmatchings/800", func_80028ECC); INCLUDE_ASM("asm/nonmatchings/800", func_80028EF0); INCLUDE_ASM("asm/nonmatchings/800", func_80028F10); INCLUDE_ASM("asm/nonmatchings/800", func_80028F34); INCLUDE_ASM("asm/nonmatchings/800", func_80028F54); INCLUDE_ASM("asm/nonmatchings/800", func_80028F78); INCLUDE_ASM("asm/nonmatchings/800", func_80028F98); INCLUDE_ASM("asm/nonmatchings/800", func_80028FBC); INCLUDE_ASM("asm/nonmatchings/800", func_80028FDC); INCLUDE_ASM("asm/nonmatchings/800", func_80029000); INCLUDE_ASM("asm/nonmatchings/800", func_80029020); 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); } INCLUDE_ASM("asm/nonmatchings/800", func_800290BC); INCLUDE_ASM("asm/nonmatchings/800", func_800290E0); INCLUDE_ASM("asm/nonmatchings/800", func_80029104); INCLUDE_ASM("asm/nonmatchings/800", func_80029124); INCLUDE_ASM("asm/nonmatchings/800", func_80029178); INCLUDE_ASM("asm/nonmatchings/800", func_800291A0); INCLUDE_ASM("asm/nonmatchings/800", func_800291B4); INCLUDE_ASM("asm/nonmatchings/800", func_800291C8); INCLUDE_ASM("asm/nonmatchings/800", func_800291DC); INCLUDE_ASM("asm/nonmatchings/800", func_800291F0); INCLUDE_ASM("asm/nonmatchings/800", func_80029204); extern s32 D_800AE6A8; extern void func_80016714(void *a0, s32 a1); void func_80029218(void) { func_80016714(&D_800AE6A8, 0x3C); } INCLUDE_ASM("asm/nonmatchings/800", func_80029240); extern s32 D_80075838; void func_80029254(void) { D_80075838 = 0; } extern s32 D_80075838; s32 func_80029264(void) { return D_80075838; } typedef struct { s32 words[38]; } Blk98_80029274; 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(); } INCLUDE_ASM("asm/nonmatchings/800", func_80029344); extern void func_80016714(void *a0, s32 a1); extern u16 D_800A6588[]; void func_8002941C(void) { func_80016714(D_800A6588, 0x2); } INCLUDE_ASM("asm/nonmatchings/800", func_80029444); extern s32 D_80078E80; s32 func_80029504(void) { return D_80078E80; } extern s32 D_80078E80; void func_80029514(s32 arg0) { D_80078E80 = arg0; } INCLUDE_ASM("asm/nonmatchings/800", func_80029524); extern s32 D_80078E84; s32 func_8002953C(void) { return D_80078E84; } extern s32 D_80078E84; void func_8002954C(s32 arg0) { D_80078E84 = arg0; } INCLUDE_ASM("asm/nonmatchings/800", func_8002955C); extern s32 D_80078E88; s32 func_8002957C(void) { return D_80078E88; } extern s32 D_80078E88; void func_8002958C(s32 arg0) { D_80078E88 = arg0; } extern void 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(); } INCLUDE_ASM("asm/nonmatchings/800", func_80029690); INCLUDE_ASM("asm/nonmatchings/800", func_800296F8); INCLUDE_ASM("asm/nonmatchings/800", func_80029774); INCLUDE_ASM("asm/nonmatchings/800", func_800298BC); INCLUDE_ASM("asm/nonmatchings/800", func_8002992C); INCLUDE_ASM("asm/nonmatchings/800", func_80029A58); INCLUDE_ASM("asm/nonmatchings/800", func_80029A94); INCLUDE_ASM("asm/nonmatchings/800", func_80029AAC); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_80029B4C); INCLUDE_ASM("asm/nonmatchings/800", func_80029BC8); INCLUDE_ASM("asm/nonmatchings/800", func_80029C44); extern s32 rand(void); s32 func_80029CD4(s32 a0) { if (a0 == 0) return 0; return rand() % a0; } INCLUDE_ASM("asm/nonmatchings/800", func_80029D3C); INCLUDE_ASM("asm/nonmatchings/800", func_80029DB4); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_80029E94); INCLUDE_ASM("asm/nonmatchings/800", func_80029EC0); extern s32 func_80029504(void); extern s32 D_80078F04; extern s32 D_80078F08; extern s32 D_80078F0C; extern void 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; } INCLUDE_ASM("asm/nonmatchings/800", func_80029FE4); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_8002A088); INCLUDE_ASM("asm/nonmatchings/800", func_8002A108); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_8002A28C); INCLUDE_ASM("asm/nonmatchings/800", func_8002A2B0); INCLUDE_ASM("asm/nonmatchings/800", func_8002A2D4); INCLUDE_ASM("asm/nonmatchings/800", func_8002A354); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_8002A4D8); INCLUDE_ASM("asm/nonmatchings/800", func_8002A4FC); INCLUDE_ASM("asm/nonmatchings/800", func_8002A520); INCLUDE_ASM("asm/nonmatchings/800", func_8002A544); INCLUDE_ASM("asm/nonmatchings/800", func_8002A5C4); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_8002A748); INCLUDE_ASM("asm/nonmatchings/800", func_8002A76C); INCLUDE_ASM("asm/nonmatchings/800", func_8002A790); INCLUDE_ASM("asm/nonmatchings/800", func_8002A7B4); INCLUDE_ASM("asm/nonmatchings/800", func_8002A834); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_8002A9B8); INCLUDE_ASM("asm/nonmatchings/800", func_8002A9DC); INCLUDE_ASM("asm/nonmatchings/800", func_8002AA00); INCLUDE_ASM("asm/nonmatchings/800", func_8002AA3C); 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; } } INCLUDE_ASM("asm/nonmatchings/800", func_8002AB64); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_8002AC98); INCLUDE_ASM("asm/nonmatchings/800", func_8002AE60); INCLUDE_ASM("asm/nonmatchings/800", func_8002AED0); INCLUDE_ASM("asm/nonmatchings/800", func_8002AEE4); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_8002AF48); 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; } INCLUDE_ASM("asm/nonmatchings/800", func_8002B0B4); /* DEFERRED: SaveLoadRoutine (0x8002B154) — Phase 7 (session G), per Drew, to Q#5. * Original intent: the save / PS1 memory-card handler. Referenced by saveHeaderTemplate * (0x80072DF0) via THREE entry-point pointers: 0x8002B154 / 0x8002B1AC / 0x8002BEA4. * Dispatch branches on (selector & 7) (case 0 -> +1 counter; 1 -> 7; 2 -> 0x1E; 3 -> 0x23). * Why deferred (NOT a clean state machine like the 3 CD loaders drafted this session): * - splat emits ONE 1139-instruction stub spanning 0x8002B154-0x8002C31C with a SINGLE `jr $ra` * => it is effectively one large MULTI-ENTRY function (the 3 saveHeaderTemplate entries share a * return; the caller passes args in $s0/$s3) — awkward to express in C at all. * - Ghidra mis-analyses it: get_code(0x8002B154) returns only a tiny fragment using unaff_s0/ * unaff_s3 (caller-set regs), so there is no faithful whole-function decompile to translate. * - It is the save-data/memcard format, explicitly Phase-3 Q#5 "format still TBD" — a different * subsystem from the file/overlay loader cluster (which IS drafted: CdReadStateMachine, * CdReadSectorReadyCB, StreamLoadStateMachine here + the matched CdReadRequest/CdQueueBusy/…). * - External helpers it calls (uncharacterised): func_800603BC (x20), func_80060614, func_8006023C, * func_80060D9C, func_80060AE0, func_8005FFB4, func_8005FD58, func_80061114/524, func_80016714(bzero). * Re-enable / revisit when Q#5 (save/memcard format) is studied: FIRST fix the Ghidra function * boundary (make 0x8002B154 span the whole 1139 ins, or model the 3 entry points), re-decompile, * characterise the func_80060xxx memcard helpers, THEN draft. A wrong faithful-looking draft here * would be worse than this honest stub (P9/G3). The default build is byte-identical via this stub. */ INCLUDE_ASM("asm/nonmatchings/800", SaveLoadRoutine); extern u8 D_80075AC0[]; u32 func_8002C320(void) { register u32 result __asm__("$8") = 0; int count = 0; register u32 bit1 __asm__("$14") = 1; register u32 bit2 __asm__("$13") = 2; register u32 shift __asm__("$7") = 0; u8 *base = D_80075AC0; u8 *ptr_data = base + 0x10; u8 *ptr_check = base + 0x2; u8 *ptr_byte = base; u32 sum; u32 masked; int j; u16 val; for (; count < 4; count++) { if (*ptr_byte != 0) { val = *(u16 *)ptr_check; sum = 0; for (j = 0; j < 0x70; j++) { sum += ptr_data[j]; } masked = sum & 0xFFFF; if (val == masked) { result |= bit1 << shift; } else { result |= bit2 << shift; } } shift += 8; ptr_data += 0x80; ptr_check += 0x80; ptr_byte += 0x80; } return result; } INCLUDE_ASM("asm/nonmatchings/800", func_8002C3B0); INCLUDE_ASM("asm/nonmatchings/800", func_8002C410); extern s16 D_800C5328[]; extern s16 D_800C532A[]; void func_8002C8BC(void) { register s32 a0 __asm__("$4"); register s32 v1 __asm__("$3"); a0 = -1; v1 = 0; do { *(s16 *)((u8 *)D_800C5328 + v1) = a0; *(s16 *)((u8 *)D_800C532A + v1) = a0; v1 += 4; } while ((u32)v1 < 0x1E4); } INCLUDE_ASM("asm/nonmatchings/800", func_8002C8F4); INCLUDE_ASM("asm/nonmatchings/800", func_8002CC4C); INCLUDE_ASM("asm/nonmatchings/800", func_8002CCB4); INCLUDE_ASM("asm/nonmatchings/800", func_8002CCD8); extern s32 D_800A4638; extern u16 D_800A4F22; extern s16 D_8006A990; extern u8 D_800A4EE6; extern u16 D_800A4EE0; extern u16 D_800A4EE2; extern u16 D_800A4EE4; extern u8 D_800A4F16; extern u8 D_800A4EFE[]; extern u16 D_800A4E8E; extern u16 D_800A4E8A; extern s16 D_800A4E86; extern s8 D_800A4F17; extern u8 D_800A4F18; extern u8 D_8006AEF4; extern void func_8002D904(s32); extern s32 func_8003D25C(s32, s32, u8 *); extern void func_80037FC4(void); extern void func_8002EC10(void); extern s32 func_8003836C(s32 a0); extern void func_8002CFE4(void); extern s32 func_8003C4F0(s32); void func_8002CDD8(void) { s32 *ctr; u16 tmp; u16 cur; u16 tgt; u8 fl; s16 t; ctr = &D_800A4638; tmp = D_800A4F22; *ctr = *ctr + 1; if (tmp != 0) { D_8006A990 = tmp; func_8002D904(tmp); D_800A4F22 = 0; } fl = D_800A4EE6; if (fl & 1) { tgt = D_800A4EE4; cur = D_800A4EE0; D_800A4F16 = 1; if (tgt < cur) { D_800A4EE0 = cur - D_800A4EE2; if (tgt >= D_800A4EE0) { D_800A4EE0 = tgt; D_800A4EE6 = fl & 0xFE; } } else { D_800A4EE0 = cur + D_800A4EE2; if (D_800A4EE0 >= tgt) { D_800A4EE0 = tgt; D_800A4EE6 = fl & 0xFE; } } } func_8003D25C(0, 0x17, D_800A4EFE); if (D_800A4E8E & 8) { func_80037FC4(); t = *(s16 *)&D_800A4E8A; if (t != 0) { t--; D_800A4E8A = t; if (t == 0) { func_8002EC10(); } } if ((s16)func_8003836C(D_800A4E86) == 0) { D_800A4E8E &= 0xFFF7; } } if (*(u8 *)&D_800A4F17 == 0) { D_800A4F18 = 0; func_8002CFE4(); } else { D_800A4F18 = 1; } if ((D_8006AEF4 & 1) && !(D_8006AEF4 & 2)) { if (func_8003C4F0(0) != 0) { D_8006AEF4 &= 0xFE; } } } INCLUDE_ASM("asm/nonmatchings/800", func_8002CFE4); extern u8 D_800A46BA; extern void func_8002D29C(void); extern s32 D_800A64B0; extern u8 D_8006A980; extern void func_8002DF80(void); extern void (*D_800A4F24)(void); extern u8 D_800A4E70; extern s32 D_800A4E68; extern s16 D_800A4E6C; extern void func_8002D240(s32 a0); extern u8 D_800A4E7A; extern s16 D_800A4E76; extern u16 D_800A4E74; extern s16 D_800A4E78; void func_8002D034(void) { s32 *ptr; s32 i; if (D_800A46BA) { func_8002D29C(); } ptr = &D_800A64B0; for (i = 0; i < 8; i++, ptr = (s32 *)((u8 *)ptr + 0xC)) { if (ptr[0] != 0 && --ptr[0] == 0) { void (*func)(void *) = (void (*)(void *))ptr[2]; func(ptr); } } if ((D_8006A980 & 0xEF) != 0) { func_8002DF80(); } if (D_800A4F24 != NULL) { D_800A4F24(); } if (D_800A4E70 != 0) { if (--D_800A4E68 == 0) { s32 a0 = D_800A4E6C; D_800A4E70 = 0; func_8002D240(a0); } } { register u8 *flagPtr __asm__("$8"); flagPtr = &D_800A4E7A; if (*flagPtr != 0) { if (D_800A4E76 > (s16)D_800A4E74) { s16 delta = D_800A4E78; if ((s16)D_800A4E74 < D_800A4E76 - delta) { D_800A4E74 = (s16)D_800A4E74 + delta; } else { D_800A4E74 = D_800A4E76; *flagPtr = 0; } } else { s16 delta = D_800A4E78; if (D_800A4E76 + delta < (s16)D_800A4E74) { D_800A4E74 = (s16)D_800A4E74 - delta; } else { D_800A4E74 = D_800A4E76; *flagPtr = 0; } } func_8002D240((s16)D_800A4E74); } } } extern void func_8003B45C(s32 *); extern s32 D_800A4ECC; extern s32 D_800A4ED0; extern u16 D_800A4ED4; extern u16 D_800A4ED6; void func_8002D1F0(s32 arg0) { s32 *v1 = &D_800A4ECC; *v1 = 1; D_800A4ED0 = (s16)arg0; D_800A4ED4 = 0; D_800A4ED6 = 0; func_8003B45C(v1); } extern s32 D_800A4ECC; extern u16 D_8006ADD8[]; extern u16 D_800A4E74; extern u16 D_800A4ED4; extern u16 D_800A4ED6; extern void func_8003B45C(s32 *a0); void func_8002D240(s32 a0) { s32 index = (a0 << 16) >> 15; s32 *p = &D_800A4ECC; u16 value = *(u16 *)((u8 *)D_8006ADD8 + index); *p = 0x6; D_800A4E74 = (u16)a0; D_800A4ED4 = value; D_800A4ED6 = value; func_8003B45C(p); } INCLUDE_ASM("asm/nonmatchings/800", func_8002D29C); INCLUDE_ASM("asm/nonmatchings/800", func_8002D320); extern u8 D_800A46BA; u8 func_8002D4B8(void) { return D_800A46BA; } INCLUDE_ASM("asm/nonmatchings/800", func_8002D4C8); INCLUDE_ASM("asm/nonmatchings/800", func_8002D59C); INCLUDE_ASM("asm/nonmatchings/800", func_8002D678); extern u8 D_8006451C[]; extern s8 D_800A4F17; extern u8 D_800A4F18; extern s16 D_800A4EFC; extern s32 func_8002F4E4(u8 *); extern void func_8003324C(s32); extern void func_80030F80(void); extern void func_8002D320(void); extern void func_80031BE0(void); void func_8002D6D8(u32 a0) { s32 id; u8 v0; s8 *p = &D_800A4F17; *p = 1; id = a0 >> 16; a0 = (u16)a0; if ((u16)id < 0x100) { return; } { u8 *q = &D_8006451C[(u16)id * 4]; if ((*q & 0x7F) == 6) { id = func_8002F4E4(q); if ((u16)id == 0) { return; } } } if (a0 == 0) { return; } a0--; if (*(u16 *)((u8 *)&D_800A4F17 + a0 * 0x54 - 0x82B) == (u16)id) { func_8003324C((u16)a0); } v0 = D_800A4F18; *p = v0; if (v0 != 0) { s16 v0s = D_800A4EFC; if (v0s != 0) { D_800A4EFC = v0s - 1; } func_80030F80(); func_8002D320(); func_80031BE0(); D_800A4F18 = 0; *p = 0; } } void func_8002D7F4(void) { } extern s32 D_800A4E7C; void func_8002D7FC(s32 arg0) { D_800A4E7C = arg0; } INCLUDE_ASM("asm/nonmatchings/800", func_8002D80C); extern s16 D_8006A990; void func_8002D834(void) { D_8006A990 = 0; } INCLUDE_ASM("asm/nonmatchings/800", func_8002D844); INCLUDE_ASM("asm/nonmatchings/800", func_8002D858); INCLUDE_ASM("asm/nonmatchings/800", func_8002D8A8); INCLUDE_ASM("asm/nonmatchings/800", func_8002D8D4); INCLUDE_ASM("asm/nonmatchings/800", func_8002D904); INCLUDE_ASM("asm/nonmatchings/800", func_8002DC68); extern u8 D_8006A980; extern s16 D_8006A982; extern u8 D_8006A984; extern s8 D_800A4F17; extern u8 D_800A4F18; extern s16 D_800A4EFC; extern void func_8002DC68(s32 a0, s32 a1); extern void func_8002E138(s32 a0, s32 a1, s32 a2); extern void func_80030F80(void); extern void func_8002D320(void); extern void func_80031BE0(void); void func_8002DF80(void) { u8 v1; v1 = D_8006A980; if ((v1 & 0x7) != 0) { if ((v1 & 0x1) != 0 && (v1 & 0x2) != 0) { func_8002DC68(0x719, 0); D_8006A980 &= 0xF8; } if ((D_8006A980 & 0x7) != 0) { s16 val = D_8006A982; s32 a0; if (val <= 0) { a0 = 0x710; } else if (val >= 4) { a0 = 0x712; } else { a0 = 0x711; } func_8002DC68(a0, 0); D_8006A982 = 0; D_8006A980 &= 0xF8; } } v1 = D_8006A980; if ((v1 & 0x8) != 0) { u8 a1 = D_8006A984; if (a1 != 0) { func_8002DC68(0xA75, a1 | 0x1000); D_8006A984 = 0; D_8006A980 |= 0x10; } else { u8 v0; D_8006A980 = v1 & 0xEF; D_800A4F17 = 1; func_8002E138(4, 0xA75, 0); v0 = D_800A4F18; D_800A4F17 = v0; if (v0 != 0) { s16 v0s = D_800A4EFC; if (v0s != 0) { D_800A4EFC = v0s - 1; } func_80030F80(); func_8002D320(); func_80031BE0(); D_800A4F18 = 0; D_800A4F17 = 0; } } D_8006A980 &= 0xF7; } } INCLUDE_ASM("asm/nonmatchings/800", func_8002E138); INCLUDE_ASM("asm/nonmatchings/800", func_8002E5BC); INCLUDE_ASM("asm/nonmatchings/800", func_8002E5F8); extern u16 D_800A4E8E; extern s16 D_800A4E86; extern s32 D_80078F10; extern u16 D_8006A99C[]; extern u16 D_800A46B8; extern u8 D_8006AEEC; extern s32 D_800A46B4; extern u8 D_800A46BA; extern u16 D_800A4EF4; extern s32 func_8003836C(s32 a0); extern void func_8002E700(s32 a0); void func_8002E638(s32 a0) { s32 s0; s16 temp; u16 a0_masked; u16 v1; s0 = a0; if ((D_800A4E8E & 4) != 0) { temp = D_800A4E86; if ((func_8003836C(temp) << 16) != 0) { func_8002E700(s0 & 0xFFFF); return; } } if (D_80078F10 == 0) { return; } a0_masked = s0 & 0xFFFF; if (a0_masked < 5) { v1 = *(u16 *)((u8 *)D_8006A99C + a0_masked * 4); } else { v1 = 0x1F; } D_800A46B8 = v1; v1 = D_8006AEEC; D_800A46B4 = 0xFFFF; D_800A46BA = 1; D_800A4EF4 = v1; } extern u16 D_800A4E8E; extern u16 D_800A4E8A; extern s16 D_800A4E86; extern s16 D_8006A9BC[]; extern u16 D_8006A9B0[]; extern s32 func_8003836C(s32 a0); extern void func_8003819C(s32 a0, s32 a1, s32 a2, s32 a3); void func_8002E700(s32 a0) { u16 index; if ((D_800A4E8E & 0x4) == 0) return; if ((s16)func_8003836C(D_800A4E86) == 0) return; index = (u16)a0; if (index >= 5) index = 4; func_8003819C(D_800A4E86, 0, 0, D_8006A9BC[index]); D_800A4E8A = D_8006A9B0[index]; } INCLUDE_ASM("asm/nonmatchings/800", func_8002E79C); INCLUDE_ASM("asm/nonmatchings/800", func_8002E818); INCLUDE_ASM("asm/nonmatchings/800", func_8002E8DC); INCLUDE_ASM("asm/nonmatchings/800", func_8002E94C); INCLUDE_ASM("asm/nonmatchings/800", func_8002EA10); extern u16 D_800A4E8E; extern s16 D_800A4E86; extern void func_80038308(s16 a0); void func_8002EAB0(void) { register u16 *s0 __asm__("$16"); u16 v0; s0 = &D_800A4E8E; v0 = *s0; if ((v0 & 0x10) != 0) { func_80038308(D_800A4E86); v0 = *s0; v0 = (v0 | 0x9) & 0xFFEF; *s0 = v0; } } INCLUDE_ASM("asm/nonmatchings/800", func_8002EB10); extern void func_80036D58(int arg0); extern void func_80038308(s16 arg0); extern u16 D_800A4E8E; extern s16 D_800A4E86; void func_8002EBAC(void) { u16 v0; func_80036D58(0); if (D_800A4E8E & 0x10) { func_80038308(D_800A4E86); v0 = D_800A4E8E; v0 |= 0x9; v0 &= 0xFFEF; D_800A4E8E = v0; } } extern u16 D_800A4E8E; extern s16 D_800A4E86; extern u16 D_800A4E8C; extern u16 D_800A4F20; extern u16 D_800A4F22; extern u16 D_800A4E8A; extern u16 D_80068B66[][8]; extern u16 D_800A4EA2; extern s16 D_800A4E9A; extern u16 D_800A4EA0; extern s32 func_8003836C(s32 a0); extern void func_800383A4(s16 a0); extern void func_800384A8(s16 a0); extern void func_800385C0(s16 a0); void func_8002EC10(void) { u16 v0; s16 *s0; D_800A4F20 = 0; D_800A4F22 = 0; D_800A4E8A = 0; if (D_800A4E8E & 4) { if ((s16)func_8003836C(D_800A4E86) && (D_80068B66[D_800A4E8C][0] & 1)) { if (D_800A4EA2 & 4) { func_800385C0(D_800A4E9A); D_800A4EA2 &= 0xFEE8; } func_800383A4(D_800A4E86); v0 = D_800A4E8E; D_800A4E8E = v0 & 0xFFFD; D_800A4EA2 = v0 & 0xFFFD; D_800A4E9A = D_800A4E86; D_800A4EA0 = D_800A4E8C; D_800A4E8E = v0 & 0xFEF9; return; } s0 = &D_800A4E86; if ((s16)func_8003836C(*s0)) { func_800384A8(*s0); } D_800A4E8E &= 0xFEE8; func_800385C0(*s0); } } extern void func_8002EC10(void); extern void func_800385C0(s16); extern u16 D_800A4EA2; extern s16 D_800A4E9A; void func_8002ED90(void) { u16* ptr; func_8002EC10(); ptr = &D_800A4EA2; if (*ptr & 0x4) { func_800385C0(D_800A4E9A); *ptr = 0; } } INCLUDE_ASM("asm/nonmatchings/800", func_8002EDE4); INCLUDE_ASM("asm/nonmatchings/800", func_8002EE64); extern void func_80036EE8(void); extern void func_8002EC10(void); void func_8002EE90(void) { func_80036EE8(); func_8002EC10(); } INCLUDE_ASM("asm/nonmatchings/800", func_8002EEB8); INCLUDE_ASM("asm/nonmatchings/800", func_8002EED8); extern void func_80034844(void); extern void func_80031B7C(void); void func_8002EFD0(void) { func_80034844(); func_80031B7C(); } extern s32 D_800A2B98; extern s32 D_800C7D20; void func_8002EFF8(s32 a0, s32 a1) { register s32 v0 __asm__("$2"); register s32 v1 __asm__("$3"); register s32 a0_tmp __asm__("$4"); v0 = 1; if (a0 == v0) { v0 = ~a1; v1 = D_800A2B98; a0_tmp = D_800C7D20; v1 = v1 & v0; a0_tmp = a0_tmp | a1; D_800A2B98 = v1; D_800C7D20 = a0_tmp; } else { v0 = ~a1; v1 = D_800C7D20; a0_tmp = D_800A2B98; v1 = v1 & v0; a0_tmp = a0_tmp | a1; D_800C7D20 = v1; D_800A2B98 = a0_tmp; } } extern s32 D_800A2BA0; extern s32 D_800C7D2C; void func_8002F064(s32 a0, s32 a1) { register s32 v0 __asm__("$2"); register s32 v1 __asm__("$3"); register s32 out_a0 __asm__("$4"); if (a0 == 1) { v0 = ~a1; v1 = D_800A2BA0; out_a0 = D_800C7D2C; v1 = v1 & v0; out_a0 = out_a0 | a1; D_800A2BA0 = v1; D_800C7D2C = out_a0; } else { v0 = ~a1; v1 = D_800C7D2C; out_a0 = D_800A2BA0; v1 = v1 & v0; out_a0 = out_a0 | a1; D_800C7D2C = v1; D_800A2BA0 = out_a0; } } INCLUDE_ASM("asm/nonmatchings/800", func_8002F0D0); extern s32 D_800A64B0; void *func_8002F0F4(void) { s32 *ptr = (s32 *)&D_800A64B0; s32 i = 0; while (i < 8) { s32 val = *ptr; if (val == 0) { return (void *)ptr; } i++; ptr = (s32 *)((s32)ptr + 0xC); } return 0; } INCLUDE_ASM("asm/nonmatchings/800", func_8002F12C); INCLUDE_ASM("asm/nonmatchings/800", func_8002F150); INCLUDE_ASM("asm/nonmatchings/800", func_8002F1CC); INCLUDE_ASM("asm/nonmatchings/800", func_8002F248); INCLUDE_ASM("asm/nonmatchings/800", func_8002F4E4); INCLUDE_ASM("asm/nonmatchings/800", func_8002F5C8); extern s16 D_800A4EF0; extern void func_80031D70(void); void func_8002F620(void) { D_800A4EF0 = 0; func_80031D70(); } extern s16 D_800A4EF0; s16 func_8002F648(void) { return D_800A4EF0; } INCLUDE_ASM("asm/nonmatchings/800", func_8002F658); extern u8 D_800A4698; extern s16 D_800A4688; extern s16 D_800C5328[]; extern s16 D_800A46A2; extern u8 D_800A46B0; extern void func_800415A8(s32); void func_8002F67C(void) { u8 *p = &D_800A4698; s32 v0; s32 t; if (*p == 0) { v0 = D_800A4688; *(s16 *)((u8 *)D_800C5328 + (v0 << 2)) = -1; *p = 1; } t = D_800A46A2; if (t >= 0) { func_800415A8(t); D_800A46A2 = -1; } if (D_800A46B0 == 0) { D_800A46B0 = 1; } } extern u8 D_800760D0; extern u8 D_800760D4; extern u8 D_800760D8; extern u8 D_800760DC; extern u8 D_8006A96E; extern void (*D_800A4F24)(void); extern void func_8002F80C(void); void func_8002F714(s32 a0, s32 a1) { switch (a0 & 0xFFFF) { case 0: D_800760D0 = a1 & 0x7F; D_800A4F24 = func_8002F80C; D_8006A96E |= 1; break; case 1: D_800760D4 = a1 & 0x7F; D_800A4F24 = func_8002F80C; D_8006A96E |= 2; break; case 2: D_800760D8 = a1 & 0x7F; D_800A4F24 = func_8002F80C; D_8006A96E |= 4; break; case 3: D_800760DC = a1 & 0x7F; D_800A4F24 = func_8002F80C; D_8006A96E |= 8; break; } } INCLUDE_ASM("asm/nonmatchings/800", func_8002F80C); INCLUDE_ASM("asm/nonmatchings/800", func_8002FA3C); extern s16 D_800A46CC; void func_8002FAE0(void) { D_800A46CC = 0; func_8002C8BC(); } INCLUDE_ASM("asm/nonmatchings/800", func_8002FB08); extern s32 func_8003C4F0(s32); extern void func_8003C498(s32); extern void SpuWrite(s32, s32); extern void func_80037D74(void); extern u8 D_8006AEF4; extern s32 D_800A46C8; int func_8002FC64(int nbytes, u32 *src); /* stage/copy a payload run */ int func_8002FC64(int nbytes, u32 *src) { register s32 s2 __asm__("$18") = nbytes; register u32 *s1 __asm__("$17") = src; register s32 *s0; s32 result; if (D_8006AEF4 & 1) { if (func_8003C4F0(0) == 0) { func_80037D74(); return 0; } } s0 = (s32 *)(&D_800A46C8); result = ((s32 (*)(s32))func_8003C498)(*s0); if (result != 0) { SpuWrite((s32)s1, s2); *s0 += s2; D_8006AEF4 |= 1; return 1; } func_80037D74(); return 0; } extern s16 D_800A46CC; extern u8 D_8006AEF4; extern void func_80037D74(void); extern s32 func_8003C4F0(s32); extern s32 func_8002FF0C(s32, s32); extern void func_8002D240(s32 a0); int func_8002FD14(int buf, int len) { s32 result; s32 temp; if (D_800A46CC == 0) { func_80037D74(); temp = func_8003C4F0(0); if (temp != 0) { D_8006AEF4 &= 0xFE; result = func_8002FF0C(buf, len); } else { result = 0; } } else { result = func_8002FF0C(buf, len); } if (result != 0) { if (buf == 0x1A) { func_8002D240(0x28); } return result; } return 0; } INCLUDE_ASM("asm/nonmatchings/800", func_8002FDC8); INCLUDE_ASM("asm/nonmatchings/800", func_8002FDE8); INCLUDE_ASM("asm/nonmatchings/800", func_8002FF0C); INCLUDE_ASM("asm/nonmatchings/800", func_800301A4); INCLUDE_ASM("asm/nonmatchings/800", func_800301C8); INCLUDE_ASM("asm/nonmatchings/800", func_80030470); INCLUDE_ASM("asm/nonmatchings/800", func_800304C8); INCLUDE_ASM("asm/nonmatchings/800", func_80030538); INCLUDE_ASM("asm/nonmatchings/800", func_80030634); INCLUDE_ASM("asm/nonmatchings/800", func_80030730); /* 0x0C stride record at D_80064D44 (D_80064D4A == this + 6, cf. src/800.c Rsc12) */ typedef struct { /* 0x00 */ u8 *unk00; /* 0x04 */ u8 pad04[2]; /* 0x06 */ u8 unk06; /* 0x07 */ u8 unk07; /* 0x08 */ u8 unk08; /* 0x09 */ u8 pad09[3]; } A12; /* 0x0C */ /* 0x0C stride record pointed at by D_8006A970[] */ typedef struct { /* 0x00 */ s32 unk00; /* 0x04 */ s16 unk04; /* 0x06 */ s16 unk06; /* 0x08 */ s16 unk08; /* 0x0A */ u8 unk0A; /* 0x0B */ u8 unk0B; } B12; /* 0x0C */ /* 0x18 scratch filled by func_8003F144 */ typedef struct { /* 0x00 */ u8 pad00[4]; /* 0x04 */ u8 unk04; /* 0x05 */ u8 pad05[11]; /* 0x10 */ u16 unk10; /* 0x12 */ u16 unk12; /* 0x14 */ u8 pad14[2]; /* 0x16 */ s16 unk16; /* 0x18 */ u8 pad18[8]; } C24; /* 0x20 */ extern s16 D_800A46CC; extern s16 D_800A46CE[]; extern s16 D_800A46D0; extern s16 D_800A46D2[]; extern s16 D_800A4644[]; extern s16 D_800A4646[]; extern s16 D_800A4648[]; extern u8 D_800A4650[]; extern s16 D_800C5328[]; extern s16 D_800C532A[]; extern A12 D_80064D44[]; extern B12 *D_8006A970[]; extern s32 D_800760F0; extern s32 D_800760F4; extern s16 func_80042374(s32); extern s16 func_8003F144(s32, s32, s32, C24 *); extern s32 func_8003F380(s32, s32); s32 func_80030A14(void) { C24 sp10; A12 *e; u8 *p; B12 *q; s32 count; s32 i; s32 r; s32 t; if (func_80042374(0) == 0) { return 0; } e = &D_80064D44[D_800A46D0]; p = e->unk00 + D_800760F0 * 2; q = &D_8006A970[D_800A46CE[0]][D_800760F0]; t = D_800A46CE[0] * 24; if (D_800760F4 < 11) { if (e->unk06 != 0) { *(s16 *)((u8 *)D_800A4646 + t) = e->unk07; D_800A4644[D_800A46CE[0] * 12] = e->unk06; D_800A4648[D_800A46CE[0] * 12] = e->unk08; D_800C532A[e->unk08 * 2] = D_800A46CE[0]; } count = D_800760F4; D_800760F4 = 0; D_800C5328[D_800A46D0 * 2] = D_800A46CE[0]; } else { count = 10; D_800760F4 -= 10; } for (i = 0; i < count; i++, p += 2, q++) { if (func_8003F144(D_800A46D2[0], p[0], p[1], &sp10) == 0) { r = func_8003F380(D_800A46D2[0], sp10.unk16); if (r >= 0) { q->unk0A = 1; q->unk04 = sp10.unk04 << 8; q->unk00 = r; q->unk06 = sp10.unk10; q->unk08 = sp10.unk12; } } } if (D_800760F4 == 0) { D_800A4650[D_800A46CE[0] * 24] = 0; D_800A46CC = 0; return 1; } D_800760F0 += 10; return 0; } INCLUDE_ASM("asm/nonmatchings/800", func_80030CA4); typedef struct Blk48_30D80 { /* 0x00 */ s32 unk00; /* 0x04 */ u8 pad04[0x3D]; /* 0x41 */ u8 unk41; /* 0x42 */ u8 pad42[6]; } Blk48_30D80; extern s8 D_800A4F17; extern s32 D_800C7D20; extern s32 D_80073140[]; extern Blk48_30D80 D_800A4C2C[]; extern void func_8002EFF8(s32 a0, s32 a1); extern void func_80031988(Ent30D80 *arg0); void func_80030D80(Ent30D80 *arg0, s16 arg1) { u8 old; u8 flag; old = *(u8 *)&D_800A4F17; *(u8 *)&D_800A4F17 = 1; if (arg0->unk00 != 0) { flag = 1; arg0->unk00 = 0; } else if (arg0->unk4E >= 2 && (D_800C7D20 & D_80073140[arg0->unk0A])) { D_800A4C2C[arg0->unk51].unk41 = 1; D_800A4C2C[arg0->unk51].unk00 &= 0xFFF9FFFF; flag = 1; func_8002EFF8(0, D_80073140[arg0->unk0A]); } else { flag = 0; if (arg1 != 0) { arg0->unk50 = 1; D_800A4C2C[arg0->unk51].unk41 = 1; D_800A4C2C[arg0->unk51].unk00 &= 0xFFF9FFFF; func_8002EFF8(0, D_80073140[arg0->unk0A]); } else { s32 mask = D_80073140[arg0->unk0A]; arg0->unk50 = 0; func_8002EFF8(0, mask); func_80031988(arg0); } } if (arg0->unk40 != 0) { arg0->unk40(arg0->unk51, arg0->unk44); } arg0->unk40 = 0; if (flag) { arg0->unk4E = 0; } *(u8 *)&D_800A4F17 = old; } INCLUDE_ASM("asm/nonmatchings/800", func_80030F80); typedef struct Obj { /* 0x00 */ u8 unk00[0xA]; /* 0x0A */ u16 unk0A; /* 0x0C */ u16 unk0C; /* 0x0E */ u16 unk0E; /* 0x10 */ u16 unk10; /* 0x12 */ u8 unk12[0x12]; /* 0x24 */ s32 unk24; /* 0x28 */ s32 unk28; /* 0x2C */ s32 unk2C; /* 0x30 */ u16 unk30; /* 0x32 */ u8 unk32[0x5]; /* 0x37 */ u8 unk37; /* 0x38 */ u8 unk38; /* 0x39 */ u8 unk39; /* 0x3A */ s16 unk3A; /* 0x3C */ s16 unk3C; /* 0x3E */ u8 unk3E[0x13]; /* 0x51 */ u8 unk51; /* 0x52 */ u8 unk52[2]; } Obj; typedef struct Slot { /* 0x00 */ s32 unk00; /* 0x04 */ s32 unk04; /* 0x08 */ u8 unk08[0xC]; /* 0x14 */ s16 unk14; /* 0x16 */ u8 unk16[2]; /* 0x18 */ s16 unk18; /* 0x1A */ u8 unk1A[0x26]; /* 0x40 */ s32 unk40; /* 0x44 */ u8 unk44; /* 0x45 */ u8 unk45[3]; } Slot; /* 0x0C stride record pointed at by D_8006A970[] -- must match src/800.c's B12 (same name, same offsets/widths) since the destination TU already declares this struct and symbol. */ extern Slot D_800A4C28[]; extern s32 D_80073140[]; extern B12 *D_8006A970[]; extern u16 D_8006AB30[]; extern u16 D_8006AB32[]; void func_800314DC(Obj *p) { Slot *e; /* dead local: the target frame is `vars= 8` with no $sp reference. */ s32 pad[2]; u16 h; u32 t; /* $2 pin #1 -- `base` is a LOCAL qty in the D_800A4C28 block. Unpinned, local-alloc.c:1825 combine_regs TIES its dest into operand 0 of the addu (the dying `t & 0xFF00` temp, which lives in $v1), so base lands in $v1; the target keeps it in $v0. A hard-reg dest cannot be tied, so the pin materialises base in $v0 exactly where the target has it. */ register s32 base __asm__("$2"); u32 d; u32 res; /* $2 pin #2 (disjoint lifetime from `base`) -- see the lerp comment. */ register u32 q __asm__("$2"); if (!(p->unk37 & 1)) { return; } if (p->unk30 != 0) { p->unk30--; return; } if (p->unk2C < 0) { p->unk24 += p->unk2C; if (p->unk24 <= p->unk28) { p->unk24 = p->unk28; p->unk37 &= 0xFE; } } else { p->unk24 += p->unk2C; if (p->unk24 >= p->unk28) { p->unk24 = p->unk28; p->unk37 &= 0xFE; } } if (!(p->unk37 & 1) && p->unk38 != 0) { if (p->unk24 != p->unk3C) { p->unk28 = p->unk3C; if (p->unk3C < p->unk24) { p->unk2C = -p->unk3A; } else { p->unk2C = p->unk3A; } } p->unk38 = 0; p->unk37 |= 1; } e = &D_800A4C28[p->unk51]; e->unk00 = D_80073140[p->unk0A]; h = D_8006A970[p->unk10][p->unk0C].unk04; /* `t` MUST be a separate SImode local: a HImode `h` used both by the 0x18 store and by SImode arithmetic is what keeps the otherwise redundant `andi $v0,$a0,0xFFFF` alive (folding it away costs an insn). */ t = h; /* splitting the -0x3C00 off keeps the sh scheduled AFTER the andi/sll/sra chain: as one expression the store's memory dependence on the 0x24 load gives it enough sched1 priority to hoist into the load-delay region. */ base = (t & 0xFF00) + ((s8)t * 2); e->unk18 = h; base -= 0x3C00; /* in-place update: the target reuses $a1 for both the 0x24 load and the difference, and global.c has no coalescing (K8) -- cross-block sharing has to be ONE pseudo in the source. */ d = p->unk24; d -= base; if (d >= 0x5300) { res = 0x3FFF; } else { /* `q` merges the D_8006AB30 load with the first product into one $v0-pinned range. That blocks $v0 across insns 4..11 of this block, which is what pushes 0x100 -> $v1, the index -> $a0, the D_8006AB32 load -> $v1, and (via the global pass) the second mflo -> $v1. */ q = D_8006AB30[d >> 8]; q = q * (0x100 - (d & 0xFF)); res = (q + D_8006AB32[d >> 8] * (d & 0xFF)) >> 8; } e->unk14 = res; e->unk40 = p->unk0A; if (e->unk44 != 0) { e->unk04 |= 0x10; } else { e->unk44 = 1; e->unk04 = 0x10; } } INCLUDE_ASM("asm/nonmatchings/800", func_800316F8); INCLUDE_ASM("asm/nonmatchings/800", func_80031988); INCLUDE_ASM("asm/nonmatchings/800", func_80031A98); extern u8 D_800A49D2[]; void func_80031B7C() { register s32 a0 asm("$4") = 0; register u8 a2 asm("$6") = 1; register u16 a1 asm("$5") = 0x7FFF; register u8 *v1 asm("$3"); v1 = D_800A49D2; do { if (v1[4] && !v1[2] && v1[3]) { v1[2] = a2; *(u16 *)v1 = a1; } v1 += 0x54; a0++; } while (a0 < 8); } extern s8 D_800A4F17; extern u16 D_800A2BB0[]; extern u16 D_800BA108[]; extern void func_8003388C(void *a0, s32 a1); void func_80031BE0(void) { u8 *e; s32 i; if (*(u8 *)&D_800A4F17 != 0) { return; } e = (u8 *)&D_800A4F17 - 0x82F; for (i = 0; i < 8; i++, e += 0x54) { u16 v1; D_800A2BB0[i] = *(u16 *)(e + 4); D_800BA108[i] = *(u16 *)(e + 6); v1 = *(u16 *)(e + 0) & 0x3F; switch (v1) { case 1: if (*(u16 *)(e + 8) != 0) { *(u16 *)(e + 8) = *(u16 *)(e + 8) - 1; } break; case 0: break; case 5: func_8003388C(e, i); break; } } } INCLUDE_ASM("asm/nonmatchings/800", func_80031CC8); INCLUDE_ASM("asm/nonmatchings/800", func_80031D70); INCLUDE_ASM("asm/nonmatchings/800", func_80031DEC); INCLUDE_ASM("asm/nonmatchings/800", func_80031E94); INCLUDE_ASM("asm/nonmatchings/800", func_80031F14); INCLUDE_ASM("asm/nonmatchings/800", func_80031FC8); /* 0x14-byte record, indexed by p[3]. D_80068304 and D_800A4EE8->unk14 are both * arrays of pointers to arrays of these. */ /* slot record inside D_800A46E8 (stride 0x54) */ extern Rec14 *D_80068304[]; extern Owner4EE8 *D_800A4EE8; extern s16 D_800A4EF0; extern u16 D_800A46E8[]; extern s8 D_800A4F17; extern s32 func_800331D4(s32); extern s32 func_8003310C(s32); extern void func_800335B8(s32, s32); extern void func_8003324C(s32); extern void func_80032A74(Slot54 *, s32, Rec14 *, s32); s32 func_80032048(u32 arg0, u8 *p, u32 flags) { Rec14 *rec; Slot54 *e; s32 idx; s32 ret; u32 x; u32 y; u32 v; u8 old; u8 b1; u8 b2; u32 b3; u32 b0; y = arg0 >> 16; b1 = p[1]; b2 = p[2]; b3 = p[3]; if (b1 == 0) { rec = &D_80068304[b2][b3]; } else { if (D_800A4EF0 != b1) { return 0; } if (D_800A4EE8 == 0) { return 0; } rec = D_800A4EE8->unk14[b2]; rec += b3; } old = *(u8 *)&D_800A4F17; *(u8 *)&D_800A4F17 = 1; ret = 0; v = rec->unk00; if ((u16)flags == 0xFFFF) { flags = 0; x = (u16)y; y = 0; } else { x = arg0; } idx = func_800331D4(x); if (idx == 0) { if (flags & 0x4000) { if ((flags & 0x3000) == 0) { goto done; } } else if (flags & 0x1000) { if ((flags & 0x7F) < 0x30) { v >>= 1; } } idx = func_8003310C(v); if (idx == 0) { goto done; } ret = idx; idx = ret - 1; e = (Slot54 *)((u8 *)D_800A46E8 + idx * 0x54); } else { if (flags & 0x4000) { func_800335B8(idx - 1, (u16)flags); goto done; } idx--; e = (Slot54 *)((u8 *)D_800A46E8 + idx * 0x54); if (e->unk08 != 0) { goto done; } ret = idx + 1; func_8003324C((u16)idx); } b0 = p[0]; e->unk04 = x; e->unk06 = y; e->unk02 = v; e->unk00 = (b0 & 0xC0) | 1; e->unk08 = rec->unk0C; e->unk0A = 5; func_80032A74(e, idx, rec, (u16)flags); done: *(u8 *)&D_800A4F17 = old; return ret; } /* ---- declarations copied verbatim from the destination TU (src/800.c, the * block that precedes the already-matched sibling func_80032048) ---- * * BANKING NOTE: src/800.c ALREADY has every typedef (Rec14 / Slot54 / * Owner4EE8, lines 7327-7350) and every extern below (lines 7353-7363), * immediately above `INCLUDE_ASM(... func_800322A8)` at line 7456. They are * reproduced here only so this file compiles standalone under match_one. * When banking, replace the INCLUDE_ASM with the FUNCTION BODY ONLY (plus the * SLOT_BASE #define) -- re-emitting the typedefs would be a C89 redefinition * error. src/800.c has NO prior declaration of func_800322A8, so there is no * DEF-side signature wall (cookbook Sec.20). */ /* 0x14-byte record, indexed by p[3]. D_80068304 and D_800A4EE8->unk14 are both * arrays of pointers to arrays of these. */ /* slot record inside D_800A46E8 (stride 0x54) */ extern Rec14 *D_80068304[]; extern Owner4EE8 *D_800A4EE8; extern s16 D_800A4EF0; extern u16 D_800A46E8[]; extern s8 D_800A4F17; extern s32 func_800331D4(s32); extern s32 func_8003310C(s32); extern void func_8003324C(s32); extern void func_80032A74(Slot54 *, s32, Rec14 *, s32); /* The slot table base D_800A46E8 sits 0x82F bytes below the D_800A4F17 lock * byte and the original code derives one from the other (`addiu v1,s1,-0x82F`), * i.e. they live in one object. Spelling it this way is what makes gcc CSE the * two symbol addresses into a single base register instead of emitting a second * lui/addiu pair (worth 2 instructions plus the whole callee-save ranking). */ #define SLOT_BASE ((u8 *)&D_800A4F17 - 0x82F) /* SECOND-PASS LEVER (sched.md S13, the bb0 head-skip escape). A first pass got * to 3-off with a pure prologue-schedule residual: sched2 emitted * `sw s5 / move s5 / srl` where the target has `srl / sw s5 / move s5`. * Cause (read off the -dS dumps, not guessed): sched.c:3189-3213 pins the * LEADING RUN of `pseudo = hard-arg-reg` param copies out of sched1's pool, so * the a2->flags copy always kept a LOWER LUID than the `srl`; at sched2's T-15 * all three candidates tie at priority 1 and rank_for_schedule falls through to * the LUID tie-break (2.7.2 sched.c:2428, highest LUID picked first = placed * last), which put the srl last of the three. * Fix: take the third parameter through a BODY-LOCAL copy declared AFTER * `y = arg0 >> 16`. The head copy `pseudo = $a2` becomes a nop-move at reload * (local-alloc ties the once-used incoming pseudo to $a2) and the REAL * `move s5,a2` materialises at its statement position, where the S2 birthing * boost sinks it below the srl -- giving it the HIGHER LUID. sched2 then picks * it first, `sw s5` follows on the potential-hazard rule, and the srl lands at * position 3. Do NOT reorder `flags = arg2;` above `y = arg0 >> 16;`. */ s32 func_800322A8(u32 arg0, u8 *p, u32 arg2) { Rec14 *rec; Slot54 *e; s32 idx; s32 ret; u16 y; u32 x; u32 flags; u32 v; u8 old; u8 b1; u8 b2; u32 b3; u32 b0; y = arg0 >> 16; flags = arg2; x = arg0; b1 = p[1]; b2 = p[2]; b3 = p[3]; if (b1 == 0) { rec = &D_80068304[b2][b3]; } else { if (D_800A4EF0 != b1) { return 0; } if (D_800A4EE8 == 0) { return 0; } rec = D_800A4EE8->unk14[b2]; rec += b3; } old = *(u8 *)&D_800A4F17; *(u8 *)&D_800A4F17 = 1; ret = 0; v = rec->unk00; idx = func_800331D4((u16)arg0); if (idx == 0) { if (flags & 0x4000) { if ((flags & 0x3000) == 0) { goto done; } } idx = func_8003310C(v); if (idx == 0) { goto done; } ret = idx; idx = ret - 1; e = (Slot54 *)(SLOT_BASE + idx * 0x54); } else { idx--; e = (Slot54 *)(SLOT_BASE + idx * 0x54); if (e->unk08 != 0) { goto done; } ret = idx + 1; func_8003324C((u16)idx); } b0 = p[0]; e->unk04 = x; e->unk06 = y; e->unk02 = v; e->unk00 = (b0 & 0xC0) | 1; e->unk08 = rec->unk0C; e->unk0A = 5; func_80032A74(e, idx, rec, (u16)flags); done: *(u8 *)&D_800A4F17 = old; return ret; } /* MATCH: 180/180 instructions, byte-exact (relocation-masked). * * S53 RECOVERY (compile conflict on func_80032A74's signature): the reported * conflict was func_80032774's draft, which independently invented a * DIFFERENT-WIDTH Slot54 (u16/u16/u16 fields + 0x48 pad, vs. this draft's * s16/s16/s16/u16/s8). Three drafts (func_800322A8, func_800324A4 here, and * func_80032774) all declare `extern void func_80032A74(Slot54 *, s32, * Rec14 *, s32);`, so all three Slot54 spellings must be textually identical * for the merged TU to compile. This draft's Slot54/Rec14/Owner4EE8 and the * func_80032A74 prototype were ALREADY the canonical spelling (byte-identical * to src/800.c's incumbent, banked for the sibling func_80032048) -- no * change was needed here. func_80032774's recovered draft (S53) was the one * fixed: it now reuses this canonical Slot54 for the extern prototype and * keeps its own different-width view under a separate name (Slot54View), * cast at use. All three now agree with each other and with the TU. * * Notes for the banker: * - src/800.c ALREADY declares Rec14 / Slot54 / Owner4EE8, D_80068304, * D_800A4EE8, D_800A4EF0, D_800A46E8, D_800A4F17, func_8003310C, * func_8003324C and func_80032A74 (they were added for the matched * func_80032048, immediately above this function's INCLUDE_ASM). Delete * the duplicated declaration block below when banking; keep only the * LOCKBYTE / SLOTBASE macros (or inline them). * - src/800.c has NO prior prototype for func_800324A4, so the signature * below is unconstrained (no DEF-side wall here). * * The three levers that made it match (pass 2; pass 1 stopped at 4 diffs): * 1. ONE-SYMBOL BASE (kept from pass 1, but re-spelled). The target derives * the slot array AND the lock byte from a single lui/addiu pair * ("addiu $a2,$v1,-0x82F" / "addiu $a0,$v1,-0x824"), which gcc-2.7.2's * cse.c can only do when both constants share a symbol_ref * (related_value chains are per-symbol). Pass 1 used D_800A46E8 as the * base and wrote the lock byte as +0x82F; that links to the same bytes * but emits the relocation against the WRONG symbol. The target's own * relocation lines are %hi/%lo(D_800A4F17) for the shared base and for * the closing sb, and %hi/%lo(D_800A46E8) for the two `e` computations, * so the base is spelled &D_800A4F17 here and the array is derived from * it (-0x82F, register-relative, no relocation). Verified with * `objdump -dr`: la $3,D_800A4F17 / la $3,D_800A46E8 x2 / sb $12,D_800A4F17, * no addends anywhere. * 2. STATEMENT ORDER, NOT A PIN, for the prologue weave (target idx 5/6: * "sw $s4" before "srl"). sched2 is a BACKWARD list scheduler; every * insn in bb0 has priority 1, so the order is decided by * potential_hazard (stores win) then by LUID. `sw $s4,0x30($sp)` only * becomes ready once `maxp = 0` (the insn that clobbers $s4) is * scheduled, so moving `maxp = 0;` ABOVE `y = t >> 16;` swaps their * LUIDs, delays the store's readiness by one tick and puts the srl in * the target's slot. (cookbook S1/S7; 4 diffs -> 2.) * 3. NO $5 PIN ON b1 -- widen b3 instead. Pass 1 pinned b1 to $a1 to fix * the b1/b3 register pair, but a pinned hard-reg dest fails * birthing_insn_p's `reg_n_sets == 1` gate, so b1's lbu lost the sched1 * birthing boost while b2's kept it; b2's load then sank BELOW b1's and * the two lbu's came out swapped (idx 16/17) -- unfixable by statement * order (all 120 permutations tried, floor of 2). The real cause is a * global-alloc density tie (K2): with `u8 b3` the sll/addu uses are * subregs and b3's allocno loses to b1's, so b1 grabs $a0 first. * Declaring b3 as u32 (like the sibling func_80032048 does) makes the * uses full-SI, raises b3's allocno density above b1's, and b3 wins $a0 * / b1 gets $a1 with NO pin at all -- which also restores the natural * lbu order. (Prompt lever B: the interloper was the narrow type.) * * The `t` pin on $4 is still required -- see its comment below. */ /* 0x14-byte record, indexed by p[3]. D_80068304 and D_800A4EE8->unk14 are both * arrays of pointers to arrays of these. */ /* slot record inside D_800A46E8 (stride 0x54) */ extern Rec14 *D_80068304[]; extern Owner4EE8 *D_800A4EE8; extern s16 D_800A4EF0; extern u16 D_800A46E8[]; extern s8 D_800A4F17; /* D_800A4F17 == (u8 *)D_800A46E8 + 0x82F. Both the lock byte and the slot * array walked by the search loop must come off the SAME symbol_ref or cse * emits a second lui/addiu pair (181 insns). The target's relocation is on * D_800A4F17, so that is the base here. */ #define LOCKBYTE (*(u8 *)&D_800A4F17) #define SLOTBASE ((u8 *)&D_800A4F17 - 0x82F) extern s32 func_8003310C(s32); extern void func_8003324C(s32); extern void func_80032A74(Slot54 *, s32, Rec14 *, s32); s32 func_800324A4(u32 arg0, u8 *p, u32 flags, s32 arg3) { Rec14 *rec; Slot54 *e; u8 *q; s32 i; s32 idx; s32 ret; s32 count; s32 minp; s32 maxp; s32 pr; u32 x; /* t: keeps the raw incoming arg0 in $a0 so the "srl $t4, $a0, 16" that * feeds the y spill reads $a0 and not $fp. Without it cse collapses * x into the parameter pseudo, the shift reads the callee-saved home and * the $s7/$fp pair flips (v <-> arg0) -- 9 diffs. Emits no instruction. */ register u32 t __asm__("$4"); u16 y; s32 v; u8 old; u8 b0; u8 b1; u8 b2; /* b3 is u32, NOT u8: the width is what wins it $a0 ahead of b1. See the * header note (lever 3) -- with u8 it loses the allocno density race and * the b1/b3 pair comes out swapped. */ u32 b3; t = arg0; x = arg0; b2 = p[2]; b1 = p[1]; b3 = p[3]; /* maxp = 0 must precede the shift: it gates when "sw $s4" becomes ready * in sched2 (lever 2). */ maxp = 0; y = t >> 16; if (b1 == 0) { rec = &D_80068304[b2][b3]; } else { if (D_800A4EF0 != b1) { return 0; } if (D_800A4EE8 == 0) { return 0; } rec = D_800A4EE8->unk14[b2]; rec += b3; } ret = 0; idx = 0; count = 0; old = LOCKBYTE; LOCKBYTE = 1; v = rec->unk00; q = SLOTBASE; minp = 0x80; for (i = 0; i < 8; i++, q += 0x54) { if ((*(u16 *)q & 0x3F) == 1 && *(u16 *)(q + 4) == (u16)x) { pr = q[0xB] & 0x7F; if (pr < minp) { idx = i + 1; minp = pr; } if (pr >= maxp) { maxp = pr; } count++; } } if (count <= arg3) { idx = 0; } else if ((s32)(flags & 0x7F) < minp) { goto done; } if (idx == 0) { if (flags & 0x4000) { if ((flags & 0x3000) == 0) { goto done; } } idx = func_8003310C(v); if (idx == 0) { goto done; } ret = idx; idx = ret - 1; e = (Slot54 *)((u8 *)D_800A46E8 + idx * 0x54); } else { idx--; e = (Slot54 *)((u8 *)D_800A46E8 + idx * 0x54); if (e->unk08 != 0) { goto done; } ret = idx + 1; func_8003324C((u16)idx); } if (flags & 0x1000) { ((u8 *)e)[0xB] = flags & 0x7F; if ((s32)(flags & 0x7F) < maxp) { flags |= 0x8000; } } b0 = p[0]; e->unk04 = x; e->unk06 = y; e->unk02 = v; e->unk00 = (b0 & 0xC0) | 1; e->unk08 = rec->unk0C; e->unk0A = 5; func_80032A74(e, idx, rec, (u16)flags); done: LOCKBYTE = old; return ret; } /* 0x14-byte record, indexed by p[3]. D_80068304 and D_800A4EE8->unk14 are both * arrays of pointers to arrays of these. Identical to the TU's already-banked * Rec14 (src/800.c, func_80032048's block) — kept verbatim, not renamed. */ /* Canonical TU spelling of the slot record inside D_800A46E8 (stride 0x54), * as already banked by func_80032048 in src/800.c. This function's own view * of the same bytes needs different field widths/signs (see Slot54View * below), so THIS typedef only exists to keep func_80032A74's prototype * byte-identical to the TU's — the callee is called through a cast, never * accessed directly as `Slot54` in this file. */ /* This function's own overlay of the same 0x54-byte slot record: it needs * unsigned 16-bit loads (lhu, not lh) on unk00/unk04/unk06 to byte-match, and * an extra unk0B field the TU's Slot54 doesn't expose. Different tag name so * it does NOT collide with the TU's `Slot54` typedef above; pointers are * cast to `Slot54 *` only at the func_80032A74 call site. */ typedef struct Slot54View { /* 0x00 */ u16 unk00; /* 0x02 */ s16 unk02; /* 0x04 */ u16 unk04; /* 0x06 */ u16 unk06; /* 0x08 */ u16 unk08; /* 0x0A */ s8 unk0A; /* 0x0B */ u8 unk0B; /* 0x0C */ u8 pad0C[0x48]; } Slot54View; /* 0x54 */ extern Rec14 *D_80068304[]; extern Owner4EE8 *D_800A4EE8; extern s16 D_800A4EF0; extern u16 D_800A46E8[]; #define SLOTS ((Slot54View *)D_800A46E8) /* &D_800A4F17 expressed as an offset inside the D_800A46E8 object: the target * derives both loop pointers from this one materialised address * (addiu $a2,$v1,-0x82F / addiu $a1,$v1,-0x824). */ #define PFLAG ((s8 *)((u8 *)D_800A46E8 + 0x82F)) extern s32 func_8003310C(s32); extern void func_800335B8(s32, s32); extern void func_8003324C(s32); extern void func_80032A74(Slot54 *, s32, Rec14 *, s32); s32 func_80032774(u32 arg0, u8 *p, u32 flags) { Rec14 *rec; Slot54View *e; Slot54View *q; s32 ret; s32 best; s32 cnt; s32 i; s32 mn; s32 mx; s32 t; s32 yy; s32 f4000; u32 x; u16 y; u16 v; u8 old; u8 b0; u8 b1; u8 b2; s32 b3; b2 = p[2]; b1 = p[1]; b3 = p[3]; mx = 0; y = arg0 >> 16; x = arg0; if (b1 == 0) { rec = &D_80068304[b2][b3]; } else { if (D_800A4EF0 != b1) { return 0; } if (D_800A4EE8 == 0) { return 0; } rec = D_800A4EE8->unk14[b2]; rec += b3; } yy = y; ret = 0; best = 0; cnt = 0; old = *PFLAG; *PFLAG = 1; q = SLOTS; mn = 0x80; v = rec->unk00; for (i = 0, f4000 = flags & 0x4000; i < 8; i++, q++) { if ((q->unk00 & 0x3F) == 1 && q->unk04 == (u16)x) { if (yy == 0 || q->unk06 == yy) { if (f4000) { func_800335B8(i, (u16)flags); } goto done; } t = q->unk0B & 0x7F; if (t < mn) { best = i + 1; mn = t; } if (mx <= t) { mx = t; } cnt++; } } if (cnt < 2) { best = 0; } else if ((s32)(flags & 0x7F) < mn) { goto done; } if (best == 0) { if (flags & 0x4000) { if ((flags & 0x3000) == 0) { goto done; } } best = func_8003310C(v); if (best == 0) { goto done; } ret = best; best = ret - 1; e = &SLOTS[best]; } else { ret = best; best = ret - 1; e = &SLOTS[best]; if (e->unk08 != 0) { goto done; } func_8003324C((u16)best); } if (flags & 0x1000) { e->unk0B = flags & 0x7F; if ((s32)(flags & 0x7F) < mx) { flags |= 0x8000; } } b0 = p[0]; e->unk04 = x; e->unk06 = y; e->unk02 = v; e->unk00 = (b0 & 0xC0) | 1; e->unk08 = rec->unk0C; e->unk0A = 5; func_80032A74((Slot54 *)e, best, rec, (u16)flags); done: *PFLAG = old; return ret; } INCLUDE_ASM("asm/nonmatchings/800", func_80032A74); INCLUDE_ASM("asm/nonmatchings/800", func_8003310C); INCLUDE_ASM("asm/nonmatchings/800", func_800331D4); /* func_8003324C — MATCH (54 ins). * * D_800A46E8 is an array of 0x54-byte slot records; records [8..15] (base * +0x2A0 == 8 * 0x54) are Ent30D80 entities. Switch on (rec->flags & 0x3F): * case 1 walks the 8 per-slot "dirty" bytes at e[i + 0xE], clearing each set * one, blanking the matching entity's unk40 callback, and calling * func_80030D80 on it when unk4E is set; then zeroes the flags halfword. * case 5 tail-calls func_80034650(e, 0). * * Second pass over the 2-instruction NEAR draft (target: `sw $zero,0x40($s0)` * in the beqz delay slot, then `addu $a0,$s0,$zero`; the first draft emitted * the pair in the opposite order with $a0 as the store base). It was NOT a * scheduler tie-break — four independent source-level levers, each verified * load-bearing by ablation: * * 1. `p->unk40 = 0;` sits BEFORE the `if (p->unk4E)`, not inside it. reorg * may only steal a store into a delay slot BACKWARD (from before the * branch) — a forward/fall-through steal of a store is unsafe. So the * target's `sw` in the beqz slot proves the store precedes the test in * source order. (Cookbook §175 lever A: statement order.) * 2. `register Ent30D80 *p __asm__("$16")`. Hoisting the store out of the * inner `if` gives p three in-loop uses, and loop.c then splits it into * two induction variables (+0x2A0 for the call argument, +0x2EE for the * 0x40/0x4E address giv) — 58 ins. loop.c's biv/giv detection only * considers PSEUDO registers, so pinning p to a hard reg disables strength * reduction for it outright and leaves the single `addiu $s0,$s0,0x54`. * This also drops the first draft's copy-fence: every access now goes * through $s0 and the `addu $a0,$s0,$zero` is emitted last, as the target * has it. Ablating the pin: 58 ins. * 3. `__asm__ ("" :: "r"(base));` after p's initialisation. With p pinned, * `addiu p, base, 0x2A0` is base's last use, so the allocator ties base to * $s0 and performs the add in place ("lui $s0 / addiu $s0,$s0,0x2A0"), * wrecking the prologue. The zero-instruction asm extends base past the * add and breaks the tie. Ablating it: 8 mismatches. * 4. `off` computed into its own local BEFORE `base` is loaded. Otherwise the * %hi/%lo pair is scheduled above the `andi $a0,$a0,0xFFFF`, base then * conflicts with the incoming argument's live range and lands in $a1 * instead of $a0. Ablating it: 8 mismatches. * * Banking note for src/800.c: `typedef struct Ent30D80`, `extern u16 * D_800A46E8[];` and `void func_80030D80(Ent30D80 *, s16)` already exist * ABOVE the INCLUDE_ASM line — do not duplicate them. Only * `extern void func_80034650(u8 *, s32);` is new (func_80034650 is still * INCLUDE_ASM further down the TU). The TU's existing * `extern void func_8003324C(s32);` matches this definition's signature. */ extern u16 D_800A46E8[]; extern void func_80030D80(Ent30D80 *arg0, s16 arg1); extern void func_80034650(u8 *, s32); void func_8003324C(s32 arg0) { u8 *base; u8 *e; s32 off; s32 code; off = (u16)arg0 * 0x54; base = (u8 *)D_800A46E8; e = base + off; code = *(u16 *)e & 0x3F; switch (code) { case 1: { register Ent30D80 *p __asm__("$16"); s32 i; i = 0; p = (Ent30D80 *)(base + 0x2A0); __asm__ ("" :: "r"(base)); for (; i < 8; i++, p++) { u8 *q = e + i; if (q[0xE] != 0) { q[0xE] = 0; p->unk40 = 0; if (p->unk4E != 0) { func_80030D80(p, 0); } } } *(u16 *)e = 0; break; } case 5: func_80034650(e, 0); break; } } INCLUDE_ASM("asm/nonmatchings/800", func_80033324); /* TU decls (src/800.c): `typedef struct Ent30D80 { ... } Ent30D80;` and * `extern void func_80030D80(Ent30D80 *arg0, s16 arg1);` already exist * above this function's INCLUDE_ASM line (see func_80030D80's own * definition and func_8003324C's banking note) — reproduced here verbatim * for standalone compilation. Do not duplicate in the TU when banking. */ extern u16 D_800A46E8[]; extern void func_80030D80(Ent30D80 *arg0, s16 arg1); /* TU already declares func_80034650 as (u8 *, s32) above func_8003324C * (src/800.c) — a `void *` redeclaration here would conflict with it. */ extern void func_80034650(u8 *a0, s32 a1); void func_80033398(u32 arg0) { /* RC-12 / cookbook §136d-1: the $0-add OPAQUE COPY. * The target's `addu $s7,$a0,$zero` must NOT be cse-linked to the * parameter, so that `srl $s6,$a0,16` keeps reading the raw incoming * $a0 (the parm pseudo dies at the srl and local-alloc gives it $a0, * deleting the real parm copy). A plain `s7v = arg0;` makes * cse.c make_regs_eqv head-promote s7v to canonical and canon_reg * rewrites the srl to `srl $s6,$s7,16` (the 1-ins residual). */ register s32 zr __asm__("$0"); u32 s7v; u8 *s5v; s32 s4v; u32 s6v; u8 *fpv; register u8 *s3v __asm__("$19"); u8 *s2v; register s32 s1v __asm__("$17"); register u8 *s0v __asm__("$16"); s32 t0; s7v = arg0 + zr; s5v = (u8 *)D_800A46E8; s4v = 0; s6v = arg0 >> 16; fpv = s5v + 0x2A0; s3v = s5v + 6; for (; s4v < 8; s3v += 0x54, s5v += 0x54) { if ((*(u16 *)s5v & 0x3F) == 1 && *(u16 *)(s3v - 2) == (u16)s7v && (s6v == 0 || *(u16 *)s3v == s6v)) { s2v = (u8 *)D_800A46E8 + (u32)(u16)s4v * 0x54; t0 = *(u16 *)s2v & 0x3F; switch (t0) { case 1: s1v = 0; s0v = fpv; for (; s1v < 8; s1v++, s0v += 0x54) { u8 *p = s2v + s1v; if (p[0xE] != 0) { p[0xE] = 0; *(u32 *)(s0v + 0x40) = 0; if (s0v[0x4E] != 0) { func_80030D80((Ent30D80 *)s0v, 0); } } } *(u16 *)s2v = 0; break; case 5: func_80034650(s2v, 0); break; default: s4v++; continue; } } s4v++; } } INCLUDE_ASM("asm/nonmatchings/800", func_8003350C); /* TU decls (src/800.c): `extern u16 D_800A46E8[];`, `extern void func_800335B8(s32, s32);` */ extern u16 D_800A46E8[]; void func_800335B8(s32 a0, s32 a1) { u8 *e = (u8 *)D_800A46E8 + a0 * 0x54; u8 *p; s32 a2; s32 v1; s32 t0; if ((*(u16 *)e & 0x3F) != 1) { return; } /* copy-fence: keeps the `addu $t0,$a1,$zero` param copy alive (cse would otherwise propagate $a1 into branch 2's `andi $a1,$t0,0x7F`). */ t0 = a1; __asm__ ("" : "=r"(t0) : "0"(t0)); if (a1 & 0x1000) { v1 = 0; a2 = *(u16 *)(e + 0xC); a1 = a1 & 0x7F; t0 = 1; p = (u8 *)D_800A46E8 + 0x2A0; for (; v1 < 8; v1++, p += 0x54) { u8 *rec; if (a2 == 0) { return; } if (*(u8 *)(e + v1 + 0xE) != 0) { /* copy-fence: materialises the target's in-loop `addu $v0,$a0,$zero` (reorg then steals it for the beqz delay slot). Walking pointer, NOT p + v1*0x54, so loop strength reduction still owns $a0. */ rec = p; __asm__ ("" : "=r"(rec) : "0"(rec)); a2--; *(u16 *)(rec + 0x48) = a1; *(u8 *)(rec + 0x4F) = t0; } } } else if (a1 & 0x2000) { a2 = *(u16 *)(e + 0xC); v1 = 0; a1 = t0 & 0x7F; p = (u8 *)D_800A46E8 + 0x2A0; for (; v1 < 8; v1++) { u8 *rec; if (a2 == 0) { return; } if (*(u8 *)(e + v1 + 0xE) != 0) { rec = p + v1 * 0x54; if (*(u8 *)(rec + 0x35) != 0) { *(u8 *)(rec + 0x53) = a1; } a2--; } } } } /* func_800336A8 — MATCH (121 ins). * * Second-pass fix over the 49/-1 NEAR draft. The whole residual was ONE unfilled * branch-delay slot: the target leaves `beqz $v0, .L80033818` (the D_800A4F19 test) * with a `nop`, our build let reorg.c's EAGER FALLTHROUGH STEAL pull the then-arm's * head insn `sll $v0,$v1,1` into it (dbr dump: insn 241 folded into a SEQUENCE with * jump_insn 234). * * Lever: reorg.c `stop_search_p` (2.7.2:675) returns TRUE for an `ASM_INPUT` insn, so * `fill_slots_from_thread`'s trial loop terminates on the FIRST insn of the thread and * never reaches the `sll`. A zero-byte `__asm__ __volatile__("")` planted as the first * statement of the then-arm therefore kills the steal and restores the `nop` — with no * emitted instruction of its own (cookbook D3 lever (c), "make the join head ineligible", * generalised: make it un-searchable). * * Also removed the first draft's `register u16 *p __asm__("$6")` pin — byte-verified * unnecessary once the slot is fixed, so this body is PIN-FREE (§42e sibling-TU safe). * * Structural levers kept from pass 1 (each byte-load-bearing): * - byte-arithmetic index `(Chan336A8 *)(D_800A4988 + i*0x54)` keeps the t1/t3/t4 layout; * - `q = D_8007319E + 1; q[0x7F - n]` reproduces cse's related-value `addiu $t6,$t5,2`; * - inverted clamp `if (n >= 0x42) ... else n = 1;` for the bnez/else-last layout; * - one `v` funnelling all three store paths so cross-jumping merges the trailing `sh`; * - `extern const u16 D_8007319E[]` (D_8007321E must stay non-const) lets the second * table load hoist across `sh $v0,0xA($a3)` and interleave into the first mult. */ typedef struct { /* 0x00 */ u8 pad00[0x0A]; /* 0x0A */ u16 unk0A; /* 0x0C */ u8 pad0C[0x28]; /* 0x34 */ u8 unk34; /* 0x35 */ u8 unk35; /* 0x36 */ u8 pad36[0x1C]; /* 0x52 */ u8 unk52; /* 0x53 */ u8 pad53[1]; } Chan336A8; /* size 0x54 */ typedef struct { /* 0x00 */ s32 unk00; /* 0x04 */ s32 unk04; /* 0x08 */ s16 unk08; /* 0x0A */ s16 unk0A; /* 0x0C */ u8 pad0C[0x34]; /* 0x40 */ s32 unk40; /* 0x44 */ u8 unk44; /* 0x45 */ u8 pad45[3]; } Voice336A8; /* size 0x48 */ typedef struct { /* 0x00 */ u8 pad00[0x16]; /* 0x16 */ u8 unk16; /* 0x17 */ u8 pad17[1]; /* 0x18 */ u16 unk18; /* 0x1A */ u8 pad1A[2]; /* 0x1C */ u16 unk1C[12]; /* 0x34 */ u8 pad34[4]; /* 0x38 */ u8 unk38; /* 0x39 */ u8 pad39[1]; /* 0x3A */ u8 unk3A[8]; } Req336A8; extern u8 D_800A4988[]; extern s32 D_80073140[]; extern u16 D_8006AA30[]; extern const u16 D_8007319E[]; extern u16 D_8007321E; extern u8 D_800A4F19; void func_800336A8(Req336A8 *arg) { Chan336A8 *ch; Voice336A8 *vo; u16 *p; const u16 *q; u32 m; u32 n; u32 v; s32 i; s32 j; for (i = 0; i < 8; i++) { if (arg->unk3A[i] == 0) { continue; } ch = (Chan336A8 *)(D_800A4988 + i * 0x54); vo = (Voice336A8 *)(D_800A4988 + 0x2A0 + i * 0x48); vo->unk00 = D_80073140[ch->unk0A]; m = ch->unk34; m = D_8006AA30[m]; m = m * *(s16 *)(D_800A4988 + 0x572); m = m >> 7; if (arg->unk16 != 0) { m = m * arg->unk18; m = m >> 15; } p = arg->unk1C; for (j = 2; j >= 0; j--, p += 4) { m = m * *p; m = m >> 14; } n = ch->unk35; if (n == 0) { v = m; vo->unk0A = v; vo->unk08 = v; } else { if (arg->unk38 != 0) { n += arg->unk38; if (n >= 0x42) { n -= 0x40; if (n >= 0x80) { n = 0x7F; } } else { n = 1; } } ch->unk52 = n; if (D_800A4F19 != 0) { /* zero-byte dbr fence: ASM_INPUT stops reorg's fallthrough trial scan * (stop_search_p), so the beqz above keeps its `nop` delay slot. */ __asm__ __volatile__(""); v = (m * D_8007319E[n]) >> 14; vo->unk0A = v; q = D_8007319E + 1; v = (m * q[0x7F - n]) >> 14; vo->unk08 = v; } else { v = (m * D_8007321E) >> 14; vo->unk0A = v; vo->unk08 = v; } } vo->unk40 = ch->unk0A; if (vo->unk44 != 0) { vo->unk04 |= 3; } else { vo->unk04 = 3; vo->unk44 = 1; } } } INCLUDE_ASM("asm/nonmatchings/800", func_8003388C); INCLUDE_ASM("asm/nonmatchings/800", func_800342E8); INCLUDE_ASM("asm/nonmatchings/800", func_80034314); INCLUDE_ASM("asm/nonmatchings/800", func_80034650); INCLUDE_ASM("asm/nonmatchings/800", func_800346D0); INCLUDE_ASM("asm/nonmatchings/800", func_800347C8); extern u16 D_800A46E8[]; void func_80034844(void) { register u8 *a0 __asm__("$4") = (u8 *)D_800A46E8; register int a1 __asm__("$5") = 0; register int t0 __asm__("$8") = 0x5; register int a3 __asm__("$7") = 0x1; register int a2 __asm__("$6") = 0x220; register u8 *v1 __asm__("$3") = (u8 *)D_800A46E8 + 0x1A; while (a1 < 8) { if (*(u16 *)a0 == t0) { u8 b = *(u8 *)(v1 + 0x1D); if ((b & 0x2) == 0) { *(u8 *)(v1 - 0x4) = a3; *(u16 *)v1 = a2; } } a1++; v1 += 0x54; a0 += 0x54; } } INCLUDE_ASM("asm/nonmatchings/800", func_800348A8); INCLUDE_ASM("asm/nonmatchings/800", func_8003491C); extern s8 D_800A4F17; void func_80034A54(s32 a0) { s32 self = a0; u8 v0 = *(u8 *)(self + 0x37); if ((v0 & 0x1) != 0) { s8 *p = &D_800A4F17; s8 old = *p; *p = 1; *(u8 *)(self + 0x32) = 1; *(s16 *)(self + 0x2E) = 0x3FF; *(s16 *)(self + 0x30) = 0xFFF; *p = old; } } extern s8 D_800A4F17; void func_80034A9C(void *a0) { register void *a1 __asm__("$5") = a0; u8 *v1; u8 old; if (*(u16 *)((u8 *)a1 + 0x30) == 0x3FFF) { return; } v1 = (u8 *)&D_800A4F17; old = *v1; *v1 = 1; *(s8 *)((u8 *)a1 + 0x32) = 1; *(s16 *)((u8 *)a1 + 0x2E) = 0x3FF; *(s16 *)((u8 *)a1 + 0x30) = 0x3FFF; *v1 = old; } extern s8 D_800A4F17; void func_80034AE0(void *a0) { s8 *v1; s8 orig; v1 = &D_800A4F17; orig = *v1; *v1 = 1; *(s8 *)(a0 + 0x2A) = 1; *(s16 *)(a0 + 0x26) = 0x3FF; *(s16 *)(a0 + 0x28) = 0; *v1 = orig; } extern s8 D_800A4F17; void func_80034B0C(s32 a0) { s8 *p = &D_800A4F17; s8 old = *p; *p = 1; *(u8 *)(a0 + 0x2A) = 1; *(s16 *)(a0 + 0x26) = 0x3FF; *(s16 *)(a0 + 0x28) = 0x3FFF; *p = old; } INCLUDE_ASM("asm/nonmatchings/800", func_80034B3C); extern s32 D_8006AEF8; extern s32 D_8006AEFC; extern u8 D_80076214; extern u8 D_8007620C; /* CD read-queue status: head==tail (D_8006AEF8/FC equal) => idle; flags D_80076214 (a * pending/error byte) and D_8007620C (bit7/bit5) classify the busy/result state. * Returns 0=idle done, 8=had pending, 2/4/1=busy variants. */ s32 CdQueueBusy(void) { if (D_8006AEF8 != D_8006AEFC) { if (D_80076214 == 0) { if (D_8007620C & 0x80) { return 2; } if (D_8007620C & 0x20) { return 4; } return 1; } } else { D_8007620C = 0; if (D_80076214 == 0) { return 0; } } D_80076214 = 0; return 8; } INCLUDE_ASM("asm/nonmatchings/800", func_80034C24); extern u8 D_8006AEF5; extern s32 D_8006AEF8; extern s32 D_8006AEFC; extern u8 D_80076118[]; s32 func_80034CF0(u8 *p) { s32 idx = D_8006AEFC; u8 saved = D_8006AEF5; s32 limit = D_8006AEF8; u8 *self; s32 f28; s32 oldIdx; s32 ret; D_8006AEF5 = 1; idx = idx + 1; if (idx >= 4) { idx = 0; } if (idx == limit) { D_8006AEF5 = saved; return 0; } self = D_80076118 + D_8006AEFC * 0x30; *(s32 *)(self + 0x8) = *(s32 *)(p + 0x8); *(s32 *)(self + 0xC) = *(s32 *)(p + 0xC); *(s32 *)(self + 0x10) = *(s32 *)(p + 0x10); *(s32 *)(self + 0x14) = *(s32 *)(p + 0x14); *(s32 *)(self + 0x18) = *(s32 *)(p + 0x18); *(s32 *)(self + 0x1C) = *(s32 *)(p + 0x1C); *(s32 *)(self + 0x20) = *(s32 *)(p + 0x20); *(s32 *)(self + 0x24) = *(s32 *)(p + 0x24); f28 = *(s32 *)(p + 0x28); self[0] = 1; self[1] = 0; self[2] = 0; self[3] = 0; self[7] = 1; oldIdx = D_8006AEFC; D_8006AEFC = idx; D_8006AEF5 = saved; ret = oldIdx + 1; *(s32 *)(self + 0x28) = f28; *(s32 *)(self + 0x2C) = oldIdx; return ret; } INCLUDE_ASM("asm/nonmatchings/800", func_80034DFC); extern u8 D_8006AEF5; extern s32 D_8006AEF8; extern s32 D_8006AEFC; extern u8 D_80076118[]; extern u8 D_80076138[]; extern s32 D_80076114; extern u8 D_8007620C; void func_8003500C(void) { if (D_8006AEF5 != 0) { return; } if (D_8006AEFC != D_8006AEF8) { s32 idx = D_8006AEF8; u8 *self = D_80076118 + idx * 0x30; if (self[0] != 0) { register s32 (*fn8)(void *) __asm__("$3") = *(s32 (**)(void *))(self + 8); register s32 type __asm__("$2") = self[4]; D_80076114 = type; if (fn8(self) == 0) { return; } { register s32 (*fn1C)(s32) __asm__("$6") = *(s32 (**)(s32))(self + 0x1C); register s32 type2 __asm__("$5") = self[4]; D_80076114 = type2; if (fn1C != NULL) { fn1C(*(s32 *)(D_80076138 + D_8006AEF8 * 0x30)); } } self[0] = 0; self[1] = 0; D_8006AEF8 += 1; if ((u32)D_8006AEF8 >= 4) { D_8006AEF8 = 0; } if (D_8006AEF8 == D_8006AEFC) { D_80076114 = 0; } return; } else { if (self[1] != 0 && self[7] == 0) { s32 (*fn24)(void *) = *(s32 (**)(void *))(self + 0x24); if (fn24 != NULL) { fn24(self); } if (self[2] != 0) { return; } } self[1] = 0; D_8006AEF8 += 1; if ((u32)D_8006AEF8 >= 4) { D_8006AEF8 = 0; } if (D_8006AEF8 == D_8006AEFC) { D_80076114 = 0; } else { D_80076114 = 1; } return; } } else { D_8007620C = 0; } } INCLUDE_ASM("asm/nonmatchings/800", func_800351E8); extern s32 D_80076218; extern void func_8003C498(s32); extern void SpuWrite(s32, s32); void func_80035210(s32 a0, s32 a1) { register s32 v1 __asm__("$3"); v1 = D_80076218; func_8003C498(v1); SpuWrite(a0, a1); v1 = D_80076218; D_80076218 = v1 + a1; } INCLUDE_ASM("asm/nonmatchings/800", func_80035270); INCLUDE_ASM("asm/nonmatchings/800", func_800359B0); INCLUDE_ASM("asm/nonmatchings/800", func_80035C4C); INCLUDE_ASM("asm/nonmatchings/800", func_8003602C); extern u8 D_800A63E4; extern s32 D_8007610C; extern s32 D_800A63E8; void func_80036130(s32 a0, u8 *a1) { u8 v1; s32 temp; s32 v3; s32 v0; s32 byte_val; s32 low; v1 = D_800A63E4; if (v1 != 0xC) { return; } v1 = a0 & 0xFF; if (v1 != 0x1) { return; } temp = D_8007610C; if (temp < 0xA) { D_8007610C = temp + 1; return; } if ((a1[4] & 0x80) != 0) { return; } byte_val = a1[1]; v1 = byte_val >> 4; v0 = v1 << 2; v0 = v0 + v1; v0 = v0 << 1; low = byte_val & 0xF; v3 = D_800A63E8; v0 = v0 + low; if (v0 != v3) { D_800A63E4 = 0xD; } } INCLUDE_ASM("asm/nonmatchings/800", func_800361CC); extern void *CdReadyCallback(void *func); extern void *streamLoad_savedReadyCB; extern u8 streamLoad_cbActive; void func_8003621C(void) { if (streamLoad_cbActive != 0) { CdReadyCallback(streamLoad_savedReadyCB); streamLoad_savedReadyCB = 0; streamLoad_cbActive = 0; } } INCLUDE_ASM("asm/nonmatchings/800", func_80036260); #ifdef NON_MATCHING extern int CdControl(u8 com, u8 *param, u8 *result); extern int CdSync(int mode, u8 *result); extern u32 CdMode(void); extern void CdFlush(void); extern void *CdReadyCallback(void *func); extern void func_800377D8(void); /* this loader's CdlReadN ready-callback */ extern void func_80036260(void); /* sub-handler passed to func_80037CD8 */ extern void func_8002EC10(void); extern void func_80037334(void); extern void func_80037358(int posInt); extern void func_80037144(int idx); extern int func_80037CD8(void *arg); extern int func_8003750C(void); extern int func_800374CC(int *out); extern int func_8003775C(void); extern void func_80037D74(void); extern void *func_80037368(int *out); extern int streamLoad_state; /* 0x8006AF00 */ extern void *streamLoad_savedReadyCB; extern u8 streamLoad_cbActive; extern int D_800A4F28; /* tick / timeout counter */ extern int D_800A4F2C; /* CdMode-derived retry budget */ extern int D_800A4F30; /* remaining sub-stream repeat count (from param_3) */ extern int D_800A4F34; /* last sector position (stall detection) */ extern int D_800A4F38; /* end/abort flag */ extern int D_800A6544; extern u16 D_800A4E8E; /* 16-bit status flags */ extern u8 D_8006AEF4; /* 8-bit status flags */ extern u8 D_80068B60[]; /* per-resource descriptor table (0x10 stride) */ /* Second CD loader, DISTINCT from CdReadStateMachine: an 18-state machine (streamLoad_state) with * its OWN ready-callback (func_800377D8). Runs SetMode(0xA0)/SeekL/ReadN with retry + CdMode * handling, looping param_3 (D_800A4F30) times over sub-streams. Returns 0 = busy, 1 = done, * 2 = error/abort. Driven by ResourceLoadStateMachine. Provenance: static trace, Phase 3 T4 (fn id * verified; full semantics partial). * NON_MATCHING: faithful translation of the Ghidra decompile — logically faithful, not byte-verified. */ int StreamLoadStateMachine(int param_1, void *param_2, int param_3) { /* param_2 is a CdlLOC* */ short sVar1; int iVar4; u32 uVar2; char *pcVar3; u8 local_28[8]; u8 local_20[8]; int local_18; int local_14; sVar1 = *(short *)(D_80068B60 + (param_1 - 0x100) * 0x10); D_800A4F28++; switch (streamLoad_state) { case 0: D_800A4F38 = 0; D_800A6544 = 0; D_800A4F30 = param_3; func_8002EC10(); streamLoad_state++; D_800A4E8E &= 0xFFDF; return 0; case 1: func_80037334(); iVar4 = CdPosToInt((CdlLOC *)param_2); func_80037358(iVar4); func_80037144(sVar1); streamLoad_state++; /* fall through */ case 2: local_14 = CdSync(1, local_20); if (local_14 != 5 && local_14 != 2) return 0; uVar2 = CdMode(); D_800A4F2C = ((uVar2 & 0x80) == 0) ? 3 : 0; streamLoad_state++; /* fall through */ case 3: local_28[0] = 0xA0; iVar4 = CdControl(0x0E, local_28, local_20); /* CdlSetmode */ if (iVar4 == 0) { if ((local_20[0] & 0x10) != 0) { func_80037334(); streamLoad_state = 0; return 2; } return 0; } D_800A4F28 = 0; streamLoad_state++; /* fall through */ case 4: iVar4 = CdSync(1, local_20); if (iVar4 != 5) { if (iVar4 == 2) { streamLoad_state++; local_14 = 2; modeWait: if (D_800A4F2C != 0) { D_800A4F2C--; return 0; } streamLoad_state++; goto issueSeek; } if (D_800A4F28 < 0x3D) return 0; } streamLoad_state = 3; return 0; case 5: goto modeWait; case 6: issueSeek: iVar4 = func_80037CD8((void *)func_80036260); if (iVar4 == 0) return 0; streamLoad_state = 7; /* fall through */ case 7: iVar4 = func_8003750C(); if (iVar4 == 0) return 0; streamLoad_state++; /* fall through */ case 8: local_14 = CdSync(1, local_20); if (local_14 != 5 && local_14 != 2) return 0; streamLoad_state++; /* fall through */ case 9: D_800A4F28 = 0; iVar4 = CdControl(0x15, (u8 *)param_2, local_20); /* CdlSeekL */ if (iVar4 == 0) return 0; streamLoad_state++; /* fall through */ case 10: local_14 = CdSync(1, local_20); if (local_14 == 5) { iVar4 = CdControl(0x01, (u8 *)0, local_20); /* CdlNop */ if (iVar4 == 0) { streamLoad_state = 9; return 0; } if ((local_20[0] & 0x10) != 0) { func_80037334(); D_8006AEF4 &= 0xFD; streamLoad_state = 0; return 2; } streamLoad_state = 9; return 0; } if (local_14 != 2) { if (D_800A4F28 < 0x12D) return 0; CdFlush(); streamLoad_state = 9; return 2; } streamLoad_state++; local_14 = 2; /* fall through */ case 0xB: D_800A4F28 = 0; iVar4 = CdControl(0x06, (u8 *)param_2, local_20); /* CdlReadN */ if (iVar4 != 0) { if (streamLoad_cbActive == 0) streamLoad_savedReadyCB = CdReadyCallback((void *)func_800377D8); else CdReadyCallback((void *)func_800377D8); streamLoad_state++; streamLoad_cbActive = 1; return 0; } if ((local_20[0] & 0x10) == 0) return 0; D_8006AEF4 &= 0xFD; func_80037334(); streamLoad_state = 0; return 2; case 0xC: iVar4 = func_800374CC(&local_18); if (iVar4 == 0) { if (local_18 != D_800A4F34) { D_800A4F34 = local_18; D_800A4F28 = 0; } if (D_800A4F28 > 300) { if (streamLoad_cbActive != 0) { CdReadyCallback(streamLoad_savedReadyCB); streamLoad_savedReadyCB = 0; streamLoad_cbActive = 0; } streamLoad_state++; } return 0; } if (streamLoad_cbActive != 0) { CdReadyCallback(streamLoad_savedReadyCB); streamLoad_savedReadyCB = 0; streamLoad_cbActive = 0; } streamLoad_state++; /* fall through */ case 0xD: iVar4 = func_8003775C(); if (iVar4 == 0) return 0; func_80037D74(); streamLoad_state++; return 0; case 0xE: pcVar3 = (char *)func_80037368(&local_14); if (local_14 != 1) { streamLoad_state = 0x11; return 0; } if (*pcVar3 != 1) { if (*pcVar3 == 2) { streamLoad_state = 0x11; D_800A4F38 = 1; return 0; } if (D_800A4F30 != 0) { if (D_800A4F30 - 1 == 0) { streamLoad_state = 0x11; D_800A4F30 = 0; return 0; } streamLoad_state = 1; D_800A4F30--; return 0; } streamLoad_state = 1; return 0; } streamLoad_state++; break; /* -> CdlPause (post-switch) */ case 0xF: break; /* -> CdlPause (post-switch) */ case 0x10: iVar4 = CdSync(1, local_20); if (iVar4 != 5 && iVar4 != 2) return 0; if (D_800A4F38 == 0) { streamLoad_state = 0; return 1; } streamLoad_state = 0; D_800A4F38 = 0; return 2; case 0x11: iVar4 = CdControl(0x09, (u8 *)0, local_20); /* CdlPause */ if (iVar4 != 0) { streamLoad_state = 0x10; return 0; } return 0; default: return 0; } /* shared tail for states 0xE (advance) and 0xF: pause, then wait for it */ iVar4 = CdControl(0x09, (u8 *)0, local_20); /* CdlPause */ if (iVar4 != 0) { streamLoad_state++; return 0; } if ((local_20[0] & 0x10) != 0) { streamLoad_state = 0; return 1; } return 0; } #else INCLUDE_ASM("asm/nonmatchings/800", StreamLoadStateMachine); #endif INCLUDE_ASM("asm/nonmatchings/800", func_80036AF8); INCLUDE_ASM("asm/nonmatchings/800", func_80036D24); INCLUDE_ASM("asm/nonmatchings/800", func_80036D58); extern u8 D_800A4F1A; extern s32 D_800C6D28; extern void func_80036F18(void); void func_80036EB4(void) { if (!D_800A4F1A) { func_80036F18(); D_800C6D28 = 0; } } extern u8 D_800A4F1A; extern s32 D_800C6D28; extern void func_80036F18(void); void func_80036EE8(void) { func_80036F18(); D_800C6D28 = 0; D_800A4F1A = 0; } INCLUDE_ASM("asm/nonmatchings/800", func_80036F18); INCLUDE_ASM("asm/nonmatchings/800", func_80036F98); INCLUDE_ASM("asm/nonmatchings/800", func_80036FB0); CLEAR_TBL40(func_80037004) /* dedup I0: shared body (src/shared/clearTbl40.h) */ INCLUDE_ASM("asm/nonmatchings/800", func_80037028); /* The 5-entry, 0x10-stride CD-resource slot table. src/800.c already declares the SAME * memory as `Rsc16 D_80076248[]` (base = 0x80076248, .unk09 = D_80076251, .unk0A = D_80076252); * this function also touches the word 4 bytes BELOW that base, so the array is spelled from * D_80076244 here (offsets shift by -4; the emitted %lo values are identical, and every one of * D_80076244/48/4C/50/51/52 is a relocation symbol in this function's own .s). * .unk00 = D_80076244 .unk04 = D_80076248 .unk08 = D_8007624C * .unk0C = D_80076250 .unk0D = D_80076251 .unk0E = D_80076252 * 0x80076244 + 5*0x10 == 0x80076294, which is the next scalar this function clears. */ typedef struct { s32 unk00; s32 unk04; s32 unk08; u8 unk0C; u8 unk0D; u8 unk0E; u8 unk0F; } Slot16; /* 0x10 */ /* 2-byte stride u16 table. Declared as an array-of-STRUCT (cookbook §18) so gcc folds * %lo(D_80065438) into each indexed load instead of materialising the base into a register * (a plain `extern u16 D_80065438[]` CSEs the two accesses into one lui/addiu/addu base). */ typedef struct { /* 0x18 stride; D_800A463C + k*0x18 */ s32 unk00; u8 unk04[0x14]; } Ent24; /* THE TAIL SCHEDULE LEVER (gcc-2.7.2 sched.c:817 true_dependence / :845 anti_dependence). * The `if (i == 0)` block interleaves the varying-address load `D_800A463C[k].unk00` * (MEM_IN_STRUCT_P=1, rtx_addr_varies_p=1) with fixed-address stores to these scalars. * true_dependence's last guard reads * ! (MEM_IN_STRUCT_P (x) && rtx_addr_varies_p (x) && GET_MODE (x) != QImode * && ! MEM_IN_STRUCT_P (mem) && ! rtx_addr_varies_p (mem)) * so a /s VARYING load and a plain NON-/s FIXED store are declared non-aliasing — every * store/load edge disappears, the load floats to the top of the block and the whole tail * comes out permuted. Declaring these scalars as single-field structs sets MEM_IN_STRUCT_P * on their MEMs too, which fails `! MEM_IN_STRUCT_P (mem)` and restores the real * store->load / load->store edges. The symbol names are untouched (offset 0 of a 1-field * struct), so every relocation is still exactly D_8007622C / D_80076228 / ... as in the .s. */ typedef struct { s32 v; } W32; typedef struct { s16 v; } W16; typedef struct { u8 v; } W8; extern Slot16 D_80076244[]; extern W32 D_80076228; extern W32 D_8007622C; extern W32 D_80076238; extern W16 D_80076240; extern W8 D_80076242; extern W8 D_80076243; extern W32 D_80076294; extern s32 D_8007629C; /* src/800.c already declares this one as `extern s32` */ extern u8 D_800BA320[]; extern H2 D_80065438[]; extern s32 D_800652F0[]; extern Ent24 D_800A463C[]; extern s32 D_800A469C; extern s16 D_800A46A0; extern s16 D_800A46A2; extern u8 D_800A46B0; extern void func_800415A8(s32); void func_80037144(s32 idx) { s32 i; s32 k; s32 t; s32 v; s32 one; s32 ent; for (i = 0; i < 5; i++) { if (D_80076244[i].unk0D == 0) { break; } } if (i < 5) { one = 1; D_80076244[i].unk00 = (s32)D_800BA320; /* scheduling barrier: the slot stores and the D_80065438 load DO disambiguate here * (both /s and both varying -> memrefs_conflict_p:614 falls through to * find_symbolic_term and the two symbols differ), so without this gcc hoists the * D_80065438 load above all the slot stores and permutes the head block. * Emits zero instructions. */ __asm__ __volatile__("" ::: "memory"); D_80076244[i].unk0C = 0; D_80076244[i].unk0D = one; D_80076244[i].unk04 = idx | 0x2000; D_80076244[i].unk0E = 4; v = D_800652F0[D_80065438[idx].f0]; D_800A469C = v; D_80076244[i].unk08 = v; k = 4; if (D_800A46B0 == 0) { if (D_800A46A0 == (D_80065438[idx].f0 | 0x4000)) { D_80076244[i].unk04 |= 0x1000; goto after; } t = D_800A46A2; D_800A46B0 = one; } else { t = D_800A46A2; } if (t >= 0) { func_800415A8(t); D_800A46A2 = -1; } after: if (i == 0) { D_8007622C.v = (s32)D_800BA320; ent = D_800A463C[k].unk00; D_80076228.v = (s32)D_800BA320; D_80076240.v = 0; D_80076243.v = 1; D_80076242.v = 0; D_80076294.v = 0; D_80076238.v = ent; } } D_8007629C = 0; } CLEAR_TBL40(func_80037334) /* dedup I0: shared body (src/shared/clearTbl40.h) */ extern s32 D_8007623C; void func_80037358(int posInt) { D_8007623C = posInt; } extern u8 D_80076251; /* Law 2: exact form from src/shared/clearTbl40.h */ extern u8 D_80076220[]; extern u8 D_80076250[]; void *func_80037368(int *out) { int count = 0; u8 *ptr = D_80076220; int i; for (i = 0; i < 0x50; i += 0x10) { *ptr = 0; ptr++; if ((&D_80076251)[i] != 0) { D_80076220[count] = D_80076250[i]; count++; } } *out = count; return D_80076220; } typedef struct { s16 unk00; u8 pad02[4]; u16 unk06; } StructA4E88; /* Law 2: the destination TU (src/800.c) already declares D_80065438 as an array-of-struct * (H2 = { u16 f0; }) rather than a plain u16[] — refuse-TYPE(D_80065438, u16 vs H2). * Adopt the TU's spelling verbatim; the use site indexes .f0 instead of dereferencing a u16*. */ extern StructA4E88 D_800A4E88; extern u8 D_800BA320[]; extern Rsc16 D_80076248[]; /* .unk09 = D_80076251, .unk0A = D_80076252 */ extern u8 D_80076251; /* Law 2: exact form from src/shared/clearTbl40.h (see func_80037368) */ extern u8 D_80076250[]; extern u8 D_80076298; extern H2 D_80065438[]; extern void func_8002D7FC(s32 arg0); extern void func_8002FDE8(s32 arg0, void *arg1); void func_800373D0(void) { register StructA4E88 *sp2 __asm__("$18"); register u8 *sp3 __asm__("$19"); register Rsc16 *p __asm__("$16"); register s32 i __asm__("$17"); sp2 = &D_800A4E88; sp3 = D_800BA320; p = D_80076248; i = 0; do { if ((&D_80076251)[i] != 0) { if (D_80076250[i] != 0) { if (p->unk00 & 0x3000) { sp2->unk00 = p->unk00 & 0xFFF; sp2->unk06 |= 0x20; func_8002D7FC((s32) sp3); if (!(p->unk00 & 0x1000)) { func_8002FDE8(D_80065438[sp2->unk00].f0, sp3 + 0x7000); } } } } p++; i += 0x10; } while ((s32) p < (s32) &D_80076298); } INCLUDE_ASM("asm/nonmatchings/800", func_800374CC); typedef struct { s16 unk00; s16 unk02; s16 unk04; s16 unk06; s16 unk08; s16 unk0A; s16 unk0C; s16 unk0E; u8 unk10; u8 unk11; u8 unk12; u8 unk13; s32 unk14; } Rsc24; /* 0x18 */ typedef struct { u8 unk00; u8 unk01[11]; } Rsc12; /* 0x0C */ extern s32 D_8007629C; extern u8 D_8006AEF4; extern u8 D_80076298; extern Rsc16 D_80076248[]; /* .unk09 = D_80076251, .unk0A = D_80076252 */ extern Rsc12 D_80064D4A[]; extern Rsc24 D_800A4640[]; extern s16 D_800C5328[]; extern s16 D_800C532A[]; extern s32 func_8003C4F0(s32); extern void func_800415A8(s32); extern void func_80031A98(void); s32 func_8003750C(void) { s32 i; s32 val; s32 k; s32 h; switch (D_8007629C) { case 0: if (D_8006AEF4 & 2) { return 0; } D_8006AEF4 |= 2; D_8007629C = 1; /* fallthrough */ case 1: if (D_8006AEF4 & 1) { if (func_8003C4F0(0) == 0) { return 0; } } D_8007629C = D_8007629C + 1; /* fallthrough */ case 2: for (i = 0; i < 5; i++) { if (D_80076248[i].unk09 == 0) { continue; } val = D_80076248[i].unk00; k = D_80076248[i].unk0A; if (val & 0x6000) { continue; } h = D_800A4640[k].unk04; if (h != 0 && h != D_80064D4A[val].unk00) { D_800C532A[D_800A4640[k].unk08 * 2] = -1; D_800A4640[k].unk04 = 0; D_800A4640[k].unk08 = 0; } if (D_800A4640[k].unk10 != 0) { continue; } D_800A4640[k].unk10 = 1; D_800C5328[D_800A4640[k].unk00 * 2] = -1; if (D_800A4640[i].unk02 >= 0) { func_800415A8(D_800A4640[i].unk02); D_800A4640[i].unk02 = -1; } func_80031A98(); } D_8007629C = 0; D_80076298 = 0; return 1; default: return 0; } } INCLUDE_ASM("asm/nonmatchings/800", func_8003775C); INCLUDE_ASM("asm/nonmatchings/800", func_800377D8); extern s32 D_800BA0F8; void func_80037CC8(void) { D_800BA0F8 = 0; } extern u8 D_8006AEF4; extern s32 D_800BA0F8; int func_80037CD8(void *arg) { if ((D_8006AEF4 & 0x2) != 0) { s32 *handler = (s32 *)&D_800BA0F8; if (*handler != 0) { int result = ((int (*)(void *))*handler)(arg); if (result != 0) { u8 flags = D_8006AEF4; *handler = (s32)arg; D_8006AEF4 = flags & 0xFD; return 1; } return 0; } } D_800BA0F8 = (s32)arg; D_8006AEF4 &= 0xFD; return 1; } INCLUDE_ASM("asm/nonmatchings/800", func_80037D74); extern u8 *D_800762B0; extern u8 D_800A4EFE[]; extern s32 D_80073140[]; extern u8 D_800C6DE0[]; extern u8 D_800C6DE4[]; extern u8 D_800C6DEC[]; extern u8 D_800C6DEE[]; extern u8 D_800C6E2A[]; extern u8 D_800C6E2B[]; extern u8 D_800C6E2C[]; extern u8 D_800C6E2D[]; extern u8 D_800C6E2E[]; extern u8 D_800B9EC8[]; extern u8 D_800B9ED2[]; extern u8 D_800B9ED3[]; extern s32 D_80079A68; extern void func_8003D424(s32 *); void func_80037D98(void) { s32 i; s32 offset; s32 val; s32 *table; D_800762B0 = D_800A4EFE; i = 0; table = D_80073140; offset = 0; while (i < 0x10) { val = *table; table++; i++; D_800C6E2A[offset] = 0; D_800C6E2B[offset] = 0; D_800C6E2C[offset] = 0; D_800C6E2D[offset] = 0; *(s16 *)&D_800C6DEC[offset] = 0; *(s16 *)&D_800C6DEE[offset] = 0; *(s32 *)&D_800C6DE4[offset] = 0; D_800C6E2E[offset] = 0; *(s32 *)&D_800C6DE0[offset] = val; offset += 0x60; } i = 0; offset = 0; for (; i < 2; i++) { D_800B9ED3[offset] = 0; D_800B9ED2[offset] = 0; *(s16 *)&D_800B9EC8[offset] = i; offset += 0x1FC; } func_8003D424(&D_80079A68); } INCLUDE_ASM("asm/nonmatchings/800", func_80037EA0); INCLUDE_ASM("asm/nonmatchings/800", func_80037F3C); INCLUDE_ASM("asm/nonmatchings/800", func_80037FC4); INCLUDE_ASM("asm/nonmatchings/800", func_8003819C); extern u8 D_800B9CF0[]; s32 func_800381E4(s32 a0, s32 a1) { s32 v0; a1 = (a1 << 16) >> 13; v0 = (a0 << 7) - a0; v0 = v0 << 2; a1 = a1 + v0; return D_800B9CF0[a1]; } extern u8 D_800B9CD8[]; extern u8 D_800C1320[]; extern u8 *D_800A6428; extern s32 func_80038698(void *arg0); extern s32 func_800387C0(void *arg0); extern void func_80038838(void *arg0); s32 func_80038210(s32 a0, s16 a1, s32 a2) { u8 *entry; s32 i; s32 k; u8 *q; s32 v1; entry = D_800B9CD8; for (i = 0; i < 2; i++, entry += 0x1FC) { if (entry[0x1FB] == 0) { D_800A6428 = entry + 0x1BA; *(s32 *)entry = a0; *(s16 *)(entry + 0x1EC) = a1; *(s32 *)(entry + 0x1E8) = a2; *(u8 **)(entry + 0x1D8) = D_800C1320; *(u8 **)(entry + 0x1DC) = D_800C1320 + 0x20; *(u8 **)(entry + 0x1E0) = D_800C1320 + 0x820; if (func_80038698(entry) != 0) { return -1; } if (func_800387C0(entry) != 0) { return -1; } func_80038838(entry); v1 = *(s32 *)entry; entry[0x1FB] = 1; *(s32 *)(entry + 4) = v1; entry[0x1FA] = 0; for (k = 0xF, q = entry + 0xF; k >= 0; k--, q--) { q[0x1BA] = 0; } return i; } } return -1; } INCLUDE_ASM("asm/nonmatchings/800", func_80038308); INCLUDE_ASM("asm/nonmatchings/800", func_8003834C); extern u8 D_800B9ED3[]; extern u8 D_800B9ED2[]; s32 func_8003836C(s32 a0) { s32 index; u8 val; index = a0 * 127; index = index * 4; val = D_800B9ED3[index]; if (val == 0) { return 0; } return D_800B9ED2[index]; } INCLUDE_ASM("asm/nonmatchings/800", func_800383A4); INCLUDE_ASM("asm/nonmatchings/800", func_800384A8); INCLUDE_ASM("asm/nonmatchings/800", func_800385C0); extern u8 D_800B9E92[]; void func_80038638(s32 a0, s32 a1) { u8 *ptr; s32 offset; offset = a0 * 127; offset = offset * 4; ptr = D_800B9E92 + offset; *(ptr + a1) |= 1; } extern u8 D_800B9E92[]; void func_80038668(s32 a0, s32 a1) { s32 offset = ((a0 << 7) - a0) << 2; char *baseptr = (char *)D_800B9E92 + offset; *(baseptr + a1) &= 0xFE; } INCLUDE_ASM("asm/nonmatchings/800", func_80038698); INCLUDE_ASM("asm/nonmatchings/800", func_800387C0); void func_80038838(void *arg0) { register unsigned char *a3 asm("$7"); register unsigned char *a1 asm("$5"); register unsigned char *v1; register unsigned char *v0 asm("$2"); register int a2 asm("$6"); register int v0_c; register int const1 asm("$8"); register int const2 asm("$9"); register int const3 asm("$3"); a3 = (unsigned char *)arg0 + 0x1A; a2 = 0; const1 = 0x40; const2 = 0x7F; a1 = (unsigned char *)arg0 + 0x1B; do { v1 = a3 + 0x9; v0_c = 0xF; a3[0x0] = a2; *(unsigned short *)(a1 + 0x1) = const1; a1[0x3] = const1; a1[0x6] = 0; a1[0x0] = const2; do { v1[0x0] = 0; v0_c--; v1++; } while (v0_c >= 0); a2++; a1 += 0x1A; a3 += 0x1A; } while (a2 < 0x10); a2 = 0; const3 = 0x4000; v0 = (unsigned char *)arg0; do { v0[0x18] = 0; *(unsigned short *)(v0 + 0x12) = const3; v0 += 0x8; a2++; } while (a2 <= 0); *(int *)((unsigned long)arg0 + 0x8) = 1; *(int *)((unsigned long)arg0 + 0x1D0) = 0xE10; *(int *)((unsigned long)arg0 + 0x1D4) = 0xE10; *(int *)((unsigned long)arg0 + 0x1CC) = 0xE10; *(unsigned char *)((unsigned long)arg0 + 0x1F4) = 0; *(unsigned short *)((unsigned long)arg0 + 0x1E4) = 0x78; *(unsigned char *)((unsigned long)arg0 + 0x1F7) = 0; *(unsigned char *)((unsigned long)arg0 + 0x1F8) = 0; *(unsigned char *)((unsigned long)arg0 + 0x1F9) = 0; *(unsigned char *)((unsigned long)arg0 + 0x1FA) = 0; *(unsigned char *)((unsigned long)arg0 + 0x1F6) = 0; } INCLUDE_ASM("asm/nonmatchings/800", func_800388E8); INCLUDE_ASM("asm/nonmatchings/800", func_80038908); extern u8 *D_800762B0; extern u8 D_800762B4[]; extern u8 D_800C6E2A[]; void func_80038958(void) { register u8 *a3 __asm__("$7"); register u8 *a2 __asm__("$6"); register s32 a1 __asm__("$5"); register u8 *a0 __asm__("$4"); register s32 t0 __asm__("$8"); register s32 v0 __asm__("$2"); register s32 v1 __asm__("$3"); a3 = D_800762B0; a2 = D_800762B4; a1 = 0; t0 = 3; a0 = D_800C6E2A; while (a1 < 0x10) { v0 = *a3; if (v0 == 0 || v0 == t0) { if (*a2 == 0) { v0 = *a0; a0[1] = 0; if (v0 != 0) { v0 = *(s16 *)(a0 - 0x54); v1 = v0 << 1; v1 = v1 + v0; v1 = v1 << 2; v1 = v1 + v0; v0 = *(s32 *)(a0 - 0xA); v1 = v1 << 1; v0 = v0 + v1; v0 = v0 + a1; *(u8 *)(v0 + 0x23) = 0; a0[0] = 0; } } } a1++; a0 += 0x60; a3++; a2++; } } extern u8 *D_800762B0; extern u8 D_800762B4[]; s32 func_80038A00(void) { u8 *ptr1 = D_800762B0; u8 *ptr2 = D_800762B4; s32 i = 0; while (i < 0x10) { if (*ptr1 == 0x2 && *ptr2 == 0) { return i; } i++; ptr1++; ptr2++; } return -1; } INCLUDE_ASM("asm/nonmatchings/800", func_80038A58); INCLUDE_ASM("asm/nonmatchings/800", func_80038FC4); INCLUDE_ASM("asm/nonmatchings/800", func_80038FFC); INCLUDE_ASM("asm/nonmatchings/800", func_8003916C); INCLUDE_ASM("asm/nonmatchings/800", func_800391D4); void func_80039300(void) { } INCLUDE_ASM("asm/nonmatchings/800", func_80039308); INCLUDE_ASM("asm/nonmatchings/800", func_80039B20); INCLUDE_ASM("asm/nonmatchings/800", func_80039C5C); INCLUDE_ASM("asm/nonmatchings/800", func_80039C70); INCLUDE_ASM("asm/nonmatchings/800", func_80039DEC); INCLUDE_ASM("asm/nonmatchings/800", func_80039F14); INCLUDE_ASM("asm/nonmatchings/800", func_80039F50); INCLUDE_ASM("asm/nonmatchings/800", func_8003A098); INCLUDE_ASM("asm/nonmatchings/800", func_8003A0D0); INCLUDE_ASM("asm/nonmatchings/800", func_8003A0E4); typedef struct { /* 0x000 */ u8 *ptr; /* 0x004 */ u8 pad_004[0x1D0 - 0x004]; /* 0x1D0 */ s32 unk1D0; /* 0x1D4 */ u8 pad_1D4[0x1E4 - 0x1D4]; /* 0x1E4 */ s16 unk1E4; /* 0x1E6 */ s16 unk1E6; /* 0x1E8 */ u8 pad_1E8[0x1F9 - 0x1E8]; /* 0x1F9 */ u8 unk1F9; } func_8003A234_Ctx; void func_8003A234(func_8003A234_Ctx *a0) { u8 *a2 = a0->ptr; u8 v1; a0->ptr = a2 + 1; v1 = *a2; switch (v1) { case 0x20: a0->ptr = a2 + 3; break; case '/': a0->ptr = a2 + 2; a0->unk1F9 = 1; break; case 'Q': { register s32 num __asm__("$4"); s32 den; s32 b3; s32 b4; s32 q; a0->ptr = a2 + 2; den = a2[1]; if (den != 3) { a0->unk1F9 = 1; break; } num = 0x3938700; a0->ptr = a2 + 3; den = a2[2]; a0->ptr = a2 + 4; b3 = a2[3]; a0->ptr = a2 + 5; b4 = a2[4]; den = den << 16; den += b3 << 8; den += b4; q = num / den; a0->unk1E4 = (s16)q; a0->unk1D0 = q * a0->unk1E6; } break; case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 'T': case 0x58: case 0x59: case 0x7F: { register u8 *p __asm__("$3") = a0->ptr; u8 *np = p + 1; a0->ptr = np; { register s32 byte __asm__("$5") = p[0]; a0->ptr = np + byte; } } break; case 0: a0->ptr = a2 + 2; { register s32 b1 __asm__("$5") = a2[1]; if (b1 != 2) { a0->unk1F9 = 1; break; } } a0->ptr = a2 + 4; break; default: a0->unk1F9 = 1; break; } } INCLUDE_ASM("asm/nonmatchings/800", func_8003A3D8); INCLUDE_ASM("asm/nonmatchings/800", func_8003A404); INCLUDE_ASM("asm/nonmatchings/800", func_8003A424);