diff --git a/src/ov_SC03_118/ov_SC03_118_jr_8017FB84.c b/src/ov_SC03_118/ov_SC03_118_jr_8017FB84.c index 84b247ba5..057ac985b 100644 --- a/src/ov_SC03_118/ov_SC03_118_jr_8017FB84.c +++ b/src/ov_SC03_118/ov_SC03_118_jr_8017FB84.c @@ -3981,7 +3981,158 @@ void func_80183C58(void *a0) { INCLUDE_ASM("asm/ov_SC03_118/nonmatchings/ov_SC03_118_jr_8017FB84", func_80183C94); -INCLUDE_ASM("asm/ov_SC03_118/nonmatchings/ov_SC03_118_jr_8017FB84", func_80183D68); +#include "common.h" + +/* ---- integration surface: every decl below AGREES with the visible file-scope decl in + * src/ov_SC03_118/ov_SC03_118_jr_8017FB84.c (the destination TU), lines noted. ---- */ +extern s32 func_8012BD14(void); /* not in TU */ +extern s32 func_80029178(s32 arg); /* TU:65 */ +extern void func_801746DC(void); /* TU:1253 */ +extern s32 func_80178B18(s32 param_1, s32 param_2); /* TU:2538 */ +extern s32 func_801789AC(s32 arg0); /* TU:2535 */ +extern void func_80178D18(void); /* TU:2545 -- called w/ arg via cast */ +extern s32 func_8014CB58(void); /* TU:561 */ +extern void func_8014B598(s32 a0, s32 a1); /* TU:472 */ +extern s32 func_8016F1AC(void); /* TU:351 */ +extern void func_80175454(void); /* TU:1105 */ +extern void func_800D0C48(s32 a0); /* TU:1469 */ +extern s32 func_8014CB2C(void); /* TU:560 */ +extern void func_8012A018(s32 a, s32 b); /* TU:2412 */ +extern void func_801842DC(s32 a0); /* TU:4023 INCLUDE_ASM (no decl) */ +extern s32 func_80183830(void); /* TU:3930 INCLUDE_ASM (no decl) */ +extern void func_80029124(s32, s32); /* TU:50 */ +extern s32 func_8016F0AC(); /* TU:1945 */ +extern void func_8012C588(s32 a0, s32 a1); /* not in TU */ +extern void func_8002959C(void); /* not in TU */ +extern void func_8018416C(s32 param_1); /* TU:3992 def (later) */ +extern void func_801841C0(s32 param_1); /* TU:4008 def (later) */ +extern void func_80184430(void *a0); /* TU:4030 def (later) */ + +extern s32 D_801151D4; /* TU:323 */ +extern M2C_UNK D_8018DA14; /* TU:2712 */ +extern u8 D_8018EBE8[]; +extern u8 D_8018EBF8[]; +extern u8 D_8018EBD8[]; +extern u8 D_8018EBC8[]; +extern u8 D_80126948[]; /* TU:667 */ + +extern s16 D_801D4348; +extern s16 D_801D434A; +extern s16 D_801D434C; +extern s16 D_801D4350; +extern s16 D_801D4352; +extern s16 D_801D4354; +extern s32 D_801D4358; +extern s32 D_801D435C; + +void func_80183D68(s32 a0) { + s32 p; + u16 t34; + + if (func_8012BD14() < 0x401 || *(u16 *)(a0 + 0x34) == 1 || *(u16 *)(a0 + 0x34) == 2) { + switch (*(u16 *)(a0 + 0x34)) { + case 0: + if ((func_80029178(0x8B) & 0xFF) == 0) { + func_801746DC(); + func_80178B18(a0, (s32)D_8018EBE8); + *(u16 *)(a0 + 0x34) = 2; + } else { + func_801746DC(); + func_80178B18(a0, (s32)D_8018EBF8); + *(u16 *)(a0 + 0x34) = *(u16 *)(a0 + 0x34) + 1; + } + break; + case 1: + if (func_801789AC(a0) == 1) { + /* file-scope decl in the host TU is `void func_80178D18(void)` but the call + site passes a0 -- call through a cast so the decl stays byte-compatible. */ + ((void (*)(s32))func_80178D18)(a0); + *(u16 *)(a0 + 0x34) = 3; + } + break; + case 2: + if (func_801789AC(a0) == 1) { + ((void (*)(s32))func_80178D18)(a0); + *(u16 *)(a0 + 0x34) = 4; + } + break; + case 3: + if ((func_80029178(0xB8) & 0xFF) == 0 && func_8014CB58() == 3) { + func_8014B598(3, (s32)&D_8018DA14); + if (func_8016F1AC() != 0 && (*(u16 *)(a0 + 0x100) & 4) == 0) { + *(u16 *)(a0 + 0x100) |= 4; + *(s32 *)(a0 + 0x1C) = 0; + func_80175454(); + } + if (*(u16 *)(a0 + 0x100) & 4) { + *(s32 *)(a0 + 0x1C) = *(s32 *)(a0 + 0x1C) + 1; + } + if ((*(u16 *)(a0 + 0x100) & 2) == 0 && *(s32 *)(a0 + 0x1C) == 0x5A) { + *(u16 *)(a0 + 0x100) |= 2; + func_800D0C48(0); + } + if ((*(u16 *)(a0 + 0x100) & 1) == 0 && + (func_8014CB2C() & 0x8000007F) == 0x80000003) { + p = D_801151D4; + t34 = *(u16 *)(a0 + 0x34) + 1; + *(u16 *)(a0 + 0x100) |= 1; + *(u16 *)(a0 + 0x34) = t34; + D_801D4348 = *(s32 *)(p + 0x48); + D_801D434A = *(s32 *)(p + 0x4C); + D_801D434C = *(s32 *)(p + 0x50); + D_801D4350 = *(s32 *)(p + 0x3C); + D_801D4352 = *(s32 *)(p + 0x40); + D_801D4358 = (s32)D_8018EBD8; + D_801D4354 = *(s32 *)(p + 0x44); + D_801D435C = (s32)&D_8018EBD8[8]; + func_8012A018((s32)func_80184430, 0); + func_801842DC(p); + *(s32 *)(a0 + 0x1C) = 0; + } + } + break; + } + if ((func_80029178(0x8B) & 0xFF) == 0) { + return; + } + /* func_801841C0(a0) is written out on all three paths: gcc cross-jumps the common + tail but leaves the three $a0 arg-setups, which then fill the three branch delay + slots (a single shared call puts the move in the jal's own slot instead). */ + if (func_8014CB58() == 3) { + func_8018416C(a0); + func_801841C0(a0); + } else { + *(u8 *)(*(s32 *)(a0 + 0xCC) + 0x27) = 0x88; + func_801841C0(a0); + } + } else { + if ((func_80029178(0x8B) & 0xFF) == 0) { + return; + } + *(u8 *)(*(s32 *)(a0 + 0xCC) + 0x27) = 0x88; + func_801841C0(a0); + } + if (*(u16 *)(a0 + 0x100) & 1) { + *(s32 *)(a0 + 0x1C) = *(s32 *)(a0 + 0x1C) + 1; + if (*(s32 *)(a0 + 0x1C) == 0x78) { + D_801D4358 = (s32)D_8018EBC8; + D_801D435C = (s32)&D_8018EBC8[8]; + func_8012A018((s32)func_80184430, 0); + func_801842DC((s32)D_80126948); + } + if (func_80183830() != 0) { + func_80029124(0xB8, 1); + *(u16 *)(a0 + 0x100) &= ~1; + func_8016F0AC(); + func_8012C588(0x249, a0); + *(u16 *)(a0 + 0x2) = 2; + *(u8 *)(*(s32 *)(a0 + 0xCC) + 0x27) = 0x88; + *(u8 *)(*(s32 *)(a0 + 0xD0) + 0x27) = 0x89; + func_8002959C(); + } + } +} + void func_80184164(void) { } diff --git a/src/ov_SC06_008/ov_SC06_008_jr_8017C294.c b/src/ov_SC06_008/ov_SC06_008_jr_8017C294.c index 566d09696..e6a6a701d 100644 --- a/src/ov_SC06_008/ov_SC06_008_jr_8017C294.c +++ b/src/ov_SC06_008/ov_SC06_008_jr_8017C294.c @@ -4192,7 +4192,83 @@ void func_8017F624(s32 param_1) } -INCLUDE_ASM("asm/ov_SC06_008/nonmatchings/ov_SC06_008_jr_8017C294", func_8017F8CC); + + + + + + +extern s16 D_8019C91C[]; +extern u16 D_80126B5E; +extern u16 D_80126B62; +extern u16 D_80126B66; + +void func_8017F8CC(s32 a0) +{ + switch (*(u16 *)(a0 + 0x34)) { + case 0: + { + s16 idx = *(s16 *)(a0 + 0x70); + s16 *p = &D_8019C91C[(idx - 1) * 4]; + s16 gx = *(s16 *)&D_80126B5E; + s16 gz; + s32 p20; + + if (!(*p < gx)) { + break; + } + p++; + if (!(gx < *p)) { + break; + } + p++; + gz = *(s16 *)&D_80126B66; + if (!(*p < gz)) { + break; + } + if (!(gz < p[1])) { + break; + } + + p20 = *(s32 *)(a0 + 0x20); + *(u32 *)(p20 + 4) = *(u32 *)(p20 + 4) & 0x7FFFFFFF; + + { + s32 t; + + *(s16 *)(a0 + 0x88) = D_80126B5E; + *(s16 *)(a0 + 0x6) = D_80126B5E; + t = D_80126B62 - 0x80; + *(s16 *)(a0 + 0x8A) = t; + + t = *(u16 *)(a0 + 0x34); + { + register s16 z __asm__("$3"); + t = t + 1; + z = D_80126B66; + *(u16 *)(a0 + 0x34) = t; + *(s16 *)(a0 + 0x8C) = z; + *(s16 *)(a0 + 0xE) = z; + } + } + break; + } + case 1: + { + s16 v; + + v = *(u16 *)(a0 + 0xA) + 8; + *(u16 *)(a0 + 0xA) = v; + if (v < *(s16 *)(a0 + 0x8A)) { + break; + } + *(s16 *)(a0 + 0x2) = 1; + *(u16 *)(a0 + 0x34) = 0; + break; + } + } +} + INCLUDE_ASM("asm/ov_SC06_008/nonmatchings/ov_SC06_008_jr_8017C294", func_8017F9FC); @@ -6444,7 +6520,64 @@ extern void func_8012B370(int a0); } -INCLUDE_ASM("asm/ov_SC06_008/nonmatchings/ov_SC06_008_jr_8017C294", func_80183BF4); +#include "common.h" + +extern void func_8012B23C(s32 a0); +extern void func_8012CBCC(s32 a0); +extern u8 *func_8012913C(s32 a0); +extern void func_8012C218(void *a0); + +void func_80183BF4(s32 a0) +{ + s32 s0 = a0; + s32 ret; + u16 val; + + if (*(u16 *)(s0 + 2) == 0) { + *(u8 *)(s0 + 0xC0) = 1; + *(u16 *)(s0 + 2) = *(u16 *)(s0 + 2) + 1; + func_8012B23C(s0); + *(s32 *)(s0 + 0x1C) = 1; + return; + } + + *(s32 *)(s0 + 0xDC) = *(s32 *)(s0 + 0x14); + ret = ((s32 (*)(s32))func_8012CBCC)(s0); + if (ret != 0 && (ret & 0x6000) != 0) { + s32 cnt = *(s32 *)(s0 + 0x1C) - 1; + *(s32 *)(s0 + 0x1C) = cnt; + if (cnt == 0) { + goto L80183CF4; + } + *(s32 *)(s0 + 0x14) = -*(s32 *)(s0 + 0xDC) >> 2; + } + + val = *(u16 *)(s0 + 0xFC) + 1; + *(u16 *)(s0 + 0xFC) = val; + if ((val & 1) != 0) { + s32 np = (s32)func_8012913C(0x23); + if (np != 0) { + *(u16 *)(np + 6) = *(u16 *)(s0 + 6); + *(u16 *)(np + 0xA) = *(u16 *)(s0 + 0xA); + { + u16 t = *(u16 *)(s0 + 0xE); + *(u32 *)(np + 0x18) = 0; + *(u32 *)(np + 0x14) = 0; + *(u32 *)(np + 0x10) = 0; + *(u16 *)(np + 0x34) = 0x3000; + *(u16 *)(np + 0xE) = t; + } + } + } + + if (*(s16 *)(s0 + 0xA) < 0x20) { + return; + } + +L80183CF4: + func_8012C218((void *)s0); +} + extern void func_80019064(void *a0); diff --git a/src/ov_SC06_011/ov_SC06_011_jr_8017BEBC.c b/src/ov_SC06_011/ov_SC06_011_jr_8017BEBC.c index a473ff34a..260d54f73 100644 --- a/src/ov_SC06_011/ov_SC06_011_jr_8017BEBC.c +++ b/src/ov_SC06_011/ov_SC06_011_jr_8017BEBC.c @@ -3961,7 +3961,51 @@ void func_8017ED68(void *a0) { } -INCLUDE_ASM("asm/ov_SC06_011/nonmatchings/ov_SC06_011_jr_8017BEBC", func_8017EDA4); +extern s32 func_8012C354(s32 a0, s32 a1); +extern void func_8001C214(s32 a0, s32 a1); +extern void func_8012A828(s32 *a0, s32 a1); +extern void func_8012B23C(void *a0); + +extern u8 D_8019E204[]; +extern s32 D_8019E238[]; +extern u8 D_8018077C[]; + +void func_8017EDA4(void *a0) { + if (func_8012C354((s32)a0, (s32)D_8019E204) != 0) { + u16 v1 = *(u16 *)((s32)a0 + 0x70); + + if (v1 & 0x10) { + u16 v0 = v1 & ~0x10; + *(u16 *)((s32)a0 + 0x70) = v0; + func_8001C214(*(s32 *)((s32)a0 + 0x20), D_8019E238[(s16)v0]); + *(s32 *)(*(s32 *)((s32)a0 + 0x20) + 0x4) |= 0x60000040; + } else { + func_8001C214(*(s32 *)((s32)a0 + 0x20), D_8019E238[(s16)v1]); + } + + if (*(s16 *)((s32)a0 + 0x70) == 8 || *(s16 *)((s32)a0 + 0x70) == 0xA) { + *(s16 *)((s32)a0 + 0x34) = 3; + *(u16 *)((s32)a0 + 0x5C) = 0x8800; + } + if (*(s16 *)((s32)a0 + 0x70) == 9) { + *(s16 *)((s32)a0 + 0x34) = 2; + *(u16 *)((s32)a0 + 0x5C) = 0x8800; + } + if (*(s16 *)((s32)a0 + 0x70) == 0xB) { + *(s16 *)((s32)a0 + 0x34) = 4; + } + + *(u8 *)((s32)a0 + 0xC0) = 1; + *(s32 *)((s32)a0 + 0xB4) = -1; + *(u8 *)((s32)a0 + 0x75) = 8; + func_8012A828((s32 *)a0, (s32)D_8018077C); + + *(u16 *)((s32)a0 + 0x2) = *(u16 *)((s32)a0 + 0x2) + 1; + func_8012B23C(a0); + *(s16 *)((s32)a0 + 0xAE) = -1; + } +} + INCLUDE_ASM("asm/ov_SC06_011/nonmatchings/ov_SC06_011_jr_8017BEBC", func_8017EEEC); diff --git a/src/ov_SC06_018/ov_SC06_018_jr_8017C24C.c b/src/ov_SC06_018/ov_SC06_018_jr_8017C24C.c index 2e024ab68..3e567d8e7 100644 --- a/src/ov_SC06_018/ov_SC06_018_jr_8017C24C.c +++ b/src/ov_SC06_018/ov_SC06_018_jr_8017C24C.c @@ -6800,7 +6800,53 @@ INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_8017C24C", func_801841E INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_8017C24C", func_8018431C); -INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_8017C24C", func_80184370); +#include "common.h" + +extern s32 func_80187130(s32 a0, s32 a1); +extern u16 D_80126B96; +extern s32 func_8018765C(s32 a0); +extern u16 D_800B99DA; +extern s32 func_80143B6C(s32 a0, s32 a1); +extern void func_80189E60(s32 a0, s32 a1); +extern s32 func_8012BEE8(); + +void func_80184370(s32 p) +{ + s32 e; + + if (func_80187130(p, 0x64) == 1) { + D_80126B96 = 4; + } + + *(s32 *)(p + 0x10) = *(s32 *)(p + 0x10) * 31 / 32; + *(s32 *)(p + 0x18) = *(s32 *)(p + 0x18) * 31 / 32; + func_8018765C(p); + + if ((u32)D_800B99DA % 6 == 0) { + e = func_80143B6C(p, 0); + if (e != 0) { + *(s16 *)(*(s32 *)(e + 0xCC) + 0x18) = 0x5000; + *(s16 *)(*(s32 *)(e + 0xCC) + 0x1A) = 0x5000; + } + } + + if (*(s32 *)(p + 0x94) == 8) { + func_80189E60(p, 0x8C0); + } + + if (*(u16 *)(p + 0x34) == 0) { + if (*(s32 *)(p + 0x94) == 0xE) { + *(u16 *)(p + 0x98) = 0; + *(s32 *)(p + 0x1C) = 0x1E; + *(u16 *)(p + 0x34) += 1; + } + } else { + if (func_8012BEE8(p) != 0) { + *(s16 *)(p + 2) = 6; + } + } +} + INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_8017C24C", func_801844C0); @@ -6945,7 +6991,61 @@ void func_80184904(s32 param_1) } -INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_8017C24C", func_80184944); +#include "common.h" + +extern s32 func_8012BCCC(s32 a0); +extern s32 func_8012B8E4(s32 a0, s32 a1); +extern s32 func_8012BEE8(); +extern s32 func_8012B8A4(s16 *a0); +extern s32 func_8012B608(s32 a0, s32 a1, s32 a2); +extern void func_8012B178(s32 a0, s32 a1); +extern s32 func_8018765C(s32 a0); +extern void func_801877E4(s32 a0); +extern s32 rand(void); + +void func_80184944(s32 p) +{ + s32 v; + s32 r; + s32 t; + + v = func_8012BCCC(p); + if (v > 0x144000) { + *(s16 *)(p + 2) = 0x13; + return; + } + + if (v > 0xC4000) { + if ((*(u32 *)(p + 0xE0) & 0x40) == 0) { + r = func_8012B8E4(p, 8); + *(u16 *)(*(s32 *)(p + 0x20) + 0x12) += r; + } + } else { + if ((u32)(v - 0x24000) <= 0xA0000) { + *(s16 *)(p + 2) = 0x15; + return; + } + + if ((*(u32 *)(p + 0xE0) & 0x40) == 0) { + if (func_8012BEE8(p) != 0) { + t = func_8012B8A4((s16 *)p); + *(u32 *)(p + 0xE8) = (t + 0x800) & 0xFFF; + r = rand(); + *(s32 *)(p + 0x1C) = r % 0x40 + 0x40; + } + } + + r = func_8012B608(*(s16 *)(*(s32 *)(p + 0x20) + 0x12), *(s32 *)(p + 0xE8), 8); + *(u16 *)(*(s32 *)(p + 0x20) + 0x12) += r; + } + + func_8012B178(p, 0xFFF9C000); + *(u32 *)(p + 0xE0) &= 0xFFFFFFBF; + if (func_8018765C(p) == 0) { + func_801877E4(p); + } +} + INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_8017C24C", func_80184AA8); diff --git a/src/ov_SC06_018/ov_SC06_018_jr_80187AEC.c b/src/ov_SC06_018/ov_SC06_018_jr_80187AEC.c index 6e69f147f..2b55fca74 100644 --- a/src/ov_SC06_018/ov_SC06_018_jr_80187AEC.c +++ b/src/ov_SC06_018/ov_SC06_018_jr_80187AEC.c @@ -4358,7 +4358,145 @@ INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_80187AEC", func_8018A7C INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_80187AEC", func_8018A86C); -INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_80187AEC", func_8018A974); +/* func_8018A974 — allocates a semi-trans LineF2 GPU packet, projects two + * world-space points via RotTransPers, and (if both are on-screen with + * non-negative depth-clip flags) links the packet into the current + * double-buffer's OT at the first point's depth, then notifies via + * func_80016638. + * + * STEP 0 sibling search (§160g) — three already-MATCHED exemplars supplied + * the whole shape, none needed independent discovery: + * + * 1. src/shared/engine_core.h DEFINE_func_8012D3B4() — identical prologue + * (temp_v0=func_80010A08(0x10); word4=arg2; func_8004914C/func_800491AC + * (&D_800AF648); two RotTransPers calls with the exact + * `(d>0) && (flag>=0) && (RotTransPers(...)>0) && (flag>=0)` guard) but + * calls a real `SetLineF2()` (code 0x40, no semi-trans) and a real + * `AddPrim()` (single evaluation). Our target's asm has NEITHER a + * `jal SetLineF2` NOR a `jal AddPrim` — both are fully inlined, which + * is the key structural difference this draft encodes (code 0x42 = + * semi-trans LineF2 needs hand-set fields; AddPrim needs the classic + * macro body since a *variable* depth offset appears nowhere in the + * real AddPrim()-calling siblings). + * + * 2. src/ov_SC03_119/ov_SC03_119_jr_8017FB84.c func_80185944 — byte-MATCH + * (177/177 ins). Its header names this exact tail idiom: "link-ins + * the packet into the current double-buffer's OT (the PSY-Q `addPrim` + * macro pair) and calls func_80016638", with `func_80016638(&D_800A6518 + * [*bidx*20], depth, 1)` as its literal call form (there depth=0x10 + * constant, here depth=temp_v0_2). Confirms the "3 lhu D_800B9A02, + * address cached / value reloaded" shape and the general addPrim(ot,p) + * == setaddr(p,getaddr(ot)); setaddr(ot,p) reading. + * + * 3. src/ov_SC03_119/ov_SC03_119_jr_8017FB84.c func_801860E8 (same TU, + * just above func_80185944) — its own header documents **Lever B**, + * byte-measured: "THE OT TABLE MUST BE AN ARRAY_REF (`D_800A651C[i].a`), + * NOT `*(s32*)((u8*)&D_800A651C + i*20)`. With the pointer-arith + * spelling gcc allocates a phantom 8-byte stack temp that is never + * referenced... The temp only appears when the SAME lookup expression + * occurs in two statements; the ARRAY_REF form kills it." This was the + * load-bearing fix for this draft too — confirmed empirically here via + * the `cpp|cc1 … | grep '.frame'` pipeline (§162i's own diagnostic + * method): pointer-arith form gave `vars=16` (0x38 frame, +8 over + * target's 0x30); switching every `D_800A651C` reference to + * `D_800A651C[idx].a` ARRAY_REF form (using the `OtBlk` shape from + * engine_types.h:525, local-suffixed here since match_one compiles + * standalone) dropped it straight to `vars=8` (0x30, exact). This + * generalizes §162i1's "only a BLKmode local reserves frame space" law + * to a second, distinct anonymous-temp source (a raw-pointer-arith + * symbol expression repeated in 2 statements) — worth a cookbook + * addendum since §162i1 as written only covers dead-local pads. + * + * REGISTER-ALLOCATION LEVERS (found empirically, byte-verified against this + * function's own .frame/.s, not inherited from the exemplars above): + * - `bidx` pinned to $8 ($t0): natural (unpinned) allocation put the + * shared D_800B9A02-address pointer in $a3 instead, cascading a + * one-register shift through the whole mask/index register set. + * - `mask1` (0xFFFFFF) pinned to $7 ($a3); `tag0` (the packet's old tag + * word, read once before the first addPrim half) pinned to $4 ($a0) — + * both needed to reproduce the target's exact a2/a3/t0/t1 register + * picks for the addPrim RMW pair. `mask2` (0xFF000000) and `depth4` + * (temp_v0_2*4) are explicitly NOT pinned — pinning them re-introduced a + * 2-instruction schedule swap between the depth<<2 and the 0xFF000000 + * `lui`; left as plain (named, for depth4) / literal (for mask2) + * locals, natural allocation lands them correctly. + * - `rgb` (arg2, the color word) pinned to $16 ($s0): unpinned, arg2 and + * the &D_800AF648 matrix address land in $s1/$s0 (swapped from target). + * `rgb`'s single SET made it a sched1 "birthing insn" + * (`birthing_insn_p`: `reg_n_sets==1`) — boosted to max priority in + * sched1's BACKWARD scan, which schedules a boosted insn late (cookbook + * "birthing-boost prologue-order" lever). A zero-byte non-volatile + * re-tie `__asm__("" : "=r"(rgb) : "0"(rgb));` placed immediately after + * `rgb = arg2;` gives it a 2nd SET (boost dead) with zero emitted code, + * and the 3-instruction prologue cluster (save $s0 / set $s0=arg2 / set + * $a0=0x10 for the alloc call) reorders to the target's exact sequence. + * + * INTEGRATION SURFACE (checked against destination TU + * src/ov_SC06_018/ov_SC06_018_jr_80187AEC.c): + * func_80010A08, func_8004914C, func_800491AC, RotTransPers, D_800AF648, + * D_800B9A02, D_800A6518 all match the TU's own existing extern spellings + * verbatim (grepped at TU lines 2448/2632-2633/4674 and the + * func_8018F694/func_8018F060 block). D_800A651C and func_80016638 have NO + * file-scope declaration anywhere in the TU (only ever appear inside other + * INCLUDE_ASM'd/unbanked functions) — declared here exactly as the + * func_801860E8/func_80185944 MATCHed precedent declares them: `OtBlk + * D_800A651C[]` (locally as `OtBlk_8018A974` — match_one compiles + * standalone without ../shared/engine_core.h; at bank time this collapses + * onto the TU's own already-visible `OtBlk` from engine_types.h:525, + * identical layout, a copy-edit not a fresh investigation) and + * `void func_80016638(void *a0, s32 a1, s32 a2)`. + */ + +typedef struct { s32 a; s32 b[4]; } OtBlk_8018A974; /* == engine_types.h OtBlk (0x14) */ + +void func_8018A974(s32 arg0, s32 arg1, s32 arg2) +{ + extern void *func_80010A08(s32); + extern void func_8004914C(void *); + extern void func_800491AC(void *); + extern s32 RotTransPers(s32, s32, s32 *, s32 *); + extern u8 D_800AF648; + extern OtBlk_8018A974 D_800A651C[]; + extern u8 D_800A6518[]; + extern short D_800B9A02; + extern void func_80016638(void *a0, s32 a1, s32 a2); + + s32 sp10; + s32 sp14; + s32 temp_v0_2; + void *temp_v0; + s32 ot; + s32 depth4; + register u16 *bidx __asm__("$8"); + register u32 mask1 __asm__("$7"); + register s32 rgb __asm__("$16"); + register u32 tag0 __asm__("$4"); + + rgb = arg2; + __asm__("" : "=r"(rgb) : "0"(rgb)); /* zero-byte 2nd SET: kills the sched1 birthing boost */ + temp_v0 = func_80010A08(0x10); + *(u8 *)((u8 *)temp_v0 + 3) = 3; + *(s32 *)((u8 *)temp_v0 + 4) = rgb; + *(u8 *)((u8 *)temp_v0 + 7) = 0x42; + func_8004914C(&D_800AF648); + func_800491AC(&D_800AF648); + temp_v0_2 = RotTransPers(arg0, temp_v0 + 8, &sp10, &sp14); + if ((temp_v0_2 > 0) && (sp14 >= 0) && + (RotTransPers(arg1, temp_v0 + 0xC, &sp10, &sp14) > 0) && (sp14 >= 0)) { + /* addPrim(otp, p) == setaddr(p, getaddr(otp)), setaddr(otp, p) */ + mask1 = 0xFFFFFF; + bidx = (u16 *)&D_800B9A02; + depth4 = temp_v0_2 * 4; + tag0 = *(u32 *)temp_v0; + *(u32 *)temp_v0 = (tag0 & 0xFF000000) | + (*(u32 *)(depth4 + D_800A651C[*bidx].a) & mask1); + ot = D_800A651C[*bidx].a; + *(u32 *)(depth4 + ot) = + (*(u32 *)(depth4 + ot) & 0xFF000000) | ((u32)temp_v0 & mask1); + func_80016638(&D_800A6518[*bidx * 20], temp_v0_2, 1); + } +} + INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_80187AEC", func_8018AB00); @@ -5757,7 +5895,159 @@ void func_8018E9BC(void *arg) { } -INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_80187AEC", func_8018EDB8); +#include "common.h" + +/* func_8018EDB8 — ov_SC06_018 / ov_SC06_018_jr_80187AEC + * + * Direct structural sibling of func_8018E188 (SAME TU, banked MATCH, + * src/ov_SC06_018/ov_SC06_018_jr_80187AEC.c:5255). Shares VERBATIM: + * - the 0x60 gate + 0x1D snapshot block, + * - the 0x78/0x60 decrement block (AND form — see E188's @stuck note), + * - the 0x82&1 finisher, the C8/C9 pokes, + * - the two 8-iteration spawn loops (0x281 / 0x23). + * Diverges: no leading func_8002D4C8 in the 0x76<0 arm, E188's CC/D0/D4 + * particle reseed is replaced by a "hand off to the 0x64 owner" block + * (5C/60/5E/D8/76/62 pokes + func_8018D870), and the else arm gains a + * leading func_8002D4C8(0x9B7, 0). + * + * Regalloc target (identical to E188): p pinned $s1, e/spawn-ptr coalesce + * on $s0, loop counter $s2, the hoisted constant 2 on $s3. + * + * @class: regalloc-order + * @stuck: none — MATCH (170 ins), iteration 3. Body was E188 verbatim on + * iteration 1; both residuals were in the 12-insn 0x64 hand-off tail: + * (1) `-0xA` stored through a `u16 *` folds to the unsigned 0xFFF6 and emits + * `ori $v0,$zero,0xfff6`; the target's `addiu $v0,$zero,-0xA` needs the + * store spelled `*(s16 *)(q + 0x76)` (§162k-adjacent width law, applied + * to a CONSTANT rather than a load). + * (2) the tail pointer lives in TWO registers — $s0 (the `lw 0x20` base) and + * $a0 (every store base + the func_8018D870 arg). Spelling that as + * `register s32 q __asm__("$4"); q = iv;` alone is NOT enough: with the + * 0x20 load left BELOW the copy, local-alloc's `optimize_reg_copy_1` + * (§162j1) rewrites that surviving use $s0 -> $a0, and the now-$a0-based + * load can no longer be scheduled above the $a0-based stores (they may + * alias), costing two load-delay nops = the +2 LENGTH-DRIFT. + * LEVER: `optimize_reg_copy_1`'s substitution scan runs FORWARD from the copy + * only, so hoisting the surviving use ABOVE `q = iv` in SOURCE order puts it + * out of reach — `sv = *(s32 *)(iv + 0x20);` before the copy. It keeps $s0, + * and being source-first it also legally precedes the store block, which is + * what recovers the target's schedule (lw / sh / sh / lhu interleave). This + * is a second, zero-side-effect defeat for §162j1 that works where its + * in-place-SET lever cannot: the surviving use here is a LOAD, which has no + * way to also SET its own base register. + */ + +extern s32 rand(void); +extern void func_8016AA50(s32, s32); +extern s32 func_8016B428(s32); +extern void func_80019064(void *); +extern void func_8002A520(int); +extern void func_8002A790(int); +extern void func_8002D4C8(s32, s32); +extern s32 func_8012C588(s32, s32); +extern u8 *func_8012913C(s32); +extern void func_8012C218(void *); +extern void func_8018D870(void *); +extern u8 D_801D1210; + +void func_8018EDB8(void *arg) { + register u8 *p __asm__("$17"); /* $s1 */ + s32 e; + s32 i; + s32 iv; + + p = (u8 *)arg; + e = *(u8 *)(p + 0x5E); + + if (*(s16 *)(p + 0x60) != 0) { + if (e == 0x1D) { + *(u16 *)(p + 0x82) = 0; + *(u16 *)(p + 0x7C) = *(u16 *)(p + 0x06); + *(u16 *)(p + 0x7E) = *(u16 *)(p + 0x0A); + *(u16 *)(p + 0x80) = *(u16 *)(p + 0x0E); + } + { + s32 dec; + s32 q = *(s32 *)(p + 0x78); + if (q != 0 && *(s16 *)(p + 0x60) != 0) { + dec = ((s32)*(s16 *)(p + 0x60) * (s32)*(s16 *)(q + 0x30)) >> 12; + if (dec < 1) dec = 1; + } else { + dec = *(s16 *)(p + 0x60); + } + *(u16 *)(p + 0x76) = *(u16 *)(p + 0x76) - dec; + ((void (*)(void *, s32))func_8016AA50)(p, dec); + } + if (*(u16 *)(p + 0x82) & 1) { + ((void (*)(void *))func_8016B428)(p); + func_80019064(&D_801D1210); + } + } + + if (e != 0x1D) { + if (*(u8 *)(p + 0xC8)) func_8002A520(p); + if (*(u8 *)(p + 0xC9)) func_8002A790(p); + } + + if (*(s16 *)(p + 0x76) < 0) { + i = 0; + do { + iv = ((s32 (*)(s32, void *))func_8012C588)(0x281, p); + if (iv != 0) { + *(s32 *)(iv + 0x1C) = 2; + *(u16 *)(iv + 0x12) = (rand() & 0x1F) - 0x10; + *(u16 *)(iv + 0x16) = -((rand() & 0x0F) + 0x10); + *(u16 *)(iv + 0x1A) = (rand() & 0x1F) - 0x10; + } + i++; + } while (i < 8); + i = 0; + do { + iv = (s32)func_8012913C(0x23); + if (iv != 0) { + s32 r; + s32 sv; + r = rand(); + *(u16 *)(iv + 0x06) = *(u16 *)(p + 0x06) + (r & 0x3F) - 0x20; + r = rand(); + *(u16 *)(iv + 0x0A) = *(u16 *)(p + 0x0A) - (r & 0x3F) - 0x20; + r = rand(); + sv = *(u16 *)(p + 0x0E); + *(s32 *)(iv + 0x18) = 0; + *(s32 *)(iv + 0x14) = 0; + *(s32 *)(iv + 0x10) = 0; + *(u16 *)(iv + 0x0E) = sv + (r & 0x3F) - 0x20; + r = rand(); + *(u16 *)(iv + 0x34) = (r & 0x17FF) + 0x1800; + } + i++; + } while (i < 8); + + { + register s32 q __asm__("$4"); + s32 sv; + iv = *(s32 *)(p + 0x64); + sv = *(s32 *)(iv + 0x20); + q = iv; + *(u16 *)(q + 0x5C) = 1; + *(u16 *)(q + 0x60) = 0; + sv = *(u16 *)(sv + 0x12); + *(u16 *)(q + 0x5E) = 0x1D; + *(s32 *)(q + 0xD8) = 0; + *(s16 *)(q + 0x76) = -0xA; + *(u16 *)(q + 0x62) = sv - 0x800; + func_8018D870((void *)q); + } + func_8012C218(p); + } else { + func_8002D4C8(0x9B7, 0); + *(u16 *)(p + 0x5C) = 0x8800; + *(u16 *)(p + 0x60) = 0; + *(u8 *)(p + 0xC1) = 0; + *(u8 *)(p + 0xC2) = 0x10; + } +} + // @class: none // @stuck: none — MATCH (397/397 ins, match_one + rtu_match). diff --git a/src/ov_SC07_006/ov_SC07_006_jr_8017BEBC.c b/src/ov_SC07_006/ov_SC07_006_jr_8017BEBC.c index 569c00762..49bf30c84 100644 --- a/src/ov_SC07_006/ov_SC07_006_jr_8017BEBC.c +++ b/src/ov_SC07_006/ov_SC07_006_jr_8017BEBC.c @@ -4085,7 +4085,165 @@ INCLUDE_ASM("asm/ov_SC07_006/nonmatchings/ov_SC07_006_jr_8017BEBC", func_8017F5D INCLUDE_ASM("asm/ov_SC07_006/nonmatchings/ov_SC07_006_jr_8017BEBC", func_8017F9AC); -INCLUDE_ASM("asm/ov_SC07_006/nonmatchings/ov_SC07_006_jr_8017BEBC", func_8017FDF8); +#include "common.h" + +/* --------------------------------------------------------------------------- + * func_8017FDF8 (ov_SC07_006 / jr_8017BEBC) — 317 ins, MATCH. + * + * A 5x-expanded 16-colour CLUT cross-fade: for each of five 16-entry 5:5:5 + * palettes, lerp channel-wise between a "from" table and a "to" table by + * t = (x - 0x800) * 2 (clamped at 0), then hand the built CLUT to + * func_800599B8(tbl, clut). The alpha/STP bit is COPIED (not lerped) from + * whichever endpoint is dominant (t < 0x800 -> the "from" table). + * + * =========================================================================== + * INTEGRATION SURFACE (§161c) — this draft needs NO edit to the host TU. + * =========================================================================== + * Host TU: src/ov_SC07_006/ov_SC07_006_jr_8017BEBC.c, INCLUDE_ASM at line 4088. + * + * - func_800599B8 takes TWO args here, but the host carries a ONE-arg + * prototype `extern void func_800599B8(u16 *);` at FILE SCOPE, line 609, + * visible at the INCLUDE_ASM slot. This draft keeps that exact prototype + * (byte-identical text) so the decl AGREES with the host and is free, and + * defuses the arity clash with a call-site cast — the same idiom already + * used by matched code at src/ov_SC03_099/ov_SC03_099_jr_8013C98C.c:1439 + * and src/ov_SC06_008/ov_SC06_008_jr_8013C98C.c:1439. + * + * BYTE-PROVEN, this wave, on the two alternatives that avoid the cast: + * (A) add `extern void func_800599B8();` beside host line 609 + * -> CC1 FAIL, "too many arguments to function" x5. A non-prototype + * decl does NOT override an already-visible prototype; gcc-2.7.2 + * forms the composite type and the prototype wins. + * (B) put `extern void func_800599B8();` at BLOCK scope inside + * func_8017FDF8, host line 609 untouched + * -> CC1 FAIL, identical errors. Block scope does not shadow a + * visible file-scope prototype for arity checking here. + * So the empty-paren form only works if host line 609 is REPLACED (not + * added beside). That variant is also byte-verified MATCH and is kept at + * probes/E_widen_decl.c if the cleaner source shape is preferred; it costs + * a one-line decl edit to the host (byte-neutral — the host has no other + * reference to func_800599B8, grep: line 609 is decl-only). + * + * - All fifteen data symbols declared below have ZERO occurrences in the host + * TU (verified by grep), so no type conflicts. `pal_lerp` likewise has 0 + * occurrences (no clash with the host's existing `morph_lerp` inline). + * Names that DO appear elsewhere in src/ (D_801F60C0, D_8018DF60..80) are + * all in other overlays' TUs, never in this host. + * + * - src/shared/engine_core.h:27426 also declares `extern void + * func_800599B8(u16 *);` but inside a macro body, so it only reaches block + * scope at an expansion site — it does not add a file-scope decl here. + * + * =========================================================================== + * LOAD-BEARING SOURCE SHAPES (all independently re-verified this wave) + * =========================================================================== + * 1. D_801F60A0 MUST stay ONE array symbol indexed [0]/[16]/[32]/[48]/[64]. + * cse's use_related_value is what emits the `addiu $s0, $s0, 0x20` that + * seeds each following block's loop pointer from the pointer just passed + * to func_800599B8. REFUTED this wave: five separate externs + * D_801F60A0/C0/E0/6100/6120 give 319 ins vs 317, 258 mismatched (the two + * extra instructions are the lost related-value adds). + * + * 2. The nr/ng/nb named temps are load-bearing — do NOT inline them back. + * All three `mult` result pseudos carry `pref LO_REG` and tie in + * allocno_compare, so the tie breaks on allocno number: the lowest-numbered + * product takes $lo, reload spills $lo, and retry_global_alloc re-places it + * on the LAST free register, shifting every loop-carried pseudo one register + * down. Writing the three channels into named temps re-orders sched1's + * output (not the final sched2 order), which re-orders reg_live_length and + * hence the allocno priorities, so the blue product keeps $lo and lands on + * $v1 while red/green take $t1/$t0. Alternatives, same 317 ins, all + * NON-matching: fully inlined 104 diffs; hoist dr,dg 35; hoist raw products + * pr,pg,pb 104; hoist unmasked sums sr,sg 114; hoist nr / nr,ng 104 / 114. + * Register pins are NOT the lever here (cf. §162j1) — this is a global.c + * priority/tie problem; statement granularity is the lever. + * + * 3. Branch polarity: the loop-invariant `slti $t7, $a3, 0x800` is LICM-hoisted + * and the target branches with `bnez` (on the TRUE arm). That requires the + * source to spell the condition INVERTED with swapped arms — `if (t >= + * 0x800) hi = *a...; else hi = *b...;`. The intuitive `if (t < 0x800)` + * with unswapped arms emits `beqz` and is a 5-instruction structural miss. + * + * FAMILY REACH x2: the second binary templates mechanically — identical shape, + * only the ten source-table symbols and the five func_800599B8 arg tables + * change. Note for the remap: constraint (1) means the sibling's five + * destination CLUTs must likewise be one base symbol at 0x20 stride. + * ------------------------------------------------------------------------- */ + +extern void func_800599B8(u16 *); + +extern u16 D_801F60A0[]; + +extern u16 D_801CACA8[]; +extern u16 D_801CAD48[]; +extern u16 D_801CAD28[]; +extern u16 D_801CADA8[]; +extern u16 D_801CAD08[]; +extern u16 D_801CAD88[]; +extern u16 D_801CAC68[]; +extern u16 D_801CACE8[]; +extern u16 D_801CAC88[]; +extern u16 D_801CACC8[]; + +extern u8 D_8018DF60[]; +extern u8 D_8018DF68[]; +extern u8 D_8018DF70[]; +extern u8 D_8018DF78[]; +extern u8 D_8018DF80[]; + +/* MUST be `inline` — 5 expansions, 0 jal, and the caller's frame is 0x20. */ +static inline void pal_lerp(u16 *dst, u16 *b, u16 *a, s32 x) +{ + s32 t; + s32 i; + s32 hi; + s32 r; + s32 g; + s32 bl; + s32 nr; + s32 ng; + s32 nb; + + t = (x - 0x800) * 2; + if (t < 0) { + t = 0; + } + for (i = 0; i < 16; i++) { + /* NOTE the polarity: `>= 0x800` (not `< 0x800` with swapped arms) — + LICM hoists the slti and gcc branches on the TRUE arm, giving the + target's `bnez` rather than a `beqz`. */ + if (t >= 0x800) { + hi = *a & 0x8000; + } else { + hi = *b & 0x8000; + } + r = *b & 0x1F; + g = *b & 0x3E0; + bl = *b & 0x7C00; + nr = (r + ((((*a & 0x1F) - r) * t) >> 12)) & 0x1F; + ng = (g + ((((*a & 0x3E0) - g) * t) >> 12)) & 0x3E0; + nb = (bl + ((((*a & 0x7C00) - bl) * t) >> 12)) & 0x7C00; + *dst = hi | nr | ng | nb; + b++; + a++; + dst++; + } +} + +void func_8017FDF8(s32 x) +{ + pal_lerp(&D_801F60A0[0], D_801CACA8, D_801CAD48, x); + ((void (*)(u8 *, u16 *))func_800599B8)(D_8018DF60, &D_801F60A0[0]); + pal_lerp(&D_801F60A0[16], D_801CAD28, D_801CADA8, x); + ((void (*)(u8 *, u16 *))func_800599B8)(D_8018DF68, &D_801F60A0[16]); + pal_lerp(&D_801F60A0[32], D_801CAD08, D_801CAD88, x); + ((void (*)(u8 *, u16 *))func_800599B8)(D_8018DF70, &D_801F60A0[32]); + pal_lerp(&D_801F60A0[48], D_801CAC68, D_801CACE8, x); + ((void (*)(u8 *, u16 *))func_800599B8)(D_8018DF78, &D_801F60A0[48]); + pal_lerp(&D_801F60A0[64], D_801CAC88, D_801CACC8, x); + ((void (*)(u8 *, u16 *))func_800599B8)(D_8018DF80, &D_801F60A0[64]); +} + /* func_801802EC — banked from the S40 wave-1 draft. * The draft carried the TYPES, the six SVECTOR2 externs and `static inline morph_lerp` so that