diff --git a/config/regions.tsv b/config/regions.tsv index aa14aea..3f392da 100644 --- a/config/regions.tsv +++ b/config/regions.tsv @@ -55,6 +55,7 @@ 0x80021F64 0x80021F88 src/func_80021F64.c 0x80021F88 0x80021FA8 src/func_80021F88.c 0x80021FA8 0x80021FF4 src/func_80021FA8.c +0x80021FF4 0x8002203C src/func_80021FF4.c 0x80022A60 0x80022A98 src/func_80022A60.c 0x80022FB8 0x80022FCC src/func_80022FB8.c 0x80022FCC 0x80022FFC src/func_80022FCC.c @@ -101,6 +102,7 @@ 0x800321EC 0x800321F8 src/func_800321EC.c 0x80034A80 0x80034ABC src/func_80034A80.c 0x80036308 0x80036328 src/func_80036308.c +0x80036328 0x8003636C src/func_80036328.c 0x8003636C 0x80036378 src/func_8003636C.c 0x80036378 0x80036380 src/func_80036378.c 0x80036380 0x80036390 src/func_80036380.c @@ -232,6 +234,7 @@ 0x800A8B48 0x800A8B8C src/func_800A8B48.c 0x800A9D58 0x800A9D90 src/func_800A9D58.c 0x800A9FD4 0x800AA01C src/func_800A9FD4.c +0x800AA2B4 0x800AA2F8 src/func_800AA2B4.c 0x800AA56C 0x800AA59C src/func_800AA56C.c 0x800AC818 0x800AC85C src/func_800AC818.c 0x800AC85C 0x800AC884 src/func_800AC85C.c diff --git a/config/symbols.tsv b/config/symbols.tsv index 9ffe69a..17db0ef 100644 --- a/config/symbols.tsv +++ b/config/symbols.tsv @@ -20,6 +20,7 @@ D_80121958 0x80121958 gp D_8012195D 0x8012195D gp D_80121970 0x80121970 gp D_80121974 0x80121974 gp +D_80121978 0x80121978 gp D_8012197C 0x8012197C gp D_80121988 0x80121988 gp D_8012199C 0x8012199C gp diff --git a/src/func_80021FF4.c b/src/func_80021FF4.c new file mode 100644 index 0000000..6bc3cf9 --- /dev/null +++ b/src/func_80021FF4.c @@ -0,0 +1,65 @@ +/* func_80021FF4 — 0x80021FF4..0x8002203C (72 bytes). + * + * Original words: + * 27BDFFE8 addiu sp,sp,-24 + * AFBF0010 sw ra,16(sp) + * 0C052084 jal 0x80148210 + * 00002021 _move a0,zero (delay slot) + * 3C048012 lui a0,0x8012 + * 2484622C addiu a0,a0,25132 a0 = D_8012622C + * 0C03DFF6 jal 0x800F7FD8 + * 24850022 _addiu a1,a0,34 (delay slot) a1 = a0 + 34 + * 0C03E18E jal 0x800F8638 + * 00000000 nop + * 0C0087EA jal 0x80021FA8 + * 00000000 nop + * 24020001 li v0,1 + * A3820920 sb v0,2336(gp) D_80122258 = 1 + * 8FBF0010 lw ra,16(sp) + * 00001021 move v0,zero + * 03E00008 jr ra + * 27BD0018 _addiu sp,sp,24 (delay slot) + * + * A four-call initialiser ending in a one-byte flag store. The frame exists only to + * preserve `ra`; every argument setup is either in a delay slot or absent. + * + * The second call's two addresses are `0x8012622C` and that value **plus 34** — + * `addiu a1,a0,34` reuses the register already holding the first address rather than + * materialising a second symbol. That is the tell for a source that passed one + * address and a displacement from it (a base and a base+34 pair), not two + * independently named globals; two symbols would each need their own `lui`. + * + * The first address's `lui`/`addiu` split needs no sign adjustment (25132 = 0x622C is + * positive as a 16-bit value), unlike the negatively-displaced cases elsewhere in + * this batch — so here the `lui` immediate *is* the top half of the address. + * + * `li v0,1` then `sb v0,2336(gp)` then `move v0,zero` is a **scratch constant, not a + * return value**: the byte written is 1 and the routine then returns 0, so the `1` + * never reaches the caller (cookbook finding 13's rule, in its clearest form). The + * flag store is gp-relative, so `gp` (0x80121938) + 2336 gives **0x80122258**, which + * needs a `gp` marker row. + * + * LIMITS: all four callees are named for their addresses and nothing here establishes + * what they do or what the two addresses mean. That 34 is a displacement into the + * object at 0x8012622C rather than an unrelated constant follows from the register + * reuse, not from the value. The returned zero is taken to be a success indicator + * only because the body also sets a flag; that is a reading, not evidence. + */ + +extern char D_8012622C[]; +extern unsigned char D_80122258; + +void func_80148210(int arg); +void func_800F7FD8(char *from, char *to); +void func_800F8638(void); +void func_80021FA8(void); + +int func_80021FF4(void) +{ + func_80148210(0); + func_800F7FD8(D_8012622C, D_8012622C + 34); + func_800F8638(); + func_80021FA8(); + D_80122258 = 1; + return 0; +} diff --git a/src/func_80036328.c b/src/func_80036328.c new file mode 100644 index 0000000..67695cb --- /dev/null +++ b/src/func_80036328.c @@ -0,0 +1,51 @@ +/* func_80036328 — 0x80036328..0x8003636C (68 bytes). + * + * Original words: + * 27BDFFE0 addiu sp,sp,-32 + * AFB10014 sw s1,20(sp) + * 00808821 move s1,a0 keep the pointer + * AFB00010 sw s0,16(sp) + * 30B000FF andi s0,a1,0xff keep the narrowed second argument + * AFBF0018 sw ra,24(sp) + * 0C00D89E jal 0x80036278 + * 02002821 _move a1,s0 (delay slot) + * 02202021 move a0,s1 + * 0C00D8C2 jal 0x80036308 + * 02002821 _move a1,s0 (delay slot) + * 8FBF0018 lw ra,24(sp) + * 8FB10014 lw s1,20(sp) + * 8FB00010 lw s0,16(sp) + * 27BD0020 addiu sp,sp,32 + * 03E00008 jr ra + * 00000000 nop + * + * Calls two routines with the same pair of arguments, the second argument masked + * to a byte. Both calls fill their delay slots with the second argument, so the + * narrowed value is the one passed — the mask happens once, before either call. + * + * `andi s0,a1,0xff` is the whole reason the value is kept at all: without the mask + * the argument could be forwarded straight from `a1` and no saved register would be + * needed for it. The two saved registers exist because two calls intervene, so the + * pointer and the masked value must survive both. + * + * The spill order (`sw s1`, `move s1`, `sw s0`, `andi s0`, `sw ra`) is the scheduler + * interleaving, not source order — the frame's three saves are emitted around the + * two register setups. + * + * LIMITS: the mask 0xff is read from the `andi`, so the second parameter is + * narrowed to 8 bits, but whether the source declared it as `unsigned char` or + * masked it explicitly cannot be told from the bytes. The frame is 32 bytes for two + * saved registers and `ra`, which is 8 more than those need — the same round-up + * anomaly seen at `0x80066E2C`. The two callees are named for their addresses. + */ + +void func_80036278(int a0, int a1); +void func_80036308(int a0, int a1); + +void func_80036328(int obj, int value) +{ + int masked = value & 0xff; + + func_80036278(obj, masked); + func_80036308(obj, masked); +} diff --git a/src/func_800AA2B4.c b/src/func_800AA2B4.c new file mode 100644 index 0000000..d836a8a --- /dev/null +++ b/src/func_800AA2B4.c @@ -0,0 +1,51 @@ +/* func_800AA2B4 — 0x800AA2B4..0x800AA2F8 (68 bytes). + * + * Original words: + * 27BDFFE8 addiu sp,sp,-24 + * AFB00010 sw s0,16(sp) + * AFBF0014 sw ra,20(sp) + * 0C02A0B4 jal 0x800A82D0 + * 00A08021 _move s0,a1 (delay slot) keep the second argument + * 10400005 beqz v0,0x800AA2E0 + * 00402021 _move a0,v0 (delay slot) first argument of the tail call + * 0C02A843 jal 0x800AA10C + * 02002821 _move a1,s0 (delay slot) + * 0802A8B9 j 0x800AA2E4 + * 00000000 _nop (delay slot) + * 00001021 move v0,zero <- 0x800AA2E0 + * 8FBF0014 lw ra,20(sp) <- 0x800AA2E4 + * 8FB00010 lw s0,16(sp) + * 27BD0018 addiu sp,sp,24 + * 03E00008 jr ra + * 00000000 nop + * + * A lookup-then-forward: the first callee's result is tested, a null result answers + * zero, and a non-null one is passed as the *first* argument to a second callee + * together with the second argument that was kept. + * + * Every argument setup is scheduled into a branch or call delay slot — `move s0,a1` + * into the first `jal`, `move a0,v0` into the `beqz`, `move a1,s0` into the second + * `jal` — so the routine has no instruction that is purely argument setup, and the + * C below is written to match that shape rather than to read naturally. + * + * The two returns share one epilogue, reached by the fall-through path and by the + * `j` from the success path, and the `move v0,zero` before it is the zero answer. + * + * LIMITS: the two callees are named for their addresses. That the first returns a + * pointer or handle is inferred from its being null-tested and then forwarded as an + * argument; nothing in this body establishes its type, and the `int` used here is a + * width choice. The second argument is passed unchanged to both callees, so no + * narrowing of it can be inferred. + */ + +int func_800A82D0(int a0, int a1); +int func_800AA10C(int a0, int a1); + +int func_800AA2B4(int a0, int a1) +{ + int found = func_800A82D0(a0, a1); + + if (found == 0) + return 0; + return func_800AA10C(found, a1); +}