Files
Sylpheed/docs/re/interpolator-hunt-bounded-negative.md
sylph-decoder cac31deccd re: sub_822380B0 is a type-tagged draw-queue walker, not the interpolator
Walked the route the bounded-negative page named -- trace down from the
quad emitter -- and got one step.

sub_822380B0 is not a geometry emitter. It fetches a vertex pointer from
sub_823C2AC0, stashes it at this+0x20, then loops over an array of item
POINTERS at this+4 bounded by this+0x0C and this+0x10, loading a type tag
from item+8 and dispatching on it. So the object is a draw QUEUE and this
function consumes it.

That is a partial answer to a different open question:
ui-quad-class-foothold.md records that no function iterating screen
elements has been found. This iterates UI items and dispatches per item.
It is a draw queue rather than a screen's declared element list, so it is
not that walk, but it is the consumer immediately downstream and the type
tag at item+8 is a concrete field to chase.

Still not the interpolator: alpha is already decided by the time an item
reaches the queue. The evaluator runs before the enqueue, so the next step
is whoever pushes items -- the writer of +0x0C/+0x10 on this object.

Recorded as an increment, not a finding: one function identified by
disassembly, no behaviour measured, identification resting on code shape
alone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jc4pciRArGHfxGGhEbwp5t
2026-09-02 16:17:12 +00:00

108 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ❔ 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 + (v1v0)·(tt0)/(t1t0)` | **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.
## Increment 2026-09-02 — route 1 walked one step, and `sub_822380B0` is identified
I took the route this page named — trace **down** from the quad emitter — and got
one step, not to the interpolator.
`sub_822380B0` disassembles as a **type-tagged draw-queue walker**, not a geometry
emitter:
```
822380CC bl 0x823C2AC0 ; get the vertex pointer
822380D0 lwz r11,12(r26) ; head
822380D4 lwz r10,16(r26) ; count -> r17 = head + count
822380E0 stw r3,32(r26) ; stash the vertex pointer in the object
82238104 subf r11,r25,r17 ; loop bound
82238134 lwz r10,4(r26) ; the ITEM ARRAY base
82238138 rlwinm r9,r11,2,0,29 ; index * 4 -> an array of POINTERS
82238140 lwzx r29,r9,r10 ; item = array[i]
82238148 lwz r11,8(r29) ; item TYPE TAG
8223814C cmpi cr6,r11,2 ; dispatch on it
```
**So the object at `r26` is a queue**`+4` item-pointer array, `+8`/`+0x0C`/`+0x10`
bounds and indices, `+0x20` the vertex write pointer — and the function **consumes**
it, switching on a per-item tag at `item+8`.
📌 **That is a partial answer to a different open question.**
[`ui-quad-class-foothold.md`](ui-quad-class-foothold.md) records *"no function that
iterates screen elements has been found"*. This iterates a list of UI items and
dispatches per item. It is a **draw queue**, not a screen's declared element list,
so it is not that walk — but it is the consumer immediately downstream of it, and
the type tag at `item+8` is a concrete field to chase.
🔴 **It is still not the interpolator.** Alpha is already decided by the time an
item reaches this queue: the emitter reads what it needs from the item and writes
vertices. **The evaluator runs before the enqueue**, so the next step is whoever
*pushes* items — the writer of `+0x0C`/`+0x10` on this object.
⚠️ Recorded as an increment rather than a finding: one function identified by
disassembly, no behaviour measured, and the identification rests on the shape of
the code alone.