diff --git a/config/regions.tsv b/config/regions.tsv index 8e4f36a..0d9b556 100644 --- a/config/regions.tsv +++ b/config/regions.tsv @@ -112,6 +112,7 @@ 0x80036378 0x80036380 src/func_80036378.c 0x80036380 0x80036390 src/func_80036380.c 0x80036A0C 0x80036A54 src/func_80036A0C.c +0x80036A54 0x80036A9C src/func_80036A54.c 0x80036A9C 0x80036AD8 src/func_80036A9C.c 0x80036AD8 0x80036B14 src/func_80036AD8.c 0x8003768C 0x800376CC src/func_8003768C.c @@ -130,6 +131,7 @@ 0x800450C4 0x80045110 src/func_800450C4.c gp=-D_80121BFC 0x80045388 0x800453C0 src/func_80045388.c 0x800453C0 0x800453F8 src/func_800453C0.c +0x80048E20 0x80048E70 src/func_80048E20.c 0x80049298 0x800492E4 src/func_80049298.c gp=-D_80121BFC 0x8004C060 0x8004C090 src/func_8004C060.c 0x8004C090 0x8004C0AC src/func_8004C090.c @@ -141,6 +143,9 @@ 0x8005182C 0x80051864 src/func_8005182C.c 0x80052C98 0x80052CAC src/func_80052C98.c 0x80057524 0x80057564 src/func_80057524.c +0x8005784C 0x8005789C src/func_8005784C.c +0x800579A0 0x800579F0 src/func_800579A0.c +0x80057AE0 0x80057B30 src/func_80057AE0.c 0x80057DFC 0x80057E04 src/func_80057DFC.c 0x80058230 0x80058288 src/func_80058230.c 0x80058288 0x800582AC src/func_80058288.c @@ -161,6 +166,7 @@ 0x80068F98 0x80068FA8 src/func_80068F98.c 0x800697A4 0x800697C4 src/func_800697A4.c 0x800697C4 0x800697FC src/func_800697C4.c +0x8006B1CC 0x8006B214 src/func_8006B1CC.c 0x8006B778 0x8006B7C0 src/func_8006B778.c 0x8006BC08 0x8006BC34 src/func_8006BC08.c 0x8006BC34 0x8006BC74 src/func_8006BC34.c @@ -200,6 +206,7 @@ 0x8008B8E4 0x8008B8F4 src/func_8008B8E4.c 0x8008B8F4 0x8008B910 src/func_8008B8F4.c 0x8008F4A0 0x8008F4AC src/func_8008F4A0.c +0x8008F4AC 0x8008F4F4 src/func_8008F4AC.c 0x8008F4F4 0x8008F508 src/func_8008F4F4.c 0x8008F508 0x8008F530 src/func_8008F508.c 0x8008FF58 0x8008FF84 src/func_8008FF58.c diff --git a/src/func_80036A54.c b/src/func_80036A54.c new file mode 100644 index 0000000..2942781 --- /dev/null +++ b/src/func_80036A54.c @@ -0,0 +1,52 @@ +/* func_80036A54 — 0x80036A54..0x80036A9C (72 bytes). + * + * Original words: + * 27BDFFE0 addiu sp,sp,-32 + * AFB00010 sw s0,16(sp) + * 00808021 move s0,a0 keep the first argument + * AFB10014 sw s1,20(sp) + * 00C08821 move s1,a2 keep the third argument + * AFBF0018 sw ra,24(sp) + * 0C00C896 jal 0x80032258 + * 24050001 _li a1,1 (delay slot) second argument = 1 + * 02002021 move a0,s0 + * 00402821 move a1,v0 + * 0C00C8C0 jal 0x80032300 + * 02203021 _move a2,s1 (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 one routine with a forced second argument, then passes its result into a + * second routine alongside the first and third arguments. + * + * **The routine's own second parameter is never read.** `a1` is overwritten by + * `li a1,1` in the first call's delay slot and never restored, so whatever the + * caller placed there is discarded — the parameter exists in the signature but the + * body drops it (the degenerate form of the pass-through idiom: a parameter that is + * silently discarded). It is written into the declaration here purely so the + * argument count matches the two callees' register expectations. + * + * Only `a0` and `a2` need saving because both are live across the first call, and + * the two `move`s into the second call's argument registers are scheduled into the + * first call's exit path and the second call's delay slot. + * + * LIMITS: the two callees are named for their addresses and nothing establishes + * their signatures; the `1` is read from the `li`. That the dropped parameter is a + * real parameter of the original — rather than the source having only two + * parameters and `a2` being something else — follows from `a2` being saved and + * forwarded, which a two-parameter routine could not explain. + */ + +int func_80032258(int a0, int a1); +void func_80032300(int a0, int a1, int a2); + +void func_80036A54(int a0, int unused, int a2) +{ + int result = func_80032258(a0, 1); + + func_80032300(a0, result, a2); +} diff --git a/src/func_80048E20.c b/src/func_80048E20.c new file mode 100644 index 0000000..cc3defa --- /dev/null +++ b/src/func_80048E20.c @@ -0,0 +1,46 @@ +/* + * func_80048E20 — 80 bytes at 0x80048E20..0x80048E70 + * + * Sign-extends a 16-bit second argument, fills a scratch buffer through a call, + * then makes a five-argument call with a fixed function address, the first + * argument saved in `s0`, and the buffer as the fourth argument. + * + * The observed instructions are: + * addiu sp,sp,-48 + * sw s0,40(sp) + * move s0,a0 ; s0 = first argument + * sll a0,a1,0x10 + * sra a0,a0,0x10 ; a0 = (short)a1 <- SIGNED 16-bit + * sw ra,44(sp) + * jal 0x80045110 + * addiu a1,sp,24 ; second argument = the buffer (delay slot) + * lui a0,0x8007 + * addiu a0,a0,-12368 ; a0 = D_8006CFB0 + * move a1,s0 ; second argument = the saved first argument + * move a2,zero + * addiu a3,sp,24 ; fourth argument = the buffer + * jal 0x8006D2FC + * sw zero,16(sp) ; fifth argument = 0 (delay slot) + * lw ra,44(sp) + * lw s0,40(sp) + * addiu sp,sp,48 + * jr ra + * nop + * + * The `sll`/`sra` pair on the second argument shows it is a SIGNED 16-bit value + * promoted to int. The fifth argument goes into the outgoing stack slot at 16(sp) + * of the 48-byte frame. + * + * LIMITS: the function address D_8006CFB0, the buffer size and the two callees are + * hypotheses read from the instruction shape; what the buffer and the callees mean + * is unknown and is not guessed here. Only the compiled bytes are evidence. + */ + +extern char D_8006CFB0[]; + +void func_80048E20(char *a0, short a1) { + short buf[8]; + + func_80045110(a1, buf); + func_8006D2FC((int)D_8006CFB0, a0, 0, buf, 0); +} diff --git a/src/func_8005784C.c b/src/func_8005784C.c new file mode 100644 index 0000000..f0be6b0 --- /dev/null +++ b/src/func_8005784C.c @@ -0,0 +1,50 @@ +/* + * func_8005784C — 80 bytes at 0x8005784C..0x8005789C + * + * One member of a THREE-MEMBER family (with 0x8005784C and 0x800579A0 and + * 0x80057AE0): a ten-argument call whose second argument is a small mode constant + * (3) and whose fourth argument is the first argument masked by whether the + * second argument's low byte is non-zero. The only difference between the members + * is 3. + * + * The observed instructions are: + * addiu sp,sp,-48 + * andi a3,a1,0xff ; low = second argument & 0xff + * andi a2,a2,0xff ; high = third argument & 0xff + * sw a3,32(sp) ; ninth argument = low + * sltu a3,zero,a3 ; (low != 0) + * negu a3,a3 ; -(low != 0) + * li a1,3 ; second argument = 3 + * sw a2,36(sp) ; tenth argument = high + * li a2,-1 ; third argument = -1 + * and a3,a0,a3 ; fourth argument = a0 & -(low != 0) + * sw ra,40(sp) + * sw zero,16(sp) ; fifth argument = 0 + * sw zero,20(sp) ; sixth argument = 0 + * sw zero,24(sp) ; seventh argument = 0 + * jal 0x80057664 + * sw zero,28(sp) ; eighth argument = 0 (delay slot) + * lw ra,40(sp) + * addiu sp,sp,48 + * jr ra + * nop + * + * The `sltu`/`negu` pair is the branchless form of a ternary producing 0 or -1, + * so the fourth argument is `a0` when the low byte is non-zero and 0 otherwise. + * The four zero arguments are materialised as four consecutive stores to the + * outgoing stack slots 16/20/24/28(sp), and the two byte arguments go to 32/36(sp) + * as the ninth and tenth arguments. + * + * LIMITS: the mode constant (3), the callee and the argument types are + * hypotheses read from the instruction shape; what the mode and the callee mean is + * unknown and is not guessed here. The `andi 0xff` pairs show the two byte + * arguments are truncated before use. Only the compiled bytes are evidence. + */ + +void func_8005784C(int a0, int a1, int a2) { + int low = a1 & 0xFF; + int high = a2 & 0xFF; + int mask = low != 0 ? -1 : 0; + + func_80057664(a0, 3, -1, a0 & mask, 0, 0, 0, 0, low, high); +} diff --git a/src/func_800579A0.c b/src/func_800579A0.c new file mode 100644 index 0000000..0523ef0 --- /dev/null +++ b/src/func_800579A0.c @@ -0,0 +1,50 @@ +/* + * func_800579A0 — 80 bytes at 0x800579A0..0x800579F0 + * + * One member of a THREE-MEMBER family (with 0x8005784C and 0x800579A0 and + * 0x80057AE0): a ten-argument call whose second argument is a small mode constant + * (10) and whose fourth argument is the first argument masked by whether the + * second argument's low byte is non-zero. The only difference between the members + * is 10. + * + * The observed instructions are: + * addiu sp,sp,-48 + * andi a3,a1,0xff ; low = second argument & 0xff + * andi a2,a2,0xff ; high = third argument & 0xff + * sw a3,32(sp) ; ninth argument = low + * sltu a3,zero,a3 ; (low != 0) + * negu a3,a3 ; -(low != 0) + * li a1,10 ; second argument = 10 + * sw a2,36(sp) ; tenth argument = high + * li a2,-1 ; third argument = -1 + * and a3,a0,a3 ; fourth argument = a0 & -(low != 0) + * sw ra,40(sp) + * sw zero,16(sp) ; fifth argument = 0 + * sw zero,20(sp) ; sixth argument = 0 + * sw zero,24(sp) ; seventh argument = 0 + * jal 0x80057664 + * sw zero,28(sp) ; eighth argument = 0 (delay slot) + * lw ra,40(sp) + * addiu sp,sp,48 + * jr ra + * nop + * + * The `sltu`/`negu` pair is the branchless form of a ternary producing 0 or -1, + * so the fourth argument is `a0` when the low byte is non-zero and 0 otherwise. + * The four zero arguments are materialised as four consecutive stores to the + * outgoing stack slots 16/20/24/28(sp), and the two byte arguments go to 32/36(sp) + * as the ninth and tenth arguments. + * + * LIMITS: the mode constant (10), the callee and the argument types are + * hypotheses read from the instruction shape; what the mode and the callee mean is + * unknown and is not guessed here. The `andi 0xff` pairs show the two byte + * arguments are truncated before use. Only the compiled bytes are evidence. + */ + +void func_800579A0(int a0, int a1, int a2) { + int low = a1 & 0xFF; + int high = a2 & 0xFF; + int mask = low != 0 ? -1 : 0; + + func_80057664(a0, 10, -1, a0 & mask, 0, 0, 0, 0, low, high); +} diff --git a/src/func_80057AE0.c b/src/func_80057AE0.c new file mode 100644 index 0000000..0feecd4 --- /dev/null +++ b/src/func_80057AE0.c @@ -0,0 +1,50 @@ +/* + * func_80057AE0 — 80 bytes at 0x80057AE0..0x80057B30 + * + * One member of a THREE-MEMBER family (with 0x8005784C and 0x800579A0 and + * 0x80057AE0): a ten-argument call whose second argument is a small mode constant + * (14) and whose fourth argument is the first argument masked by whether the + * second argument's low byte is non-zero. The only difference between the members + * is 14. + * + * The observed instructions are: + * addiu sp,sp,-48 + * andi a3,a1,0xff ; low = second argument & 0xff + * andi a2,a2,0xff ; high = third argument & 0xff + * sw a3,32(sp) ; ninth argument = low + * sltu a3,zero,a3 ; (low != 0) + * negu a3,a3 ; -(low != 0) + * li a1,14 ; second argument = 14 + * sw a2,36(sp) ; tenth argument = high + * li a2,-1 ; third argument = -1 + * and a3,a0,a3 ; fourth argument = a0 & -(low != 0) + * sw ra,40(sp) + * sw zero,16(sp) ; fifth argument = 0 + * sw zero,20(sp) ; sixth argument = 0 + * sw zero,24(sp) ; seventh argument = 0 + * jal 0x80057664 + * sw zero,28(sp) ; eighth argument = 0 (delay slot) + * lw ra,40(sp) + * addiu sp,sp,48 + * jr ra + * nop + * + * The `sltu`/`negu` pair is the branchless form of a ternary producing 0 or -1, + * so the fourth argument is `a0` when the low byte is non-zero and 0 otherwise. + * The four zero arguments are materialised as four consecutive stores to the + * outgoing stack slots 16/20/24/28(sp), and the two byte arguments go to 32/36(sp) + * as the ninth and tenth arguments. + * + * LIMITS: the mode constant (14), the callee and the argument types are + * hypotheses read from the instruction shape; what the mode and the callee mean is + * unknown and is not guessed here. The `andi 0xff` pairs show the two byte + * arguments are truncated before use. Only the compiled bytes are evidence. + */ + +void func_80057AE0(int a0, int a1, int a2) { + int low = a1 & 0xFF; + int high = a2 & 0xFF; + int mask = low != 0 ? -1 : 0; + + func_80057664(a0, 14, -1, a0 & mask, 0, 0, 0, 0, low, high); +} diff --git a/src/func_8006B1CC.c b/src/func_8006B1CC.c new file mode 100644 index 0000000..f286715 --- /dev/null +++ b/src/func_8006B1CC.c @@ -0,0 +1,63 @@ +/* func_8006B1CC — 0x8006B1CC..0x8006B214 (72 bytes). + * + * Original words: + * 8F840550 lw a0,1360(gp) a0 = D_80121E88 (gp + 0x550) + * 27BDFFE8 addiu sp,sp,-24 + * AFBF0014 sw ra,20(sp) + * 10800009 beqz a0,0x8006B200 + * AFB00010 _sw s0,16(sp) (delay slot) + * 8C85000C lw a1,12(a0) <- loop top + * 8CB0018C lw s0,396(a1) + * 0C01AAA2 jal 0x8006AA88 + * 00000000 nop + * 02002021 move a0,s0 + * 1480FFF9 bnez a0,loop + * 00000000 _nop (delay slot) + * 8FBF0014 lw ra,20(sp) <- 0x8006B200 + * 8FB00010 lw s0,16(sp) + * 27BD0018 addiu sp,sp,24 + * 03E00008 jr ra + * 00000000 nop + * + * Walks a linked list held in a global and calls one routine per node, following a + * **second** link to get the next node rather than the one it was reached by. + * + * The loop reads `node->0xc` (a sub-object) and then the sub-object's field 0x18c as + * the successor, so the list is threaded through the inner object, not through the + * node itself. That is why `a1` is reloaded every iteration from `a0`: both the node + * and its sub-object are needed by the call. + * + * The guard is a single `beqz` before the loop, so a null global does nothing at all + * — the frame is still set up and torn down, and the `sw s0` sits in the `beqz` + * delay slot, which is why the saved-register spill appears before the loop rather + * than in a contiguous prologue. + * + * The global is gp-relative (`1360(gp)`, cookbook finding 10), so `gp` is 0x80121938 + * and the address is **0x80121E88**; the symbol needs a `gp` marker row. + * + * LIMITS: the displacements 0xc and 0x18c and the gp offset are read from the bytes. + * That the successor field belongs to the inner object rather than the node is read + * directly from which base each load uses; whether the chain is a list or a tree + * walked by one child is not provable from a single successor pointer. The callee is + * named for its address and its two arguments are inferred from the registers left + * in place. + */ + +extern int D_80121E88; + +void func_8006AA88(int node, int inner); + +void func_8006B1CC(void) +{ + int node = D_80121E88; + + if (node != 0) { + do { + int inner = *(int *)(node + 0xc); + int next = *(int *)(inner + 0x18c); + + func_8006AA88(node, inner); + node = next; + } while (node != 0); + } +} diff --git a/src/func_8008F4AC.c b/src/func_8008F4AC.c new file mode 100644 index 0000000..d3369da --- /dev/null +++ b/src/func_8008F4AC.c @@ -0,0 +1,58 @@ +/* func_8008F4AC — 0x8008F4AC..0x8008F4F4 (72 bytes). + * + * Original words: + * 27BDFFE8 addiu sp,sp,-24 + * 1CA00003 bgtz a1,0x8008F4C0 + * AFBF0010 _sw ra,16(sp) (delay slot) + * 08023D34 j 0x8008F4D0 + * 24050001 _li a1,1 (delay slot) clamp low + * 28A20080 slti v0,a1,128 <- 0x8008F4C0 + * 14400002 bnez v0,0x8008F4D0 + * 00000000 _nop (delay slot) + * 2405007F li a1,127 clamp high + * 3C018012 lui at,0x8012 <- 0x8008F4D0 + * 00240821 addu at,at,a0 + * A0251ED8 sb a1,7896(at) D_80121ED8[index] = value + * 0C03FA1E jal 0x800FE878 + * 00000000 nop + * 8FBF0010 lw ra,16(sp) + * 27BD0018 addiu sp,sp,24 + * 03E00008 jr ra + * 00000000 nop + * + * Clamps a value into 1..127, stores it into a byte array indexed by the first + * argument, and then calls an update routine. + * + * The clamp is **two-sided and open at both ends**: `value <= 0` becomes 1 and + * `value >= 128` becomes 127, so the stored byte always has its high bit clear — + * which is consistent with a signed `char` target. The low clamp is reached by a + * `bgtz` falling through to a `j` whose delay slot loads the 1, and the high clamp + * by a `slti`/`bnez` pair; both arms converge on the store, so this is one shared + * epilogue for the clamp and not two returns. + * + * The store is the symbol-plus-register macro form (`lui at` + `addu at,at,a0` + + * `sb %lo(at)`), so the base is the global array at **0x80121ED8** — the same array + * that `func_8008F4F4` reads from, which is independent corroboration that the + * symbol is a byte array and that the index is used raw (no scaling). + * + * The update routine receives no fresh argument setup, so it is called with `a0` + * (the index) and `a1` (the clamped value) still in place. + * + * LIMITS: the bounds 1, 128 and 127 and the displacement are read from the bytes; + * that the range is a hardware or protocol limit rather than a policy choice is not + * observable. The callee is named for its address. + */ + +extern signed char D_80121ED8[]; + +void func_800FE878(int index, int value); + +void func_8008F4AC(int index, int value) +{ + if (value <= 0) + value = 1; + else if (value >= 128) + value = 127; + D_80121ED8[index] = value; + func_800FE878(index, value); +}