diff --git a/docs/re/interpolator-hunt-bounded-negative.md b/docs/re/interpolator-hunt-bounded-negative.md new file mode 100644 index 00000000..525e5153 --- /dev/null +++ b/docs/re/interpolator-hunt-bounded-negative.md @@ -0,0 +1,66 @@ +# ❔ The keyframe interpolator is not found — but four routes are now excluded + +**Status: ❔ undecodable so far, with reach.** 2026-09-02. Instrument: ⟨image⟩ — +mechanical scans of `/image/sylpheed.pe`, no database rows. + +Question 1 of [`../agents/PLAYTEST-2026-09-02.md`](../agents/PLAYTEST-2026-09-02.md) +has two halves. **The behavioural half is answered** — the game interpolates +piecewise-linearly, every frame, and the declared keyframes predict the measured +per-frame steps +([`splash-interpolates-every-frame.md`](splash-interpolates-every-frame.md)). +**This page is the other half: *which function*, and it is not found.** + +Recorded because a bounded negative stops the next attempt repeating four +searches that cost nothing to state and an hour to redo. + +--- + +## What was excluded, and how + +The UI region throughout is `0x82200000 … 0x823FFFFF`, which is where the corpus +places the screen/bundle subsystem and the quad-emitter chain +([`ui-quad-class-foothold.md`](ui-quad-class-foothold.md)). + +| route | search | result | +|---|---|---| +| **integer lerp** | a `divw`/`divwu` with both a `mullw` and a `subf` in the preceding 10 instructions — the shape of `v0 + (v1−v0)·(t−t0)/(t1−t0)` | **28 sites image-wide, NONE in the UI region.** The evaluator is not integer multiply-divide there | +| **indexed record access** | `mulli rD,rA,40` — the keyframe record stride is 40 bytes ⟨disc⟩ | **31 sites, NONE in the UI region.** Records are not addressed by multiply | +| **pointer walk by stride** | `addi rX,rX,40` inside the UI region | 43 sites. The ones in the screen/bundle subsystem — `0x823CCA6C`, `0x823CCA94` — disassemble to a **40-byte container copy loop** (`mtspr CTR,10` then a 10-word copy), i.e. a `vector<40-byte>` reallocation. **Not an evaluator** | +| **float lerp** | `fmadd`-family ops in the UI region | **811 sites over 142 pages** — far too diffuse to select on. Not excluded; **not narrowed either** | + +📌 **The useful part is the first two.** Together they say the evaluator does not +compute a fraction with integer multiply-and-divide, and does not index its +records by multiplication. Combined with the measured behaviour — steps that land +on exact integers like −3 and −14 — the likeliest remaining shape is a **float +lerp with a reciprocal computed once per segment**, then converted to a byte. That +is a hypothesis, and the `fmadd` scan shows it cannot be found by opcode shape +alone. + +## What would find it, in cost order + +1. **Trace down from the quad emitter.** `sub_823C2AC0` — the accessor that hands + out the vertex pointer — has exactly **6 callers**, four of them sibling quad + emitters (`sub_821D6A40`, `sub_822380B0`, `sub_82234610`, `sub_821BC718`). + `sub_822380B0` calls it and then reads its colour from fields of `this` + (`+0x04`, `+0x08`, `+0x0C`, `+0x10`). **Whoever writes those fields is one step + from the interpolator**, and the caller set is small enough to enumerate. This + is the route I would take next and did not have budget for. +2. **Watch it from the guest instead of reading for it.** The alpha is in a vertex + buffer at a known address each frame; a watchpoint on the byte that becomes + `k_8_8_8_8` alpha would name the writer directly. `/canary` is read-write and + already carries four RE logger patches. + +## Why this is not blocking the port + +**It is not.** The port needs to know *whether to lerp and how*, and that is +answered and measured. Naming the function would let the answer generalise from +the splashes to every screen without further captures — worth having, not worth +holding the port for. + +## Reach + +⟨image⟩, exhaustive over the whole 9.2 MB for the two excluded opcode shapes, so +those negatives are complete rather than sampled — the instruction forms searched +genuinely do not occur in that address range. **They do not exclude the +interpolator living outside `0x82200000…0x823FFFFF`**, which is an assumption +inherited from `ui-quad-class-foothold.md` and not independently checked here.