From 6141079f9b67ab11bd1cf8bfae26247f02426af1 Mon Sep 17 00:00:00 2001 From: Christopher Williams Date: Thu, 24 Sep 2026 11:43:43 -0400 Subject: [PATCH] =?UTF-8?q?phase11:=20merge=2061=20=E2=80=94=20worker=20E'?= =?UTF-8?q?s=200x8007F9B0=20->=20602=20bodies=20/=20611=20regions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/regions.tsv | 1 + src/func_8007F9B0.c | 59 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 src/func_8007F9B0.c diff --git a/config/regions.tsv b/config/regions.tsv index dab3b4e..b15b35a 100644 --- a/config/regions.tsv +++ b/config/regions.tsv @@ -327,6 +327,7 @@ 0x8007E8B8 0x8007E904 src/func_8007E8B8.c 0x8007EB8C 0x8007EBEC src/func_8007EB8C.c 0x8007ED4C 0x8007ED8C src/func_8007ED4C.c +0x8007F9B0 0x8007FA1C src/func_8007F9B0.c 0x80082750 0x800827A8 src/func_80082750.c 0x800827A8 0x800827C4 src/func_800827A8.c 0x800827C4 0x80082868 src/func_800827C4.c diff --git a/src/func_8007F9B0.c b/src/func_8007F9B0.c new file mode 100644 index 0000000..0e9e449 --- /dev/null +++ b/src/func_8007F9B0.c @@ -0,0 +1,59 @@ +/* + * func_8007F9B0 — 108 bytes at 0x8007F9B0..0x8007FA1C + * + * Gate on a three-level dereference, then set two fields and call a helper. No frame beyond the + * return address. **Second spelling, default toolchain.** + * + * addiu sp,sp,-0x18 / sw ra,16(sp) frame 24, ra at 0x10, no other save. + * lw v0,12(a0) / nop v0 = *(int *)(a0 + 12) + * lw v0,344(v0) / nop v0 = *(int *)(v0 + 344) + * lw v1,56(v0) v1 = *(int *)(v0 + 56) <-- THE THIRD LEVEL + * li v0,6 / bne v1,v0,END / nop if (that != 6) return; + * lw v0,28(a0) / nop the a0->f28 chain is RE-READ for each statement + * lw v1,8(v0) / li v0,62 + * sw v0,48(v1) *(int *)(*(int *)(a0+28)+8)+48) = 62; WORD store + * lw v0,28(a0) / nop / lw v1,8(v0) ... re-read for the SECOND statement + * li v0,20 / sb v0,52(v1) *(... +52) = 20; BYTE store + * lh a0,2(a0) the call's first argument is a SIGNED HALFWORD + * jal 0x80043DC4 / li a1,62 func_80043DC4(*(short *)(a0 + 2), 62); + * + * FOUR THINGS ARE BYTE-REQUIRED: + * + * 1. **THE GATE IS THREE DEREFERENCES, NOT TWO.** `a0->f12 -> f344 -> f56`, and the innermost + * load is easy to drop when transcribing from a disassembly: writing + * `*(int *)(*(int *)(a0 + 12) + 344) != 6` compiles to a body **two instructions SHORT** + * (100 vs 108) because the missing load and its load-delay `nop` are exactly the deficit. + * **Diagnostic (cookbook 49's family): a body exactly two instructions short where the + * original has one more `lw` in a pointer chain means a MISSING DEREFERENCE LEVEL, not a + * missing statement -- recompute the chain from the displacements rather than eyeballing it.** + * 2. **THE a0->f28 CHAIN IS RE-READ PER STATEMENT** (cookbook 105's per-statement setting): the + * original loads `28(a0)` and `8(v0)` TWICE, once for each of the two stores. Binding either + * to a local would collapse one of the pairs. + * 3. **THE STORE WIDTHS ARE THE LAYOUT** (cookbook 93): `sw` for the field at 48 and `sb` for the + * field at 52 -- there is no other evidence for these two fields' types. + * 4. **THE CALL'S FIRST ARGUMENT IS A SIGNED HALFWORD**: `lh a0,2(a0)`, not `lw`. The `lh` is the + * only reason to call it `short`, and it is the same parameter register being reused. + * + * The gate's chained loads keep the SAME register (`lw v0,12(a0)` then `lw v0,344(v0)`), which is + * why the body carries a load-delay `nop` after each of the first two loads: the next instruction + * loads FROM the register just loaded, which is exactly maspsx's nop predicate (cookbook 27). + * + * LIMITS: func_80043DC4 is a cross-reference by address only and its purpose is not established. + * The object at a0 and the two objects reached through the chains are typed by their access widths + * and displacements only -- offsets 12, 344, 56, 28, 8, 48, 52 and 2, and the `short` at +2 is the + * one type claim, earned from the `lh`. The field names and the meaning of the `!= 6` gate are not + * recovered. The function returns nothing (`void`): the epilogue sets no value in v0. + */ + +extern void func_80043DC4(int a0, int a1); + +void func_8007F9B0(int a0) +{ + if (*(int *)(*(int *)(*(int *)(a0 + 12) + 344) + 56) != 6) + return; + + *(int *)(*(int *)(*(int *)(a0 + 28) + 8) + 48) = 62; + *(char *)(*(int *)(*(int *)(a0 + 28) + 8) + 52) = 20; + + func_80043DC4(*(short *)(a0 + 2), 62); +}