phase9: merge cycle 2 close — 260 regions / 251 distinct bodies

14 more regions merged and gated MATCH (c_regions=260, whole-binary SHA-1
e173426c157384ebf1b6caf8c6fea18a85a14af9); make check green (AGREE + MATCH).
Full audit passed from clean state (CMP_OK, SHA-1, 223 tests, gate, extents).

Worker A: 12 more (tier-2 wrappers incl. 7-arg o32 forwarder, GTE-forwarder
adjacent to the registered maspsx=off 0x800F3160, and 0x80036380 — a
byte-proven 48-byte empty-frame registered under an honest limits header).
New: maspsx-conflict scope limit (finding 17 corrected: maspsx=off unsafe
for bodies with move pseudo-instructions — addu-vs-or expansion), alloc-
tiebreak negative 0x80019700.
Worker C: 2 boundary-crossing matches accepted under the decided policy
(verified work is claimable across regenerated partition edges); handoff
0x8010A748 accepted. F10 named-pointer base-first for register bases (closes
the F5 gap), F11 duplicate stores need volatile, F12 if-conversion is a
compiler class (0x80010418 exclusion recommended and accepted).

Collision policy decided: verified work is claimable regardless of the
regenerated partition boundary; overlapping staging is deduped by the merge.
All boundary crossings reported by the workers.
This commit is contained in:
Christopher Williams
2026-09-24 00:26:45 -04:00
parent 6e8c3df7a7
commit e317c8ed5a
23 changed files with 865 additions and 0 deletions
+14
View File
@@ -34,6 +34,7 @@
0x80017C50 0x80017C60 src/func_80017C50.c
0x80017C60 0x80017C6C src/func_80017C60.c
0x80017D1C 0x80017D48 src/func_80017D1C.c
0x80017D48 0x80017D88 src/func_80017D48.c
0x80017DD0 0x80017DF0 src/func_80017DD0.c
0x800182D4 0x800182F4 src/func_800182D4.c
0x800198C0 0x800198F4 src/func_800198C0.c
@@ -67,6 +68,7 @@
0x8002D288 0x8002D2A0 src/func_8002D288.c gp=-D_80121F84
0x8002D2A0 0x8002D2BC src/func_8002D2A0.c
0x8002D2BC 0x8002D2D4 src/func_8002D2BC.c
0x8002D5D0 0x8002D608 src/func_8002D5D0.c
0x8002DEB4 0x8002DF1C src/func_8002DEB4.c
0x8002E7C4 0x8002E7E4 src/func_8002E7C4.c
0x8002F2F8 0x8002F300 src/func_8002F2F8.c
@@ -76,16 +78,19 @@
0x80036308 0x80036328 src/func_80036308.c
0x8003636C 0x80036378 src/func_8003636C.c
0x80036378 0x80036380 src/func_80036378.c
0x80036380 0x80036390 src/func_80036380.c
0x80036AD8 0x80036B14 src/func_80036AD8.c
0x8003768C 0x800376CC src/func_8003768C.c
0x80038788 0x80038790 src/func_80038788.c
0x80038790 0x8003879C src/func_80038790.c
0x8003B2F0 0x8003B320 src/func_8003B2F0.c
0x8003B320 0x8003B34C src/func_8003B320.c
0x80041A24 0x80041A58 src/func_80041A24.c
0x80042088 0x80042090 src/func_80042088.c
0x80042D64 0x80042D88 src/func_80042D64.c
0x80043D8C 0x80043DC4 src/func_80043D8C.c
0x80044F58 0x80044FA4 src/func_80044F58.c gp=-D_80121BFC
0x800453C0 0x800453F8 src/func_800453C0.c
0x8004C060 0x8004C090 src/func_8004C060.c
0x8004C090 0x8004C0AC src/func_8004C090.c
0x8004C0AC 0x8004C0F0 src/func_8004C0AC.c
@@ -112,6 +117,7 @@
0x8006F944 0x8006F95C src/func_8006F944.c
0x8006FA78 0x8006FAA0 src/func_8006FA78.c
0x8007049C 0x800704BC src/func_8007049C.c
0x80072BA8 0x80072BDC src/func_80072BA8.c
0x8007A404 0x8007A428 src/func_8007A404.c
0x8007C4EC 0x8007C524 src/func_8007C4EC.c
0x8007DC40 0x8007DC4C src/func_8007DC40.c
@@ -137,6 +143,7 @@
0x80090048 0x80090058 src/func_80090048.c
0x80090A44 0x80090A70 src/func_80090A44.c
0x80090B64 0x80090B7C src/func_80090B64.c
0x80090C8C 0x80090CAC src/func_80090C8C.c
0x800912D4 0x800912FC src/func_800912D4.c
0x800912FC 0x8009132C src/func_800912FC.c
0x80092068 0x80092088 src/func_80092068.c
@@ -147,6 +154,7 @@
0x800943C4 0x800943E0 src/func_800943C4.c
0x80099A94 0x80099AE4 src/func_80099A94.c
0x80099E14 0x80099E34 src/func_80099E14.c
0x8009AC08 0x8009AC28 src/func_8009AC08.c
0x8009D8A0 0x8009D8E0 src/func_8009D8A0.c
0x8009E8D0 0x8009E95C src/func_8009E8D0.c
0x8009F0E8 0x8009F120 src/func_8009F0E8.c
@@ -155,6 +163,7 @@
0x800A74BC 0x800A74D0 src/func_800A74BC.c
0x800A8B48 0x800A8B8C src/func_800A8B48.c
0x800AA56C 0x800AA59C src/func_800AA56C.c
0x800AC818 0x800AC85C src/func_800AC818.c
0x800ACC00 0x800ACC20 src/func_800ACC00.c
0x800AE0F4 0x800AE10C src/func_800AE0F4.c
0x800AE548 0x800AE574 src/func_800AE548.c
@@ -170,9 +179,11 @@
0x800B7230 0x800B7264 src/func_800B7230.c
0x800BBDEC 0x800BBDF8 src/func_800BBDEC.c
0x800BFE80 0x800BFEA0 src/func_800BFE80.c
0x800BFEC0 0x800BFEE0 src/func_800BFEC0.c
0x800BFEE0 0x800BFF00 src/func_800BFEE0.c
0x800C5C84 0x800C5CBC src/func_800C5C84.c
0x800F2F6C 0x800F2FB8 src/func_800F2F6C.c maspsx=off
0x800F3140 0x800F3160 src/func_800F3140.c
0x800F3160 0x800F316C src/func_800F3160.c maspsx=off
0x800F3E70 0x800F3E88 src/func_800F3E70.c
0x800F5200 0x800F521C src/func_800F5200.c
@@ -189,13 +200,16 @@
0x800F8ACC 0x800F8ADC src/func_800F8ACC.c
0x800F8ADC 0x800F8AEC src/func_800F8ADC.c
0x800F8AEC 0x800F8AF8 src/func_800F8AEC.c maspsx=off
0x800F8B18 0x800F8B38 src/func_800F8B18.c
0x800F8B38 0x800F8B58 src/func_800F8B38.c
0x800F8B58 0x800F8B6C src/func_800F8B58.c maspsx=off
0x800F8B6C 0x800F8B80 src/func_800F8B6C.c maspsx=off
0x800F8F5C 0x800F8F7C src/func_800F8F5C.c
0x800F8F7C 0x800F8F9C src/func_800F8F7C.c
0x800F8F9C 0x800F8FC0 src/func_800F8F9C.c
0x800F8FE4 0x800F8FF8 src/func_800F8FE4.c maspsx=off
0x800F8FF8 0x800F9008 src/func_800F8FF8.c
0x800FA960 0x800FA980 src/func_800FA960.c
0x800FB13C 0x800FB1BC src/func_800FB13C.c
0x800FB5D4 0x800FB5DC src/func_800FB5D4.c
0x800FB5E4 0x800FB5FC src/func_800FB5E4.c
1 # Code-region registry: one C region per matched function.
34 0x80017C50
35 0x80017C60
36 0x80017D1C
37 0x80017D48
38 0x80017DD0
39 0x800182D4
40 0x800198C0
68 0x8002D288
69 0x8002D2A0
70 0x8002D2BC
71 0x8002D5D0
72 0x8002DEB4
73 0x8002E7C4
74 0x8002F2F8
78 0x80036308
79 0x8003636C
80 0x80036378
81 0x80036380
82 0x80036AD8
83 0x8003768C
84 0x80038788
85 0x80038790
86 0x8003B2F0
87 0x8003B320
88 0x80041A24
89 0x80042088
90 0x80042D64
91 0x80043D8C
92 0x80044F58
93 0x800453C0
94 0x8004C060
95 0x8004C090
96 0x8004C0AC
117 0x8006F944
118 0x8006FA78
119 0x8007049C
120 0x80072BA8
121 0x8007A404
122 0x8007C4EC
123 0x8007DC40
143 0x80090048
144 0x80090A44
145 0x80090B64
146 0x80090C8C
147 0x800912D4
148 0x800912FC
149 0x80092068
154 0x800943C4
155 0x80099A94
156 0x80099E14
157 0x8009AC08
158 0x8009D8A0
159 0x8009E8D0
160 0x8009F0E8
163 0x800A74BC
164 0x800A8B48
165 0x800AA56C
166 0x800AC818
167 0x800ACC00
168 0x800AE0F4
169 0x800AE548
179 0x800B7230
180 0x800BBDEC
181 0x800BFE80
182 0x800BFEC0
183 0x800BFEE0
184 0x800C5C84
185 0x800F2F6C
186 0x800F3140
187 0x800F3160
188 0x800F3E70
189 0x800F5200
200 0x800F8ACC
201 0x800F8ADC
202 0x800F8AEC
203 0x800F8B18
204 0x800F8B38
205 0x800F8B58
206 0x800F8B6C
207 0x800F8F5C
208 0x800F8F7C
209 0x800F8F9C
210 0x800F8FE4
211 0x800F8FF8
212 0x800FA960
213 0x800FB13C
214 0x800FB5D4
215 0x800FB5E4
+49
View File
@@ -0,0 +1,49 @@
/*
* func_80017D48 — 64 bytes at 0x80017D48..0x80017D88
*
* Reads a gp-relative packed value, splits it into two 16-bit halves, and derives
* a scale factor from them: the low half is the base, the high half minus one is
* a variable shift count, and the result is the base plus a shifted complement.
* A low half of 4096 or more is clamped to a fixed 15000.
*
* The observed instructions are:
* lw v0,32(gp) ; v0 = D_80121958 (gp = 0x80121938)
* li v1,4096
* andi a0,v0,0xffff ; low = value & 0xffff
* srl v0,v0,0x10 ; value >> 16
* addiu v0,v0,-1 ; shift = (value >> 16) - 1
* subu v1,v1,a0 ; 4096 - low
* srlv v1,v1,v0 ; (4096 - low) >> shift <- UNSIGNED, variable
* addu v1,a0,v1 ; result = low + that
* sltiu a0,a0,4096 ; low < 4096 <- UNSIGNED compare
* beqz a0,0x80017D7C ; if (!(low < 4096)) goto 15000
* srl v0,v1,0x2 ; result >> 2 (delay slot)
* j 0x80017D80
* addu v0,v1,v0 ; return result + (result >> 2) (delay slot)
* 7C: li v0,15000
* 80: jr ra
* nop
*
* Both the variable shift (`srlv`) and the range test (`sltiu`) are UNSIGNED, so
* the source operands are unsigned — a signed source would emit `srav`/`slti`.
*
* LIMITS: the symbol name D_80121958, the constants (4096, 15000), the shift
* amount (2) and the packing (two 16-bit halves) are hypotheses read from the
* instruction shape; the offset 32 is a fact about this executable's gp layout.
* What the packed value means is unknown and is not guessed here. Only the
* compiled bytes are evidence.
*/
extern int D_80121958;
int func_80017D48(void) {
unsigned int value = D_80121958;
unsigned int low = value & 0xFFFF;
unsigned int shift = (value >> 16) - 1;
unsigned int result = low + ((4096 - low) >> shift);
if (low >= 4096)
return 15000;
return result + (result >> 2);
}
+39
View File
@@ -0,0 +1,39 @@
/*
* func_80021F24 — 44 bytes at 0x80021F24..0x80021F50
*
* Framed wrapper: calls one routine with two `gp`-relative halfwords and its own argument.
*
* The observed instructions are:
* move a2,a0 00803021 a2 = a0
* lh a0,0x922(gp) 87a40922 a0 = *(short *)(gp + 0x922)
* lh a1,0x924(gp) 87a50924 a1 = *(short *)(gp + 0x924)
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* sw ra,0x10(sp) afbf0010 save ra
* jal 0x80021d90 0c008764 call func_80021D90
* nop 00000000 (delay slot)
* lw ra,0x10(sp) 8fbf0010 restore ra
* addiu sp,sp,0x18 27bd0018 frame release
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* The incoming argument moves to a2 **before** a0 and a1 are overwritten, so the call is
* `f(global0, global1, arg)` — the argument is shifted two slots. Both globals are read as
* signed **halfwords** (`lh`), which fixes them as 16-bit signed objects, and both loads are
* hoisted above the frame setup.
*
* `0x922(gp)` and `0x924(gp)` are `gp`-relative accesses off gp 0x80121938, i.e. 0x8012225A
* and 0x8012225C, both already `gp`-marked in `config/symbols.tsv` as `D_8012225A` and
* `D_8012225C`, so no request row is needed.
*
* LIMITS: the function name, the callee and the two globals' meanings are hypotheses; only
* the bytes are evidence. The wrapper sets no result, so it is `void`.
*/
extern short D_8012225A;
extern short D_8012225C;
extern void func_80021D90(int a0, int a1, int a2);
void func_80021F24(int a0)
{
func_80021D90(D_8012225A, D_8012225C, a0);
}
+43
View File
@@ -0,0 +1,43 @@
/*
* func_80026F14 — 40 bytes at 0x80026F14..0x80026F3C
*
* Framed routine that makes an **indirect** call through a `gp`-relative function pointer,
* but only when the pointer is non-null.
*
* The observed instructions are:
* lw v0,0x214(gp) 8f820214 v0 = *(int *)(gp + 0x214)
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* beq v0,zero,0x80026f2c 10400003 if (v0 == 0) goto epilogue
* sw ra,0x10(sp) afbf0010 save ra (delay slot)
* jalr v0 0040f809 call v0
* nop 00000000 (delay slot)
* 0x80026f2c:
* lw ra,0x10(sp) 8fbf0010 restore ra
* addiu sp,sp,0x18 27bd0018 frame release
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* `0x214(gp)` is a `gp`-relative access off gp 0x80121938, i.e. 0x80121B4C. It is **not**
* in `config/symbols.tsv`, so this region needs the registry row `D_80121B4C 0x80121B4C gp`
* (staged in `symbols-request.tsv`); without the `gp` marker the harness emits an absolute
* access and the region cannot match.
*
* The pointer is loaded **once** into v0 and used for both the null test and the `jalr`, so
* the source binds it to a local rather than reading the global twice. The guard is checked
* before the frame is established: the `sw ra` that saves the return address is placed in
* the branch's delay slot, so `ra` is only saved on the path that reaches the call.
*
* LIMITS: the function name, the claim that the global holds a function pointer and the
* callee's identity are hypotheses; only the bytes are evidence — the value is data, so
* nothing here proves it is code. The routine sets no result, so it is `void`.
*/
extern int D_80121B4C;
void func_80026F14(void)
{
void (*fn)(void) = (void (*)(void))D_80121B4C;
if (fn != 0)
fn();
}
+51
View File
@@ -0,0 +1,51 @@
/*
* func_8002D5D0 — 56 bytes at 0x8002D5D0..0x8002D608
*
* Leaf routine that appends a value to a bounded array, using a `gp`-relative counter.
*
* The observed instructions are:
* lh a1,0x2b8(gp) 87a102b8 a1 = *(short *)(gp + 0x2b8)
* addiu sp,sp,-0x8 27bdfff8 frame, 8 bytes
* slti v0,a1,0x1e 28a2001e v0 = (a1 < 30) signed
* beq v0,zero,0x8002d5fc 10400007 if (!(a1 < 30)) goto epilogue
* move v1,a1 00a01821 v1 = a1 (delay slot)
* addiu v0,v1,0x1 24620001 v0 = v1 + 1
* sh v0,0x2b8(gp) a7a202b8 *(short *)(gp + 0x2b8) = v0
* sll v0,a1,0x2 00051080 v0 = a1 * 4
* lui at,0x8013 3c018013 \
* addu at,at,v0 00220821 / at = 0x80130000 + a1 * 4
* sw a0,-0x5390(at) ac24ac70 *(int *)(at - 0x5390) = a0
* 0x8002d5fc:
* addiu sp,sp,0x8 27bd0008 frame release
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* `0x2b8(gp)` is a `gp`-relative access off gp 0x80121938, i.e. 0x80121BF0, which the
* registry already carries as `D_80121BF0` with the `gp` marker. It is read as a signed
* **halfword** (`lh`) and incremented as one (`sh`), and the bound test is signed
* (`slti`), so the counter is a 16-bit signed object and 30 is the limit.
*
* The array store is the explicit indexed symbol form: `lui at,%hi` / `addu at,at,index` /
* `sw rt,%lo(at)`. The displacement is **-0x5390 with high half 0x8013**, i.e. the base is
* 0x80130000 - 0x5390 = **0x8012AC70**; reading the pair as 0x8013AC70 would be wrong.
*
* The 8-byte frame is allocated and never written — cc1 reserves it for the local and then
* keeps everything in registers. It is reproduced by declaring the counter as a local
* rather than by any flag.
*
* LIMITS: the function name, the array, the counter's purpose and the bound 30 are
* hypotheses; only the bytes are evidence. The array elements are 32-bit.
*/
extern short D_80121BF0;
extern int D_8012AC70[];
void func_8002D5D0(int a0)
{
short i = D_80121BF0;
if (i < 30) {
D_80121BF0 = i + 1;
D_8012AC70[i] = a0;
}
}
+39
View File
@@ -0,0 +1,39 @@
/*
* func_80036380 — 16 bytes at 0x80036380..0x80036390
*
* An empty function that still allocates and releases a 48-byte stack frame.
*
* The observed instructions are:
* addiu sp,sp,-0x30 27bdffd0 allocate 48 bytes
* addiu sp,sp,0x30 27bd0030 release 48 bytes
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* There is no body, no saved register and **no `sw ra`** — so this is not a call frame;
* `ra` is untouched and the function is a leaf. The frame exists only because a local was
* allocated, and the exact pair `addiu sp,sp,-0x30` / `addiu sp,sp,0x30` with nothing in
* between is what this compiler emits when a local object is *allocated* but its uses are
* all gone: the size survives, the accesses do not.
*
* Three spellings were measured to find what keeps the frame:
* - `volatile int buf[12];` (unused) -> 16 bytes, MATCH
* - `int buf[12]; buf[11] = 0;` (dead store) -> 20 bytes (the store survives)
* - `int buf[12]; (void)buf;` (address taken, no use) -> 16 bytes, MATCH
* The last is the spelling used here, because taking the local's address is the ordinary
* reason a compiler allocates stack it then never touches, and it does not depend on a
* qualifier that would have no other effect.
*
* LIMITS: the function name is a hypothesis and there is no evidence at all about what the
* local was — only that 48 bytes of stack were reserved for something. A body that was
* conditionally compiled out, a removed call, or an unused scratch buffer are all consistent
* with these four instructions; the C below is a reconstruction of the *codegen*, not a
* recovery of the original source. This is the one region in this batch whose C cannot be
* argued from the bytes beyond the frame size.
*/
void func_80036380(void)
{
int buf[12];
(void)buf;
}
+44
View File
@@ -0,0 +1,44 @@
/*
* func_80041A24 — 52 bytes at 0x80041A24..0x80041A58
*
* Framed routine: calls a routine that returns a pointer, and if the pointer is
* non-null initialises two bytes at its start.
*
* The observed instructions are:
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* sw ra,0x10(sp) afbf0010 save ra
* jal 0x800419d0 0c010674 call func_800419D0
* nop 00000000 (delay slot)
* move v1,v0 00401821 v1 = result
* beq v1,zero,0x80041a48 10200004 if (v1 == 0) goto epilogue
* li v0,0xff 240200ff v0 = 0xff (delay slot)
* sb zero,0x0(v1) a0200000 *(char *)v1 = 0
* sb v0,0x1(v1) a0220001 *(char *)(v1 + 1) = 0xff
* 0x80041a48:
* lw ra,0x10(sp) 8fbf0010 restore ra
* addiu sp,sp,0x18 27bd0018 frame release
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* The callee's result is copied to v1 rather than tested in place, and the constant
* 0xff is materialised in the guard's branch delay slot — so the byte value is written
* by the guard, not inside the taken block. Both stores are byte-wide (`sb`), which is
* what fixes the pointed-to type as `char`.
*
* LIMITS: the function name, the callee, the two initialised bytes and their meaning are
* hypotheses; only the bytes are evidence. The routine sets no result of its own, so it
* is `void`; whether the callee's pointer is to a 2-byte object or a longer structure is
* not recoverable from these bytes.
*/
extern char *func_800419D0(void);
void func_80041A24(void)
{
char *v1 = func_800419D0();
if (v1 != 0) {
v1[0] = 0;
v1[1] = 0xff;
}
}
+50
View File
@@ -0,0 +1,50 @@
/*
* func_800453C0 — 56 bytes at 0x800453C0..0x800453F8
*
* Framed routine guarded by a byte flag: when the flag is clear it calls one routine with
* a global pointer and its own argument.
*
* The observed instructions are:
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* lui v0,0x8012 3c028012 \
* lbu v0,0x2374(v0) 90422374 / v0 = *(unsigned char *)0x80122374
* move a1,a0 00802821 a1 = a0
* bne v0,zero,0x800453e8 14400004 if (v0 != 0) goto epilogue
* sw ra,0x10(sp) afbf0010 save ra (delay slot)
* lui a0,0x8013 3c048013 \
* lw a0,-0x2774(a0) 8c84d88c / a0 = *(int *)0x8012D88C
* jal 0x8005a81c 0c016a07 call func_8005A81C
* nop 00000000 (delay slot)
* 0x800453e8:
* lw ra,0x10(sp) 8fbf0010 restore ra
* addiu sp,sp,0x18 27bd0018 frame release
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* Two things are scheduled unusually and both follow from the C. The flag load is hoisted
* **above** the frame setup, and the `sw ra` that establishes the frame is placed in the
* guard's branch delay slot — so `ra` is only saved on the path that reaches the call, and
* the epilogue's `lw ra` is likewise only correct on that path. The incoming argument is
* moved to a1 before a0 is overwritten with the global, so the call is
* `f(global, arg)` with the two operands in the opposite order from the parameters.
*
* Both globals use the same-register `lui`+load form (cookbook finding 2), so they are
* written as named symbols. The address arithmetic is exact and carries: `lui 0x8013` with
* the signed displacement -0x2774 gives **0x8012D88C**.
*
* LIMITS: the function name, the flag's meaning, the global's type and the callee are
* hypotheses; only the bytes are evidence. The flag is a byte (`lbu`) and the global is read
* as a 32-bit pointer. The routine sets no result, so it is `void`.
*/
extern unsigned char D_80122374;
extern int D_8012D88C;
extern void func_8005A81C(int a0, int a1);
void func_800453C0(int a0)
{
unsigned char v0 = D_80122374;
if (v0 == 0)
func_8005A81C(D_8012D88C, a0);
}
+29
View File
@@ -0,0 +1,29 @@
/*
* func_80068D54 — 36 bytes at 0x80068D54..0x80068D78
*
* Framed wrapper: calls one routine with a zero second argument and a literal third.
*
* The observed instructions are:
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* sw ra,0x10(sp) afbf0010 save ra
* addu a1,zero,zero 00002821 a1 = 0
* jal 0x800f6f00 0c03dbc0 call func_800F6F00
* li a2,0x190 24060190 a2 = 0x190 (delay slot)
* lw ra,0x10(sp) 8fbf0010 restore ra
* addiu sp,sp,0x18 27bd0018 frame release
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* a0 is forwarded untouched; the zero uses `addu rd,zero,zero` and the literal uses `li`.
* The zero is issued before the `jal` while the literal is scheduled into its delay slot.
*
* LIMITS: the function name, the callee and the meaning of 0x190 are hypotheses; only the
* bytes are evidence. The wrapper sets no result, so it is `void`.
*/
extern void func_800F6F00(int a0, int a1, int a2);
void func_80068D54(int a0)
{
func_800F6F00(a0, 0, 0x190);
}
+38
View File
@@ -0,0 +1,38 @@
/*
* func_80068F6C — 44 bytes at 0x80068F6C..0x80068F98
*
* Framed wrapper: calls one routine with four zero arguments and masks the result to a byte.
*
* The observed instructions are:
* addiu sp,sp,-0x20 27bdffe0 frame, 32 bytes
* addu a1,zero,zero 00002821 a1 = 0
* addu a2,zero,zero 00003021 a2 = 0
* addu a3,zero,zero 00003821 a3 = 0
* sw ra,0x18(sp) afbf0018 save ra
* jal 0x80068d78 0c01a35e call func_80068D78
* sw zero,0x10(sp) afa00010 arg5 = 0 (delay slot)
* lw ra,0x18(sp) 8fbf0018 restore ra
* andi v0,v0,0xff 304200ff v0 &= 0xff
* jr ra 03e00008
* addiu sp,sp,0x20 27bd0020 frame release (delay slot)
*
* The frame is 32 bytes: 16 for the four register arguments' home slots, 8 for the fifth
* argument at 0x10, and 8 for the saved `ra` at 0x18 — so the call takes **five** arguments,
* with a0 forwarded and the rest zero. All four register zeroes use `addu rd,zero,zero` (this
* compiler's zeroing form, not `li rd,0`), and the stack argument is scheduled into the `jal`
* delay slot.
*
* The `andi ...,0xff` after the call is the mask this ABI applies when an `unsigned char`
* return value is widened to `int`, so the callee returns `unsigned char` and the wrapper
* widens it. That is why the return type below is `int` with a `unsigned char` callee.
*
* LIMITS: the function name, the callee and the meaning of the zeros are hypotheses; only
* the bytes are evidence.
*/
extern unsigned char func_80068D78(int a0, int a1, int a2, int a3, int a4);
int func_80068F6C(int a0)
{
return func_80068D78(a0, 0, 0, 0, 0);
}
+42
View File
@@ -0,0 +1,42 @@
/*
* func_80072BA8 — 52 bytes at 0x80072BA8..0x80072BDC
*
* Framed routine: calls the routine that immediately follows it with seven arguments,
* three of them constants passed on the stack.
*
* The observed instructions are:
* addiu sp,sp,-0x28 27bdffd8 frame, 40 bytes
* li v0,0x8 24020008 v0 = 8
* move a2,a3 00e03021 a2 = a3
* li a3,-0x1 2407ffff a3 = -1
* sw ra,0x20(sp) afbf0020 save ra
* sw zero,0x10(sp) afa00010 arg5 = 0
* sw zero,0x14(sp) afa00014 arg6 = 0
* jal 0x80072bdc 0c01caf7 call func_80072BDC
* sw v0,0x18(sp) afa20018 arg7 = 8 (delay slot)
* lw ra,0x20(sp) 8fbf0020 restore ra
* addiu sp,sp,0x28 27bd0028 frame release
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* The callee is the **next address** (0x80072BDC = 0x80072BA8 + 0x34), so this is a
* forwarder into the following body rather than a call into unrelated code.
*
* The frame is 40 bytes: 16 for the four register arguments' home slots, 16 for the
* three extra arguments at 0x10/0x14/0x18, and 8 for the saved `ra` at 0x20. The extra
* arguments are stored at 16(sp), 20(sp) and 24(sp), which is the o32 convention for
* arguments five, six and seven — so the call takes seven arguments, with a0 and a1
* forwarded untouched, a3 moved into a2's slot, and -1 in a3.
*
* LIMITS: the function name, the callee and the meanings of the constants (8, 0, 0) are
* hypotheses; only the bytes are evidence. The stack argument slots are written as a
* seven-parameter call; whether the original declared them individually or as a struct
* is not recoverable from these instructions.
*/
extern void func_80072BDC(int a0, int a1, int a2, int a3, int a4, int a5, int a6);
void func_80072BA8(int a0, int a1, int a2, int a3)
{
func_80072BDC(a0, a1, a3, -1, 0, 0, 8);
}
+36
View File
@@ -0,0 +1,36 @@
/*
* func_800900A0 — 44 bytes at 0x800900A0..0x800900CC
*
* Framed wrapper: calls one routine with a fixed address, its own argument, and a zero.
*
* The observed instructions are:
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* sw ra,0x10(sp) afbf0010 save ra
* move a1,a0 00802821 a1 = a0
* lui a0,0x8009 3c048009 \
* addiu a0,a0,0x58 24840058 / a0 = 0x80090058 (D_80090058)
* jal 0x800fb6c4 0c03edb1 call func_800FB6C4
* addu a2,zero,zero 00003021 a2 = 0 (delay slot)
* lw ra,0x10(sp) 8fbf0010 restore ra
* addiu sp,sp,0x18 27bd0018 frame release
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* The argument is moved to a1 before a0 is overwritten, so the call is `f(address, arg, 0)`
* with the address in the first slot. The address is materialised as `lui`+`addiu` (the
* linker-resolved symbol form, cookbook finding 4), so it is written as a named symbol and
* not a literal (finding 5's `lui`+`ori`).
*
* LIMITS: the function name, the callee, the pointed-to object and the meaning of the zero
* argument are hypotheses; only the bytes are evidence. Whether the address names data or code
* is not recoverable; it is written as a `char[]` so it stays an address. The wrapper sets no
* result, so it is `void`.
*/
extern char D_80090058[];
extern void func_800FB6C4(char *a0, int a1, int a2);
void func_800900A0(int a0)
{
func_800FB6C4(D_80090058, a0, 0);
}
+30
View File
@@ -0,0 +1,30 @@
/*
* func_80090C8C — 32 bytes at 0x80090C8C..0x80090CAC
*
* Framed wrapper: calls one routine with its argument masked to eight bits.
*
* The observed instructions are:
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* sw ra,0x10(sp) afbf0010 save ra
* jal 0x800fe844 0c03fa11 call func_800FE844
* andi a0,a0,0xff 308400ff a0 &= 0xff (delay slot)
* lw ra,0x10(sp) 8fbf0010 restore ra
* addiu sp,sp,0x18 27bd0018 frame release
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* The `andi ...,0xff` is the promotion mask this ABI applies to an unsigned `char`
* parameter (cookbook finding 7), so the parameter is declared `unsigned char` and the
* mask is not written in the C. cc1 schedules it into the `jal` delay slot, so the
* callee receives the masked value.
*
* LIMITS: the function name and the callee's purpose are hypotheses; only the bytes are
* evidence. The wrapper sets no result, so it is `void`.
*/
extern void func_800FE844(unsigned char a0);
void func_80090C8C(unsigned char a0)
{
func_800FE844(a0);
}
+29
View File
@@ -0,0 +1,29 @@
/*
* func_8009AC08 — 32 bytes at 0x8009AC08..0x8009AC28
*
* Framed wrapper: calls one routine with a zero second argument.
*
* The observed instructions are:
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* sw ra,0x10(sp) afbf0010 save ra
* jal 0x8009a904 0c026a41 call func_8009A904
* addu a1,zero,zero 00002821 a1 = 0 (delay slot)
* lw ra,0x10(sp) 8fbf0010 restore ra
* addiu sp,sp,0x18 27bd0018 frame release
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* a0 is never touched, so the wrapper forwards its first argument; the zero uses
* `addu rd,zero,zero` (this compiler's zeroing form, not `li rd,0`) and is scheduled
* into the `jal` delay slot.
*
* LIMITS: the function name, the callee and the meaning of the zero argument are
* hypotheses; only the bytes are evidence. The wrapper sets no result, so it is `void`.
*/
extern void func_8009A904(int a0, int a1);
void func_8009AC08(int a0)
{
func_8009A904(a0, 0);
}
+63
View File
@@ -0,0 +1,63 @@
/*
* func_800AC818 — 68 bytes at 0x800AC818..0x800AC85C
*
* Selects one of three globals from two byte-wide flags and stores it through the
* output pointer, returning it as well. All three arms converge on ONE epilogue,
* so the source keeps a single `value` local.
*
* The observed instructions are:
* andi a2,a2,0xff ; a2 &= 0xff
* beqz a2,0x800AC84C ; if (a2 == 0) use the third global
* andi v0,a3,0xff ; a3 &= 0xff (delay slot)
* beqz v0,0x800AC83C ; if (a3 == 0) use the second global
* nop
* lui v0,0x8014
* lw v0,-15556(v0) ; value = D_8013C33C
* j 0x800AC854
* nop
* 3C: lui v0,0x8014
* lw v0,-15552(v0) ; value = D_8013C340
* j 0x800AC854
* nop
* 4C: lui v0,0x8014
* lw v0,-15548(v0) ; value = D_8013C344
* 54: jr ra
* sw v0,0(a0) ; *out = value (delay slot)
*
* The return value is the LOADED global, not the output pointer: `v0` holds the
* global at the `jr ra` and the store in the delay slot consumes it. The first
* parameter is a pointer while the other three are used only as masked bytes.
*
* The blocks are MIRRORED (cookbook finding 28): the `first_flag == 0` arm is
* emitted LAST and reached by a forward branch, so the natural `== 0` chain
* spelling emits an inverted `bne` and the wrong block order. The nested
* `!= 0` guards below reproduce the original's order. The symbol addresses are
* 0x8013C33C / 0x8013C340 / 0x8013C344 — reachable as `lui 0x8014` plus the
* sign-extended displacements -15556 / -15552 / -15548.
*
* LIMITS: the three symbol addresses (0x8013C34C/350/354), the byte masks and the
* parameter types are hypotheses read from the instruction shape; the second
* parameter is not referenced by any instruction in the body and its purpose is
* NOT guessed. What the globals mean is unknown. Only the compiled bytes are
* evidence.
*/
extern int D_8013C33C;
extern int D_8013C340;
extern int D_8013C344;
int func_800AC818(int *out, int unused, int first_flag, int second_flag) {
int value;
if ((first_flag & 0xFF) != 0) {
if ((second_flag & 0xFF) != 0)
value = D_8013C33C;
else
value = D_8013C340;
} else {
value = D_8013C344;
}
*out = value;
return value;
}
+33
View File
@@ -0,0 +1,33 @@
/*
* func_800AC85C — 40 bytes at 0x800AC85C..0x800AC884
*
* Framed wrapper: calls one routine with a global loaded from a fixed address.
*
* The observed instructions are:
* lui a0,0x8014 3c048014 \
* lw a0,-0x3cb8(a0) 8c84c348 / a0 = *(int *)0x8013C348
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* sw ra,0x10(sp) afbf0010 save ra
* jal 0x8002e870 0c00ba1c call func_8002E870
* nop 00000000 (delay slot)
* lw ra,0x10(sp) 8fbf0010 restore ra
* addiu sp,sp,0x18 27bd0018 frame release
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* The global load is hoisted **above** the frame setup, and the address arithmetic carries:
* `lui 0x8014` with the signed displacement -0x3cb8 gives **0x8013C348**, not 0x8014C348 —
* the same trap as func_800F4B54 and func_800F7990.
*
* LIMITS: the function name, the callee and the global's type are hypotheses; only the bytes
* are evidence. The global is read as a 32-bit value and passed as the only argument. The
* wrapper sets no result, so it is `void`.
*/
extern int D_8013C348;
extern void func_8002E870(int a0);
void func_800AC85C(void)
{
func_8002E870(D_8013C348);
}
+31
View File
@@ -0,0 +1,31 @@
/*
* func_800BFEC0 — 32 bytes at 0x800BFEC0..0x800BFEE0
*
* Framed wrapper: calls one routine, deriving the third argument from the second.
*
* The observed instructions are:
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* sw ra,0x10(sp) afbf0010 save ra
* jal 0x800bf734 0c02fdcd call func_800BF734
* addiu a2,a1,0x378 24a60378 a2 = a1 + 0x378 (delay slot)
* lw ra,0x10(sp) 8fbf0010 restore ra
* addiu sp,sp,0x18 27bd0018 frame release
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* The third callee is func_800BF734, reached here with offset 0x378 — the same callee
* and the same shape as func_800BFE80 (0x128) and func_800BFEE0 (0x4a0), so the three
* are one callee reached through three different record offsets. The offset is again
* computed in the `jal` delay slot.
*
* LIMITS: the function name, the callee and the meaning of the 0x378 offset are
* hypotheses; only the bytes are evidence. The wrapper is `void` because no return value
* is set.
*/
extern void func_800BF734(int a0, int a1, int a2);
void func_800BFEC0(int a0, int a1)
{
func_800BF734(a0, a1, a1 + 0x378);
}
+35
View File
@@ -0,0 +1,35 @@
/*
* func_800F3140 — 32 bytes at 0x800F3140..0x800F3160
*
* Framed wrapper: calls the GTE control-register write that this project has already
* matched as func_80102FD4.
*
* The observed instructions are:
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* sw ra,0x10(sp) afbf0010 save ra
* jal 0x80102fd4 0c040bf5 call func_80102FD4
* nop 00000000 (delay slot)
* lw ra,0x10(sp) 8fbf0010 restore ra
* addiu sp,sp,0x18 27bd0018 frame release
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* Nothing is set up before the `jal`, so the argument is **forwarded**: the callee is
* `void func_80102FD4(int h)` (registered at 0x80102FD4..0x80102FE0, a single `ctc2` to
* the GTE H register), and a forwarding wrapper emits no instructions for a0. Writing the
* wrapper with no parameter and a literal argument would instead emit a `clear a0`, so the
* absence of any setup is the evidence for the parameter.
*
* This region also sits immediately before the registered 0x800F3160..0x800F316C, which
* needs `maspsx=off` — the two are adjacent, not overlapping.
*
* LIMITS: the function name and the claim that the value is a projection-plane distance
* are hypotheses; only the bytes are evidence. The wrapper sets no result, so it is `void`.
*/
extern void func_80102FD4(int h);
void func_800F3140(int h)
{
func_80102FD4(h);
}
+30
View File
@@ -0,0 +1,30 @@
/*
* func_800F3A00 — 36 bytes at 0x800F3A00..0x800F3A24
*
* Framed wrapper: loads a field from its argument and passes it on.
*
* The observed instructions are:
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* sw ra,0x10(sp) afbf0010 save ra
* lw a0,0x10(a0) 8c840010 a0 = *(int *)(a0 + 0x10)
* jal 0x800f452c 0c03d14b call func_800F452C
* nop 00000000 (delay slot)
* lw ra,0x10(sp) 8fbf0010 restore ra
* addiu sp,sp,0x18 27bd0018 frame release
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* The field load targets **a0 itself**, overwriting the incoming pointer, so the wrapper
* dereferences its argument once and forwards the loaded word. Nothing else is set up, so
* the callee takes exactly one argument.
*
* LIMITS: the function name, the callee and the field at +0x10 are hypotheses; only the
* bytes are evidence. The load is 32-bit. The wrapper sets no result, so it is `void`.
*/
extern void func_800F452C(int a0);
void func_800F3A00(int a0)
{
func_800F452C(*(int *)(a0 + 0x10));
}
+29
View File
@@ -0,0 +1,29 @@
/*
* func_800F8B18 — 32 bytes at 0x800F8B18..0x800F8B38
*
* Framed wrapper: calls one routine and returns its result untouched.
*
* The observed instructions are:
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* sw ra,0x10(sp) afbf0010 save ra
* jal 0x80106154 0c041855 call func_80106154
* nop 00000000 (delay slot)
* lw ra,0x10(sp) 8fbf0010 restore ra
* addiu sp,sp,0x18 27bd0018 frame release
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* Nothing touches v0 after the call, so the callee's result is the return value. Same
* shape as func_800F8638 and func_800F8B38 with a different callee.
*
* LIMITS: the function name and the callee's purpose are hypotheses; only the bytes are
* evidence. A parameter-forwarding wrapper would produce the same instructions, so the
* parameter list is not recoverable.
*/
extern int func_80106154(void);
int func_800F8B18(void)
{
return func_80106154();
}
+30
View File
@@ -0,0 +1,30 @@
/*
* func_800F8F5C — 32 bytes at 0x800F8F5C..0x800F8F7C
*
* Framed wrapper: calls one routine and reports whether the result is zero.
*
* The observed instructions are:
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* sw ra,0x10(sp) afbf0010 save ra
* jal 0x8010703c 0c041c0f call func_8010703C
* nop 00000000 (delay slot)
* lw ra,0x10(sp) 8fbf0010 restore ra
* sltiu v0,v0,0x1 2c420001 v0 = (v0 < 1) unsigned
* jr ra 03e00008
* addiu sp,sp,0x18 27bd0018 frame release (delay slot)
*
* The `sltiu` against 1 is this compiler's idiom for `x == 0`; writing the comparison as
* `== 0` (rather than `< 1`) keeps the immediate form. Same shape as func_800F8F7C with a
* different callee.
*
* LIMITS: the function name and the callee's purpose are hypotheses; only the bytes are
* evidence. The callee's return type is written `int`; an `unsigned` or pointer result
* would produce the same unsigned comparison, so the type is not pinned.
*/
extern int func_8010703C(void);
int func_800F8F5C(void)
{
return func_8010703C() == 0;
}
+31
View File
@@ -0,0 +1,31 @@
/*
* func_800FA960 — 32 bytes at 0x800FA960..0x800FA980
*
* Framed wrapper: calls one routine with a literal third argument and reports whether the
* result is zero.
*
* The observed instructions are:
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* sw ra,0x10(sp) afbf0010 save ra
* jal 0x80102e10 0c040b84 call func_80102E10
* li a2,0xc 2406000c a2 = 0xc (delay slot)
* lw ra,0x10(sp) 8fbf0010 restore ra
* sltiu v0,v0,0x1 2c420001 v0 = (v0 < 1) unsigned
* jr ra 03e00008
* addiu sp,sp,0x18 27bd0018 frame release (delay slot)
*
* a0 and a1 are never touched, so the first two arguments are forwarded; the third is the
* literal 0xc, materialised in the `jal` delay slot. The `sltiu` against 1 is this
* compiler's idiom for `x == 0`, so the wrapper returns a boolean.
*
* LIMITS: the function name, the callee and the meaning of 0xc are hypotheses; only the
* bytes are evidence. The callee's return type is written `int`; the unsigned comparison
* does not pin it.
*/
extern int func_80102E10(int a0, int a1, int a2);
int func_800FA960(int a0, int a1)
{
return func_80102E10(a0, a1, 0xc) == 0;
}
+50
View File
@@ -0,0 +1,50 @@
/*
* func_8010A748 — 68 bytes at 0x8010A748..0x8010A78C
*
* Writes a 16-bit value into a table indexed by the first argument, shifting the
* value down by a runtime amount from a global when a flag is set. The index
* scaling is computed once and lands in the first branch's delay slot.
*
* The observed instructions are:
* bnez a2,0x8010A768 ; if (flag != 0) take the shifted arm
* sll v0,a0,0x1 ; offset = index * 2 (delay slot)
* lui v1,0x8012
* lw v1,4168(v1) ; v1 = D_80121048 (table base)
* nop
* addu v0,v0,v1 ; table + offset (offset first)
* j 0x8010A784
* sh a1,0(v0) ; *(short *)... = value (delay slot)
* 68: lui a0,0x8012 ; RELOAD the table base
* lw a0,4168(a0)
* lui v1,0x8012
* lw v1,0x1070(v1) ; v1 = D_80121070 (shift amount)
* addu v0,v0,a0 ; table + offset (offset first)
* srlv v1,a1,v1 ; value >> shift <- UNSIGNED, variable
* sh v1,0(v0)
* 84: jr ra
* nop
*
* The table base is loaded in EACH arm, so the source references the global in
* both — caching it in one local would emit a single load and change the bytes.
* The index scaling must ALSO stay inline: binding `index * 2` to a local makes
* cc1 compute it into the argument register before the branch and then copy it,
* which costs an extra instruction (72 vs 68). Written inline, cc1 CSEs it into
* the branch delay slot exactly as the original does. The shift is `srlv`, so the
* shifted operand is unsigned.
*
* LIMITS: the two symbol names, the element stride (2), the field width and the
* parameter types are hypotheses read from the instruction shape; the offsets
* 4168/0x1070 are facts about this executable's globals. What the table holds is
* unknown and is not guessed here. Only the compiled bytes are evidence.
*/
extern int D_80121048;
extern int D_80121070;
void func_8010A748(int index, int value, int flag) {
if (flag == 0)
*(short *)((char *)D_80121048 + index * 2) = value;
else
*(short *)((char *)D_80121048 + index * 2) =
(short)((unsigned int)value >> D_80121070);
}