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:
@@ -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
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user