phase9: merge worker C handoff close — 283 regions / 274 distinct bodies

Worker C rotated out on budget with a clean handoff (28 claims total, all
verified; 18 negatives with hypotheses). Its final 2 rows verified MATCH
(0x80101838 reopening closed via do/while + sentinel: this closes B's cycle-2
negative on the same address!; 0x8007C4A8). Handoff inheritance: orig.py
disassembly helper, F1-F14 findings confirmed, 2-byte free claim 0x800266A8
left with its exact hypothesis. Gate MATCH whole-binary SHA-1
e173426c157384ebf1b6caf8c6fea18a85a14af9.
This commit is contained in:
Christopher Williams
2026-09-24 00:33:42 -04:00
parent c160fa9326
commit c286bc4355
8 changed files with 330 additions and 0 deletions
+2
View File
@@ -130,6 +130,7 @@
0x80072BA8 0x80072BDC src/func_80072BA8.c
0x800734A4 0x800734DC src/func_800734A4.c
0x8007A404 0x8007A428 src/func_8007A404.c
0x8007C4A8 0x8007C4EC src/func_8007C4A8.c
0x8007C4EC 0x8007C524 src/func_8007C4EC.c
0x8007DC40 0x8007DC4C src/func_8007DC40.c
0x8007DF00 0x8007DF34 src/func_8007DF00.c
@@ -245,6 +246,7 @@
0x80100964 0x8010097C src/func_80100964.c
0x8010097C 0x80100998 src/func_8010097C.c
0x80101244 0x8010128C src/func_80101244.c
0x80101838 0x80101878 src/func_80101838.c
0x80101CAC 0x80101CDC src/func_80101CAC.c
0x801027CC 0x801027F8 src/func_801027CC.c
0x80102B10 0x80102B2C src/func_80102B10.c maspsx=off
1 # Code-region registry: one C region per matched function.
130 0x80072BA8
131 0x800734A4
132 0x8007A404
133 0x8007C4A8
134 0x8007C4EC
135 0x8007DC40
136 0x8007DF00
246 0x80100964
247 0x8010097C
248 0x80101244
249 0x80101838
250 0x80101CAC
251 0x801027CC
252 0x80102B10
+48
View File
@@ -0,0 +1,48 @@
/*
* func_800281A4 — 64 bytes at 0x800281A4..0x800281E4
*
* Framed routine: passes two words of its first argument to a routine and stores the result
* through its second argument, returning 0.
*
* The observed instructions are:
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* move v0,a0 00801021 v0 = a0
* sw s0,0x10(sp) afb00010 save s0
* move s0,a1 00a08021 s0 = a1
* sw ra,0x14(sp) afbf0014 save ra
* lw a0,0x0(v0) 8c440000 a0 = *(int *)v0
* lw a1,0x8(v0) 8c450008 a1 = *(int *)(v0 + 8)
* jal 0x800f2fc0 0c03cbf0 call func_800F2FC0
* nop 00000000 (delay slot)
* sw v0,0x0(s0) ae020000 *(int *)s0 = v0
* addu v0,zero,zero 00001021 v0 = 0
* lw ra,0x14(sp) 8fbf0014 restore ra
* lw s0,0x10(sp) 8e100010 restore s0
* addiu sp,sp,0x18 27bd0018 frame release
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* The first argument is copied to **v0** and both of its words are read through that copy, so
* a0 is free to receive the callee's first argument — the pointer is preserved in a scratch
* register rather than on the stack. The second argument is copied to **s0**, a callee-saved
* register, because it is used as the store base *after* the call. The frame saves s0 at 0x10
* and ra at 0x14.
*
* The wrapper's own result is the constant 0, set after the store, so it is not the callee's
* value.
*
* LIMITS: the function name, the callee, the two source words and the claim that the second
* argument is an output pointer are hypotheses; only the bytes are evidence. All accesses are
* 32-bit.
*/
extern int func_800F2FC0(int a0, int a1);
int func_800281A4(int a0, int *a1)
{
int v0 = a0;
*a1 = func_800F2FC0(*(int *)v0, *(int *)(v0 + 8));
return 0;
}
+51
View File
@@ -0,0 +1,51 @@
/*
* func_8002A9D4 — 68 bytes at 0x8002A9D4..0x8002AA18
*
* Leaf routine that walks a 133-entry table of pointers and clears one field of every entry
* whose field holds 1.
*
* The observed instructions are:
* addu a1,zero,zero 00002821 i = 0
* li a2,0x1 24060001 a2 = 1
* lui a0,0x8011 3c048011 \
* addiu a0,a0,-0xf10 2484f0f0 / a0 = 0x8010F0F0 (D_8010F0F0)
* 0x8002a9e4:
* lw v1,0x0(a0) 8c830000 v1 = *p
* nop 00000000 load-delay slot
* lw v0,0x14(v1) 8c620014 v0 = *(int *)(v1 + 0x14)
* nop 00000000 load-delay slot
* bne v0,a2,0x8002aa00 14460001 if (v0 != 1) goto the increment
* nop 00000000 (delay slot)
* sw zero,0x14(v1) ac600014 *(int *)(v1 + 0x14) = 0
* 0x8002aa00:
* addiu a1,a1,0x1 24a50001 i++
* slti v0,a1,0x85 28a20085 v0 = (i < 133) signed
* bne v0,zero,0x8002a9e4 1440fff8 if (v0) loop
* addiu a0,a0,0x4 24840004 p++ (delay slot)
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* The table is a walked pointer of 4-byte elements built with `lui`+`addiu` (the linker-resolved
* symbol form, cookbook finding 4), each element being a **pointer** whose +0x14 field is
* compared with 1 and zeroed on a match. The comparison constant is materialised once outside
* the loop, the bound test is `slti` (signed) against 133, and the pointer is advanced in the
* branch delay slot.
*
* LIMITS: the function name, the table, the pointer elements, the field at +0x14 and the meaning
* of the value 1 are hypotheses; only the bytes are evidence. All accesses are 32-bit. Whether
* the elements are valid pointers for all 133 entries is not established.
*/
extern int D_8010F0F0[];
void func_8002A9D4(void)
{
int i;
for (i = 0; i < 133; i++) {
int v1 = D_8010F0F0[i];
if (*(int *)(v1 + 0x14) == 1)
*(int *)(v1 + 0x14) = 0;
}
}
+41
View File
@@ -0,0 +1,41 @@
/*
* func_80057524 — 64 bytes at 0x80057524..0x80057564
*
* Framed routine: calls one routine with a field reached through two dereferences, then calls
* a second routine with its own argument.
*
* The observed instructions are:
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* sw s0,0x10(sp) afb00010 save s0
* move s0,a0 00808021 s0 = a0
* sw ra,0x14(sp) afbf0014 save ra
* lw v0,0x20(s0) 8e020020 v0 = *(int *)(a0 + 0x20)
* nop 00000000 load-delay slot
* lw a0,0xf8(v0) 8c4400f8 a0 = *(int *)(v0 + 0xf8)
* jal 0x8003636c 0c00d8db call func_8003636C
* nop 00000000 (delay slot)
* jal 0x80057418 0c015d06 call func_80057418
* move a0,s0 02002021 a0 = s0 (delay slot)
* lw ra,0x14(sp) 8fbf0014 restore ra
* lw s0,0x10(sp) 8e100010 restore s0
* addiu sp,sp,0x18 27bd0018 frame release
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* The argument is copied to **s0**, a callee-saved register, because it must survive the first
* call to become the second call's argument — the same tell as func_800128E4. The second call
* restores it in its own `jal` delay slot. The first callee, 0x8003636C, is the store-only
* function cookbook finding 13 is built on.
*
* LIMITS: the function name, both callees, the pointer chain and the field at +0xf8 are
* hypotheses; only the bytes are evidence. The routine sets no result, so it is `void`.
*/
extern void func_8003636C(int a0);
extern void func_80057418(int a0);
void func_80057524(int a0)
{
func_8003636C(*(int *)(*(int *)(a0 + 0x20) + 0xf8));
func_80057418(a0);
}
+50
View File
@@ -0,0 +1,50 @@
/*
* func_8007C4A8 — 68 bytes at 0x8007C4A8..0x8007C4EC
*
* Copies three words out of a 16-byte-stride table entry into a caller buffer and
* RETURNS the fourth. The entry index is a byte field of the source structure, and
* the table base is materialised as `lui` + `addiu` with a sign-adjusted low half
* — the symbol form (cookbook finding 4) — so the base is written as an
* address-shaped array.
*
* The observed instructions are:
* lbu v0,37(a0) ; index = p->byte_25
* lui v1,0x8014 ; %hi of the table base
* addiu v1,v1,-31224 ; v1 = D_80138608 (%lo, sign-adjusted)
* sll v0,v0,0x4 ; index * 16
* addu v0,v0,v1 ; table + index*16 (index first)
* lw v1,0(v0) / nop / sw v1,0(a1)
* lw v1,4(v0) / nop / sw v1,4(a1)
* lw v1,8(v0) / nop / sw v1,8(a1)
* lw v0,12(v0) ; the returned word
* jr ra
* nop
*
* The element stride is 16 and the table is indexed by a struct, so the entry is
* modelled as a four-word record and the index scaling falls out of `sizeof`.
* The `nop`s after each load are maspsx's load-delay fills.
*
* LIMITS: the table base (0x80138608), the stride (16), the index field offset
* (0x25) and the record's four-word layout are hypotheses read from the
* instruction shape; what the table holds is unknown and is not guessed here. The
* `lbu` is what shows the index byte is unsigned. Only the compiled bytes are
* evidence.
*/
typedef struct {
int word_00;
int word_04;
int word_08;
int word_0c;
} func_8007C4A8_entry;
extern func_8007C4A8_entry D_80138608[];
int func_8007C4A8(char *p, int *dst) {
func_8007C4A8_entry *entry = &D_80138608[*(unsigned char *)(p + 37)];
dst[0] = entry->word_00;
dst[1] = entry->word_04;
dst[2] = entry->word_08;
return entry->word_0c;
}
+44
View File
@@ -0,0 +1,44 @@
/*
* func_8007ED4C — 64 bytes at 0x8007ED4C..0x8007ED8C
*
* Framed routine: calls a routine returning a pointer and, when it is non-null, stores -1 into
* an element of a halfword array selected by a byte field of the returned object.
*
* The observed instructions are:
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* sw ra,0x10(sp) afbf0010 save ra
* jal 0x8007eb8c 0c01fae3 call func_8007EB8C
* nop 00000000 (delay slot)
* beq v0,zero,0x8007ed7c 10400005 if (v0 == 0) goto epilogue
* li v1,-0x1 2403ffff v1 = -1 (delay slot)
* lbu v0,0x25(v0) 90420025 v0 = *(unsigned char *)(v0 + 0x25)
* nop 00000000 load-delay slot
* sll v0,v0,0x1 00021040 v0 *= 2
* lui at,0x8012 3c018012 \
* addu at,at,v0 00220821 / at = 0x80120000 + index * 2
* sh v1,0x2618(at) a4232618 *(short *)(at + 0x2618) = -1
* 0x8007ed7c:
* lw ra,0x10(sp) 8fbf0010 restore ra
* addiu sp,sp,0x18 27bd0018 frame release
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* The constant -1 is materialised in the guard's branch delay slot, so the guard carries the
* stored value. The array access is the explicit indexed symbol form with a 2-byte stride
* (`sll ...,1`), so the element type is 16-bit, and the address arithmetic carries:
* `lui 0x8012` with the signed displacement 0x2618 gives **0x80122618**.
*
* LIMITS: the function name, the callee, the byte field at +0x25 and the halfword array are
* hypotheses; only the bytes are evidence. The routine sets no result, so it is `void`.
*/
extern char *func_8007EB8C(void);
extern short D_80122618[];
void func_8007ED4C(void)
{
char *v0 = func_8007EB8C();
if (v0 != 0)
D_80122618[*(unsigned char *)(v0 + 0x25)] = -1;
}
+44
View File
@@ -0,0 +1,44 @@
/*
* func_80096324 — 64 bytes at 0x80096324..0x80096364
*
* Framed routine that dispatches to one of two routines on a byte field.
*
* The observed instructions are:
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* sw ra,0x10(sp) afbf0010 save ra
* lbu v1,0x26(a0) 90830026 v1 = *(unsigned char *)(a0 + 0x26)
* li v0,0x9 24020009 v0 = 9
* bne v1,v0,0x8009634c 14620003 if (v1 != 9) goto the else arm
* nop 00000000 (delay slot)
* jal 0x800964f8 0c02593e call func_800964F8
* nop 00000000 (delay slot)
* j 0x80096354 081002d5 goto epilogue
* nop 00000000 (delay slot)
* 0x8009634c:
* jal 0x80095d74 0c02575d call func_80095D74
* nop 00000000 (delay slot)
* 0x80096354:
* lw ra,0x10(sp) 8fbf0010 restore ra
* addiu sp,sp,0x18 27bd0018 frame release
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* The comparison constant is materialised into v0 by `li` (SGIs have no branch-immediate form),
* the taken arm is the first callee, and the else arm is the branch target. The first arm ends
* with a `j` over the second, so the source is a plain `if`/`else` and not a `switch` — a
* two-case switch would produce a different layout.
*
* LIMITS: the function name, both callees, the field at +0x26 and the meaning of the value 9
* are hypotheses; only the bytes are evidence. The routine sets no result, so it is `void`.
*/
extern void func_800964F8(void);
extern void func_80095D74(void);
void func_80096324(int a0)
{
if (*(unsigned char *)(a0 + 0x26) == 9)
func_800964F8();
else
func_80095D74();
}
+50
View File
@@ -0,0 +1,50 @@
/*
* func_80101838 — 64 bytes at 0x80101838..0x80101878
*
* Initialises sixteen parallel fields of a structure: two byte arrays indexed by
* the loop counter and two 16-bit arrays indexed by twice the counter, with two
* single 16-bit fields cleared outside the loop (one before, one after).
*
* The observed instructions are:
* sh zero,64(a0) ; *(short *)(base + 0x40) = 0
* move a1,zero ; i = 0
* li a3,127 ; the byte constant
* li a2,8192 ; the 16-bit constant (0x2000)
* move v1,a0 ; v1 walks base + i*2
* 4C: addu v0,a0,a1 ; base + i
* sb zero,68(v0) ; *(char *)(base + i + 0x44) = 0
* sb a3,84(v0) ; *(char *)(base + i + 0x54) = 127
* sh zero,100(v1) ; *(short *)(base + i*2 + 0x64) = 0
* sh a2,132(v1) ; *(short *)(base + i*2 + 0x84) = 0x2000
* addiu a1,a1,1 ; i++
* slti v0,a1,16 ; i < 16
* bnez v0,0x8010184C ; loop
* addiu v1,v1,2 ; v1 += 2 (delay slot)
* jr ra
* sh zero,66(a0) ; *(short *)(base + 0x42) = 0 (delay slot)
*
* The two indexings differ on purpose and must be kept distinct: the byte fields
* use `base + i` (an `addu` of base and the counter) while the 16-bit fields use
* a separate pointer walked by two. Writing all four as `base + i * 2` or all
* four as `base + i` will not reproduce the `addu v0,a0,a1` plus walking-pointer
* pair.
*
* LIMITS: every offset, the count (16), the two constants (127 and 8192) and the
* field widths are hypotheses read from the instruction shape; what the structure
* holds is unknown and is not guessed here. The `sh` at the very end lands in the
* `jr ra` delay slot, which is why it appears out of source order. Only the
* compiled bytes are evidence.
*/
void func_80101838(char *base) {
int i;
*(short *)(base + 64) = 0;
for (i = 0; i < 16; i++) {
*(char *)(base + i + 68) = 0;
*(char *)(base + i + 84) = 127;
*(short *)(base + i * 2 + 100) = 0;
*(short *)(base + i * 2 + 132) = 8192;
}
*(short *)(base + 66) = 0;
}