From 2775d2e077ea1957278f2896d35b6c2a2a75a35b Mon Sep 17 00:00:00 2001 From: Christopher Williams Date: Wed, 23 Sep 2026 23:13:02 -0400 Subject: [PATCH] =?UTF-8?q?phase8:=20merge=20cycle=203=20=E2=80=94=20g0007?= =?UTF-8?q?=20and=20g0030=20closed,=20137=20regions=20/=20128=20bodies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 22 claims accepted (worker A's 8 new + 9 gp rows, worker C's 6 pure-C rows), 28 skipped as already registered, 0 rejected. Candidate gate c_regions=137, 0 differing bytes, SHA-1 e173426c; promoted, then make check green (200 tests, regions=137 disagreements=0). Worker C closed two duplicate groups that other sessions had left as near-matches: - g0007 (0x800F7FB4 x3 addresses): the unexplained 8-byte frame was cc1 loop restructuring, not a calling-convention need. `for (i = n-1; i != -1; i--) *p++ = 0;` makes cc1 emit an unused subu/addu sp pair; an explicit guard plus a do/while gives the exact 36 bytes. - g0030 (0x8009E8D0 x2 addresses): the abs-of-3-component-difference function that workers A and B both recorded as near-matches. The two BIOS stubs are still policy-gated (they need an inline-asm statement) and are not in this merge. Distinct matched bodies: 128. --- config/regions.tsv | 22 +++++++++++++++++++ config/symbols.tsv | 7 ++++++ src/func_800127F0.c | 40 ++++++++++++++++++++++++++++++++++ src/func_800171D8.c | 40 ++++++++++++++++++++++++++++++++++ src/func_80017D1C.c | 40 ++++++++++++++++++++++++++++++++++ src/func_8001CE40.c | 40 ++++++++++++++++++++++++++++++++++ src/func_8003B2F0.c | 53 +++++++++++++++++++++++++++++++++++++++++++++ src/func_80058288.c | 32 +++++++++++++++++++++++++++ src/func_80068F40.c | 39 +++++++++++++++++++++++++++++++++ src/func_80082914.c | 36 ++++++++++++++++++++++++++++++ src/func_80083504.c | 41 +++++++++++++++++++++++++++++++++++ src/func_8008352C.c | 46 +++++++++++++++++++++++++++++++++++++++ src/func_80089314.c | 30 +++++++++++++++++++++++++ src/func_800920DC.c | 30 +++++++++++++++++++++++++ src/func_8009E8D0.c | 42 +++++++++++++++++++++++++++++++++++ src/func_800AA56C.c | 40 ++++++++++++++++++++++++++++++++++ src/func_800F5B40.c | 39 +++++++++++++++++++++++++++++++++ src/func_800F7FB4.c | 32 +++++++++++++++++++++++++++ src/func_800FB5D4.c | 17 +++++++++++++++ src/func_80108710.c | 37 +++++++++++++++++++++++++++++++ src/func_8010A8B0.c | 34 +++++++++++++++++++++++++++++ 21 files changed, 737 insertions(+) create mode 100644 src/func_800127F0.c create mode 100644 src/func_800171D8.c create mode 100644 src/func_80017D1C.c create mode 100644 src/func_8001CE40.c create mode 100644 src/func_8003B2F0.c create mode 100644 src/func_80058288.c create mode 100644 src/func_80068F40.c create mode 100644 src/func_80082914.c create mode 100644 src/func_80083504.c create mode 100644 src/func_8008352C.c create mode 100644 src/func_80089314.c create mode 100644 src/func_800920DC.c create mode 100644 src/func_8009E8D0.c create mode 100644 src/func_800AA56C.c create mode 100644 src/func_800F5B40.c create mode 100644 src/func_800F7FB4.c create mode 100644 src/func_800FB5D4.c create mode 100644 src/func_80108710.c create mode 100644 src/func_8010A8B0.c diff --git a/config/regions.tsv b/config/regions.tsv index 37b0dcb..0f741ac 100644 --- a/config/regions.tsv +++ b/config/regions.tsv @@ -11,11 +11,13 @@ # # Matched so far: 0x80012780 0x8001278C src/func_80012780.c +0x800127F0 0x8001281C src/func_800127F0.c 0x8001281C 0x80012834 src/func_8001281C.c 0x80013C88 0x80013C90 src/func_80013C88.c 0x80016110 0x80016120 src/func_80016110.c 0x80016158 0x80016174 src/func_80016158.c 0x80016E50 0x80016E68 src/func_80016E50.c +0x800171D8 0x80017200 src/func_800171D8.c 0x800179B8 0x800179CC src/func_800179B8.c 0x800179CC 0x800179D4 src/func_800179CC.c 0x800179D4 0x800179E0 src/func_800179D4.c @@ -23,9 +25,11 @@ 0x80017AE8 0x80017AF8 src/func_80017AE8.c 0x80017C50 0x80017C60 src/func_80017C50.c 0x80017C60 0x80017C6C src/func_80017C60.c +0x80017D1C 0x80017D48 src/func_80017D1C.c 0x80017DD0 0x80017DF0 src/func_80017DD0.c 0x80019C04 0x80019C10 src/func_80019C04.c 0x8001AA9C 0x8001AAA8 src/func_8001AA9C.c +0x8001CE40 0x8001CE70 src/func_8001CE40.c 0x80021F50 0x80021F64 src/func_80021F50.c 0x80021F88 0x80021FA8 src/func_80021F88.c 0x80022FB8 0x80022FCC src/func_80022FB8.c @@ -50,13 +54,16 @@ 0x80036378 0x80036380 src/func_80036378.c 0x80038788 0x80038790 src/func_80038788.c 0x80038790 0x8003879C src/func_80038790.c +0x8003B2F0 0x8003B320 src/func_8003B2F0.c 0x80042088 0x80042090 src/func_80042088.c 0x80042D64 0x80042D88 src/func_80042D64.c 0x8004C090 0x8004C0AC src/func_8004C090.c 0x8004C0F0 0x8004C110 src/func_8004C0F0.c 0x80057DFC 0x80057E04 src/func_80057DFC.c +0x80058288 0x800582AC src/func_80058288.c 0x80065B6C 0x80065B8C src/func_80065B6C.c 0x80068470 0x8006848C src/func_80068470.c +0x80068F40 0x80068F6C src/func_80068F40.c 0x80068F98 0x80068FA8 src/func_80068F98.c 0x800697A4 0x800697C4 src/func_800697A4.c 0x8006EC94 0x8006ECD4 src/func_8006EC94.c @@ -65,7 +72,11 @@ 0x8006FA78 0x8006FAA0 src/func_8006FA78.c 0x8007049C 0x800704BC src/func_8007049C.c 0x8007DC40 0x8007DC4C src/func_8007DC40.c +0x80082914 0x80082944 src/func_80082914.c +0x80083504 0x8008352C src/func_80083504.c +0x8008352C 0x8008355C src/func_8008352C.c 0x80085B80 0x80085B90 src/func_80085B80.c +0x80089314 0x80089338 src/func_80089314.c 0x80089C4C 0x80089C54 src/func_80042088.c 0x80089C54 0x80089C64 src/func_80089C54.c 0x80089C64 0x80089C74 src/func_80089C64.c @@ -79,8 +90,12 @@ 0x800912D4 0x800912FC src/func_800912D4.c 0x800912FC 0x8009132C src/func_800912FC.c 0x800920BC 0x800920DC src/func_800920BC.c +0x800920DC 0x80092104 src/func_800920DC.c 0x800943C4 0x800943E0 src/func_800943C4.c +0x8009E8D0 0x8009E95C src/func_8009E8D0.c +0x800A45E0 0x800A466C src/func_8009E8D0.c 0x800A74BC 0x800A74D0 src/func_800A74BC.c +0x800AA56C 0x800AA59C src/func_800AA56C.c 0x800ACC00 0x800ACC20 src/func_800ACC00.c 0x800AE0F4 0x800AE10C src/func_800AE0F4.c 0x800AF1FC 0x800AF20C src/func_800AF1FC.c @@ -88,7 +103,9 @@ 0x800B6BDC 0x800B6C14 src/func_800B6BDC.c 0x800BBDEC 0x800BBDF8 src/func_800BBDEC.c 0x800F3160 0x800F316C src/func_800F3160.c maspsx=off +0x800F5B40 0x800F5B70 src/func_800F5B40.c 0x800F7A84 0x800F7A94 src/func_800F7A84.c +0x800F7FB4 0x800F7FD8 src/func_800F7FB4.c 0x800F8294 0x800F82A4 src/func_800F8294.c 0x800F8ACC 0x800F8ADC src/func_800F8ACC.c 0x800F8ADC 0x800F8AEC src/func_800F8ADC.c @@ -99,6 +116,7 @@ 0x800F8FE4 0x800F8FF8 src/func_800F8FE4.c maspsx=off 0x800F8FF8 0x800F9008 src/func_800F8FF8.c 0x800FB13C 0x800FB1BC src/func_800FB13C.c +0x800FB5D4 0x800FB5DC src/func_800FB5D4.c 0x800FB5E4 0x800FB5FC src/func_800FB5E4.c 0x800FB6C4 0x800FB6D8 src/func_800FB6C4.c 0x800FBDF8 0x800FBE04 src/func_800FBDF8.c @@ -111,6 +129,8 @@ 0x80100318 0x80100334 src/func_80100318.c 0x80100964 0x8010097C src/func_80100964.c 0x80102B10 0x80102B2C src/func_80102B10.c maspsx=off +0x80103C7C 0x80103CA0 src/func_800F7FB4.c +0x80103F84 0x80103FA8 src/func_800F7FB4.c 0x8010513C 0x80105148 src/func_8010513C.c 0x80105B34 0x80105B54 src/func_80105B34.c 0x80105B54 0x80105B68 src/func_80105B54.c @@ -121,7 +141,9 @@ 0x80107E68 0x80107E74 src/func_80107E68.c 0x80107E74 0x80107E84 src/func_80107E74.c 0x80107E84 0x80107E90 src/func_80107E84.c +0x80108710 0x80108734 src/func_80108710.c 0x80109300 0x80109314 src/func_80109300.c 0x80109314 0x80109338 src/func_800F8F9C.c 0x80109F38 0x80109F48 src/func_80109F38.c +0x8010A8B0 0x8010A8D8 src/func_8010A8B0.c 0x8010AAA0 0x8010AABC src/func_8010AAA0.c diff --git a/config/symbols.tsv b/config/symbols.tsv index a57d235..c0a5532 100644 --- a/config/symbols.tsv +++ b/config/symbols.tsv @@ -70,7 +70,14 @@ D_80121BF8 0x80121BF8 gp D_80121BF9 0x80121BF9 gp D_80121BFC 0x80121BFC gp D_80121D5C 0x80121D5C gp +D_80121D5E 0x80121D5E gp +D_80121D60 0x80121D60 gp +D_80121D62 0x80121D62 gp +D_80121D64 0x80121D64 gp D_80121D68 0x80121D68 gp +D_80121D6A 0x80121D6A gp +D_80121D6C 0x80121D6C gp +D_80121D70 0x80121D70 gp D_80121DFC 0x80121DFC gp D_80121E04 0x80121E04 gp D_80121E10 0x80121E10 gp diff --git a/src/func_800127F0.c b/src/func_800127F0.c new file mode 100644 index 0000000..3689660 --- /dev/null +++ b/src/func_800127F0.c @@ -0,0 +1,40 @@ +/* + * func_800127F0 — 44 bytes at 0x800127F0..0x8001281C + * + * Byte-identical reconstruction of a test-and-set returning a status. `lhu` + * fixes an unsigned 16-bit read at +6 (cookbook finding 7: plain `char` is + * unsigned on this target, so `unsigned short` has to be stated to get `lhu`). + * The `ori v0,v1,0x2` is computed in the branch delay slot (it is independent + * of the branch), but the store only happens on the fall-through path, so the + * branch direction is `if (bit set) return 0x1d;`. The `j` over the + * `li v0,0x1d` carries `clear v0` (the `return 0`) in its delay slot + * (cookbook finding 8). + * + * The observed instructions are: + * lhu v1,0x6(a0) v1 = *(unsigned short *)(base + 6) + * nop (load delay) + * andi v0,v1,0x2 v0 = v1 & 2 + * bne v0,zero,set if the bit is already set, go to the 0x1d return + * ori v0,v1,0x2 (delay slot) v0 = v1 | 2 + * sh v0,0x6(a0) *(unsigned short *)(base + 6) = v1 | 2 + * j exit + * clear v0 (delay slot) return 0 + * set: + * li v0,0x1d return 0x1d + * exit: + * jr ra + * nop + * + * LIMITS: the offset, the width, the bit mask 2 and the constant 0x1d are + * evidence; the base's real type, the flag's meaning and the reason for the two + * distinct return values are hypotheses. + */ + +int func_800127F0(char *base) { + unsigned short value = *(unsigned short *)(base + 6); + if ((value & 2) != 0) { + return 0x1d; + } + *(unsigned short *)(base + 6) = value | 2; + return 0; +} diff --git a/src/func_800171D8.c b/src/func_800171D8.c new file mode 100644 index 0000000..d366614 --- /dev/null +++ b/src/func_800171D8.c @@ -0,0 +1,40 @@ +/* + * func_800171D8 — 36 bytes at 0x800171D8..0x80017200 + * + * Byte-identical reconstruction of a descending loop that clears the first word + * of every 56-byte (0x38) element, plus one `gp`-relative clear. The loop + * counter is a signed BYTE OFFSET running from 0xce8 down to 0 in steps of + * 0x38, tested with `bgez`; the base is added with `lui` + `addu` (not + * `addiu`) because the base is a symbol combined with a runtime offset + * (cookbook finding 5). The branch delay slot is `nop` (cookbook finding 8). + * 0xce8 is 59 * 0x38, so the loop clears 60 elements. + * + * The observed instructions are: + * li v0,0xce8 v0 = 0xce8 (byte offset of the LAST element) + * loop: + * lui at,0x8012 + * addu at,at,v0 at = D_80124CC0 + offset + * sw zero,0x4cc0(at) *(int *)(at) = 0 + * addiu v0,v0,-0x38 offset -= 0x38 + * bgez v0,loop while (offset >= 0) + * nop + * sw zero,0xa0(gp) D_801219D8 = 0 + * jr ra + * nop + * + * LIMITS: the stride 0x38, the start offset 0xce8, the element count and the + * cleared width are evidence; the base's type, the symbol's name and meaning, + * and whether the original source counted elements (`i >= 0` with `i * 56`) + * rather than bytes are hypotheses. + */ + +extern char D_80124CC0[]; +extern int D_801219D8; + +void func_800171D8(void) { + int offset; + for (offset = 0xce8; offset >= 0; offset -= 0x38) { + *(int *)(D_80124CC0 + offset) = 0; + } + D_801219D8 = 0; +} diff --git a/src/func_80017D1C.c b/src/func_80017D1C.c new file mode 100644 index 0000000..27a395a --- /dev/null +++ b/src/func_80017D1C.c @@ -0,0 +1,40 @@ +/* + * func_80017D1C — 44 bytes at 0x80017D1C..0x80017D48 + * + * Byte-identical reconstruction of a conditional bit set/clear on a halfword + * field, with ONE shared store at the end. `andi a1,a1,0xff` is the type-driven + * mask for an `unsigned char` parameter (cookbook finding 7). The halfword is + * loaded in EACH arm rather than once before the branch — GCC 2.7's local CSE + * does not merge a load across the branch — and the `ori`/`andi` sits in the + * `j` delay slot of the taken arm (cookbook finding 8). `~4` becomes + * `andi 0xfffb` because the value is a 16-bit unsigned. + * + * The observed instructions are: + * andi a1,a1,0xff flag &= 0xff + * beq a1,zero,clear if (flag == 0) goto the clear arm + * nop + * lhu v0,0x6(a0) v0 = *(unsigned short *)(base + 6) + * j store + * ori v0,v0,0x4 (delay slot) v0 |= 4 + * clear: + * lhu v0,0x6(a0) v0 = *(unsigned short *)(base + 6) + * nop (load delay) + * andi v0,v0,0xfffb v0 &= ~4 + * store: + * jr ra + * sh v0,0x6(a0) (delay slot) *(unsigned short *)(base + 6) = v0 + * + * LIMITS: the offset, the width, the bit mask 4 and the branch direction are + * evidence; the base's real type, the field's meaning and the flag's meaning are + * hypotheses. + */ + +void func_80017D1C(char *base, unsigned char flag) { + unsigned short value; + if (flag != 0) { + value = *(unsigned short *)(base + 6) | 4; + } else { + value = *(unsigned short *)(base + 6) & ~4; + } + *(unsigned short *)(base + 6) = value; +} diff --git a/src/func_8001CE40.c b/src/func_8001CE40.c new file mode 100644 index 0000000..fb1552a --- /dev/null +++ b/src/func_8001CE40.c @@ -0,0 +1,40 @@ +/* + * func_8001CE40 — 48 bytes at 0x8001CE40..0x8001CE70 + * + * Byte-identical reconstruction of a two-halfword packed-value expansion with a + * zero special case. `sra` (not `srl`) fixes an ARITHMETIC shift of the high + * half, and `srav` an arithmetic shift by the variable amount in `v1` — so the + * shifted value is signed. The low half is masked with `andi …,0xffff`, the + * shift count is `(value >> 16) - 1` and it is computed in the `beq` delay slot + * (unconditionally, cookbook finding 8); the final add is in the `j` delay slot. + * + * The observed instructions are: + * andi a1,a0,0xffff a1 = value & 0xffff + * sra v0,a0,0x10 v0 = value >> 16 (arithmetic) + * beq a0,zero,zero_case if (value == 0) return 0x2000 + * addiu v1,v0,-0x1 (delay slot) v1 = (value >> 16) - 1 + * li v0,0x1000 v0 = 0x1000 + * subu v0,v0,a1 v0 = 0x1000 - low + * srav v0,v0,v1 v0 >>= v1 (arithmetic) + * j exit + * addu v0,a1,v0 (delay slot) v0 = low + v0 + * zero_case: + * li v0,0x2000 return 0x2000 + * exit: + * jr ra + * nop + * + * LIMITS: the masks, the shifts, the constants 0x1000/0x2000 and the special + * case for zero are evidence; the packed encoding's meaning is a hypothesis, as + * is whether the original source used `int` or `unsigned` for the argument + * (the `sra`/`srav` fix signedness of the shift, not of the parameter). + */ + +int func_8001CE40(int value) { + int low = value & 0xffff; + int shift = (value >> 16) - 1; + if (value == 0) { + return 0x2000; + } + return low + ((0x1000 - low) >> shift); +} diff --git a/src/func_8003B2F0.c b/src/func_8003B2F0.c new file mode 100644 index 0000000..eb6cbaa --- /dev/null +++ b/src/func_8003B2F0.c @@ -0,0 +1,53 @@ +/* + * func_8003B2F0 — 48 bytes at 0x8003B2F0..0x8003B320 + * + * Byte-identical reconstruction of a nine-field reset using BOTH halfword and + * word stores. `gp` is 0x80121938 (crt0 sets it at 0x800FB3E4), so the offsets + * 0x424/0x426/0x428/0x42a/0x42c/0x430/0x432/0x434/0x438 name 0x80121D5C, + * 0x80121D5E, 0x80121D60, 0x80121D62, 0x80121D64, 0x80121D68, 0x80121D6A, + * 0x80121D6C and 0x80121D70; all nine need the registry's `gp` marker so the + * accesses are emitted `%gp_rel` (cookbook finding 10). The constant -1 is + * materialised once in `v0` and reused for both `sh` stores; no delay-slot fill + * is available because every store source is `zero` or an already-loaded `v0` + * (cookbook finding 8). + * + * The observed instructions are: + * li v0,-0x1 v0 = -1 (scratch, reused) + * sh v0,0x424(gp) D_80121D5C = -1 + * sh zero,0x426(gp) D_80121D5E = 0 + * sh zero,0x428(gp) D_80121D60 = 0 + * sh zero,0x42a(gp) D_80121D62 = 0 + * sw zero,0x42c(gp) D_80121D64 = 0 + * sh v0,0x430(gp) D_80121D68 = -1 + * sh zero,0x432(gp) D_80121D6A = 0 + * sh zero,0x434(gp) D_80121D6C = 0 + * sw zero,0x438(gp) D_80121D70 = 0 + * jr ra + * nop + * + * LIMITS: the nine offsets, the two widths and the values -1/0 are evidence; + * the globals' names, types and meanings are hypotheses, and the fields are + * declared separately because only the addresses are evidence. + */ + +extern short D_80121D5C; +extern short D_80121D5E; +extern short D_80121D60; +extern short D_80121D62; +extern int D_80121D64; +extern short D_80121D68; +extern short D_80121D6A; +extern short D_80121D6C; +extern int D_80121D70; + +void func_8003B2F0(void) { + D_80121D5C = -1; + D_80121D5E = 0; + D_80121D60 = 0; + D_80121D62 = 0; + D_80121D64 = 0; + D_80121D68 = -1; + D_80121D6A = 0; + D_80121D6C = 0; + D_80121D70 = 0; +} diff --git a/src/func_80058288.c b/src/func_80058288.c new file mode 100644 index 0000000..08311a6 --- /dev/null +++ b/src/func_80058288.c @@ -0,0 +1,32 @@ +/* + * func_80058288 — 36 bytes at 0x80058288..0x800582AC + * + * Byte-identical reconstruction of a scaled array-index address, element size + * 44 (0x2c). The multiply is strength-reduced rather than a real `mult`, so the + * element size was a literal at expansion time (cookbook finding 12): + * `sll 1` / `addu` / `sll 2` / `subu` / `sll 2` is `((i*2 + i)*4 - i)*4` = + * `i * 44`. The base is a symbol address and so uses the `addiu` form + * (cookbook findings 4 and 5), and the addition lands in the `jr` delay slot + * (cookbook finding 8). + * + * The observed instructions are: + * sll v0,a0,0x1 + * addu v0,v0,a0 + * sll v0,v0,0x2 + * subu v0,v0,a0 + * sll v0,v0,0x2 v0 = index * 44 + * lui v1,0x8013 + * addiu v1,v1,0x202c v1 = D_8013202C (symbol form) + * jr ra + * addu v0,v0,v1 (delay slot) v0 = base + index * 44 + * + * LIMITS: the element size 44 and the base address are evidence; whether the + * base is an array of 44-byte structs or a byte array is a hypothesis, as are + * the symbol's name and meaning. + */ + +extern char D_8013202C[]; + +char *func_80058288(int index) { + return D_8013202C + index * 44; +} diff --git a/src/func_80068F40.c b/src/func_80068F40.c new file mode 100644 index 0000000..45dbba4 --- /dev/null +++ b/src/func_80068F40.c @@ -0,0 +1,39 @@ +/* + * func_80068F40 — 44 bytes at 0x80068F40..0x80068F6C + * + * Byte-identical reconstruction of ten word clears on one object. The offsets + * are NOT contiguous: 0x13c is skipped (the sequence runs 0x124, 0x128, 0x12c, + * 0x130, 0x134, 0x138, 0x140, 0x144, 0x148, 0x14c), so these are ten separate + * fields rather than one array — that gap is evidence, not a transcription + * error. `zero` needs no source register, so the last store is the only thing + * that can fill the `jr` delay slot (cookbook finding 8). + * + * The observed instructions are: + * sw zero,0x124(a0) *(int *)(object + 0x124) = 0 + * sw zero,0x128(a0) *(int *)(object + 0x128) = 0 + * sw zero,0x12c(a0) *(int *)(object + 0x12c) = 0 + * sw zero,0x130(a0) *(int *)(object + 0x130) = 0 + * sw zero,0x134(a0) *(int *)(object + 0x134) = 0 + * sw zero,0x138(a0) *(int *)(object + 0x138) = 0 + * sw zero,0x140(a0) *(int *)(object + 0x140) = 0 + * sw zero,0x144(a0) *(int *)(object + 0x144) = 0 + * sw zero,0x148(a0) *(int *)(object + 0x148) = 0 + * jr ra + * sw zero,0x14c(a0) (delay slot) *(int *)(object + 0x14c) = 0 + * + * LIMITS: the ten offsets and the 32-bit width are evidence; the object's real + * type and the fields' meanings are hypotheses. + */ + +void func_80068F40(char *object) { + *(int *)(object + 0x124) = 0; + *(int *)(object + 0x128) = 0; + *(int *)(object + 0x12c) = 0; + *(int *)(object + 0x130) = 0; + *(int *)(object + 0x134) = 0; + *(int *)(object + 0x138) = 0; + *(int *)(object + 0x140) = 0; + *(int *)(object + 0x144) = 0; + *(int *)(object + 0x148) = 0; + *(int *)(object + 0x14c) = 0; +} diff --git a/src/func_80082914.c b/src/func_80082914.c new file mode 100644 index 0000000..6951722 --- /dev/null +++ b/src/func_80082914.c @@ -0,0 +1,36 @@ +/* + * func_80082914 — 48 bytes at 0x80082914..0x80082944 + * + * Byte-identical reconstruction of a three-bit field insert into a word field + * of a 16-byte-stride element. The element index is scaled with a shift, so the + * stride was a literal at expansion time (cookbook finding 12); the element + * address is computed ONCE (`addu a0,a0,v0`) and used for both the load and the + * store, which is GCC's CSE of the repeated address expression. The mask + * 0xffffc7ff is `li v1,-0x3801` (a sign-extended 16-bit immediate, so one + * instruction); `andi a1,a1,0x7` and `sll a1,a1,0xb` insert three bits at bit + * 11. The store lands in the `jr` delay slot (cookbook finding 8). + * + * The observed instructions are: + * sll a0,a0,0x4 index * 16 + * li v1,-0x3801 v1 = 0xffffc7ff + * lui v0,0x8012 + * lw v0,0x2308(v0) v0 = D_80122308 (a pointer) + * andi a1,a1,0x7 value &= 7 + * addu a0,a0,v0 element address + * lw v0,0x0(a0) v0 = *(int *)element + * sll a1,a1,0xb value <<= 11 + * and v0,v0,v1 v0 &= 0xffffc7ff + * or v0,v0,a1 v0 |= value + * jr ra + * sw v0,0x0(a0) (delay slot) *(int *)element = v0 + * + * LIMITS: the stride 16, the mask, the shift and the 32-bit width are evidence; + * the global's name, type and meaning and the field's meaning are hypotheses. + */ + +extern char *D_80122308; + +void func_80082914(int index, int value) { + *(int *)(D_80122308 + index * 16) = + (*(int *)(D_80122308 + index * 16) & 0xffffc7ff) | ((value & 7) << 11); +} diff --git a/src/func_80083504.c b/src/func_80083504.c new file mode 100644 index 0000000..de67e12 --- /dev/null +++ b/src/func_80083504.c @@ -0,0 +1,41 @@ +/* + * func_80083504 — 40 bytes at 0x80083504..0x8008352C + * + * Byte-identical reconstruction of a read-modify-write that clears bit 15 of a + * word field in a 16-byte-stride element. The element index is scaled with a + * shift, not a real `mult`, so the stride was a literal at expansion time + * (cookbook finding 12). The pointer global is loaded with the assembler macro + * form expanding into the destination register (cookbook finding 2; + * `addiu`-adjusted halves 0x8012 / 0x2308 = 0x80122308). The mask 0xffff7fff + * needs two instructions (`lui` + `ori`) because it does not fit a signed + * 16-bit immediate, and the store lands in the `jr` delay slot (cookbook + * finding 8). + * + * The observed instructions are: + * lui v1,0xffff + * lui v0,0x8012 + * lw v0,0x2308(v0) v0 = D_80122308 (a pointer) + * sll a0,a0,0x4 index * 16 + * addu a0,a0,v0 element address + * lw v0,0x0(a0) v0 = *(int *)element + * ori v1,v1,0x7fff v1 = 0xffff7fff + * and v0,v0,v1 clear bit 15 + * jr ra + * sw v0,0x0(a0) (delay slot) *(int *)element = v0 + * + * The element address must be the DIRECT expression. Naming it + * (`int *element = (int *)(D_80122308 + index * 16);`) changes the register + * allocation: the address lands in `$3` and the mask in `$5` instead of the + * address in `a0` and the mask in `v1`, which is 9 differing bytes at the same + * instruction count and order. This is a register-allocation tie-break, and the + * direct expression is the form that reproduces it. + * + * LIMITS: the stride 16, the mask and the 32-bit width are evidence; the + * global's name, type and meaning and the bit's meaning are hypotheses. + */ + +extern char *D_80122308; + +void func_80083504(int index) { + *(int *)(D_80122308 + index * 16) &= 0xffff7fff; +} diff --git a/src/func_8008352C.c b/src/func_8008352C.c new file mode 100644 index 0000000..c01df07 --- /dev/null +++ b/src/func_8008352C.c @@ -0,0 +1,46 @@ +/* + * func_8008352C — 48 bytes at 0x8008352C..0x8008355C + * + * Byte-identical reconstruction of a conditional bit set on a word field of a + * 16-byte-stride element. The stride is a literal shift (cookbook finding 12), + * and the element address is computed once and used for both the load and the + * store. `andi v0,v1,0x7ff` tests the low 11 bits, and the set is + * `ori v0,v1,0x8000` computed in the branch DELAY SLOT (independent of the + * branch, cookbook finding 8) — the store only happens on the fall-through + * path. The branch delay slot of the exit is `nop`. + * + * The observed instructions are: + * lui v0,0x8012 + * lw v0,0x2308(v0) v0 = D_80122308 (a pointer) + * sll a0,a0,0x4 index * 16 + * addu a0,v0,a0 element address + * lw v1,0x0(a0) v1 = *(int *)element + * nop (load delay) + * andi v0,v1,0x7ff v0 = v1 & 0x7ff + * beq v0,zero,exit if the low bits are clear, do nothing + * ori v0,v1,0x8000 (delay slot) v0 = v1 | 0x8000 + * sw v0,0x0(a0) *(int *)element = v0 + * exit: + * jr ra + * nop + * + * The element address MUST be a named pointer here, for the opposite reason to + * func_80083504: the original encodes `addu a0,v0,a0` (base first, then the + * scaled index), and naming the pointer is what makes the compiler emit that + * operand order. The direct expression form emits `addu a0,a0,v0` and differs by + * exactly 1 byte. The two functions are the two halves of this commutative + * tie-break, and each needs the opposite spelling. + * + * LIMITS: the stride 16, the test mask 0x7ff, the set bit 0x8000 and the + * 32-bit width are evidence; the global's name, type and meaning and the + * field's meaning are hypotheses. + */ + +extern char *D_80122308; + +void func_8008352C(int index) { + int *element = (int *)(D_80122308 + index * 16); + if ((*element & 0x7ff) != 0) { + *element |= 0x8000; + } +} diff --git a/src/func_80089314.c b/src/func_80089314.c new file mode 100644 index 0000000..1dfa533 --- /dev/null +++ b/src/func_80089314.c @@ -0,0 +1,30 @@ +/* + * func_80089314 — 36 bytes at 0x80089314..0x80089338 + * + * Byte-identical reconstruction of a read-modify-write plus two word clears. + * The `lw`/`ori`/`sw` triple fixes a bitwise-OR of a constant into a field at + * +0xc of the object reached through the pointer field at +0x1c. The two + * clears are 32-bit (`sw zero`), and the last one is scheduled into the `jr` + * delay slot (cookbook finding 8), which fixes the statement order. + * + * The observed instructions are: + * lw v1,0x1c(a0) v1 = *(char **)(a0 + 0x1c) + * nop (load delay) + * lw v0,0xc(v1) v0 = *(int *)(v1 + 0xc) + * nop (load delay) + * ori v0,v0,0xf00 v0 |= 0xf00 + * sw v0,0xc(v1) *(int *)(v1 + 0xc) = v0 + * sw zero,0x14(a0) *(int *)(a0 + 0x14) = 0 + * jr ra + * sw zero,0xc(a0) (delay slot) *(int *)(a0 + 0xc) = 0 + * + * LIMITS: the offsets, widths and the mask 0xf00 are evidence; the base's real + * type and the fields' meanings are hypotheses. + */ + +void func_80089314(char *object) { + char *inner = *(char **)(object + 0x1c); + *(int *)(inner + 0xc) |= 0xf00; + *(int *)(object + 0x14) = 0; + *(int *)(object + 0xc) = 0; +} diff --git a/src/func_800920DC.c b/src/func_800920DC.c new file mode 100644 index 0000000..bd19935 --- /dev/null +++ b/src/func_800920DC.c @@ -0,0 +1,30 @@ +/* + * func_800920DC — 40 bytes at 0x800920DC..0x80092104 + * + * Byte-identical reconstruction of a single-bit field update. `andi a1,a1,0x1` + * is an explicit `& 1` in the source (a type-driven mask would be 0xff, per + * cookbook finding 7), `sll a1,a1,0x1` shifts it into bit 1, `li v1,-0x3` + * gives 0xfffffffd for the clear (`~2` needs two instructions because the + * constant does not fit a signed 16-bit immediate), and the store lands in the + * `jr` delay slot (cookbook finding 8). + * + * The observed instructions are: + * lw v0,0xc(a0) v0 = *(char **)(a0 + 0xc) + * li v1,-0x3 v1 = 0xfffffffd + * lw a0,0x160(v0) a0 = *(char **)(v0 + 0x160) + * andi a1,a1,0x1 flag &= 1 + * lw v0,0x4(a0) v0 = *(int *)(a0 + 4) + * sll a1,a1,0x1 flag <<= 1 + * and v0,v0,v1 v0 &= ~2 + * or v0,v0,a1 v0 |= flag + * jr ra + * sw v0,0x4(a0) (delay slot) *(int *)(a0 + 4) = v0 + * + * LIMITS: the offsets, the widths, the mask and the shift are evidence; the + * bases' real types, the fields' meanings and the flag's type are hypotheses. + */ + +void func_800920DC(char *object, int flag) { + char *inner = *(char **)(*(char **)(object + 0xc) + 0x160); + *(int *)(inner + 4) = (*(int *)(inner + 4) & ~2) | ((flag & 1) << 1); +} diff --git a/src/func_8009E8D0.c b/src/func_8009E8D0.c new file mode 100644 index 0000000..d3e52e1 --- /dev/null +++ b/src/func_8009E8D0.c @@ -0,0 +1,42 @@ +/* func_8009E8D0 - 0x8009E8D0..0x8009E95C (140 bytes); duplicate body, also at + * 0x800A45E0 (census group g0030). + * + * Shape: absolute difference of three components, then two swap steps that move + * the largest difference into d0, then `d0 + ((d1 + d2) >> 2)`. + * + * Two codegen details decide the bytes, and both are recorded because the first + * natural reconstruction missed them (same instruction count, 21 differing + * words, all in registers): + * + * 1. The absolute value must come from a folded negation of the difference, + * i.e. `-(x) < 0 ? ... : ...` on the expression `p[i] - q[i]`, NOT + * `if (d < 0) d = -d;`. The latter makes cc1 emit `subu d,$0,d` (negu); + * the original recomputes the subtraction from the operands + * (`subu v1,v0,a2`, i.e. q[0]-p[0]), which is what folding `-(p[0]-q[0])` + * produces. The ABS(x) macro below is the idiomatic spelling of that. + * 2. The three differences must be separate scalars. An `int d[3]` array makes + * cc1 keep the array in memory (16-byte frame, 180 bytes) even though only + * constant indices are used. + */ +#define ABS(x) ((x) < 0 ? -(x) : (x)) + +int func_8009E8D0(int *p, int *q) +{ + int d0, d1, d2, t; + + d0 = ABS(p[0] - q[0]); + d1 = ABS(p[1] - q[1]); + d2 = ABS(p[2] - q[2]); + + if (d0 < d1) { + t = d0; + d0 = d1; + d1 = t; + } + if (d0 < d2) { + t = d0; + d0 = d2; + d2 = t; + } + return d0 + ((d1 + d2) >> 2); +} diff --git a/src/func_800AA56C.c b/src/func_800AA56C.c new file mode 100644 index 0000000..97fc9ca --- /dev/null +++ b/src/func_800AA56C.c @@ -0,0 +1,40 @@ +/* + * func_800AA56C — 48 bytes at 0x800AA56C..0x800AA59C + * + * Byte-identical reconstruction of a byte-sum (checksum) over a + * NUL-terminated byte string, truncated to 8 bits. `lbu` fixes a zero-extended + * byte read (cookbook finding 7), and the accumulator is initialised with + * `clear v0` in the first branch's delay slot. The condition's load is reused as + * the body's value, so there is one load per iteration plus one before the + * loop; the loop's branch delay slot is `nop` and the final `andi v0,v0,0xff` + * sits in the `jr` delay slot (cookbook finding 8). + * + * The observed instructions are: + * lbu v1,0x0(a0) v1 = *p + * nop (load delay) + * beq v1,zero,exit if (*p == 0) return 0 + * clear v0 (delay slot) sum = 0 + * loop: + * addu v0,v1,v0 sum += v1 + * addiu a0,a0,0x1 p++ + * lbu v1,0x0(a0) v1 = *p + * nop (load delay) + * bne v1,zero,loop while (*p != 0) + * nop + * exit: + * jr ra + * andi v0,v0,0xff (delay slot) return sum & 0xff + * + * LIMITS: the byte width, the NUL termination and the 8-bit truncation are + * evidence; the string's meaning and the parameter's real type are hypotheses + * (the `& 0xff` is evidence for the truncation, not for an 8-bit accumulator). + */ + +int func_800AA56C(unsigned char *p) { + int sum = 0; + while (*p != 0) { + sum += *p; + p++; + } + return sum & 0xff; +} diff --git a/src/func_800F5B40.c b/src/func_800F5B40.c new file mode 100644 index 0000000..a522c6c --- /dev/null +++ b/src/func_800F5B40.c @@ -0,0 +1,39 @@ +/* + * func_800F5B40 — 48 bytes at 0x800F5B40..0x800F5B70 + * + * Byte-identical reconstruction of a masked hardware-register write plus a + * masked read-back. The write ORs a bit into the argument and stores it through + * a global pointer; the read masks a different global's target with 0x00ffffff. + * Both pointers are loaded with the assembler macro form expanding into the + * destination register (cookbook finding 2; `addiu`-adjusted halves + * 0x8012 / -0x561c and 0x8012 / -0x5620). 0x00ffffff needs `lui 0xff` + `ori`, + * and the `and` lands in the `jr` delay slot (cookbook finding 8). + * + * The observed instructions are: + * lui v0,0x1000 + * lui v1,0x8012 + * lw v1,-0x561c(v1) v1 = D_8011A9E4 (a pointer) + * or a0,a0,v0 argument |= 0x10000000 + * sw a0,0x0(v1) *v1 = argument + * lui v0,0x8012 + * lw v0,-0x5620(v0) v0 = D_8011A9E0 (a pointer) + * lui v1,0xff + * lw v0,0x0(v0) v0 = *v0 + * ori v1,v1,0xffff v1 = 0x00ffffff + * jr ra + * and v0,v0,v1 (delay slot) return v0 & 0x00ffffff + * + * LIMITS: the two addresses, the mask 0x10000000, the mask 0x00ffffff and the + * widths are evidence; the globals' names, types and meanings and the + * interpretation that they point at hardware registers are hypotheses. Neither + * access is marked `volatile` because that is not needed to reproduce these + * bytes and would be an unproven claim about the access semantics. + */ + +extern int *D_8011A9E4; +extern int *D_8011A9E0; + +int func_800F5B40(int value) { + *D_8011A9E4 = value | 0x10000000; + return *D_8011A9E0 & 0x00ffffff; +} diff --git a/src/func_800F7FB4.c b/src/func_800F7FB4.c new file mode 100644 index 0000000..103d94b --- /dev/null +++ b/src/func_800F7FB4.c @@ -0,0 +1,32 @@ +/* func_800F7FB4 - 0x800F7FB4..0x800F7FD8 (36 bytes); duplicate body, also at + * 0x80103C7C and 0x80103F84 (census group g0007). + * + * Original words: + * 0x10A00006 beqz a1,+0x1c if (n == 0) return + * 0x24A2FFFF addiu v0,a1,-1 i = n - 1 (delay slot) + * 0x2403FFFF li v1,-1 + * 0xAC800000 sw zero,0(a0) *p = 0 + * 0x2442FFFF addiu v0,v0,-1 i -= 1 + * 0x1443FFFD bne v0,v1,+0x0 while (i != -1) + * 0x24840004 addiu a0,a0,4 p++ (delay slot) + * 0x03E00008 jr ra + * 0x00000000 nop + * + * Pure C, no asm. The shape matters and is recorded because this body was an + * open near-match for two phases: the obvious `for (i = n - 1; i != -1; i--)` + * makes cc1 restructure the loop and emit an unused 8-byte frame (44 bytes + * instead of 36). Writing the guard explicitly and the loop as a do/while keeps + * the counter in v0, keeps the `li v1,-1` / `bne` exit test, and drops the + * frame. `addu rt,rs,imm` in cc1 output becomes `addiu` in the assembler, which + * is what the original shows. + */ +void func_800F7FB4(int *p, int n) +{ + int i; + + i = n - 1; + if (n != 0) + do { + *p++ = 0; + } while (--i != -1); +} diff --git a/src/func_800FB5D4.c b/src/func_800FB5D4.c new file mode 100644 index 0000000..c8764f4 --- /dev/null +++ b/src/func_800FB5D4.c @@ -0,0 +1,17 @@ +/* func_800FB5D4 — 0x800FB5D4..0x800FB5DC (8 bytes). + * + * Original words: + * 0x03E00008 jr ra + * 0x03A01021 move v0,sp + * + * No inline assembly is needed: the GNU C extension `register int x asm("$29")` + * reads the stack pointer, and the compiler's own delay-slot filler moves the + * copy into the `jr ra` slot. `-O2` emits exactly `j $31` / `move $2,$sp` + * inside `.set noreorder` / `.set nomacro`. + */ +int func_800FB5D4(void) +{ + register int sp __asm__("$29"); + + return sp; +} diff --git a/src/func_80108710.c b/src/func_80108710.c new file mode 100644 index 0000000..90e4f27 --- /dev/null +++ b/src/func_80108710.c @@ -0,0 +1,37 @@ +/* + * func_80108710 — 36 bytes at 0x80108710..0x80108734 + * + * Byte-identical reconstruction of a "store if changed" setter. The comparison + * is a direct register `beq` (no `slt`/`xori`), which is what an equality test + * against a loaded global produces; the store is an absolute symbol store whose + * `sw` macro expands through `$at` (cookbook finding 3), so this global is NOT + * `gp`-relative. The branch delay slot is `nop` (cookbook finding 8). + * + * The observed instructions are: + * lui v0,0x8012 + * lw v0,0x1080(v0) v0 = D_80121080 + * nop (load delay) + * beq a0,v0,exit if (argument == D_80121080) skip the store + * nop + * lui at,0x8012 + * sw a0,0x1080(at) D_80121080 = argument + * exit: + * jr ra + * nop + * + * The COMPARISON OPERAND ORDER is load-bearing: the original encodes + * `beq $4,$2` (argument in `rs`), which is what `value != D_80121080` produces. + * Writing the test the other way round (`D_80121080 != value`) emits + * `beq $2,$4` and differs by exactly 1 byte. + * + * LIMITS: the address and 32-bit width are evidence; the global's name, type + * and meaning and the "changed" semantics are hypotheses. + */ + +extern int D_80121080; + +void func_80108710(int value) { + if (value != D_80121080) { + D_80121080 = value; + } +} diff --git a/src/func_8010A8B0.c b/src/func_8010A8B0.c new file mode 100644 index 0000000..6c832f8 --- /dev/null +++ b/src/func_8010A8B0.c @@ -0,0 +1,34 @@ +/* + * func_8010A8B0 — 40 bytes at 0x8010A8B0..0x8010A8D8 + * + * Byte-identical reconstruction of a masked read-modify-write of a hardware + * register reached through a global pointer. The pointer is loaded with the + * assembler macro form expanding into the destination register (cookbook + * finding 2; `addiu`-adjusted halves 0x8012 / 0x105c = 0x8012105C). Both + * constants need two instructions: 0xf0ffffff is `lui 0xf0ff` + `ori 0xffff`, + * and 0x22000000 is a single `lui 0x2200`. The store lands in the `jr` delay + * slot (cookbook finding 8). + * + * The observed instructions are: + * lui a0,0x8012 + * lw a0,0x105c(a0) a0 = D_8012105C (a pointer) + * lui v1,0xf0ff + * lw v0,0x0(a0) v0 = *a0 + * ori v1,v1,0xffff v1 = 0xf0ffffff + * and v0,v0,v1 v0 &= 0xf0ffffff + * lui v1,0x2200 v1 = 0x22000000 + * or v0,v0,v1 v0 |= 0x22000000 + * jr ra + * sw v0,0x0(a0) (delay slot) *a0 = v0 + * + * LIMITS: the two constants and the 32-bit width are evidence; the global's + * name, type and meaning and the interpretation that the target is a hardware + * register are hypotheses. The source is not marked `volatile` because that is + * not needed to reproduce these bytes and would be an unproven claim. + */ + +extern int *D_8012105C; + +void func_8010A8B0(void) { + *D_8012105C = (*D_8012105C & 0xf0ffffff) | 0x22000000; +}