re: the clock rate follows the VBLANK -- reconciling three pages that disagreed

h3 says 2 units per guest frame, units-per-second-measured says
time-integrated at 56.8 units/s, and my own page says 1 unit per present.
All three rest on real data and one mechanism explains all of them: the
clock advances one unit per VBLANK, and presents can be dropped without the
clock caring.

That accounts for the corpus's steps of +17/+34/+51 (1, 2, 3 vblanks
between two logged presents), for the same animation spanning 21 labels in
one capture and 33 in another -- which a strict per-present clock cannot
produce -- for h3's three plate steps of 23, and for my own 14 consecutive
steps of 17 in a run that dropped almost nothing.

My "1 unit per present" was nearly right and named the wrong clock. At
--framerate_limit=30 Xenia vblanks at 30 Hz and the guest presents ~30/s,
so presents and vblanks coincide and the test could not tell them apart. It
correctly refuted time-integration; it could not locate the tick.

Solid: the RATE follows the vblank rate. 255 declared units take 4.26/4.16 s
at a 60 Hz vblank and 8.45 s at 30 Hz -- 59.8/61.3 against 30.2 units/s. A
time-integrated clock predicts 4.25 s in both. So a console at 60 Hz gives
60 units/s, and the port's value stands on a third independent route.

Not solid: per-vblank vs per-present. My step samples are ~12 per run,
which cannot separate them, and I say so rather than reading 18% vs 0% of
two events as support. The load-bearing observation for the mechanism is
the corpus's 21-vs-33, not mine.

The discriminating experiment is named and not run: log Xenia's
D1MODE_V_COUNTER beside each present and check every step is exactly
17 x (vblanks elapsed).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jc4pciRArGHfxGGhEbwp5t
This commit is contained in:
sylph-decoder
2026-09-02 15:05:03 +00:00
parent b15effa587
commit c114b6239d

View File

@@ -0,0 +1,92 @@
# The clock rate follows the **vblank rate** — and that reconciles three pages that disagreed
**Status: ✅ measured for the rate; 🟡 for the mechanism, with the discriminating
test named and my own sample declared underpowered.** 2026-09-01. Instrument:
⟨capture⟩ ×3, one at a forced frame rate.
---
## The disagreement this resolves
Three pages held incompatible positions on how the UI clock advances:
| page | says |
|---|---|
| [`h3-units-per-frame-measured.md`](h3-units-per-frame-measured.md) | **2 units per guest frame** (later self-weakened to 🟡) |
| [`units-per-second-measured.md`](units-per-second-measured.md) | **time-integrated at 56.8 units/s** — "how many per frame is whatever that frame took" |
| [`clock-is-frame-based-one-unit-per-present.md`](clock-is-frame-based-one-unit-per-present.md) (mine) | **1 unit per present**, from a forced-frame-rate test |
Each rests on real data. **All three are explained by one mechanism.**
## The reconciliation
> **The clock advances one unit per VBLANK. Presents can be dropped; the clock
> does not care.**
| observation | page | explained by |
|---|---|---|
| steps `+17, +51, +34, +34, +17, +17` — always multiples of 17 | `units-per-second` | 1, 3, 2, 2, 1, 1 **vblanks** between two *logged presents* |
| the same animation spans **21 labels in one capture and 33 in another** | `units-per-second` | different drop rates. A strict per-present clock cannot do this; a per-vblank clock must |
| three consecutive plate steps of exactly **23** (2 units, T=22) | `h3` | a run dropping every other present |
| **14 consecutive steps of exactly 17** | mine | a run dropping almost none |
| splash dwell **4.26 s** at a 60 Hz vblank, **8.45 s** at `--framerate_limit=30` | mine | the vblank *rate* sets the unit rate: 59.9 and 30.2 units/s |
📌 **My "1 unit per present" was nearly right and named the wrong clock.** At
`--framerate_limit=30` Xenia vblanks at 30 Hz and the guest presents ~30 times a
second, so presents and vblanks coincide and the test could not tell them apart.
It correctly refuted *time-integration*; it could not locate the tick.
## What is solid, and what is not
**Solid — the rate follows the vblank rate.** Two forced conditions:
| vblank | splash dwell, 255 declared units | ⇒ units/s |
|---|---|---|
| 60 Hz (default) | **4.263 / 4.162 s** | **59.8 / 61.3** |
| 30 Hz (`--framerate_limit=30`) | **8.450 s** | **30.2** |
A time-integrated clock predicts 4.25 s in *both*. It doubled. **So the rate is
set by the vblank, and on a console — which vblanks at 60 Hz — that is
60 units/s.** The port's value stands, and this is the third independent route to
it.
🟡 **Not solid — per-vblank vs per-present**, because my own step samples cannot
separate them:
```
hashcap 52.2 presents/s +17 x13 (100%)
boot2 55.1 presents/s +17 x9 (82%) +34 x2 (18%)
fr30 28.7 presents/s +17 x11 (92%) +34 x1 ( 8%)
```
**Twelve-odd steps per run is far too few.** At 5255 presents/s under a 60 Hz
vblank a per-vblank clock predicts ~10 % of intervals spanning two vblanks; boot2's
18 % and hashcap's 0 % straddle that, and both are two events either way. I am
**not** claiming the distribution supports the model — it is consistent with it and
powerless against the alternative.
⚠️ **The per-vblank model is favoured by the corpus's data, not mine.** The
21-vs-33 label observation is the load-bearing one, and it is
`units-per-second-measured.md`'s, not this page's. A strict per-present clock
makes that impossible.
## The discriminating experiment, not run
Log the **vblank counter** beside each present in the draw logger — Xenia
increments `D1MODE_V_COUNTER` in `MarkVblank()` — and read the step against
*vblanks elapsed* rather than presents elapsed. If every step is exactly
`17 × (vblanks since last present)`, the mechanism is decoded rather than inferred.
That is a small addition to `tools/canary-patches/`, and it would settle a
question three pages have now circled.
## What this changes for the port
**Nothing.** 60 units/s stands, now reached three ways. What changes is the
*reason*: not "2 units per frame", not "time-integrated at 56.8", but "one unit
per 60 Hz vblank, and a console vblanks at 60".
## Reach
⟨capture⟩ ×3, one machine, English locale, at two vblank rates. The rate result is
a manipulation; the mechanism is a synthesis of other pages' observations with
mine, and is 🟡 until the vblank counter is logged.