From db93c353718f194e30611115fbf79e579ee9ece7 Mon Sep 17 00:00:00 2001 From: Christopher Williams Date: Thu, 24 Sep 2026 01:42:16 -0400 Subject: [PATCH] =?UTF-8?q?phase9:=20merge=20B=202=20+=20coordinator=201?= =?UTF-8?q?=20=E2=80=94=20351=20regions=20/=20342=20distinct=20bodies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Worker B matched 0x800F6330 (correcting my stale negative: destination is 0x8011AA18 via the signed-displacement finding-4 trap) and 0x80090B7C (18th/19th first-attempt matches this cycle). Coordinator absorbed 0x80025070 (conditional dispatch wrapper). B's correction of its own void-vs-valued epilogue tell accepted (0x800FBDC0 counterexample; the shared-epilogue hypothesis stands untested — recorded as observation, not rule). Gate MATCH whole-binary SHA-1 e173426c157384ebf1b6caf8c6fea18a85a14af9. --- config/regions.tsv | 3 +++ src/func_80025070.c | 43 +++++++++++++++++++++++++++++++++++++++ src/func_80090B7C.c | 43 +++++++++++++++++++++++++++++++++++++++ src/func_800F6330.c | 49 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 138 insertions(+) create mode 100644 src/func_80025070.c create mode 100644 src/func_80090B7C.c create mode 100644 src/func_800F6330.c diff --git a/config/regions.tsv b/config/regions.tsv index 40784d2..36efdaf 100644 --- a/config/regions.tsv +++ b/config/regions.tsv @@ -60,6 +60,7 @@ 0x80022FCC 0x80022FFC src/func_80022FCC.c 0x800230E4 0x8002311C src/func_800230E4.c 0x80024C14 0x80024C34 src/func_80024C14.c +0x80025070 0x800250AC src/func_80025070.c 0x80026180 0x800261C0 src/func_80026180.c 0x80026258 0x80026264 src/func_80026258.c 0x80026264 0x80026274 src/func_80026264.c @@ -198,6 +199,7 @@ 0x800900A0 0x800900CC src/func_800900A0.c 0x80090A44 0x80090A70 src/func_80090A44.c 0x80090B64 0x80090B7C src/func_80090B64.c +0x80090B7C 0x80090BB4 src/func_80090B7C.c 0x80090C8C 0x80090CAC src/func_80090C8C.c 0x8009107C 0x800910B0 src/func_8009107C.c 0x800912D4 0x800912FC src/func_800912D4.c @@ -263,6 +265,7 @@ 0x800F5200 0x800F521C src/func_800F5200.c 0x800F5AF8 0x800F5B40 src/func_800F5AF8.c 0x800F5B40 0x800F5B70 src/func_800F5B40.c +0x800F6330 0x800F6364 src/func_800F6330.c 0x800F6570 0x800F6594 src/func_800F6570.c 0x800F75D0 0x800F760C src/func_800F75D0.c 0x800F7990 0x800F79C0 src/func_800F7990.c diff --git a/src/func_80025070.c b/src/func_80025070.c new file mode 100644 index 0000000..70769e7 --- /dev/null +++ b/src/func_80025070.c @@ -0,0 +1,43 @@ +/* func_80025070 — 0x80025070..0x800250AC (60 bytes). + * + * Dispatches to func_800250AC with three arguments where the second is + * selected by a null-conditional. + * + * Original words: + * 0x27BDFFE8 addiu sp,sp,-24 + * 0xAFBF0010 sw ra,16(sp) + * 0x8C820020 lw v0,32(a0) ; v0 = *(int *)(a0 + 0x20) + * 0x00000000 nop + * 0x14400003 bnez v0,else ; if (v0 != 0) else + * 0x00A03021 move a2,a1 ; (delay) a2 = a1 (forward) + * 0x08009425 j call + * 0x00002821 move a1,zero ; (delay) a1 = 0 + * else: + * 0x8C450020 lw a1,32(v0) ; a1 = *(int *)(v0 + 0x20) + * call: + * 0x0C00942B jal func_800250AC + * 0x00000000 nop + * 0x8FBF0010 lw ra,16(sp) + * 0x27BD0018 addiu sp,sp,24 + * 0x03E00008 jr ra + * 0x00000000 nop + * + * Dataflow: obj2 = *(a0+0x20); if (obj2 == 0) { a2 = a1; a1 = 0; } + * else { a1 = *(obj2 + 0x20); } call with (a0, a1, a2). The a0 stays + * forwarded unchanged. The j-with-delay structure selects the argument. + * + * LIMITS: the object layout and callee role are hypotheses. Only the + * compiled bytes are evidence. + */ + +void func_800250AC(int, int, int); + +int func_80025070(int *a0, int a1) +{ + int *obj2 = *(int **)((char *)a0 + 0x20); + + if (obj2 == 0) + func_800250AC((int)a0, 0, a1); + else + func_800250AC((int)a0, *(int *)((char *)obj2 + 0x20), a1); +} \ No newline at end of file diff --git a/src/func_80090B7C.c b/src/func_80090B7C.c new file mode 100644 index 0000000..3d95b62 --- /dev/null +++ b/src/func_80090B7C.c @@ -0,0 +1,43 @@ +/* func_80090B7C — 0x80090B7C..0x80090BB4 (56 bytes). + * + * Original words: + * 27BDFFE8 addiu sp,sp,-24 + * AFBF0010 sw ra,16(sp) + * 00042400 sll a0,a0,0x10 + * 00051100 sll v0,a1,0x4 + * 00451023 subu v0,v0,a1 v0 = a1*15 + * 00021480 sll v0,v0,0x12 << 18 + * 00042403 sra a0,a0,0x10 a0 = (short)a0 + * 00022C03 sra a1,v0,0x10 >> 16 + * 0C03FE00 jal 0x800FF800 + * 24060003 _li a2,3 (delay slot) third argument = 3 + * 8FBF0010 lw ra,16(sp) + * 27BD0018 addiu sp,sp,24 + * 03E00000 jr ra + * 00000000 nop + * + * Narrows two values and forwards them with a constant third. The first is a + * plain sign-extension; the second is the interesting one. + * + * `a1 << 4; - a1; << 18; >> 16` looks like two unrelated operations but is one: + * `<< 18` is the fusion of a `<< 2` (from a multiply by 4) with a `<< 16` (from a + * 16-bit sign-extension), so the whole sequence is + * `((a1 * 15) << 2 << 16) >> 16` — that is, `(short)(a1 * 60)`. The `>> 16` is + * what remains of the cast after cc1 folded the other shift into it, which is why + * the emitted shift amount is 18 and not 2. Writing this as anything other than a + * multiply-then-cast changes the shift amounts and will not match. + * + * LIMITS: the multipliers 15 and 4, and the fused shift amount 18, are read from + * the instructions; that they express a single `* 60` is an arithmetic identity + * rather than source evidence, but it is the only reading that yields a shift of + * 18 in a 16-bit-narrowing context. The third argument's constant 3 is literal. + * The callee is named for its address and its signature is inferred from the three + * registers that are set. + */ + +void func_800FF800(short a, short b, int c); + +void func_80090B7C(int a0, int a1) +{ + func_800FF800((short)a0, (short)(a1 * 60), 3); +} diff --git a/src/func_800F6330.c b/src/func_800F6330.c new file mode 100644 index 0000000..d10f8d4 --- /dev/null +++ b/src/func_800F6330.c @@ -0,0 +1,49 @@ +/* func_800F6330 — 0x800F6330..0x800F6364 (52 bytes). + * + * Original words: + * 27BDFFE8 addiu sp,sp,-24 + * AFBF0010 sw ra,16(sp) + * 0C03DD98 jal 0x800F7660 + * 2404FFFF _li a0,-0x1 (delay slot) + * 244200F0 addiu v0,v0,240 result + 240 + * 3C018012 lui at,0x8012 + * AC22AA18 sw v0,-21992(at) D_8011AA18 = result + 240 + * 3C018012 lui at,0x8012 + * AC20AA1C sw zero,-21988(at) D_8011AA1C = 0 + * 8FBF0010 lw ra,16(sp) + * 27BD0018 addiu sp,sp,24 + * 03E00008 jr ra + * 00000000 nop + * + * Calls one routine with the sentinel -1, adds 240 to what it returns, publishes + * the sum and clears the neighbouring global. Nothing is returned. + * + * `lui at,0x8012` + `sw v0,%lo(at)` is the assembler's expansion of a **symbol + * store** through `$at` (cookbook finding 3: a store must not clobber its source + * register, so the macro uses `$at`), so both destinations are symbols, not + * computed addresses. The two `lui`s load the same high half for two adjacent + * words, which is why the `lui` is re-emitted rather than shared — cc1 does not + * hoist a macro-expanded constant across the store. + * + * LIMITS: the `-1` sentinel, the 240 offset and the two displacements are read + * from the instructions. **The destination addresses are 0x8011AA18 and + * 0x8011AA1C, not 0x8012AA18** — `lui at,0x8012` with a displacement the + * disassembler prints as **-21992** is the sign-adjusted `%lo` split (cookbook + * finding 4), so the effective address is 0x80120000 - 0x55E8. Naming the symbol + * from the `lui` immediate alone puts it 64 KB away and produces a silently wrong + * claim; that mistake was caught here only by recomputing the address. The two + * globals are adjacent words (`0xAA1C` is `0xAA18` + 4). That the pair is a + * counter and its reset is a guess from their adjacency and the zero store, not + * evidence: nothing in the body reads either one. + */ + +extern int D_8011AA18; +extern int D_8011AA1C; + +int func_800F7660(int arg); + +void func_800F6330(void) +{ + D_8011AA18 = func_800F7660(-1) + 240; + D_8011AA1C = 0; +}