phase11: merge 16 + cookbook 83 — 525 bodies / 534 regions
Worker C's 0x800320D8 (276 B), matched on the FIRST spelling where its sibling 0x80031FC4 took 5 -- the family lever measured, on one family, both ways. Finding 55's limit confirmed on the same family: a third row calling the same callee is NOT the same body and sits at +16 instructions. The family transfers the derivation method and the stable positions, never the body. Also recorded: an OR nested inside an && chain is observable from the branch DIRECTIONS -- bne to the call block on one test and bnez to the manual-copy block on the other is if (x == 0 && (a != 6 || b == 0)) call; else manual;
This commit is contained in:
@@ -141,6 +141,7 @@
|
||||
0x80031F2C 0x80031F78 src/func_80031F2C.c
|
||||
0x80031F78 0x80031FC4 src/func_80031F78.c
|
||||
0x80031FC4 0x800320D8 src/func_80031FC4.c
|
||||
0x800320D8 0x800321EC src/func_800320D8.c
|
||||
0x800321EC 0x800321F8 src/func_800321EC.c
|
||||
0x80034A80 0x80034ABC src/func_80034A80.c
|
||||
0x800354F8 0x80035548 src/func_800354F8.c
|
||||
|
||||
|
@@ -1331,3 +1331,25 @@ because it is used twice (`s1 = r + 6`), and there it lands in a0 in both compil
|
||||
**Rule: name a result only where the original REUSES it. Where the original consumes it
|
||||
immediately, leave the expression inline.** Do not apply the decision once per function — apply it
|
||||
once per value.
|
||||
|
||||
### 83. The family lever, MEASURED — and its limit on the same family (worker C)
|
||||
|
||||
Finding 55 said the family transfers the derivation method, never the body. Worker C has now
|
||||
measured both halves on one family.
|
||||
|
||||
**The lever working.** `0x800320D8` is the sibling of `0x80031FC4` (callee `0x80031F78` vs
|
||||
`0x80031F2C`). Only the object offsets changed — the two flags stay at `+976`/`+2272` while the
|
||||
two 4-word blocks move to `+1032`/`+2328` (48 bytes later). The body, the local 3x4 matrix, both
|
||||
branch polarities and the epilogue are identical, and the match came on the **FIRST spelling**.
|
||||
Contrast the original: **5 attempts**.
|
||||
|
||||
**The limit, on the same family.** A third row calling `0x80031F78` — `0x80033DC8` (360 B) — is
|
||||
**not the same body** at all: it is a matrix add (fill A from one block, add a global vector, fill
|
||||
B from another block or a call, subtract), and it is a near-match at +16 instructions.
|
||||
**The family transfers the derivation method and the stable positions — never the body.**
|
||||
|
||||
**Bonus tell from the row that did not close: an OR nested inside an `&&` chain is observable
|
||||
from the branch DIRECTIONS.** The original does `bne v1,v0` **to the call block** when
|
||||
`f308 != 6` and `bnez` **to the manual-copy block** when `f312 != 0`, with the call inline —
|
||||
which is `if (f4762 == 0 && (f308 != 6 || f312 == 0)) call; else manual;`. Branch direction
|
||||
distinguishes `&&` from `||` without guessing at the operator.
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* func_8004857C — 100 bytes at 0x8004857C..0x800485E0
|
||||
*
|
||||
* Byte-identical reconstruction of a one-shot initialiser guarded by a
|
||||
* byte-valued global: the first call sets up two static descriptors, publishes
|
||||
* the guard byte, and raises a fixed event through a fourth callee.
|
||||
*
|
||||
* The observed instructions are:
|
||||
* lui v0,0x8012 \ if (D_80122438) return;
|
||||
* lbu v0,9272(v0) |
|
||||
* addiu sp,sp,-24 |
|
||||
* bnez v0,0x800485D0 |
|
||||
* sw ra,16(sp) / (delay slot)
|
||||
* lui a0,0x8011 \ func_80046348(&D_80116B90);
|
||||
* addiu a0,a0,27536 |
|
||||
* jal 0x80046348 |
|
||||
* nop /
|
||||
* lui a0,0x8011 \ func_80046348(&D_80116BD0);
|
||||
* addiu a0,a0,27600 |
|
||||
* jal 0x80046348 |
|
||||
* nop /
|
||||
* li v0,1 \ D_80122438 = 1;
|
||||
* lui at,0x8012 |
|
||||
* sb v0,9272(at) /
|
||||
* move a0,zero \ func_8008FA58(0, 35, 0, 0);
|
||||
* li a1,35 |
|
||||
* move a2,zero |
|
||||
* jal 0x8008FA58 |
|
||||
* move a3,zero / (delay slot)
|
||||
* lw ra,16(sp)
|
||||
* addiu sp,sp,24
|
||||
* jr ra
|
||||
* nop
|
||||
*
|
||||
* The guard is read **before** the frame is set up (`lbu` precedes
|
||||
* `addiu sp,sp,-24`), which is why the early `return` costs nothing: the frame is
|
||||
* only allocated when the body will run. The guard is written with `sb` through
|
||||
* `$at` (a symbol store, cookbook finding 3) while the read folds the constant
|
||||
* address into the displacement (cookbook finding 18) — the same global accessed
|
||||
* both ways, per finding 16.
|
||||
*
|
||||
* LIMITS: names are address placeholders and every type is inferred from
|
||||
* register usage alone; only the compiled bytes are evidence. The guard is
|
||||
* modelled `char` because it is read with `lbu` and written with `sb`. The two
|
||||
* descriptor symbols are passed only by address, so their contents are
|
||||
* unobservable, and `func_8008FA58`'s four arguments are modelled from the
|
||||
* registers the call site actually sets.
|
||||
*/
|
||||
|
||||
extern char D_80122438;
|
||||
extern char D_80116B90;
|
||||
extern char D_80116BD0;
|
||||
extern void func_80046348(void *);
|
||||
extern void func_8008FA58(int, int, int, int);
|
||||
|
||||
void func_8004857C(void) {
|
||||
if (D_80122438)
|
||||
return;
|
||||
func_80046348(&D_80116B90);
|
||||
func_80046348(&D_80116BD0);
|
||||
D_80122438 = 1;
|
||||
func_8008FA58(0, 35, 0, 0);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* func_800B255C — 92 bytes at 0x800B255C..0x800B25B8
|
||||
*
|
||||
* Byte-identical reconstruction of a framed two-conversion helper: it expands
|
||||
* each of its two halfword arguments into its own 16-byte stack buffer, then
|
||||
* combines the two buffers through a third callee and returns the word that
|
||||
* callee wrote into a third stack slot.
|
||||
*
|
||||
* The observed instructions are:
|
||||
* addiu sp,sp,-64
|
||||
* sw s0,56(sp)
|
||||
* move s0,a1 the second argument survives the first call
|
||||
* sll a0,a0,0x10 \ a0 = (short)a0
|
||||
* sra a0,a0,0x10 /
|
||||
* sw ra,60(sp)
|
||||
* jal 0x80044FA4 \ func_80044FA4((short)a0, b1);
|
||||
* addiu a1,sp,16 / (delay slot) b1 is 16 bytes at sp+16
|
||||
* sll s0,s0,0x10 \ a0 = (short)a1
|
||||
* sra a0,s0,0x10 /
|
||||
* addiu s0,sp,32 s0 = b2 (16 bytes at sp+32)
|
||||
* jal 0x80044FA4 \ func_80044FA4((short)a1, b2);
|
||||
* move a1,s0 / (delay slot)
|
||||
* addiu a0,sp,16 \ func_80027D88(b1, b2, &out);
|
||||
* move a1,s0 |
|
||||
* jal 0x80027D88 |
|
||||
* addiu a2,sp,48 / (delay slot) out is at sp+48
|
||||
* lw v0,48(sp) return out;
|
||||
* lw ra,60(sp)
|
||||
* lw s0,56(sp)
|
||||
* addiu sp,sp,64
|
||||
* jr ra
|
||||
* nop
|
||||
*
|
||||
* Both parameters are **narrowed explicitly** with `sll`/`sra`, so they are
|
||||
* `short` (or narrower) rather than `int` — an `int` parameter would be passed
|
||||
* through untouched. The frame is 64 bytes: two 16-byte buffers at sp+16 and
|
||||
* sp+32, the result word at sp+48, and the saved `s0`/`ra` above them.
|
||||
*
|
||||
* LIMITS: names are address placeholders and every type is inferred from
|
||||
* register usage alone; only the compiled bytes are evidence. The buffers are
|
||||
* modelled as `char[16]` because they are only ever passed by pointer — their
|
||||
* contents and element type are unobservable. The third callee's third parameter
|
||||
* is modelled `int *` from the address the call site passes, and its result is
|
||||
* only observed through the stack word at sp+48.
|
||||
*/
|
||||
|
||||
extern void func_80044FA4(short, char *);
|
||||
extern void func_80027D88(char *, char *, int *);
|
||||
|
||||
int func_800B255C(short a0, short a1) {
|
||||
char b1[16];
|
||||
char b2[16];
|
||||
int out;
|
||||
|
||||
func_80044FA4(a0, b1);
|
||||
func_80044FA4(a1, b2);
|
||||
func_80027D88(b1, b2, &out);
|
||||
return out;
|
||||
}
|
||||
Reference in New Issue
Block a user