diff --git a/src/ov_SC03_118/ov_SC03_118_jr_801863CC.c b/src/ov_SC03_118/ov_SC03_118_jr_801863CC.c index 06d3b2638..3200dc50e 100644 --- a/src/ov_SC03_118/ov_SC03_118_jr_801863CC.c +++ b/src/ov_SC03_118/ov_SC03_118_jr_801863CC.c @@ -4192,7 +4192,17 @@ void func_80188694(void *a0) { } -INCLUDE_ASM("asm/ov_SC03_118/nonmatchings/ov_SC03_118_jr_801863CC", func_80188748); +void func_80188748(void *a0) { + s32 *p = *(s32 **)((s32)a0 + 0x20); + p[1] = p[1] | 0x58000000; + *(s16 *)((s32)p + 0x1E) = 0; + *(s16 *)((s32)p + 0x18) = 0; + *(s16 *)((s32)p + 0x1A) = 0; + *(s16 *)((s32)p + 0x1C) = 0; + *(s32 *)((s32)a0 + 0x2C) = 0x400; + *(u16 *)((s32)a0 + 0x2) = *(u16 *)((s32)a0 + 0x2) + 1; + } + INCLUDE_ASM("asm/ov_SC03_118/nonmatchings/ov_SC03_118_jr_801863CC", func_80188788); @@ -4244,7 +4254,152 @@ s32 func_801887DC(s32 a0, s16 a1, s16 a2) { } -INCLUDE_ASM("asm/ov_SC03_118/nonmatchings/ov_SC03_118_jr_801863CC", func_80188990); +#include "common.h" + +/* + * func_80188990 -- adapted from func_80181500 (src/ov_SC03_107/ov_SC03_107_jr_801789AC.c), + * a byte-matched sibling in ov_SC03_107. Measured 9-site / 30-instruction diff vs that seed: + * + * 1. The seed's leading `if (*(s16*)(a0+0xA) >= 0x10) { func_80131C78(a0); ... }` guard is + * ABSENT here -- the target .s goes straight from the prologue into the `lbu $v1,0xC2($s0)` + * switch read with no lh/slti/bnez/jal func_80131C78 at all. + * 2. In case 0's `if (D & 0x6000) { if (D & 0x1000) {...return;} ... }` body, the order of the + * two calls is swapped relative to the seed: func_8002D4C8(0x754, 0) now precedes + * func_80143B6C(a0, 1) (seed had func_80143B6C first). Case 1 already had this order in the + * seed (that's the whole point of the `t = D_..` scheduling comment there), so case 1 is + * unchanged. + * 3. In case 2, both `* 15 / 16` roundings lost their `bgez/addiu +0xF` sign-correction dance -- + * the target uses a plain arithmetic right shift (`(x * 15) >> 4`), not a C signed division. + * 4. The seed's tail call `func_8018134C(a0, -0x28, 1);` is not present anywhere in the target + * .s (func_8018134C's opcode/address never appears) -- it is simply not called here. + * + * Everything else (switch skeleton, both early-return blocks, case1 body, the shared >=0x78/>=0x28 + * reset tail, and the final 0x108 countdown) is structurally identical to the seed. + * + * Decls chosen to agree with the seed's own choices (func_8012CBCC returns void there too, read + * through the same function-pointer cast; D_801D46D8/D_801D46DC are this overlay's counterparts of + * the seed's D_8019BBC8/D_8019BBCC, declared nowhere else in this standalone draft -- free choice). + */ + +extern void func_8002D4C8(s32 a0, s32 a1); +extern s32 func_8012B030(u8 *a0); +extern void func_8012CBCC(s32); +extern void func_80131C78(s32 a0); +extern s32 func_80143B6C(s32 a0, s32 a1); +extern void func_800291C8(s32, s32); + +extern s32 D_801D46D8; +extern s32 D_801D46DC; + +void func_80188990(s32 a0) { + /* ยง162i1 dead-local pad: target frame 0x28 = args 0x10 + vars 0x10 + 4*2 regs; + * the raw draft compiles vars=0. Never read, never &-taken, >=2 elements. */ + s32 pad[4]; + + switch (*(u8 *)(a0 + 0xC2)) { + case 0: + D_801D46D8 = ((s32 (*)(s32))func_8012CBCC)(a0); + if (D_801D46D8 & 0x6000) { + if (D_801D46D8 & 0x1000) { + *(s16 *)(a0 + 0x2) = 2; + *(u8 *)(a0 + 0xC1) = 0; + *(s16 *)(a0 + 0x5E) = 0; + *(s32 *)(a0 + 0x1C) = 0x30; + func_8002D4C8(0x7DF, 0); + func_800291C8(0xA, *(s16 *)(a0 + 0xFE)); + return; + } + func_8002D4C8(0x754, 0); + func_80143B6C(a0, 1); + *(u8 *)(a0 + 0xC2) = 1; + *(s32 *)(a0 + 0x14) = 0xFFF30000; + *(s32 *)(a0 + 0x1C) = 0; + *(s32 *)(a0 + 0x10) = *(s32 *)(a0 + 0x10) >> 2; + *(s32 *)(a0 + 0x18) = *(s32 *)(a0 + 0x18) >> 2; + } + *(s32 *)(a0 + 0x1C) += 1; + if (*(s32 *)(a0 + 0x1C) >= 0x78) { + func_80131C78(a0); + *(s16 *)(a0 + 0x2) = 2; + *(s32 *)(a0 + 0x1C) = 1; + } + break; + + case 1: + D_801D46DC = *(s32 *)(a0 + 0x14); + D_801D46D8 = ((s32 (*)(s32))func_8012CBCC)(a0); + if (D_801D46D8 & 0x6000) { + if (D_801D46D8 & 0x1000) { + *(s16 *)(a0 + 0x2) = 2; + *(u8 *)(a0 + 0xC1) = 0; + *(s16 *)(a0 + 0x5E) = 0; + *(s32 *)(a0 + 0x1C) = 0x30; + func_8002D4C8(0x7DF, 0); + func_800291C8(0xA, *(s16 *)(a0 + 0xFE)); + return; + } + { + /* The read of D_801D46DC MUST precede the two pointer stores in the + * source: gcc-2.7.2's scheduler cannot hoist a symbol_ref load above a + * store through a register base (may-alias), so writing it inline in the + * `-D_801D46DC` expression leaves the load-delay slot unfilled -> a nop, + * and the function is 1 instruction long. */ + s32 t; + func_8002D4C8(0x754, 0); + func_80143B6C(a0, 1); + t = D_801D46DC; + *(u8 *)(a0 + 0xC2) = 2; + *(s32 *)(a0 + 0x1C) = 0; + *(s32 *)(a0 + 0x14) = -t; + } + } + *(s32 *)(a0 + 0x1C) += 1; + if (*(s32 *)(a0 + 0x1C) >= 0x28) { + func_80131C78(a0); + *(s16 *)(a0 + 0x2) = 2; + *(s32 *)(a0 + 0x1C) = 1; + } + break; + + case 2: + *(s32 *)(a0 + 0x10) = (*(s32 *)(a0 + 0x10) * 15) >> 4; + *(s32 *)(a0 + 0x18) = (*(s32 *)(a0 + 0x18) * 15) >> 4; + D_801D46D8 = ((s32 (*)(s32))func_8012CBCC)(a0); + if (D_801D46D8 & 0x6000) { + *(s32 *)(a0 + 0x14) = 0; + if (D_801D46D8 & 0x1000) { + *(s16 *)(a0 + 0x2) = 2; + *(u8 *)(a0 + 0xC1) = 0; + *(s16 *)(a0 + 0x5E) = 0; + *(s32 *)(a0 + 0x1C) = 0x30; + func_8002D4C8(0x7DF, 0); + func_800291C8(0xA, *(s16 *)(a0 + 0xFE)); + return; + } + if (!(*(u16 *)(a0 + 0x84) & 1)) { + *(u16 *)(a0 + 0x84) |= 1; + func_8002D4C8(0x754, 0); + } + if ((*(s32 *)(a0 + 0x1C) & 3) == 3) { + func_80143B6C(a0, 1); + } + *(s32 *)(a0 + 0x1C) += 1; + if (*(s32 *)(a0 + 0x1C) >= 0x11) { + func_80131C78(a0); + func_8012B030((u8 *)a0); + *(s16 *)(a0 + 0x2) = 1; + *(u16 *)(a0 + 0x84) &= 0xFFFE; + } + } + break; + } + + *(s16 *)(a0 + 0x108) = *(u16 *)(a0 + 0x108) - 1; + if (*(s16 *)(a0 + 0x108) == 0) { + func_800291C8(0xA, *(s16 *)(a0 + 0xFE)); + } +} + INCLUDE_ASM("asm/ov_SC03_118/nonmatchings/ov_SC03_118_jr_801863CC", func_80188C8C);