phase8: merge cycle 3 — g0007 and g0030 closed, 137 regions / 128 bodies

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.
This commit is contained in:
Christopher Williams
2026-09-23 23:13:02 -04:00
parent 62983c9a8a
commit 2775d2e077
21 changed files with 737 additions and 0 deletions
+22
View File
@@ -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
1 # Code-region registry: one C region per matched function.
11 #
12 # Matched so far:
13 0x80012780
14 0x800127F0
15 0x8001281C
16 0x80013C88
17 0x80016110
18 0x80016158
19 0x80016E50
20 0x800171D8
21 0x800179B8
22 0x800179CC
23 0x800179D4
25 0x80017AE8
26 0x80017C50
27 0x80017C60
28 0x80017D1C
29 0x80017DD0
30 0x80019C04
31 0x8001AA9C
32 0x8001CE40
33 0x80021F50
34 0x80021F88
35 0x80022FB8
54 0x80036378
55 0x80038788
56 0x80038790
57 0x8003B2F0
58 0x80042088
59 0x80042D64
60 0x8004C090
61 0x8004C0F0
62 0x80057DFC
63 0x80058288
64 0x80065B6C
65 0x80068470
66 0x80068F40
67 0x80068F98
68 0x800697A4
69 0x8006EC94
72 0x8006FA78
73 0x8007049C
74 0x8007DC40
75 0x80082914
76 0x80083504
77 0x8008352C
78 0x80085B80
79 0x80089314
80 0x80089C4C
81 0x80089C54
82 0x80089C64
90 0x800912D4
91 0x800912FC
92 0x800920BC
93 0x800920DC
94 0x800943C4
95 0x8009E8D0
96 0x800A45E0
97 0x800A74BC
98 0x800AA56C
99 0x800ACC00
100 0x800AE0F4
101 0x800AF1FC
103 0x800B6BDC
104 0x800BBDEC
105 0x800F3160
106 0x800F5B40
107 0x800F7A84
108 0x800F7FB4
109 0x800F8294
110 0x800F8ACC
111 0x800F8ADC
116 0x800F8FE4
117 0x800F8FF8
118 0x800FB13C
119 0x800FB5D4
120 0x800FB5E4
121 0x800FB6C4
122 0x800FBDF8
129 0x80100318
130 0x80100964
131 0x80102B10
132 0x80103C7C
133 0x80103F84
134 0x8010513C
135 0x80105B34
136 0x80105B54
141 0x80107E68
142 0x80107E74
143 0x80107E84
144 0x80108710
145 0x80109300
146 0x80109314
147 0x80109F38
148 0x8010A8B0
149 0x8010AAA0
+7
View File
@@ -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
1 # Symbol registry: absolute addresses for cross-references used by C regions.
70 D_80121BF9
71 D_80121BFC
72 D_80121D5C
73 D_80121D5E
74 D_80121D60
75 D_80121D62
76 D_80121D64
77 D_80121D68
78 D_80121D6A
79 D_80121D6C
80 D_80121D70
81 D_80121DFC
82 D_80121E04
83 D_80121E10
+40
View File
@@ -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;
}
+40
View File
@@ -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;
}
+40
View File
@@ -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;
}
+40
View File
@@ -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);
}
+53
View File
@@ -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;
}
+32
View File
@@ -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;
}
+39
View File
@@ -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;
}
+36
View File
@@ -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);
}
+41
View File
@@ -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;
}
+46
View File
@@ -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;
}
}
+30
View File
@@ -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;
}
+30
View File
@@ -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);
}
+42
View File
@@ -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);
}
+40
View File
@@ -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;
}
+39
View File
@@ -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;
}
+32
View File
@@ -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);
}
+17
View File
@@ -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;
}
+37
View File
@@ -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;
}
}
+34
View File
@@ -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;
}