phase9: merge B 3 + C 4 — 372 regions / 363 distinct bodies

Worker B: 26 new this cycle, incl. the rare-epilogue three-way model
confirmed textually (sub-case b: 0x800F8928 mutual exclusion, before/after
68->64 then or-encoded moves), the discard-an-argument-outright pass-through
5th flavour, and D_80121ED8 corroboration. Worker C: 4 matched (0x80048E20,
0x8005784C, 0x800579A0, 0x80057AE0). GATE INCIDENT: C's 0x8002E3FC claimed
but its source is LENGTH-MISMATCH (88 vs expected) against the tracked
registry — dropped from the merge, reported back to C. Gate MATCH
whole-binary SHA-1 e173426c157384ebf1b6caf8c6fea18a85a14af9.
This commit is contained in:
Christopher Williams
2026-09-24 02:06:43 -04:00
parent c738b97698
commit d7149cb22d
8 changed files with 376 additions and 0 deletions
+7
View File
@@ -112,6 +112,7 @@
0x80036378 0x80036380 src/func_80036378.c
0x80036380 0x80036390 src/func_80036380.c
0x80036A0C 0x80036A54 src/func_80036A0C.c
0x80036A54 0x80036A9C src/func_80036A54.c
0x80036A9C 0x80036AD8 src/func_80036A9C.c
0x80036AD8 0x80036B14 src/func_80036AD8.c
0x8003768C 0x800376CC src/func_8003768C.c
@@ -130,6 +131,7 @@
0x800450C4 0x80045110 src/func_800450C4.c gp=-D_80121BFC
0x80045388 0x800453C0 src/func_80045388.c
0x800453C0 0x800453F8 src/func_800453C0.c
0x80048E20 0x80048E70 src/func_80048E20.c
0x80049298 0x800492E4 src/func_80049298.c gp=-D_80121BFC
0x8004C060 0x8004C090 src/func_8004C060.c
0x8004C090 0x8004C0AC src/func_8004C090.c
@@ -141,6 +143,9 @@
0x8005182C 0x80051864 src/func_8005182C.c
0x80052C98 0x80052CAC src/func_80052C98.c
0x80057524 0x80057564 src/func_80057524.c
0x8005784C 0x8005789C src/func_8005784C.c
0x800579A0 0x800579F0 src/func_800579A0.c
0x80057AE0 0x80057B30 src/func_80057AE0.c
0x80057DFC 0x80057E04 src/func_80057DFC.c
0x80058230 0x80058288 src/func_80058230.c
0x80058288 0x800582AC src/func_80058288.c
@@ -161,6 +166,7 @@
0x80068F98 0x80068FA8 src/func_80068F98.c
0x800697A4 0x800697C4 src/func_800697A4.c
0x800697C4 0x800697FC src/func_800697C4.c
0x8006B1CC 0x8006B214 src/func_8006B1CC.c
0x8006B778 0x8006B7C0 src/func_8006B778.c
0x8006BC08 0x8006BC34 src/func_8006BC08.c
0x8006BC34 0x8006BC74 src/func_8006BC34.c
@@ -200,6 +206,7 @@
0x8008B8E4 0x8008B8F4 src/func_8008B8E4.c
0x8008B8F4 0x8008B910 src/func_8008B8F4.c
0x8008F4A0 0x8008F4AC src/func_8008F4A0.c
0x8008F4AC 0x8008F4F4 src/func_8008F4AC.c
0x8008F4F4 0x8008F508 src/func_8008F4F4.c
0x8008F508 0x8008F530 src/func_8008F508.c
0x8008FF58 0x8008FF84 src/func_8008FF58.c
1 # Code-region registry: one C region per matched function.
112 0x80036378
113 0x80036380
114 0x80036A0C
115 0x80036A54
116 0x80036A9C
117 0x80036AD8
118 0x8003768C
131 0x800450C4
132 0x80045388
133 0x800453C0
134 0x80048E20
135 0x80049298
136 0x8004C060
137 0x8004C090
143 0x8005182C
144 0x80052C98
145 0x80057524
146 0x8005784C
147 0x800579A0
148 0x80057AE0
149 0x80057DFC
150 0x80058230
151 0x80058288
166 0x80068F98
167 0x800697A4
168 0x800697C4
169 0x8006B1CC
170 0x8006B778
171 0x8006BC08
172 0x8006BC34
206 0x8008B8E4
207 0x8008B8F4
208 0x8008F4A0
209 0x8008F4AC
210 0x8008F4F4
211 0x8008F508
212 0x8008FF58
+52
View File
@@ -0,0 +1,52 @@
/* func_80036A54 — 0x80036A54..0x80036A9C (72 bytes).
*
* Original words:
* 27BDFFE0 addiu sp,sp,-32
* AFB00010 sw s0,16(sp)
* 00808021 move s0,a0 keep the first argument
* AFB10014 sw s1,20(sp)
* 00C08821 move s1,a2 keep the third argument
* AFBF0018 sw ra,24(sp)
* 0C00C896 jal 0x80032258
* 24050001 _li a1,1 (delay slot) second argument = 1
* 02002021 move a0,s0
* 00402821 move a1,v0
* 0C00C8C0 jal 0x80032300
* 02203021 _move a2,s1 (delay slot)
* 8FBF0018 lw ra,24(sp)
* 8FB10014 lw s1,20(sp)
* 8FB00010 lw s0,16(sp)
* 27BD0020 addiu sp,sp,32
* 03E00008 jr ra
* 00000000 nop
*
* Calls one routine with a forced second argument, then passes its result into a
* second routine alongside the first and third arguments.
*
* **The routine's own second parameter is never read.** `a1` is overwritten by
* `li a1,1` in the first call's delay slot and never restored, so whatever the
* caller placed there is discarded — the parameter exists in the signature but the
* body drops it (the degenerate form of the pass-through idiom: a parameter that is
* silently discarded). It is written into the declaration here purely so the
* argument count matches the two callees' register expectations.
*
* Only `a0` and `a2` need saving because both are live across the first call, and
* the two `move`s into the second call's argument registers are scheduled into the
* first call's exit path and the second call's delay slot.
*
* LIMITS: the two callees are named for their addresses and nothing establishes
* their signatures; the `1` is read from the `li`. That the dropped parameter is a
* real parameter of the original — rather than the source having only two
* parameters and `a2` being something else — follows from `a2` being saved and
* forwarded, which a two-parameter routine could not explain.
*/
int func_80032258(int a0, int a1);
void func_80032300(int a0, int a1, int a2);
void func_80036A54(int a0, int unused, int a2)
{
int result = func_80032258(a0, 1);
func_80032300(a0, result, a2);
}
+46
View File
@@ -0,0 +1,46 @@
/*
* func_80048E20 — 80 bytes at 0x80048E20..0x80048E70
*
* Sign-extends a 16-bit second argument, fills a scratch buffer through a call,
* then makes a five-argument call with a fixed function address, the first
* argument saved in `s0`, and the buffer as the fourth argument.
*
* The observed instructions are:
* addiu sp,sp,-48
* sw s0,40(sp)
* move s0,a0 ; s0 = first argument
* sll a0,a1,0x10
* sra a0,a0,0x10 ; a0 = (short)a1 <- SIGNED 16-bit
* sw ra,44(sp)
* jal 0x80045110
* addiu a1,sp,24 ; second argument = the buffer (delay slot)
* lui a0,0x8007
* addiu a0,a0,-12368 ; a0 = D_8006CFB0
* move a1,s0 ; second argument = the saved first argument
* move a2,zero
* addiu a3,sp,24 ; fourth argument = the buffer
* jal 0x8006D2FC
* sw zero,16(sp) ; fifth argument = 0 (delay slot)
* lw ra,44(sp)
* lw s0,40(sp)
* addiu sp,sp,48
* jr ra
* nop
*
* The `sll`/`sra` pair on the second argument shows it is a SIGNED 16-bit value
* promoted to int. The fifth argument goes into the outgoing stack slot at 16(sp)
* of the 48-byte frame.
*
* LIMITS: the function address D_8006CFB0, the buffer size and the two callees are
* hypotheses read from the instruction shape; what the buffer and the callees mean
* is unknown and is not guessed here. Only the compiled bytes are evidence.
*/
extern char D_8006CFB0[];
void func_80048E20(char *a0, short a1) {
short buf[8];
func_80045110(a1, buf);
func_8006D2FC((int)D_8006CFB0, a0, 0, buf, 0);
}
+50
View File
@@ -0,0 +1,50 @@
/*
* func_8005784C — 80 bytes at 0x8005784C..0x8005789C
*
* One member of a THREE-MEMBER family (with 0x8005784C and 0x800579A0 and
* 0x80057AE0): a ten-argument call whose second argument is a small mode constant
* (3) and whose fourth argument is the first argument masked by whether the
* second argument's low byte is non-zero. The only difference between the members
* is 3.
*
* The observed instructions are:
* addiu sp,sp,-48
* andi a3,a1,0xff ; low = second argument & 0xff
* andi a2,a2,0xff ; high = third argument & 0xff
* sw a3,32(sp) ; ninth argument = low
* sltu a3,zero,a3 ; (low != 0)
* negu a3,a3 ; -(low != 0)
* li a1,3 ; second argument = 3
* sw a2,36(sp) ; tenth argument = high
* li a2,-1 ; third argument = -1
* and a3,a0,a3 ; fourth argument = a0 & -(low != 0)
* sw ra,40(sp)
* sw zero,16(sp) ; fifth argument = 0
* sw zero,20(sp) ; sixth argument = 0
* sw zero,24(sp) ; seventh argument = 0
* jal 0x80057664
* sw zero,28(sp) ; eighth argument = 0 (delay slot)
* lw ra,40(sp)
* addiu sp,sp,48
* jr ra
* nop
*
* The `sltu`/`negu` pair is the branchless form of a ternary producing 0 or -1,
* so the fourth argument is `a0` when the low byte is non-zero and 0 otherwise.
* The four zero arguments are materialised as four consecutive stores to the
* outgoing stack slots 16/20/24/28(sp), and the two byte arguments go to 32/36(sp)
* as the ninth and tenth arguments.
*
* LIMITS: the mode constant (3), the callee and the argument types are
* hypotheses read from the instruction shape; what the mode and the callee mean is
* unknown and is not guessed here. The `andi 0xff` pairs show the two byte
* arguments are truncated before use. Only the compiled bytes are evidence.
*/
void func_8005784C(int a0, int a1, int a2) {
int low = a1 & 0xFF;
int high = a2 & 0xFF;
int mask = low != 0 ? -1 : 0;
func_80057664(a0, 3, -1, a0 & mask, 0, 0, 0, 0, low, high);
}
+50
View File
@@ -0,0 +1,50 @@
/*
* func_800579A0 — 80 bytes at 0x800579A0..0x800579F0
*
* One member of a THREE-MEMBER family (with 0x8005784C and 0x800579A0 and
* 0x80057AE0): a ten-argument call whose second argument is a small mode constant
* (10) and whose fourth argument is the first argument masked by whether the
* second argument's low byte is non-zero. The only difference between the members
* is 10.
*
* The observed instructions are:
* addiu sp,sp,-48
* andi a3,a1,0xff ; low = second argument & 0xff
* andi a2,a2,0xff ; high = third argument & 0xff
* sw a3,32(sp) ; ninth argument = low
* sltu a3,zero,a3 ; (low != 0)
* negu a3,a3 ; -(low != 0)
* li a1,10 ; second argument = 10
* sw a2,36(sp) ; tenth argument = high
* li a2,-1 ; third argument = -1
* and a3,a0,a3 ; fourth argument = a0 & -(low != 0)
* sw ra,40(sp)
* sw zero,16(sp) ; fifth argument = 0
* sw zero,20(sp) ; sixth argument = 0
* sw zero,24(sp) ; seventh argument = 0
* jal 0x80057664
* sw zero,28(sp) ; eighth argument = 0 (delay slot)
* lw ra,40(sp)
* addiu sp,sp,48
* jr ra
* nop
*
* The `sltu`/`negu` pair is the branchless form of a ternary producing 0 or -1,
* so the fourth argument is `a0` when the low byte is non-zero and 0 otherwise.
* The four zero arguments are materialised as four consecutive stores to the
* outgoing stack slots 16/20/24/28(sp), and the two byte arguments go to 32/36(sp)
* as the ninth and tenth arguments.
*
* LIMITS: the mode constant (10), the callee and the argument types are
* hypotheses read from the instruction shape; what the mode and the callee mean is
* unknown and is not guessed here. The `andi 0xff` pairs show the two byte
* arguments are truncated before use. Only the compiled bytes are evidence.
*/
void func_800579A0(int a0, int a1, int a2) {
int low = a1 & 0xFF;
int high = a2 & 0xFF;
int mask = low != 0 ? -1 : 0;
func_80057664(a0, 10, -1, a0 & mask, 0, 0, 0, 0, low, high);
}
+50
View File
@@ -0,0 +1,50 @@
/*
* func_80057AE0 — 80 bytes at 0x80057AE0..0x80057B30
*
* One member of a THREE-MEMBER family (with 0x8005784C and 0x800579A0 and
* 0x80057AE0): a ten-argument call whose second argument is a small mode constant
* (14) and whose fourth argument is the first argument masked by whether the
* second argument's low byte is non-zero. The only difference between the members
* is 14.
*
* The observed instructions are:
* addiu sp,sp,-48
* andi a3,a1,0xff ; low = second argument & 0xff
* andi a2,a2,0xff ; high = third argument & 0xff
* sw a3,32(sp) ; ninth argument = low
* sltu a3,zero,a3 ; (low != 0)
* negu a3,a3 ; -(low != 0)
* li a1,14 ; second argument = 14
* sw a2,36(sp) ; tenth argument = high
* li a2,-1 ; third argument = -1
* and a3,a0,a3 ; fourth argument = a0 & -(low != 0)
* sw ra,40(sp)
* sw zero,16(sp) ; fifth argument = 0
* sw zero,20(sp) ; sixth argument = 0
* sw zero,24(sp) ; seventh argument = 0
* jal 0x80057664
* sw zero,28(sp) ; eighth argument = 0 (delay slot)
* lw ra,40(sp)
* addiu sp,sp,48
* jr ra
* nop
*
* The `sltu`/`negu` pair is the branchless form of a ternary producing 0 or -1,
* so the fourth argument is `a0` when the low byte is non-zero and 0 otherwise.
* The four zero arguments are materialised as four consecutive stores to the
* outgoing stack slots 16/20/24/28(sp), and the two byte arguments go to 32/36(sp)
* as the ninth and tenth arguments.
*
* LIMITS: the mode constant (14), the callee and the argument types are
* hypotheses read from the instruction shape; what the mode and the callee mean is
* unknown and is not guessed here. The `andi 0xff` pairs show the two byte
* arguments are truncated before use. Only the compiled bytes are evidence.
*/
void func_80057AE0(int a0, int a1, int a2) {
int low = a1 & 0xFF;
int high = a2 & 0xFF;
int mask = low != 0 ? -1 : 0;
func_80057664(a0, 14, -1, a0 & mask, 0, 0, 0, 0, low, high);
}
+63
View File
@@ -0,0 +1,63 @@
/* func_8006B1CC — 0x8006B1CC..0x8006B214 (72 bytes).
*
* Original words:
* 8F840550 lw a0,1360(gp) a0 = D_80121E88 (gp + 0x550)
* 27BDFFE8 addiu sp,sp,-24
* AFBF0014 sw ra,20(sp)
* 10800009 beqz a0,0x8006B200
* AFB00010 _sw s0,16(sp) (delay slot)
* 8C85000C lw a1,12(a0) <- loop top
* 8CB0018C lw s0,396(a1)
* 0C01AAA2 jal 0x8006AA88
* 00000000 nop
* 02002021 move a0,s0
* 1480FFF9 bnez a0,loop
* 00000000 _nop (delay slot)
* 8FBF0014 lw ra,20(sp) <- 0x8006B200
* 8FB00010 lw s0,16(sp)
* 27BD0018 addiu sp,sp,24
* 03E00008 jr ra
* 00000000 nop
*
* Walks a linked list held in a global and calls one routine per node, following a
* **second** link to get the next node rather than the one it was reached by.
*
* The loop reads `node->0xc` (a sub-object) and then the sub-object's field 0x18c as
* the successor, so the list is threaded through the inner object, not through the
* node itself. That is why `a1` is reloaded every iteration from `a0`: both the node
* and its sub-object are needed by the call.
*
* The guard is a single `beqz` before the loop, so a null global does nothing at all
* — the frame is still set up and torn down, and the `sw s0` sits in the `beqz`
* delay slot, which is why the saved-register spill appears before the loop rather
* than in a contiguous prologue.
*
* The global is gp-relative (`1360(gp)`, cookbook finding 10), so `gp` is 0x80121938
* and the address is **0x80121E88**; the symbol needs a `gp` marker row.
*
* LIMITS: the displacements 0xc and 0x18c and the gp offset are read from the bytes.
* That the successor field belongs to the inner object rather than the node is read
* directly from which base each load uses; whether the chain is a list or a tree
* walked by one child is not provable from a single successor pointer. The callee is
* named for its address and its two arguments are inferred from the registers left
* in place.
*/
extern int D_80121E88;
void func_8006AA88(int node, int inner);
void func_8006B1CC(void)
{
int node = D_80121E88;
if (node != 0) {
do {
int inner = *(int *)(node + 0xc);
int next = *(int *)(inner + 0x18c);
func_8006AA88(node, inner);
node = next;
} while (node != 0);
}
}
+58
View File
@@ -0,0 +1,58 @@
/* func_8008F4AC — 0x8008F4AC..0x8008F4F4 (72 bytes).
*
* Original words:
* 27BDFFE8 addiu sp,sp,-24
* 1CA00003 bgtz a1,0x8008F4C0
* AFBF0010 _sw ra,16(sp) (delay slot)
* 08023D34 j 0x8008F4D0
* 24050001 _li a1,1 (delay slot) clamp low
* 28A20080 slti v0,a1,128 <- 0x8008F4C0
* 14400002 bnez v0,0x8008F4D0
* 00000000 _nop (delay slot)
* 2405007F li a1,127 clamp high
* 3C018012 lui at,0x8012 <- 0x8008F4D0
* 00240821 addu at,at,a0
* A0251ED8 sb a1,7896(at) D_80121ED8[index] = value
* 0C03FA1E jal 0x800FE878
* 00000000 nop
* 8FBF0010 lw ra,16(sp)
* 27BD0018 addiu sp,sp,24
* 03E00008 jr ra
* 00000000 nop
*
* Clamps a value into 1..127, stores it into a byte array indexed by the first
* argument, and then calls an update routine.
*
* The clamp is **two-sided and open at both ends**: `value <= 0` becomes 1 and
* `value >= 128` becomes 127, so the stored byte always has its high bit clear —
* which is consistent with a signed `char` target. The low clamp is reached by a
* `bgtz` falling through to a `j` whose delay slot loads the 1, and the high clamp
* by a `slti`/`bnez` pair; both arms converge on the store, so this is one shared
* epilogue for the clamp and not two returns.
*
* The store is the symbol-plus-register macro form (`lui at` + `addu at,at,a0` +
* `sb %lo(at)`), so the base is the global array at **0x80121ED8** — the same array
* that `func_8008F4F4` reads from, which is independent corroboration that the
* symbol is a byte array and that the index is used raw (no scaling).
*
* The update routine receives no fresh argument setup, so it is called with `a0`
* (the index) and `a1` (the clamped value) still in place.
*
* LIMITS: the bounds 1, 128 and 127 and the displacement are read from the bytes;
* that the range is a hardware or protocol limit rather than a policy choice is not
* observable. The callee is named for its address.
*/
extern signed char D_80121ED8[];
void func_800FE878(int index, int value);
void func_8008F4AC(int index, int value)
{
if (value <= 0)
value = 1;
else if (value >= 128)
value = 127;
D_80121ED8[index] = value;
func_800FE878(index, value);
}