phase9: merge B 3 — 355 regions / 346 distinct bodies

Worker B: 3 more tier-2 frames (0x80036328, 0x800AA2B4, 0x80021FF4 — the
latter with the base+offset decode and the clearest finding-13 scratch
constant yet) + symbol D_80121978 (gp). 23 new bodies this cycle.

GATE INCIDENT: the first candidate gate DIFFed with 4 extra bytes because
src/func_80025070.c was DELETED from the working tree after my commit
( status) — the build used a stale/wrong object. Restored from git and
re-gated clean. Lesson recorded: every tracked src/ file is a claim; a
registry row whose source is missing breaks the whole-binary gate with a
cascade, so the pre-gate 'git status src/' check must catch deletions too.
Gate MATCH whole-binary SHA-1
e173426c157384ebf1b6caf8c6fea18a85a14af9.
This commit is contained in:
Christopher Williams
2026-09-24 01:51:09 -04:00
parent a23222dcfb
commit 814340cb20
5 changed files with 171 additions and 0 deletions
+3
View File
@@ -55,6 +55,7 @@
0x80021F64 0x80021F88 src/func_80021F64.c
0x80021F88 0x80021FA8 src/func_80021F88.c
0x80021FA8 0x80021FF4 src/func_80021FA8.c
0x80021FF4 0x8002203C src/func_80021FF4.c
0x80022A60 0x80022A98 src/func_80022A60.c
0x80022FB8 0x80022FCC src/func_80022FB8.c
0x80022FCC 0x80022FFC src/func_80022FCC.c
@@ -101,6 +102,7 @@
0x800321EC 0x800321F8 src/func_800321EC.c
0x80034A80 0x80034ABC src/func_80034A80.c
0x80036308 0x80036328 src/func_80036308.c
0x80036328 0x8003636C src/func_80036328.c
0x8003636C 0x80036378 src/func_8003636C.c
0x80036378 0x80036380 src/func_80036378.c
0x80036380 0x80036390 src/func_80036380.c
@@ -232,6 +234,7 @@
0x800A8B48 0x800A8B8C src/func_800A8B48.c
0x800A9D58 0x800A9D90 src/func_800A9D58.c
0x800A9FD4 0x800AA01C src/func_800A9FD4.c
0x800AA2B4 0x800AA2F8 src/func_800AA2B4.c
0x800AA56C 0x800AA59C src/func_800AA56C.c
0x800AC818 0x800AC85C src/func_800AC818.c
0x800AC85C 0x800AC884 src/func_800AC85C.c
1 # Code-region registry: one C region per matched function.
55 0x80021F64
56 0x80021F88
57 0x80021FA8
58 0x80021FF4
59 0x80022A60
60 0x80022FB8
61 0x80022FCC
102 0x800321EC
103 0x80034A80
104 0x80036308
105 0x80036328
106 0x8003636C
107 0x80036378
108 0x80036380
234 0x800A8B48
235 0x800A9D58
236 0x800A9FD4
237 0x800AA2B4
238 0x800AA56C
239 0x800AC818
240 0x800AC85C
+1
View File
@@ -20,6 +20,7 @@ D_80121958 0x80121958 gp
D_8012195D 0x8012195D gp
D_80121970 0x80121970 gp
D_80121974 0x80121974 gp
D_80121978 0x80121978 gp
D_8012197C 0x8012197C gp
D_80121988 0x80121988 gp
D_8012199C 0x8012199C gp
1 # Symbol registry: absolute addresses for cross-references used by C regions.
20 D_8012195D
21 D_80121970
22 D_80121974
23 D_80121978
24 D_8012197C
25 D_80121988
26 D_8012199C
+65
View File
@@ -0,0 +1,65 @@
/* func_80021FF4 — 0x80021FF4..0x8002203C (72 bytes).
*
* Original words:
* 27BDFFE8 addiu sp,sp,-24
* AFBF0010 sw ra,16(sp)
* 0C052084 jal 0x80148210
* 00002021 _move a0,zero (delay slot)
* 3C048012 lui a0,0x8012
* 2484622C addiu a0,a0,25132 a0 = D_8012622C
* 0C03DFF6 jal 0x800F7FD8
* 24850022 _addiu a1,a0,34 (delay slot) a1 = a0 + 34
* 0C03E18E jal 0x800F8638
* 00000000 nop
* 0C0087EA jal 0x80021FA8
* 00000000 nop
* 24020001 li v0,1
* A3820920 sb v0,2336(gp) D_80122258 = 1
* 8FBF0010 lw ra,16(sp)
* 00001021 move v0,zero
* 03E00008 jr ra
* 27BD0018 _addiu sp,sp,24 (delay slot)
*
* A four-call initialiser ending in a one-byte flag store. The frame exists only to
* preserve `ra`; every argument setup is either in a delay slot or absent.
*
* The second call's two addresses are `0x8012622C` and that value **plus 34** —
* `addiu a1,a0,34` reuses the register already holding the first address rather than
* materialising a second symbol. That is the tell for a source that passed one
* address and a displacement from it (a base and a base+34 pair), not two
* independently named globals; two symbols would each need their own `lui`.
*
* The first address's `lui`/`addiu` split needs no sign adjustment (25132 = 0x622C is
* positive as a 16-bit value), unlike the negatively-displaced cases elsewhere in
* this batch — so here the `lui` immediate *is* the top half of the address.
*
* `li v0,1` then `sb v0,2336(gp)` then `move v0,zero` is a **scratch constant, not a
* return value**: the byte written is 1 and the routine then returns 0, so the `1`
* never reaches the caller (cookbook finding 13's rule, in its clearest form). The
* flag store is gp-relative, so `gp` (0x80121938) + 2336 gives **0x80122258**, which
* needs a `gp` marker row.
*
* LIMITS: all four callees are named for their addresses and nothing here establishes
* what they do or what the two addresses mean. That 34 is a displacement into the
* object at 0x8012622C rather than an unrelated constant follows from the register
* reuse, not from the value. The returned zero is taken to be a success indicator
* only because the body also sets a flag; that is a reading, not evidence.
*/
extern char D_8012622C[];
extern unsigned char D_80122258;
void func_80148210(int arg);
void func_800F7FD8(char *from, char *to);
void func_800F8638(void);
void func_80021FA8(void);
int func_80021FF4(void)
{
func_80148210(0);
func_800F7FD8(D_8012622C, D_8012622C + 34);
func_800F8638();
func_80021FA8();
D_80122258 = 1;
return 0;
}
+51
View File
@@ -0,0 +1,51 @@
/* func_80036328 — 0x80036328..0x8003636C (68 bytes).
*
* Original words:
* 27BDFFE0 addiu sp,sp,-32
* AFB10014 sw s1,20(sp)
* 00808821 move s1,a0 keep the pointer
* AFB00010 sw s0,16(sp)
* 30B000FF andi s0,a1,0xff keep the narrowed second argument
* AFBF0018 sw ra,24(sp)
* 0C00D89E jal 0x80036278
* 02002821 _move a1,s0 (delay slot)
* 02202021 move a0,s1
* 0C00D8C2 jal 0x80036308
* 02002821 _move a1,s0 (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 two routines with the same pair of arguments, the second argument masked
* to a byte. Both calls fill their delay slots with the second argument, so the
* narrowed value is the one passed — the mask happens once, before either call.
*
* `andi s0,a1,0xff` is the whole reason the value is kept at all: without the mask
* the argument could be forwarded straight from `a1` and no saved register would be
* needed for it. The two saved registers exist because two calls intervene, so the
* pointer and the masked value must survive both.
*
* The spill order (`sw s1`, `move s1`, `sw s0`, `andi s0`, `sw ra`) is the scheduler
* interleaving, not source order — the frame's three saves are emitted around the
* two register setups.
*
* LIMITS: the mask 0xff is read from the `andi`, so the second parameter is
* narrowed to 8 bits, but whether the source declared it as `unsigned char` or
* masked it explicitly cannot be told from the bytes. The frame is 32 bytes for two
* saved registers and `ra`, which is 8 more than those need — the same round-up
* anomaly seen at `0x80066E2C`. The two callees are named for their addresses.
*/
void func_80036278(int a0, int a1);
void func_80036308(int a0, int a1);
void func_80036328(int obj, int value)
{
int masked = value & 0xff;
func_80036278(obj, masked);
func_80036308(obj, masked);
}
+51
View File
@@ -0,0 +1,51 @@
/* func_800AA2B4 — 0x800AA2B4..0x800AA2F8 (68 bytes).
*
* Original words:
* 27BDFFE8 addiu sp,sp,-24
* AFB00010 sw s0,16(sp)
* AFBF0014 sw ra,20(sp)
* 0C02A0B4 jal 0x800A82D0
* 00A08021 _move s0,a1 (delay slot) keep the second argument
* 10400005 beqz v0,0x800AA2E0
* 00402021 _move a0,v0 (delay slot) first argument of the tail call
* 0C02A843 jal 0x800AA10C
* 02002821 _move a1,s0 (delay slot)
* 0802A8B9 j 0x800AA2E4
* 00000000 _nop (delay slot)
* 00001021 move v0,zero <- 0x800AA2E0
* 8FBF0014 lw ra,20(sp) <- 0x800AA2E4
* 8FB00010 lw s0,16(sp)
* 27BD0018 addiu sp,sp,24
* 03E00008 jr ra
* 00000000 nop
*
* A lookup-then-forward: the first callee's result is tested, a null result answers
* zero, and a non-null one is passed as the *first* argument to a second callee
* together with the second argument that was kept.
*
* Every argument setup is scheduled into a branch or call delay slot — `move s0,a1`
* into the first `jal`, `move a0,v0` into the `beqz`, `move a1,s0` into the second
* `jal` — so the routine has no instruction that is purely argument setup, and the
* C below is written to match that shape rather than to read naturally.
*
* The two returns share one epilogue, reached by the fall-through path and by the
* `j` from the success path, and the `move v0,zero` before it is the zero answer.
*
* LIMITS: the two callees are named for their addresses. That the first returns a
* pointer or handle is inferred from its being null-tested and then forwarded as an
* argument; nothing in this body establishes its type, and the `int` used here is a
* width choice. The second argument is passed unchanged to both callees, so no
* narrowing of it can be inferred.
*/
int func_800A82D0(int a0, int a1);
int func_800AA10C(int a0, int a1);
int func_800AA2B4(int a0, int a1)
{
int found = func_800A82D0(a0, a1);
if (found == 0)
return 0;
return func_800AA10C(found, a1);
}