diff --git a/src/func_80042E10.c b/src/func_80042E10.c new file mode 100644 index 0000000..0bca902 --- /dev/null +++ b/src/func_80042E10.c @@ -0,0 +1,75 @@ +/* + * func_80042E10 — 88 bytes at 0x80042E10..0x80042E68 + * + * PHASE 12 RESUME LANE C (autonomous matching). Row from the UN-ATTEMPTED band + * (negatives-d-unattempted.tsv) filtered through config/function_extents.tsv + * (size 88 grade exact term=jr_ra). NOT previously in the ledger. + * + * MATCH: candidate_bytes=88, differing_bytes=0, result=MATCH, exit 0 on the DEFAULT toolchain + * (tools/old-gcc/gcc-2.7.2-psx/cc1). No region overrides. + * + * Hypothesis, not a claim about meaning: `a2` is a BIT count; `n = a2 >> 5` is the word index at + * which the range ends. Walk words `n` down to `0` of two same-sized arrays and count how many + * satisfy `(x & y) == y`; report whether ALL `n + 1` of them do. Used with `n` = the last word the + * two bit maps share, this is a "is every remaining bit of x present in y" test. + * + * Original words (22): + * 00063143 sra a2,a2,0x5 n = a2 >> 5 + * 00C03821 move a3,a2 i = n + * 00004021 move t0,zero count = 0 + * 2409FFFF li t1,-1 ---- the end value lives in a REGISTER ---- + * 00061080 sll v0,a2,0x2 + * 00A22821 addu a1,v0,a1 a1 += n * 4 + * 00A22021 addu a0,v0,a0 a0 += n * 4 + * $L: 8C820000 lw v0,0(a0) + * 8CA30000 lw v1,0(a1) + * 00000000 nop + * 00431024 and v0,v0,v1 + * 14430002 bne v0,v1,0x80042e48 if ((x & y) != y) skip the count + * 00000000 nop + * 25080001 addiu t0,t0,1 count++ + * 24A5FFFC addiu a1,a1,-4 + * 24E7FFFF addiu a3,a3,-1 i-- <-- INSIDE the loop body + * 14E9FFF6 bne a3,t1,0x80042e2c <-- tested AFTER the decrement, against t1 + * 2484FFFC addiu a0,a0,-4 (delay slot) + * 24C20001 addiu v0,a2,1 n + 1 + * 01021026 xor v0,t0,v0 + * 03E00008 jr ra + * 2C420001 sltiu v0,v0,1 (count ^ (n + 1)) < 1 == count == n + 1 + * + * THE LEVER: THE LOOP MUST BE A DO-WHILE WHOSE DECREMENT IS IN THE CONDITION, COUNTING DOWN TO -1. + * The natural `for (i = n; i >= 0; i--)` spelling is 88 bytes with 16 differing bytes: it emits + * `bltz a3,` and NEVER materialises -1, because gcc tests `i >= 0` with a register-immediate + * branch. Writing + * i = n; count = 0; + * do { ... body ...; i--; } while (i != -1); + * makes gcc hoist `li t1,-1` BEFORE the loop and test `bne a3,t1` after the decrement, which is + * the original's form byte-for-byte. (The `for (i = n; i != -1; i--)` spelling is NOT equivalent + * here: it is 96 bytes, because a for-loop's test is placed at the top.) **The `li ,-1` + + * `bne` pair is therefore a fingerprint of a do-while countdown to -1, not of a `!= -1` condition + * written in any loop form** -- the same fingerprint appears in 0x800A86B4, where the natural `i >= 0` + * spelling also emits `bgez` and one induction variable instead of the original's two pointers. + * SECOND, SMALLER: the statement order matters -- `i = n;` must precede `count = 0;` for the + * prologue (`sra` / `move a3,a2` / `move t0,zero` / `li t1,-1`) to line up. + * + * LIMITS: `a2` is treated as a bit count (`>> 5`) because only that makes the `* 4` byte scaling + * consistent, and the two arrays are assumed to be bit maps of the same size; neither is + * independently evidenced. The `==` is compiled to `(a ^ b) < 1` (gcc's equality idiom for this + * shape), which is why the source's `count == n + 1` does not appear as a `beq`. + */ + +int func_80042E10(int a0, int a1, int a2) +{ + int n = a2 >> 5; + int count; + int i; + + i = n; + count = 0; + do { + if ((*(int *)(a0 + i * 4) & *(int *)(a1 + i * 4)) == *(int *)(a1 + i * 4)) + count++; + i--; + } while (i != -1); + return count == n + 1; +}