diff --git a/config/regions.tsv b/config/regions.tsv index 5eeb724..1fd74b4 100644 --- a/config/regions.tsv +++ b/config/regions.tsv @@ -111,6 +111,7 @@ 0x80031F78 0x80031FC4 src/func_80031F78.c 0x800321EC 0x800321F8 src/func_800321EC.c 0x80034A80 0x80034ABC src/func_80034A80.c +0x800354F8 0x80035548 src/func_800354F8.c 0x80036308 0x80036328 src/func_80036308.c 0x80036328 0x8003636C src/func_80036328.c 0x8003636C 0x80036378 src/func_8003636C.c @@ -150,6 +151,7 @@ 0x8005182C 0x80051864 src/func_8005182C.c 0x80052C98 0x80052CAC src/func_80052C98.c 0x80057524 0x80057564 src/func_80057524.c +0x80057748 0x80057798 src/func_80057748.c 0x8005784C 0x8005789C src/func_8005784C.c 0x800579A0 0x800579F0 src/func_800579A0.c 0x80057AE0 0x80057B30 src/func_80057AE0.c diff --git a/src/func_800354F8.c b/src/func_800354F8.c new file mode 100644 index 0000000..ffa1774 --- /dev/null +++ b/src/func_800354F8.c @@ -0,0 +1,59 @@ +/* func_800354F8 — 0x800354F8..0x80035548 (80 bytes). + * + * Original words: + * 27BDFFE8 addiu sp,sp,-24 + * AFB00010 sw s0,16(sp) + * AFBF0014 sw ra,20(sp) + * 0C00CEB7 jal 0x80033ADC + * 00808021 _move s0,a0 (delay slot) keep the argument + * 0C00D18E jal 0x80034638 + * 02002021 _move a0,s0 (delay slot) + * 0C00D2A0 jal 0x80034A80 + * 02002021 _move a0,s0 (delay slot) + * 0C00C7AF jal 0x80031EBC + * 02002021 _move a0,s0 (delay slot) + * 0C00D2AF jal 0x80034ABC + * 02002021 _move a0,s0 (delay slot) + * 0C00D394 jal 0x80034E50 + * 02002021 _move a0,s0 (delay slot) + * 8FBF0014 lw ra,20(sp) + * 8FB00010 lw s0,16(sp) + * 27BD0018 addiu sp,sp,24 + * 03E00008 jr ra + * 00000000 nop + * + * Six calls in sequence, all with the routine's single argument, none returning a value. + * + * The first call receives the argument directly (`move s0,a0` is in its delay slot because + * `s0` is the *save*, not the setup), and every later call is set up by `move a0,s0` in its + * own delay slot — so all six call sites share one saved register and the frame exists only + * for that. Six identical `move`-into-delay-slot patterns is the signature of a flat call + * sequence rather than a loop, and the order below is the order they appear. + * + * One of the callees, `0x80034A80`, is already registered in this project as a + * compiler-artefact row (a 112-byte frame receiving a 16-byte struct copy that is then + * discarded), which is independent corroboration that this address is a real routine start + * and that this row calls what it appears to call. + * + * LIMITS: the callees are named for their addresses and nothing establishes their + * signatures beyond the single register set at each site; all six are written as taking one + * `int` because that is all the original passes. Whether any of them returns a value is + * invisible here, since no result is used. + */ + +void func_80033ADC(int a0); +void func_80034638(int a0); +void func_80034A80(int a0); +void func_80031EBC(int a0); +void func_80034ABC(int a0); +void func_80034E50(int a0); + +void func_800354F8(int a0) +{ + func_80033ADC(a0); + func_80034638(a0); + func_80034A80(a0); + func_80031EBC(a0); + func_80034ABC(a0); + func_80034E50(a0); +} diff --git a/src/func_80057748.c b/src/func_80057748.c new file mode 100644 index 0000000..a11b3fc --- /dev/null +++ b/src/func_80057748.c @@ -0,0 +1,58 @@ +/* func_80057748 — 0x80057748..0x80057798 (80 bytes). + * + * Original words: + * 27BDFFD0 addiu sp,sp,-48 + * 30A700FF andi a3,a1,0xff narrow the second argument + * 30C600FF andi a2,a2,0xff narrow the third + * AFA70020 sw a3,32(sp) local[8] = narrowed a1 + * 0007382B sltu a3,zero,a3 a3 = (narrowed a1 != 0) + * 00073823 negu a3,a3 a3 = 0 or -1 + * 24050001 li a1,1 + * AFA60024 sw a2,36(sp) local[9] = narrowed a2 + * 2406FFFF li a2,-1 + * 00873824 and a3,a0,a3 a3 = a0 & that mask + * AFBF0028 sw ra,40(sp) + * AFA00010 sw zero,16(sp) local[0] = 0 + * AFA00014 sw zero,20(sp) local[1] = 0 + * AFA00018 sw zero,24(sp) local[2] = 0 + * 0C015D99 jal 0x80057664 + * AFA0001C _sw zero,28(sp) (delay slot) local[3] = 0 + * 8FBF0028 lw ra,40(sp) + * 27BD0030 addiu sp,sp,48 + * 03E00008 jr ra + * 00000000 nop + * + * **The zero-or-minus-one mask is the interesting construction, and it is the + * branchless conditional.** `sltu a3,zero,a3` sets `a3` to 1 iff the narrowed argument is + * non-zero, and the following `negu a3,a3` turns that into 0 or **-1** — so `and a3,a0,a3` + * either keeps `a0` or clears it with **no branch at all**. That is exactly what a + * conditional expression `(narrowed ? a0 : 0)` compiles to when cc1 avoids a branch, and it + * is why the same argument is written as a ternary here rather than an `if`. + * + * `negu`, not the trapping `sub`, is again positive evidence for this toolchain (finding + * 26's direction), and it appears twice in this family. + * + * The frame is 48 bytes and the call's outgoing register arguments are `a0`, 1, -1 and the + * masked value, while the four words at 16-28(sp) are zeroed — which is the outgoing stack + * argument area, so the callee takes **eight** arguments and the last four are all zero. + * The two narrowed values land at 32 and 36(sp), i.e. beyond the outgoing area, so they are + * frame locals that this routine writes and never reads. + * + * LIMITS: the mask 0xff, the constants 1 and -1, and the frame layout are read from the + * bytes. That the narrowed values are never read back follows from their frame offsets being + * past the argument area; nothing establishes why they are stored. Whether the callee's last + * four arguments are genuinely meant to be zero or are a partially filled structure cannot + * be told from this body, and the eight-argument signature is inferred purely from the four + * zeroed stack words. + */ + +void func_80057664(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, + int a8, int a9); + +void func_80057748(int a0, int a1, int a2) +{ + int narrowed1 = a1 & 0xff; + int narrowed2 = a2 & 0xff; + + func_80057664(a0, 1, -1, narrowed1 ? a0 : 0, 0, 0, 0, 0, narrowed1, narrowed2); +}