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
+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);
}