phase9: merge worker A handoff — 306 regions / 297 distinct bodies

Worker A rotated out at 108 claims (+79 from its session) with a clean
handoff; final 4 rows verified MATCH by the coordinator. 18 new regions
gated MATCH whole-binary (c_regions=306, SHA-1
e173426c157384ebf1b6caf8c6fea18a85a14af9), make check green.

P9-T4 checkpoint (>=290 total) CROSSED at 297 bodies.
All three workers have now rotated out; the coordinator absorbs the
remaining partitions. Negatives census now 34 report rows from A alone
(25 match-class + 9 bad-extent triage), with the bad-extent class verified
independently by the coordinator (disjoint from 288 registered regions;
7 worklist rows flagged incl. the runaway 0x801800C4/0x80180808 that were
never excluded because they are fallthrough-graded).

Handoff notes recorded: two high-value unattempted GTE rows (0x80101C2C,
0x80102FA4 — need raw lwc2/swc2 asm with memory operands), 8 lever findings
for the next charter (srl=sra-unsigned, slti-sltiu 16-bit tell, single-exit
result shape, struct-assignment for a0/a1 loads, maspsx=off scope x2, side-
effect statement order, lui/lw address arithmetic, CSE-preventing symbols).
The bad-extent detector is implemented in sf3_triage with synthetic tests;
the regenerated worklist will exclude the class.
This commit is contained in:
Christopher Williams
2026-09-24 00:42:33 -04:00
parent 80a4e9e5ed
commit a106afe5ab
20 changed files with 956 additions and 37 deletions
+18
View File
@@ -13,6 +13,7 @@
0x80012780 0x8001278C src/func_80012780.c
0x800127F0 0x8001281C src/func_800127F0.c
0x8001281C 0x80012834 src/func_8001281C.c
0x8001289C 0x800128E4 src/func_8001289C.c
0x800128E4 0x80012918 src/func_800128E4.c
0x80012D8C 0x80012DBC src/func_80012D8C.c
0x80012DBC 0x80012DE8 src/func_80012DBC.c
@@ -24,6 +25,7 @@
0x80016174 0x80016198 src/func_80016174.c
0x80016198 0x800161E0 src/func_80016198.c
0x80016E50 0x80016E68 src/func_80016E50.c
0x800170C0 0x80017108 src/func_800170C0.c
0x800171D8 0x80017200 src/func_800171D8.c
0x8001761C 0x80017660 src/func_8001761C.c
0x800179B8 0x800179CC src/func_800179B8.c
@@ -35,6 +37,7 @@
0x80017C60 0x80017C6C src/func_80017C60.c
0x80017D1C 0x80017D48 src/func_80017D1C.c
0x80017D48 0x80017D88 src/func_80017D48.c
0x80017D88 0x80017DD0 src/func_80017D88.c
0x80017DD0 0x80017DF0 src/func_80017DD0.c
0x800182D4 0x800182F4 src/func_800182D4.c
0x800183B8 0x800183EC src/func_800183B8.c
@@ -66,6 +69,7 @@
0x800290D0 0x800290F0 src/func_800290D0.c
0x800290F0 0x80029118 src/func_800290F0.c
0x8002A9D4 0x8002AA18 src/func_8002A9D4.c
0x8002AC84 0x8002ACBC src/func_8002AC84.c
0x8002C6EC 0x8002C728 src/func_8002C6EC.c
0x8002C7BC 0x8002C7EC src/func_8002C7BC.c
0x8002C888 0x8002C894 src/func_8002C888.c
@@ -76,17 +80,22 @@
0x8002D2BC 0x8002D2D4 src/func_8002D2BC.c
0x8002D5D0 0x8002D608 src/func_8002D5D0.c
0x8002DEB4 0x8002DF1C src/func_8002DEB4.c
0x8002E028 0x8002E070 src/func_8002E028.c
0x8002E7C4 0x8002E7E4 src/func_8002E7C4.c
0x8002F160 0x8002F1A4 src/func_8002F160.c
0x8002F1A4 0x8002F1D8 src/func_8002F1A4.c
0x8002F2F8 0x8002F300 src/func_8002F2F8.c
0x8002F404 0x8002F450 src/func_8002F404.c
0x800301FC 0x8003022C src/func_800301FC.c
0x80030358 0x80030390 src/func_80030358.c
0x80031F78 0x80031FC4 src/func_80031F78.c
0x800321EC 0x800321F8 src/func_800321EC.c
0x80036308 0x80036328 src/func_80036308.c
0x8003636C 0x80036378 src/func_8003636C.c
0x80036378 0x80036380 src/func_80036378.c
0x80036380 0x80036390 src/func_80036380.c
0x80036A0C 0x80036A54 src/func_80036A0C.c
0x80036A9C 0x80036AD8 src/func_80036A9C.c
0x80036AD8 0x80036B14 src/func_80036AD8.c
0x8003768C 0x800376CC src/func_8003768C.c
0x80038788 0x80038790 src/func_80038788.c
@@ -95,6 +104,7 @@
0x8003B320 0x8003B34C src/func_8003B320.c
0x80041A24 0x80041A58 src/func_80041A24.c
0x80042088 0x80042090 src/func_80042088.c
0x80042964 0x800429B0 src/func_80042964.c
0x80042D64 0x80042D88 src/func_80042D64.c
0x80043D8C 0x80043DC4 src/func_80043D8C.c
0x80044F58 0x80044FA4 src/func_80044F58.c gp=-D_80121BFC
@@ -105,6 +115,7 @@
0x8004C0AC 0x8004C0F0 src/func_8004C0AC.c
0x8004C0F0 0x8004C110 src/func_8004C0F0.c
0x8004CEEC 0x8004CF0C src/func_8004CEEC.c
0x8004E3FC 0x8004E440 src/func_8004E3FC.c
0x800516E0 0x800516FC src/func_800516E0.c
0x8005182C 0x80051864 src/func_8005182C.c
0x80052C98 0x80052CAC src/func_80052C98.c
@@ -114,6 +125,7 @@
0x8005E3D0 0x8005E3F4 src/func_8005E3D0.c
0x8005ED6C 0x8005EDBC src/func_8005ED6C.c
0x80065B6C 0x80065B8C src/func_80065B6C.c
0x800668A8 0x800668F0 src/func_800668A8.c
0x800681A4 0x800681E0 src/func_800681A4.c
0x800681E0 0x8006821C src/func_800681E0.c
0x800683B0 0x800683E4 src/func_800683B0.c
@@ -123,6 +135,7 @@
0x80068F6C 0x80068F98 src/func_80068F6C.c
0x80068F98 0x80068FA8 src/func_80068F98.c
0x800697A4 0x800697C4 src/func_800697A4.c
0x8006B778 0x8006B7C0 src/func_8006B778.c
0x8006BC08 0x8006BC34 src/func_8006BC08.c
0x8006EC94 0x8006ECD4 src/func_8006EC94.c
0x8006F6BC 0x8006F6F4 src/func_8006F6BC.c
@@ -137,10 +150,12 @@
0x8007C4EC 0x8007C524 src/func_8007C4EC.c
0x8007DC40 0x8007DC4C src/func_8007DC40.c
0x8007DF00 0x8007DF34 src/func_8007DF00.c
0x8007E8B8 0x8007E904 src/func_8007E8B8.c
0x8007ED4C 0x8007ED8C src/func_8007ED4C.c
0x800827A8 0x800827C4 src/func_800827A8.c
0x80082914 0x80082944 src/func_80082914.c
0x80083440 0x80083470 src/func_80083440.c
0x80083470 0x800834B8 src/func_80083470.c
0x80083504 0x8008352C src/func_80083504.c
0x8008352C 0x8008355C src/func_8008352C.c
0x80085B80 0x80085B90 src/func_80085B80.c
@@ -161,6 +176,7 @@
0x80090A44 0x80090A70 src/func_80090A44.c
0x80090B64 0x80090B7C src/func_80090B64.c
0x80090C8C 0x80090CAC src/func_80090C8C.c
0x8009107C 0x800910B0 src/func_8009107C.c
0x800912D4 0x800912FC src/func_800912D4.c
0x800912FC 0x8009132C src/func_800912FC.c
0x80092068 0x80092088 src/func_80092068.c
@@ -183,6 +199,7 @@
0x800A74BC 0x800A74D0 src/func_800A74BC.c
0x800A8B48 0x800A8B8C src/func_800A8B48.c
0x800A9D58 0x800A9D90 src/func_800A9D58.c
0x800A9FD4 0x800AA01C src/func_800A9FD4.c
0x800AA56C 0x800AA59C src/func_800AA56C.c
0x800AC818 0x800AC85C src/func_800AC818.c
0x800AC85C 0x800AC884 src/func_800AC85C.c
@@ -215,6 +232,7 @@
0x800F75D0 0x800F760C src/func_800F75D0.c
0x800F7990 0x800F79C0 src/func_800F7990.c
0x800F79F0 0x800F7A20 src/func_800F79F0.c
0x800F7A20 0x800F7A54 src/func_800F7A20.c
0x800F7A54 0x800F7A84 src/func_800F7A54.c
0x800F7A84 0x800F7A94 src/func_800F7A84.c
0x800F7A94 0x800F7AAC src/func_800F7A94.c
1 # Code-region registry: one C region per matched function.
13 0x80012780
14 0x800127F0
15 0x8001281C
16 0x8001289C
17 0x800128E4
18 0x80012D8C
19 0x80012DBC
25 0x80016174
26 0x80016198
27 0x80016E50
28 0x800170C0
29 0x800171D8
30 0x8001761C
31 0x800179B8
37 0x80017C60
38 0x80017D1C
39 0x80017D48
40 0x80017D88
41 0x80017DD0
42 0x800182D4
43 0x800183B8
69 0x800290D0
70 0x800290F0
71 0x8002A9D4
72 0x8002AC84
73 0x8002C6EC
74 0x8002C7BC
75 0x8002C888
80 0x8002D2BC
81 0x8002D5D0
82 0x8002DEB4
83 0x8002E028
84 0x8002E7C4
85 0x8002F160
86 0x8002F1A4
87 0x8002F2F8
88 0x8002F404
89 0x800301FC
90 0x80030358
91 0x80031F78
92 0x800321EC
93 0x80036308
94 0x8003636C
95 0x80036378
96 0x80036380
97 0x80036A0C
98 0x80036A9C
99 0x80036AD8
100 0x8003768C
101 0x80038788
104 0x8003B320
105 0x80041A24
106 0x80042088
107 0x80042964
108 0x80042D64
109 0x80043D8C
110 0x80044F58
115 0x8004C0AC
116 0x8004C0F0
117 0x8004CEEC
118 0x8004E3FC
119 0x800516E0
120 0x8005182C
121 0x80052C98
125 0x8005E3D0
126 0x8005ED6C
127 0x80065B6C
128 0x800668A8
129 0x800681A4
130 0x800681E0
131 0x800683B0
135 0x80068F6C
136 0x80068F98
137 0x800697A4
138 0x8006B778
139 0x8006BC08
140 0x8006EC94
141 0x8006F6BC
150 0x8007C4EC
151 0x8007DC40
152 0x8007DF00
153 0x8007E8B8
154 0x8007ED4C
155 0x800827A8
156 0x80082914
157 0x80083440
158 0x80083470
159 0x80083504
160 0x8008352C
161 0x80085B80
176 0x80090A44
177 0x80090B64
178 0x80090C8C
179 0x8009107C
180 0x800912D4
181 0x800912FC
182 0x80092068
199 0x800A74BC
200 0x800A8B48
201 0x800A9D58
202 0x800A9FD4
203 0x800AA56C
204 0x800AC818
205 0x800AC85C
232 0x800F75D0
233 0x800F7990
234 0x800F79F0
235 0x800F7A20
236 0x800F7A54
237 0x800F7A84
238 0x800F7A94
+51
View File
@@ -0,0 +1,51 @@
/*
* func_8001289C — 72 bytes at 0x8001289C..0x800128E4
*
* Framed routine: when its second argument is a non-null pointer to a non-zero word, calls a
* routine with a field of its first argument and that word, then clears the word.
*
* The observed instructions are:
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* sw s0,0x10(sp) afb00010 save s0
* move s0,a1 00a08021 s0 = a1
* beq s0,zero,0x800128cc 12000007 if (a1 == 0) goto the shared zero return
* sw ra,0x14(sp) afbf0014 save ra (delay slot)
* lw a1,0x0(s0) 8e050000 a1 = *a1
* nop 00000000 load-delay slot
* beq a1,zero,0x800128d0 10a00002 if (a1 == 0) goto epilogue
* addu v0,zero,zero 00001021 v0 = 0 (delay slot)
* jal 0x80026560 0c009958 call func_80026560
* addiu a0,a0,0x94 24840094 a0 = a0 + 0x94 (delay slot)
* sw zero,0x0(s0) ae000000 *s0 = 0
* 0x800128cc:
* addu v0,zero,zero 00001021 v0 = 0
* 0x800128d0:
* 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 second argument is parked in **s0** because it is dereferenced before the call and written
* after it. The first argument is advanced by 0x94 in the `jal` delay slot, and the loaded word
* is passed as the call's second argument. The result is a constant 0 set on both exits, and the
* `beq a1,zero` exit materialises it in its own branch delay slot.
*
* LIMITS: the function name, the callee, the offset 0x94 and the claim that the second argument
* is a pointer to a single word are hypotheses; only the bytes are evidence. All accesses are
* 32-bit. Whether the pointer and the word are a handle or a slot is not recoverable.
*/
extern void func_80026560(int a0, int a1);
int func_8001289C(int a0, int *a1)
{
int v0 = 0;
if (a1 != 0 && *a1 != 0) {
func_80026560(a0 + 0x94, *a1);
*a1 = 0;
}
return v0;
}
+47
View File
@@ -0,0 +1,47 @@
/*
* func_800170C0 — 72 bytes at 0x800170C0..0x80017108
*
* Framed routine: forwards all three arguments to a routine, then stores a 24-bit-masked copy of
* the third into a field reached through the first.
*
* The observed instructions are:
* addiu sp,sp,-0x20 27bdffe0 frame, 32 bytes
* sw s1,0x14(sp) afb10014 save s1
* move s1,a0 00808821 s1 = a0
* sw s0,0x10(sp) afb00010 save s0
* sw ra,0x18(sp) afbf0018 save ra
* jal 0x8001703c 0c005c0f call func_8001703C
* move s0,a2 00c08021 s0 = a2 (delay slot)
* lui v1,0xff 3c0300ff \
* ori v1,v1,0xffff 3463ffff / v1 = 0x00ffffff
* lw v0,0x10(s1) 8e220010 v0 = *(int *)(a0 + 0x10)
* and s0,s0,v1 02038024 s0 &= 0xffffff
* sw s0,0x1c(v0) ac50001c *(int *)(v0 + 0x1c) = s0
* lw ra,0x18(sp) 8fbf0018 restore ra
* lw s1,0x14(sp) 8e310014 restore s1
* lw s0,0x10(sp) 8e100010 restore s0
* addiu sp,sp,0x20 27bd0020 frame release
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* Both the first and third arguments are parked in callee-saved registers (s1, s0) **before** the
* call because both are needed after it: a0 is the base of the pointer chain and a2 is the stored
* value. The second argument is not touched at all, so the call is `f(a0, a1, a2)` with all three
* forwarded.
*
* The mask 0x00ffffff is built as `lui 0xff` + `ori 0xffff`, so the stored value is a 24-bit
* truncation — this is the `lui`+`ori` **literal** form (cookbook finding 5), not a symbol.
*
* LIMITS: the function name, the callee, the pointer chain, the field at +0x1c and the meaning of
* the 24-bit mask are hypotheses; only the bytes are evidence. All accesses are 32-bit. Whether
* the mask is a colour, an address or an index is not recoverable.
*/
extern void func_8001703C(int a0, int a1, int a2);
void func_800170C0(int a0, int a1, int a2)
{
func_8001703C(a0, a1, a2);
*(int *)(*(int *)(a0 + 0x10) + 0x1c) = a2 & 0xffffff;
}
+49
View File
@@ -0,0 +1,49 @@
/*
* func_80017D88 — 72 bytes at 0x80017D88..0x80017DD0
*
* Framed routine: calls one routine four times with four consecutive fields of its argument, then
* clears a fifth field.
*
* 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
* jal 0x80026650 0c009994 call func_80026650
* addiu a0,s0,0x8c 2604008c a0 = a0 + 0x8c (delay slot)
* jal 0x80026650 0c009994 call func_80026650
* addiu a0,s0,0x90 26040090 a0 = a0 + 0x90 (delay slot)
* jal 0x80026650 0c009994 call func_80026650
* addiu a0,s0,0x94 26040094 a0 = a0 + 0x94 (delay slot)
* jal 0x80026650 0c009994 call func_80026650
* addiu a0,s0,0x98 26040098 a0 = a0 + 0x98 (delay slot)
* sw zero,0x9c(s0) ae00009c *(int *)(a0 + 0x9c) = 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 argument is parked in **s0** because it is needed for all five accesses. Each call's
* argument is computed in that call's `jal` delay slot as a fixed offset off the saved pointer,
* so the four fields are 0x8c, 0x90, 0x94 and 0x98 — a 4-byte-strided run — and the cleared word
* at 0x9c is the next one after them. No return value is used, so the callee's result (if any) is
* discarded and the routine is `void`.
*
* LIMITS: the function name, the callee, the four field offsets and the claim that the cleared word
* belongs to the same object are hypotheses; only the bytes are evidence. All five accesses are
* 32-bit. Whether the four fields are an array is not recoverable from these instructions — the
* four calls are written out because that is what the bytes show, not because an unrolled loop was
* ruled out.
*/
extern void func_80026650(int a0);
void func_80017D88(int a0)
{
func_80026650(a0 + 0x8c);
func_80026650(a0 + 0x90);
func_80026650(a0 + 0x94);
func_80026650(a0 + 0x98);
*(int *)(a0 + 0x9c) = 0;
}
+48
View File
@@ -0,0 +1,48 @@
/*
* func_8002AC84 — 56 bytes at 0x8002AC84..0x8002ACBC
*
* Framed routine: when a `gp`-relative global is not -1, passes it to a routine and then sets it
* to -1.
*
* The observed instructions are:
* lw a0,0x268(gp) 8f840268 a0 = *(int *)(gp + 0x268)
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* sw s0,0x10(sp) afb00010 save s0
* li s0,-0x1 2410ffff s0 = -1
* beq a0,s0,0x8002aca8 10800002 if (a0 == -1) goto epilogue
* sw ra,0x14(sp) afbf0014 save ra (delay slot)
* jal 0x800a8920 0c02a248 call func_800A8920
* nop 00000000 (delay slot)
* sw s0,0x268(gp) af900268 *(int *)(gp + 0x268) = -1
* 0x8002aca8:
* 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)
*
* `0x268(gp)` is a `gp`-relative access off gp 0x80121938, i.e. 0x80121BA0, which the registry
* already carries with the `gp` marker. The sentinel -1 is materialised **once** into s0 (a
* callee-saved register) and used for both the comparison and the post-call store, which is the
* tell that the same constant appears twice in the source: it must survive the call, so cc1 had
* to put it in a saved register. The `sw ra` is in the guard's branch delay slot.
*
* The global's load is hoisted above the frame setup.
*
* LIMITS: the function name, the callee, the global's meaning and the sentinel's meaning are
* hypotheses; only the bytes are evidence. The global is written as a 32-bit `int` because both
* its load and its store are 32-bit.
*/
extern int D_80121BA0;
extern void func_800A8920(int a0);
void func_8002AC84(void)
{
int a0 = D_80121BA0;
if (a0 != -1) {
func_800A8920(a0);
D_80121BA0 = -1;
}
}
+45
View File
@@ -0,0 +1,45 @@
/*
* func_8002E028 — 72 bytes at 0x8002E028..0x8002E070
*
* Framed routine: calls one routine with a global, its own argument and its own pointer, and
* clears the pointed-to word when the call reports success.
*
* 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
* move a1,v0 00402821 a1 = a0
* lui a0,0x8012 3c048012 \
* lw a0,0x235c(a0) 8c84235c / a0 = *(int *)0x8012235C (D_8012235C)
* sw ra,0x14(sp) afbf0014 save ra
* jal 0x800277ac 0c009deb call func_800277AC
* move a2,s0 02003021 a2 = a1 (delay slot)
* beq v0,zero,0x8002e05c 10400002 if (v0 == 0) goto epilogue
* nop 00000000 (delay slot)
* sw zero,0x0(s0) ae000000 *a1 = 0
* 0x8002e05c:
* 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 call's three arguments are set up as: a0 = the global 0x8012235C (a same-register
* `lui`+`lw` symbol load, cookbook finding 2), a1 = the incoming first argument (via a v0
* round-trip, because a0 was needed for the global), a2 = the incoming second argument (parked
* in s0). The second argument is saved in **s0** because it is also the store base after the
* call. The result is tested for zero and the pointed-to word is cleared on success.
*
* LIMITS: the function name, the callee, the global 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 D_8012235C;
extern int func_800277AC(int a0, int a1, int a2);
void func_8002E028(int a0, int *a1)
{
if (func_800277AC(D_8012235C, a0, a1))
*a1 = 0;
}
+53
View File
@@ -0,0 +1,53 @@
/*
* func_8002F160 — 68 bytes at 0x8002F160..0x8002F1A4
*
* Framed routine: calls one routine with a zero, then if a `gp`-relative byte flag is set it
* clears the flag and calls a second routine with an address, a literal and a zero.
*
* The observed instructions are:
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* sw ra,0x10(sp) afbf0010 save ra
* jal 0x800acac8 0c02b2b2 call func_800ACAC8
* addu a0,zero,zero 00002021 a0 = 0 (delay slot)
* lbu v0,0x9dc(gp) 93a209dc v0 = *(unsigned char *)(gp + 0x9dc)
* nop 00000000 load-delay slot
* beq v0,zero,0x8002f194 10400005 if (v0 == 0) goto epilogue
* li a1,0x28 24050028 a1 = 0x28 (delay slot)
* sb zero,0x9dc(gp) a3a009dc *(unsigned char *)(gp + 0x9dc) = 0
* lui a0,0x8003 3c048003 \
* addiu a0,a0,-0xee8 2484f118 / a0 = 0x8002F118 (D_8002F118)
* jal 0x8002d0a8 0c00b42a call func_8002D0A8
* addu a2,zero,zero 00003021 a2 = 0 (delay slot)
* 0x8002f194:
* lw ra,0x10(sp) 8fbf0010 restore ra
* addiu sp,sp,0x18 27bd0018 frame release
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* `0x9dc(gp)` is a `gp`-relative access off gp 0x80121938, i.e. 0x80122314, which the registry
* already carries with the `gp` marker. It is read and cleared as a **byte** (`lbu`/`sb`) and the
* clear happens before the second call. The `0x28` argument is materialised in the guard's
* branch delay slot, so it is set up on the path that reaches the call.
*
* The address carries: `lui 0x8003` with the signed displacement -0xee8 gives **0x8002F118**,
* not 0x8003F118.
*
* LIMITS: the function name, both callees, the flag's meaning, the pointed-to object and the
* literal 0x28 are hypotheses; only the bytes are evidence. The routine sets no result, so it is
* `void`.
*/
extern unsigned char D_80122314;
extern char D_8002F118[];
extern void func_800ACAC8(int a0);
extern void func_8002D0A8(char *a0, int a1, int a2);
void func_8002F160(void)
{
func_800ACAC8(0);
if (D_80122314 != 0) {
D_80122314 = 0;
func_8002D0A8(D_8002F118, 0x28, 0);
}
}
+64
View File
@@ -0,0 +1,64 @@
/*
* func_80031F78 — 76 bytes at 0x80031F78..0x80031FC4
*
* Leaf routine that copies either one or four words out of a structure into the caller's buffer,
* depending on a flag word.
*
* The observed instructions are:
* lw a2,0xa4(a0) 8c8600a4 a2 = *(int *)(a0 + 0xa4)
* nop 00000000 load-delay slot
* lw v0,0x514(a2) 8cc20514 v0 = *(int *)(a2 + 0x514)
* nop 00000000 load-delay slot
* bne v0,zero,0x80031f9c 14400005 if (v0 != 0) goto the four-word path
* move a3,a1 00a03821 a3 = a1 (delay slot)
* lw v0,0x54c(a2) 8cc2054c v0 = *(int *)(a2 + 0x54c)
* j 0x80031fbc 0800c7ef goto epilogue
* sw v0,0x0(a3) ace20000 *a1 = v0 (delay slot)
* 0x80031f9c:
* lw v0,0x54c(a2) 8cc2054c v0 = *(int *)(a2 + 0x54c)
* lw v1,0x550(a2) 8cc30550 v1 = *(int *)(a2 + 0x550)
* lw a0,0x554(a2) 8cc40554 a0 = *(int *)(a2 + 0x554)
* lw a1,0x558(a2) 8cc50558 a1 = *(int *)(a2 + 0x558)
* sw v0,0x0(a3) ace20000 a1[0] = v0
* sw v1,0x4(a3) ace30004 a1[1] = v1
* sw a0,0x8(a3) ace40008 a1[2] = a0
* sw a1,0xc(a3) ace5000c a1[3] = a1
* 0x80031fbc:
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* The four-word path loads **all four words before storing any**, which is what puts the loads in
* v0/v1/a0/a1 and requires the destination pointer to be parked in a3 first — so the source reads
* four consecutive fields and writes four consecutive elements, and cc1 batched the loads because
* the registers were free. The one-word path re-reads only the first field rather than reusing the
* four-word path's first load, which is why the two arms each carry their own `lw ... 0x54c`.
*
* The four loads landing in **a1** (the argument register) is the tell that the else arm is a
* four-word STRUCT ASSIGNMENT rather than four element stores: element stores keep the destination
* pointer in a1 and pick other registers for the loaded values, which costs 16 extra bytes
* (measured: 92 vs 76). Writing `*(struct Q4 *)a1 = *(struct Q4 *)(a2 + 0x54c);` reproduces the
* original exactly, and the pointer copy into a3 is what the compiler emits to free a1.
*
* The pointer hop is `a0 + 0xa4` then the fields at +0x514 (the flag) and +0x54c..+0x558 (the
* data), so the four data words are consecutive and the flag is 0x38 bytes before them.
*
* LIMITS: the function name, the pointer chain, the flag field, the four data fields and the claim
* that the second argument is an output buffer are hypotheses; only the bytes are evidence. All
* accesses are 32-bit. Whether the one-word path is a degenerate case or a different record type is
* not recoverable.
*/
struct func_80031F78_quad {
int w[4];
};
void func_80031F78(int a0, int *a1)
{
int a2 = *(int *)(a0 + 0xa4);
if (*(int *)(a2 + 0x514) == 0) {
a1[0] = *(int *)(a2 + 0x54c);
} else {
*(struct func_80031F78_quad *)a1 = *(struct func_80031F78_quad *)(a2 + 0x54c);
}
}
+43
View File
@@ -0,0 +1,43 @@
/*
* func_80036A0C — 72 bytes at 0x80036A0C..0x80036A54
*
* Framed routine: calls one routine, then passes its result into a second call along with the
* original first and third arguments.
*
* The observed instructions are:
* addiu sp,sp,-0x20 27bdffe0 frame, 32 bytes
* sw s0,0x10(sp) afb00010 save s0
* move s0,a0 00808021 s0 = a0
* sw s1,0x14(sp) afb10014 save s1
* move s1,a2 00c08821 s1 = a2
* sw ra,0x18(sp) afbf0018 save ra
* jal 0x80032258 0c00c896 call func_80032258
* addu a1,zero,zero 00002821 a1 = 0 (delay slot)
* move a0,s0 02002021 a0 = s0
* move a1,v0 00402821 a1 = result
* jal 0x80032300 0c00c8c0 call func_80032300
* move a2,s1 02203021 a2 = s1 (delay slot)
* lw ra,0x18(sp) 8fbf0018 restore ra
* lw s1,0x14(sp) 8e310014 restore s1
* lw s0,0x10(sp) 8e100010 restore s0
* addiu sp,sp,0x20 27bd0020 frame release
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* The first and third arguments are parked in callee-saved registers (s0, s1) before the first
* call because both are needed after it; the second argument is not used at all, so the first
* call is `f(a0, 0)` and the second is `g(a0, result, a2)`. Both `jal` delay slots carry the
* next argument rather than a nop.
*
* LIMITS: the function names, both callees and the claim that the first call's result becomes the
* second call's second argument are hypotheses; only the bytes are evidence. Whether the first
* callee's zero second argument is a flag or a count is not recoverable.
*/
extern int func_80032258(int a0, int a1);
extern void func_80032300(int a0, int a1, int a2);
void func_80036A0C(int a0, int a1, int a2)
{
func_80032300(a0, func_80032258(a0, 0), a2);
}
+47
View File
@@ -0,0 +1,47 @@
/*
* func_80036A9C — 60 bytes at 0x80036A9C..0x80036AD8
*
* Leaf routine that indexes two nested tables and copies out one word.
*
* The observed instructions are:
* lw v1,0x134(a0) 8c830134 v1 = *(int *)(a0 + 0x134)
* nop 00000000 load-delay slot
* sll v0,v1,0x4 00031080 v0 = v1 * 16
* subu v0,v0,v1 00431023 v0 = 16a - a (= 15a)
* sll v0,v0,0x2 00021080 v0 = 60a
* addu v0,a0,v0 00821021 v0 = a0 + 60a
* lw v1,0xe60(v0) 8c430e60 v1 = *(int *)(v0 + 0xe60)
* nop 00000000 load-delay slot
* sll v0,v1,0x3 000310c0 v0 = v1 * 8
* subu v0,v0,v1 00431023 v0 = 8b - b (= 7b)
* sll v0,v0,0x5 000210c0 v0 = 224b
* addu a0,a0,v0 00822021 a0 += 224b
* lw v0,0x164(a0) 8c820164 v0 = *(int *)(a0 + 0x164)
* jr ra 03e00008
* sw v0,0x0(a1) aca20000 *a1 = v0 (delay slot)
*
* Two nested index computations, both strength-reduced from a non-power-of-two multiply:
* `* 60` becomes `(16a - a) << 2` and `* 224` becomes `(8b - b) << 5`, so the strides are **60**
* and **224** bytes and the first index lives at +0x134 of the base while the second lives at
* +0xe60 of the first-level element. That is the tell that both multiplies were by literals
* (cookbook finding 12 is the opposite case: a real `mult` means the operand was not a literal).
*
* The address arithmetic is **base first** (`addu v0,a0,v0` and `addu a0,a0,v0`), which finding
* 22 identifies as the `base + index * stride` spelling rather than `index * stride + base`.
*
* LIMITS: the function name, the two strides, the two index fields and the copied field at
* +0x164 are hypotheses; only the bytes are evidence. All accesses are 32-bit. Whether the
* second-level base is `a0 + 60*idx` or a nested structure is not recoverable — the code folds
* both levels onto the original pointer.
*/
void func_80036A9C(int a0, int *a1)
{
int v1 = *(int *)(a0 + 0x134);
int v0 = a0 + v1 * 60;
v1 = *(int *)(v0 + 0xe60);
a0 += v1 * 224;
*a1 = *(int *)(a0 + 0x164);
}
+53
View File
@@ -0,0 +1,53 @@
/*
* func_80042964 — 76 bytes at 0x80042964..0x800429B0
*
* Framed routine that calls two routines with values scaled out of a global structure and a global
* halfword.
*
* The observed instructions are:
* lui v0,0x8012 3c028012 \
* lw v0,0x2430(v0) 8c422430 / v0 = *(int *)0x80122430 (D_80122430)
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* sw ra,0x10(sp) afbf0010 save ra
* lh a0,0x4e(v0) 8444004e a0 = *(short *)(v0 + 0x4e)
* lh a1,0x50(v0) 84450050 a1 = *(short *)(v0 + 0x50)
* sll a0,a0,0x1 00042040 a0 *= 2
* jal 0x80017c50 0c005f14 call func_80017C50
* sll a1,a1,0x1 00052840 a1 *= 2 (delay slot)
* lui a0,0x8012 3c048012 \
* lh a0,0x242c(a0) 8484242c / a0 = *(short *)0x8012242C (D_8012242C)
* nop 00000000 load-delay slot
* sll a0,a0,0x11 000427c0 a0 <<= 17
* jal 0x80017c60 0c005f18 call func_80017C60
* sra a0,a0,0x10 00042703 a0 = (short)a0 (delay slot)
* lw ra,0x10(sp) 8fbf0010 restore ra
* addiu sp,sp,0x18 27bd0018 frame release
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* Both callees take **16-bit** parameters and the three values come from three signed halfword
* loads. The first two are simply doubled (`sll` by 1) with no mask, which means those callees'
* parameters are wide enough that cc1 did not need to narrow them; the third is doubled AND
* narrowed with `sll 17` + `sra 16`, which is this compiler's mask-and-sign-extend for a `short`
* argument — `(x << 17) >> 16` keeps the low 16 bits of `x * 2` and sign-extends them.
*
* The pointer load is hoisted above the frame setup. The first two arguments are computed into a0
* and a1 and the second is scheduled into the `jal` delay slot.
*
* LIMITS: the function name, both callees, the two globals and the meaning of the doubling are
* hypotheses; only the bytes are evidence. The three loads are 16-bit signed; the parameter widths
* are inferred from which values cc1 narrowed, not from a declaration.
*/
extern int D_80122430;
extern short D_8012242C;
extern void func_80017C50(int a0, int a1);
extern void func_80017C60(short a0);
void func_80042964(void)
{
int v0 = D_80122430;
func_80017C50(*(short *)(v0 + 0x4e) * 2, *(short *)(v0 + 0x50) * 2);
func_80017C60(D_8012242C * 2);
}
+54
View File
@@ -0,0 +1,54 @@
/*
* func_8004E3FC — 68 bytes at 0x8004E3FC..0x8004E440
*
* Framed routine: calls one routine and, when its result is the sentinel -1, falls back to a
* halfword reached through a field of its own argument.
*
* The observed instructions are:
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* sw s0,0x10(sp) afb00010 save s0
* sw ra,0x14(sp) afbf0014 save ra
* jal 0x8004dd74 0c01375d call func_8004DD74
* move s0,a0 00808021 s0 = a0 (delay slot)
* move v1,v0 00401821 v1 = result
* li v0,-0x1 2402ffff v0 = -1
* bne v1,v0,0x8004e42c 14620005 if (v1 != -1) goto epilogue
* move v0,v1 00601021 v0 = v1 (delay slot)
* lw v0,0x14(s0) 8e020014 v0 = *(int *)(a0 + 0x14)
* nop 00000000 load-delay slot
* lh v0,0x0(v0) 84420000 v0 = *(short *)v0
* 0x8004e42c:
* 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 is needed *after* the
* call as the base of the fallback load. The callee's result is copied to v1 and compared
* against a `li`-materialised -1, and the result is moved into v0 **in the branch delay slot** —
* so the non-fallback path returns v1 and the fallback path overwrites v0 with the halfword. One
* shared epilogue.
*
* The fallback is a sign-extended 16-bit load (`lh`) through a pointer at +0x14 of the argument,
* so the function's return type is at least 16-bit and the value is signed.
*
* LIMITS: the function name, the callee, the sentinel -1, the pointer field at +0x14 and the
* loaded halfword are hypotheses; only the bytes are evidence. Whether the callee's `int` result
* and the `short` fallback share a meaningful range is not established.
*/
extern int func_8004DD74(void);
int func_8004E3FC(int a0)
{
int v1 = func_8004DD74();
int v0;
if (v1 == -1)
v0 = *(short *)(*(int *)(a0 + 0x14));
else
v0 = v1;
return v0;
}
+48
View File
@@ -0,0 +1,48 @@
/*
* func_800668A8 — 72 bytes at 0x800668A8..0x800668F0
*
* Framed routine: when a global halfword is not the sentinel -1, calls a routine with eight
* arguments, four of them constants.
*
* The observed instructions are:
* addiu sp,sp,-0x28 27bdffd8 frame, 40 bytes
* move a2,a0 00803021 a2 = a0
* lui a3,0x8012 3c078012 \
* lh a3,0x237c(a3) 84e7237c / a3 = *(short *)0x8012237C (D_8012237C)
* li v0,-0x1 2402ffff v0 = -1
* beq a3,v0,0x800668e0 10e2000b if (a3 == -1) goto epilogue
* sw ra,0x20(sp) afbf0020 save ra (delay slot)
* li a0,0x6 24040006 a0 = 6
* sw a1,0x10(sp) afa50010 arg5 = a1
* li a1,0x5 24050005 a1 = 5
* sw zero,0x14(sp) afa00014 arg6 = 0
* sw zero,0x18(sp) afa00018 arg7 = 0
* jal 0x8002b608 0c00ad82 call func_8002B608
* sw zero,0x1c(sp) afa0001c arg8 = 0 (delay slot)
* 0x800668e0:
* lw ra,0x20(sp) 8fbf0020 restore ra
* addiu sp,sp,0x28 27bd0028 frame release
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* The global is read as a signed **halfword** (`lh`) and compared against a `li`-materialised -1;
* the same-register `lui`+`lh` form makes it a named symbol (cookbook finding 2). The incoming
* first argument is moved to a2 before a0 is overwritten with the literal 6, so the call is
* `f(6, 5, arg0, global, arg1, 0, 0, 0)` — eight arguments, with arguments five through eight
* stored at 16(sp), 20(sp), 24(sp) and 28(sp) per the o32 convention. The frame is 40 bytes:
* 16 for the register arguments' home slots, 16 for the four stack arguments, 8 for the saved
* `ra` at 0x20.
*
* LIMITS: the function name, the callee, the global's meaning and the four constants are
* hypotheses; only the bytes are evidence. The global is 16-bit and signed; the stack arguments
* are 32-bit.
*/
extern short D_8012237C;
extern void func_8002B608(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7);
void func_800668A8(int a0, int a1)
{
if (D_8012237C != -1)
func_8002B608(6, 5, a0, D_8012237C, a1, 0, 0, 0);
}
+55
View File
@@ -0,0 +1,55 @@
/*
* func_8006B778 — 72 bytes at 0x8006B778..0x8006B7C0
*
* Framed routine that walks a `gp`-relative list of objects and calls a routine once per node.
*
* The observed instructions are:
* lw a0,0x550(gp) 8f840550 a0 = *(int *)(gp + 0x550)
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* sw ra,0x14(sp) afbf0014 save ra
* beq a0,zero,0x8006b7ac 10800009 if (a0 == 0) goto epilogue
* sw s0,0x10(sp) afb00010 save s0 (delay slot)
* 0x8006b78c:
* lw v0,0xc(a0) 8c82000c v0 = *(int *)(a0 + 0xc)
* nop 00000000 load-delay slot
* lw s0,0x18c(v0) 8c50018c s0 = *(int *)(v0 + 0x18c)
* jal 0x800c2ce4 0c030b39 call func_800C2CE4
* nop 00000000 (delay slot)
* move a0,s0 02002021 a0 = s0
* bne a0,zero,0x8006b78c 1480fffb if (a0 != 0) loop
* nop 00000000 (delay slot)
* 0x8006b7ac:
* 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)
*
* `0x550(gp)` is a `gp`-relative access off gp 0x80121938, i.e. 0x80121E88, which the registry
* already carries with the `gp` marker. The next node is computed **before** the call (through
* +0xc then +0x18c) and parked in s0, because it must survive the call — so the loop advances
* after the call returns even though the load was hoisted. The call receives the *current* node
* in a0, which is why the `jal` delay slot is a `nop` and a0 is reloaded from s0 afterwards.
*
* The head load is hoisted above the frame setup, and the guard's `sw s0` is in the branch delay
* slot.
*
* LIMITS: the function name, the callee, the list layout and the field offsets are hypotheses;
* only the bytes are evidence. All accesses are 32-bit. The list is assumed acyclic — nothing in
* these bytes proves it.
*/
extern int D_80121E88;
extern void func_800C2CE4(int a0);
void func_8006B778(void)
{
int a0 = D_80121E88;
while (a0 != 0) {
int s0 = *(int *)(*(int *)(a0 + 0xc) + 0x18c);
func_800C2CE4(a0);
a0 = s0;
}
}
+51
View File
@@ -0,0 +1,51 @@
/*
* func_8007E8B8 — 76 bytes at 0x8007E8B8..0x8007E904
*
* Framed routine that builds a three-word difference record on the stack and passes its address on
* along with a fourth argument.
*
* The observed instructions are:
* addiu sp,sp,-0x28 27bdffd8 frame, 40 bytes
* sw ra,0x20(sp) afbf0020 save ra
* lw a0,0xc(a1) 8ca4000c a0 = *(int *)(a1 + 0xc)
* lw v0,0x0(a2) 8cc20000 v0 = a2[0]
* lw v1,0x0(a0) 8c830000 v1 = *(int *)a0
* move a1,a3 00e02821 a1 = a3
* sw zero,0x14(sp) afa00014 local[1] = 0
* subu v0,v0,v1 00431023 v0 -= v1
* sw v0,0x10(sp) afa20010 local[0] = v0
* lw v0,0x8(a2) 8cc20008 v0 = a2[2]
* lw v1,0x8(a0) 8c830008 v1 = *(int *)(a0 + 8)
* addiu a0,sp,0x10 27a40010 a0 = &local
* subu v0,v0,v1 00431023 v0 -= v1
* jal 0x80010b14 0c0042c5 call func_80010B14
* sw v0,0x18(sp) afa20018 local[2] = v0 (delay slot)
*
* The record is 12 bytes at sp+0x10..sp+0x1c and holds `a2[0] - p[0]`, 0 and `a2[2] - p[2]` where
* `p` is the pointer at +0xc of the second argument. The **first argument is never read**: a0 is
* overwritten by the load of `a1 + 0xc` before any use, so the source's first parameter is unused —
* the shape of a function whose signature must match a table slot. The fourth argument is moved to
* a1 before a0 is taken for the record address, so the call is `f(&local, arg3)`.
*
* The 40-byte frame is 12 bytes of record plus padding, 16 bytes of argument home slots and 8 bytes
* for the saved `ra` at 0x20.
*
* LIMITS: the function name, the callee, the record's meaning and the claim that a2 is an array of
* at least three words are hypotheses; only the bytes are evidence. All accesses are 32-bit. The
* first parameter's absence of use is evidence from the bytes; whether the original declared it at
* all is not recoverable.
*/
extern void func_80010B14(int *a0, int a1);
void func_8007E8B8(int a0, int a1, int *a2, int a3)
{
int *p = *(int **)(a1 + 0xc);
int local[3];
local[0] = a2[0] - p[0];
local[1] = 0;
local[2] = a2[2] - p[2];
func_80010B14(local, a3);
}
+50
View File
@@ -0,0 +1,50 @@
/*
* func_80083470 — 72 bytes at 0x80083470..0x800834B8
*
* Framed routine: calls one routine with its argument, then sets bit 15 of a 16-byte record
* selected by that argument.
*
* The observed instructions are:
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* sw s0,0x10(sp) afb00010 save s0
* sw ra,0x14(sp) afbf0014 save ra
* jal 0x80083440 0c020d10 call func_80083440
* move s0,a0 00808021 s0 = a0 (delay slot)
* lui v0,0x8012 3c028012 \
* lw v0,0x2308(v0) 8c422308 / v0 = *(int *)0x80122308 (D_80122308)
* sll s0,s0,0x4 00108080 s0 = a0 * 16
* addu s0,s0,v0 02028021 s0 = (a0 * 16) + base
* lw v0,0x0(s0) 8e020000 v0 = *(int *)s0
* nop 00000000 load-delay slot
* ori v0,v0,0x8000 34428000 v0 |= 0x8000
* sw v0,0x0(s0) ae020000 *(int *)s0 = v0
* 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 parked in **s0** because it is used after the call as the record index, and it
* is scaled by 16 (`sll`), so each record is 16 bytes. The address arithmetic is **stride first,
* base second** (`addu s0,s0,v0`), the spelling cookbook finding 22 identifies as
* `(index * 16) + symbol` rather than `symbol + index * 16`; the rule's stated scope (a symbol or
* `gp` base) applies here, and this is the same global that finding 22's own evidence
* (0x80083504) uses.
*
* The field is read, OR-ed with 0x8000 and written back, so bit 15 of the record's first word is
* set. The call's argument is the incoming a0 unchanged (the `move s0,a0` is a copy for later,
* not an argument setup).
*
* LIMITS: the function name, the callee, the global's type, the record size of 16 bytes and the
* meaning of bit 15 are hypotheses; only the bytes are evidence. All accesses are 32-bit.
*/
extern int D_80122308;
extern void func_80083440(int a0);
void func_80083470(int a0)
{
func_80083440(a0);
*(int *)(D_80122308 + a0 * 16) |= 0x8000;
}
+43
View File
@@ -0,0 +1,43 @@
/*
* func_8009107C — 52 bytes at 0x8009107C..0x800910B0
*
* Framed routine: sets a `gp`-relative byte to 1 and calls one routine with an address, a
* literal and a zero.
*
* The observed instructions are:
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* li v0,0x1 24020001 v0 = 1
* lui a0,0x8009 3c048009 \
* addiu a0,a0,0x1070 24841070 / a0 = 0x80091070 (D_80091070)
* li a1,0xa 2405000a a1 = 0xa
* sw ra,0x10(sp) afbf0010 save ra
* sb v0,0x5a3(gp) a3a205a3 *(char *)(gp + 0x5a3) = 1
* jal 0x800137c8 0c004df2 call func_800137C8
* 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)
*
* `0x5a3(gp)` is a `gp`-relative access off gp 0x80121938, i.e. 0x80121EDB, which the registry
* already carries with the `gp` marker, so no request row is needed. The store is **byte-wide**
* (`sb`) and happens before the call, with the constant 1 materialised into v0 at the top of
* the function.
*
* The address argument is `lui`+`addiu` (the linker-resolved symbol form, cookbook finding 4),
* the literal 0xa uses `li`, and the zero uses `addu rd,zero,zero` scheduled into the `jal`
* delay slot.
*
* LIMITS: the function name, the callee, the pointed-to object and the meaning of 0xa are
* hypotheses; only the bytes are evidence. The routine sets no result, so it is `void`.
*/
extern char D_80121EDB;
extern char D_80091070[];
extern void func_800137C8(char *a0, int a1, int a2);
void func_8009107C(void)
{
D_80121EDB = 1;
func_800137C8(D_80091070, 0xa, 0);
}
+51
View File
@@ -0,0 +1,51 @@
/*
* func_800A9FD4 — 72 bytes at 0x800A9FD4..0x800AA01C
*
* Framed routine: calls one routine, and when it returns non-null passes the result plus its own
* two arguments to a second routine.
*
* The observed instructions are:
* addiu sp,sp,-0x20 27bdffe0 frame, 32 bytes
* sw s0,0x10(sp) afb00010 save s0
* move s0,a1 00a08021 s0 = a1
* sw s1,0x14(sp) afb10014 save s1
* sw ra,0x18(sp) afbf0018 save ra
* jal 0x800a82d0 0c02a0b4 call func_800A82D0
* move s1,a2 00c08821 s1 = a2 (delay slot)
* beq v0,zero,0x800aa004 10400004 if (v0 == 0) goto epilogue
* move a0,v0 00402021 a0 = result (delay slot)
* move a1,s0 02002821 a1 = s0
* jal 0x800a84e8 0c02a13a call func_800A84E8
* move a2,s1 02203021 a2 = s1 (delay slot)
* 0x800aa004:
* lw ra,0x18(sp) 8fbf0018 restore ra
* lw s1,0x14(sp) 8e310014 restore s1
* lw s0,0x10(sp) 8e100010 restore s0
* addiu sp,sp,0x20 27bd0020 frame release
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* Both of the wrapper's arguments are parked in callee-saved registers (s0, s1) before the first
* call because both are needed after it, and the first call's result moves into a0 in its own
* branch delay slot — so the second call is `g(result, arg1, arg2)` and the guard carries the
* argument setup.
*
* The first callee, 0x800A82D0, is one of the project's recorded open negatives
* (`near_match_negatives.tsv`, class `-`); it is referenced here as an unresolved symbol, not as a
* matched region.
*
* LIMITS: the function names, both callees and the claim that the first call's result becomes the
* second call's first argument are hypotheses; only the bytes are evidence. The routine sets no
* result, so it is `void`.
*/
extern int func_800A82D0(void);
extern void func_800A84E8(int a0, int a1, int a2);
void func_800A9FD4(int a0, int a1, int a2)
{
int v0 = func_800A82D0();
if (v0 != 0)
func_800A84E8(v0, a1, a2);
}
+45
View File
@@ -0,0 +1,45 @@
/*
* func_800F7A20 — 52 bytes at 0x800F7A20..0x800F7A54
*
* Framed routine that makes an **indirect** call through a function pointer taken from a global
* structure, with a literal first argument and its own argument second.
*
* The observed instructions are:
* addiu sp,sp,-0x18 27bdffe8 frame, 24 bytes
* lui v0,0x8012 3c028012 \
* lw v0,-0x4b4(v0) 8c42fb4c / v0 = *(int *)0x8011FB4C (D_8011FB4C)
* move a1,a0 00802821 a1 = a0
* sw ra,0x10(sp) afbf0010 save ra
* lw v0,0x14(v0) 8c420014 v0 = *(int *)(v0 + 0x14)
* nop 00000000 load-delay slot
* jalr v0 0040f809 call v0
* li a0,0x4 24040004 a0 = 4 (delay slot)
* lw ra,0x10(sp) 8fbf0010 restore ra
* addiu sp,sp,0x18 27bd0018 frame release
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* The **fourth** indirect-call wrapper through the same global (the others are func_800F7990 at
* +0xc, func_800F79F0 at +4 and func_800F7A54 at +0x14), so the global holds a table of
* callable entries and each wrapper invokes a different slot. This one differs from its
* siblings in that it also **passes arguments**: the wrapper's own argument moves to a1 and the
* literal 4 is materialised into a0 in the `jal` delay slot, so the callee takes at least two
* arguments.
*
* The address arithmetic carries: `lui 0x8012` with the signed displacement -0x4b4 gives
* **0x8011FB4C**, not 0x8012FB4C.
*
* LIMITS: the function name, the claim that +0x14 holds a function pointer, the callee's
* identity and the meaning of 4 are hypotheses; only the bytes are evidence. No result is set,
* so the wrapper is `void`; whether the callee returns a value that is discarded is not
* recoverable.
*/
extern int D_8011FB4C;
void func_800F7A20(int a0)
{
void (*fn)(int, int) = *(void (**)(int, int))(D_8011FB4C + 0x14);
fn(4, a0);
}
+41 -37
View File
@@ -1,50 +1,54 @@
/*
* 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.
* Leaf routine that stores a halfword into an array element, optionally shifted right by a
* global amount.
*
* 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
* bne a2,zero,0x8010a768 14c00007 if (a2 != 0) goto the shifted path
* sll v0,a0,0x1 00041040 v0 = a0 * 2 (delay slot)
* lui v1,0x8012 3c038012 \
* lw v1,0x1048(v1) 8c631048 / v1 = *(int *)0x80121048 (D_80121048)
* nop 00000000 load-delay slot
* addu v0,v0,v1 00431021 v0 = index * 2 + base
* j 0x8010a784 080429e1 goto epilogue
* sh a1,0x0(v0) a4450000 *(short *)v0 = a1 (delay slot)
* 0x8010a768:
* lui a0,0x8012 3c048012 \
* lw a0,0x1048(a0) 8c841048 / a0 = *(int *)0x80121048
* lui v1,0x8012 3c038012 \
* lw v1,0x1070(v1) 8c631070 / v1 = *(int *)0x80121070 (D_80121070)
* addu v0,v0,a0 00441021 v0 = index * 2 + base
* srlv v1,a1,v1 00650806 v1 = a1 >> v1
* sh v1,0x0(v0) a4410000 *(short *)v0 = v1
* 0x8010a784:
* jr ra 03e00008
* nop 00000000 (delay slot)
*
* 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.
* The `sll` that scales the index by 2 is computed in the branch delay slot and is shared by
* **both** arms, so the element type is 16-bit and the stride is 2 bytes. The unshifted arm
* stores the argument directly; the shifted arm shifts it by the value in the global
* 0x80121070 with `srlv` — a **logical** shift by a register, so the shifted value 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.
* Both globals are read with the same-register `lui`+`lw` symbol form (cookbook finding 2), so
* they are written as named symbols and not as literal addresses (finding 5). The two arms each
* reload the array base rather than sharing one load, which is what the duplicated `lui`/`lw`
* pairs show.
*
* LIMITS: the function name, the array, the shift-amount global and the meaning of the third
* argument are hypotheses; only the bytes are evidence. The array elements are 16-bit. Whether
* the third argument is a flag or a count is not recoverable — only that zero selects the
* unshifted path.
*/
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);
void func_8010A748(int a0, unsigned int a1, int a2)
{
if (a2 == 0) {
*(short *)(D_80121048 + a0 * 2) = a1;
} else {
*(short *)(D_80121048 + a0 * 2) = a1 >> D_80121070;
}
}