diff --git a/config/regions.tsv b/config/regions.tsv index 225e034..8e4f36a 100644 --- a/config/regions.tsv +++ b/config/regions.tsv @@ -75,6 +75,7 @@ 0x80026F14 0x80026F3C src/func_80026F14.c 0x800276B0 0x800276D4 src/func_800276B0.c 0x800281A4 0x800281E4 src/func_800281A4.c +0x8002864C 0x80028698 src/func_8002864C.c 0x800290D0 0x800290F0 src/func_800290D0.c 0x800290F0 0x80029118 src/func_800290F0.c 0x8002A9D4 0x8002AA18 src/func_8002A9D4.c @@ -129,6 +130,7 @@ 0x800450C4 0x80045110 src/func_800450C4.c gp=-D_80121BFC 0x80045388 0x800453C0 src/func_80045388.c 0x800453C0 0x800453F8 src/func_800453C0.c +0x80049298 0x800492E4 src/func_80049298.c gp=-D_80121BFC 0x8004C060 0x8004C090 src/func_8004C060.c 0x8004C090 0x8004C0AC src/func_8004C090.c 0x8004C0AC 0x8004C0F0 src/func_8004C0AC.c @@ -188,6 +190,7 @@ 0x8008352C 0x8008355C src/func_8008352C.c 0x80085B80 0x80085B90 src/func_80085B80.c 0x80089314 0x80089338 src/func_80089314.c +0x800893E8 0x80089434 src/func_800893E8.c 0x80089C4C 0x80089C54 src/func_80042088.c 0x80089C54 0x80089C64 src/func_80089C54.c 0x80089C64 0x80089C74 src/func_80089C64.c @@ -329,6 +332,7 @@ 0x80102B30 0x80102B5C src/func_80102B30.c maspsx=off 0x80102FA4 0x80102FD4 src/func_80102FA4.c 0x80102FD4 0x80102FE0 src/func_80102FD4.c +0x801032D4 0x80103320 src/func_801032D4.c 0x80103A94 0x80103AA0 src/func_80103A94.c 0x80103B54 0x80103B60 src/func_80103B54.c 0x80103B60 0x80103B6C src/func_80103B60.c diff --git a/src/func_8002864C.c b/src/func_8002864C.c new file mode 100644 index 0000000..d1f6365 --- /dev/null +++ b/src/func_8002864C.c @@ -0,0 +1,44 @@ +/* + * func_8002864C — 76 bytes at 0x8002864C..0x800286A0 + * + * Two-argument wrapper with a scratch buffer: fills a 16-byte local buffer, hands + * it and the two caller arguments to a second routine, and returns 1. Both + * caller arguments are kept in callee-saved registers across the first call. + * + * The observed instructions are: + * addiu sp,sp,-48 + * sw s0,32(sp) + * move s0,a1 ; s0 = first caller argument + * sw s1,36(sp) + * move s1,a2 ; s1 = second caller argument + * sw ra,40(sp) + * jal 0x80010B14 + * addiu a1,sp,16 ; second argument = the buffer (delay slot) + * addiu a0,sp,16 ; first argument = the buffer + * move a1,s0 + * jal 0x800283F4 + * move a2,s1 ; third argument (delay slot) + * li v0,1 ; return 1 + * lw ra,40(sp) + * lw s1,36(sp) + * lw s0,32(sp) + * addiu sp,sp,48 + * jr ra + * nop + * + * The 48-byte frame holds a 16-byte buffer at sp+16 plus the two saved registers + * and `ra`; the buffer is addressed as `sp + 16`, which is what a 16-byte local + * array produces in this frame. + * + * LIMITS: the buffer size (16) 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. + */ + +int func_8002864C(char *a0, int a1, int a2) { + int buf[4]; + + func_80010B14(a0, buf); + func_800283F4(buf, a1, a2); + return 1; +} diff --git a/src/func_80049298.c b/src/func_80049298.c new file mode 100644 index 0000000..193ad39 --- /dev/null +++ b/src/func_80049298.c @@ -0,0 +1,46 @@ +/* + * func_80049298 — 76 bytes at 0x80049298..0x800492EC + * + * Indexes a 76-byte-stride record table, reads a signed 16-bit field out of it, + * and makes two calls. The stride is `cc1`'s strength reduction of `* 76` into + * `*5`, `*4`, `-self`, `*4`. + * + * The observed instructions are: + * addiu sp,sp,-24 + * sll v0,a0,0x2 ; index * 4 + * addu v0,v0,a0 ; index * 5 + * sll v0,v0,0x2 ; index * 20 + * subu v0,v0,a0 ; index * 19 + * lui v1,0x8012 + * lw v1,7164(v1) ; v1 = D_80121BFC (the record table base) + * sll v0,v0,0x2 ; index * 76 + * sw ra,16(sp) + * addu v0,v0,v1 ; table + index*76 (index first) + * lh a0,74(v0) ; *(short *)(record + 74) <- SIGNED 16-bit + * jal 0x800909D8 + * move a1,zero ; second argument = 0 (delay slot) + * jal 0x800ACA10 + * nop + * lw ra,16(sp) + * addiu sp,sp,24 + * jr ra + * nop + * + * This is the sibling of the MATCHED func_800450C4: same table base, same stride, + * same direct-expression `addu` order. **D_80121BFC is gp-marked in the tracked + * registry but read ABSOLUTELY here**, so this region needs the per-region + * `gp=-D_80121BFC` override (cookbook finding 16, the per-SITE rule). + * + * LIMITS: the table base (0x80121BFC), the stride (76), the field offset (74) and + * the callees are hypotheses read from the instruction shape; what the records + * hold is unknown and is not guessed here. Only the compiled bytes are evidence. + */ + +extern int D_80121BFC; + +void func_80049298(int index) { + char *record = (char *)(index * 76 + D_80121BFC); + + func_800909D8(*(short *)(record + 74), 0); + func_800ACA10(); +} diff --git a/src/func_800893E8.c b/src/func_800893E8.c new file mode 100644 index 0000000..97f8695 --- /dev/null +++ b/src/func_800893E8.c @@ -0,0 +1,51 @@ +/* + * func_800893E8 — 76 bytes at 0x800893E8..0x8008943C + * + * Extracts a signed 4-bit field out of a word reached through two pointer hops, + * uses it to index a table of words and writes -1 there, then re-reads the first + * hop and clears a field on it before a call. + * + * The observed instructions are: + * addiu sp,sp,-24 + * sw ra,16(sp) + * lw v0,28(a0) ; q = p->ptr_1c + * nop + * lw v0,12(v0) ; q->word_0c + * li v1,-1 + * sll v0,v0,0x14 ; << 20 + * sra v0,v0,0x1c ; >> 28 <- ARITHMETIC, so a SIGNED 4-bit field + * sll v0,v0,0x2 ; * 4 + * lui at,0x8014 + * addu at,at,v0 ; table + field*4 + * sw v1,-31648(at) ; D_80138460[field] = -1 (%lo as displacement) + * lw v0,28(a0) ; RELOAD q = p->ptr_1c + * jal 0x80089314 + * sw zero,8(v0) ; q->word_08 = 0 (delay slot) + * lw ra,16(sp) + * addiu sp,sp,24 + * jr ra + * nop + * + * The `sll`/`sra` PAIR is the tell that the field is a signed bitfield: a plain + * `(x >> 8) & 0xf` would emit `srl`/`andi`. The shift pair is therefore written + * explicitly rather than as a mask. The pointer is loaded twice, so the C re-reads + * it rather than caching it. + * + * LIMITS: the pointer offsets (0x1c, 0x0c, 0x08), the table base (0x80138460) and + * the bitfield position (bits 8-11, signed) are hypotheses read from the + * instruction shape; what the object and the table mean is unknown and is not + * guessed here. Only the compiled bytes are evidence. + */ + +extern int D_80138460[]; + +void func_800893E8(char *p) { + char *q = *(char **)(p + 28); + int field = (*(int *)(q + 12) << 20) >> 28; + + D_80138460[field] = -1; + + q = *(char **)(p + 28); + *(int *)(q + 8) = 0; + func_80089314(); +} diff --git a/src/func_801032D4.c b/src/func_801032D4.c new file mode 100644 index 0000000..436abf3 --- /dev/null +++ b/src/func_801032D4.c @@ -0,0 +1,47 @@ +/* + * func_801032D4 — 76 bytes at 0x801032D4..0x80103328 + * + * GTE state reset: one no-argument call, the far-colour triple zeroed, the + * OFX/OFY pair zeroed, then two adjacent 16-bit globals cleared with absolute + * stores. + * + * The observed instructions are: + * addiu sp,sp,-24 + * sw ra,16(sp) + * jal 0x801097A0 + * nop + * move a0,zero + * move a1,zero + * jal 0x80103B6C ; gte_ldRFC / gte_ldGFC / gte_ldBFC + * move a2,zero ; all three zero (delay slot) + * move a0,zero + * jal 0x80109778 ; gte_ldOFX / gte_ldOFY + * move a1,zero ; both zero (delay slot) + * lui at,0x8014 + * sh zero,11812(at) ; D_80142E24 = 0 + * lui at,0x8014 + * sh zero,11808(at) ; D_80142E20 = 0 + * lw ra,16(sp) + * addiu sp,sp,24 + * jr ra + * nop + * + * The two final stores are ABSOLUTE (through `$at`), not gp-relative, so the two + * symbols must NOT be gp-marked: `lui 0x8014` plus 11812/11808 gives 0x80142E24 + * and 0x80142E20. + * + * LIMITS: the two global addresses and the three callees are hypotheses read from + * the instruction shape; what the state means is unknown and is not guessed here. + * Only the compiled bytes are evidence. + */ + +extern short D_80142E24; +extern short D_80142E20; + +void func_801032D4(void) { + func_801097A0(); + func_80103B6C(0, 0, 0); + func_80109778(0, 0); + D_80142E24 = 0; + D_80142E20 = 0; +}