Put the player on a real volume, and find the write window is the frame
ROADMAP K3. src/player/packed.s (2,898 B) brings up its own display, builds
its own 193-entry DMA chain, keeps its own frame clock off V-DISP and fetches
every record itself with READ(10) off a CZ-6BS1. The rig writes no picture
byte, no palette entry and no CRTC register.
120 of 120 frames pixel-exact, every one compared, in both palette orders --
the gate had to grow to do it, because a packed frame is a LITERAL and the
codec's recursion was what made one comparison audit 120.
And the write window turns out to be the frame. A packed write requires R20
bit 11, buffer mode blanks the layer, and a DMAC-direct player holds the
window open for the whole data phase, so
dark fraction of a slot = record bytes / (DATA-PHASE rate x slot)
which is 1.0 at the container's own 582.0 KB/s: every frame delivered, on
time, pixel-exact, and none of them displayed. The rate in that expression is
the BURST rate, a third hardware number B1 has no test for. It reverses 61.5's
ranking -- a packed player that DMAs to RAM and paints with the measured 27.3%
blit is on screen 72.7% of every slot at any rate, and the two are equal only
at 2,131 KB/s = 3.7x the wire.
And a held channel costs the frame clock half its ticks without the clock
being able to tell: 487 of 1,038 V-DISP edges lost, zero late frames reported,
the player believing 12 fps while the screen ran at 6.37.
FINDINGS 64. ROADMAP K4 opened and fenced behind B2.
check.sh ALL GREEN before and after.
Claude-Session: https://claude.ai/code/session_01194oWYW8DQXK1SZ2DnChW6
This commit is contained in:
@@ -6590,3 +6590,181 @@ resource the packed branch is short of. The per-frame palette is worth +2.31 dB
|
||||
container by a 68000 or by a channel. That is **K3**, and 63.2 is the strongest
|
||||
statement available without it: the bytes are right, on a second emulator's
|
||||
own GVRAM model, with the harness computing nothing.
|
||||
|
||||
---
|
||||
|
||||
## 64. The packed player runs end to end off the disc — and the write window is the frame (session 32)
|
||||
|
||||
**ROADMAP K3.** `src/player/packed.s` is **2,898 bytes** of 68000 code that
|
||||
brings up its own display, builds its own 193-entry DMA chain, keeps its own
|
||||
frame clock off V-DISP and fetches every record itself with `READ(10)` off a
|
||||
CZ-6BS1. `tools/bench/packed.lua` writes **no picture byte, no palette entry and
|
||||
no CRTC register**; it pushes the code and eleven mailbox words and then reads.
|
||||
`tools/bench/packed_run.sh` is the gate, `tools/bench/verify_packed.py` the
|
||||
comparison, `tools/analysis/31_display_duty.py` the arithmetic underneath the
|
||||
result.
|
||||
|
||||
**THE HEADLINE, and it is two facts that point opposite ways.**
|
||||
|
||||
**120 of 120 frames are pixel-exact**, every one of them compared, off a real
|
||||
volume, on a clock the machine keeps itself — the strongest end-to-end result
|
||||
this project has. And **the picture was on screen for none of the frame slot it
|
||||
belongs to**, because the write window that a packed frame requires is the whole
|
||||
of its transfer, and buffer mode blanks the layer it is written through.
|
||||
|
||||
### 64.1 What was run, and why every frame had to be checked
|
||||
|
||||
`tools/bench/verify_decode.py` checks **one** frame — the last — and that audits
|
||||
all 120, because the codec is temporally recursive: a SKIP block is a claim that
|
||||
the previous frame is still in GVRAM. **A packed frame is a LITERAL.** Frame 119
|
||||
being right says nothing whatever about frame 60. The simplification that
|
||||
deleted the ring, the codebooks and the decoder also deleted the gate's free
|
||||
lunch, so `verify_packed.py` snapshots and compares **every frame**, letterbox
|
||||
included — the 64 static rows are written once at scene setup and never touched
|
||||
again, so a picture can be pixel-exact while the screen is not.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| container | `tmp/packed_singe.dlxp`, DLXP1, 120 records of 49,664 B = 97 sectors |
|
||||
| record `i` | at LBA `1 + i*97` — **arithmetic**, no index, nothing walked |
|
||||
| chain | **193 entries**, built by the 68000: `$E82000`/512 B, then 192 rows of 256 B a 1,024 B stride apart |
|
||||
| result | **120 of 120 pixel-exact**, 0 frames unsampled, `FLAG=$FF` |
|
||||
| passes with the palette LAST too | chain starts `$C08000`/256 instead, **120 of 120 pixel-exact** |
|
||||
|
||||
The array is **scene-constant** and the seek is **subtraction**: the packed
|
||||
layout spends both 256-colour pages, so there is no page to flip, and a record's
|
||||
length is geometry, so a new pass is `LBA0` again. That is the whole of what K3
|
||||
deletes, and it deleted it without incident.
|
||||
|
||||
### 64.2 The write window is the frame, and the rate it needs is not the rate it costs
|
||||
|
||||
The free-running run — the one that asks for record *i+1* the instant record
|
||||
*i* lands, which is what a 12 fps player becomes the moment the transfer is
|
||||
longer than the slot — reported a number no budget in this tree has a column
|
||||
for: **the write window was open on 99.5% of the host frames.** Every frame was
|
||||
pixel-exact and almost none of them was visible.
|
||||
|
||||
It is **arithmetic, not an emulator artefact**. 256-colour GVRAM masks the high
|
||||
byte of every write unless R20 bit 11 is set (46.5/47.1), and the packed
|
||||
layout's entire 1.0 B/pixel claim is that one word carries two pixels — so a
|
||||
packed write *requires* the bit. If buffer mode blanks the layer while the bit is
|
||||
set (47.4/B2 — MAME says it does, 48.1's prior leans that way), the layer is dark
|
||||
for exactly as long as the window is open, and for a DMAC-direct player the
|
||||
window is open for the whole data phase. There is no second page to hide behind:
|
||||
the packed layout spends both, which is the same fact that made a frame one
|
||||
channel start (62).
|
||||
|
||||
dark fraction of a slot = record bytes / (DATA-PHASE rate x slot)
|
||||
|
||||
**And the rate in that expression is the BURST rate, not the sustained one.**
|
||||
This is the correction the session had to make to itself. 582.0 KB/s is a
|
||||
*sustained* requirement and it decides whether record *i* arrives before slot
|
||||
*i*. The dark fraction is set by how fast bytes move **during the data phase**,
|
||||
which for a drive with a read-ahead cache can be several times the sustained
|
||||
figure. **They are independent, and a medium can pass one and fail the other:**
|
||||
|
||||
| requirement | figure | status |
|
||||
|---|---|---|
|
||||
| sustained, or frames arrive late | **≥ 582.0 KB/s** | B1, known since 63 |
|
||||
| data phase, or the frame is never displayed | **see below** | **NEW — B1 has no test for it** |
|
||||
|
||||
| data phase | transfer | window open | picture on screen |
|
||||
|---:|---:|---:|---:|
|
||||
| 582.0 KB/s (= the wire) | 83.33 ms | 100.0% | **0.0%** |
|
||||
| 700 KB/s | 69.29 ms | 83.1% | 16.9% |
|
||||
| 1,164 KB/s | 41.67 ms | 50.0% | 50.0% |
|
||||
| 2,131 KB/s | 22.75 ms | 27.3% | 72.7% |
|
||||
| 3,000 KB/s | 16.17 ms | 19.4% | 80.6% |
|
||||
|
||||
**A medium that exactly meets the sustained requirement delivers every frame, on
|
||||
time, pixel-exact, and displays none of them.**
|
||||
|
||||
**AND THIS REVERSES 61.5's RANKING.** There are two packed players, and the
|
||||
difference between them is *when* the window is open:
|
||||
|
||||
- **A, DMAC-direct** (the one that is built): one channel start, the CPU halted
|
||||
or nearly, window open for the whole data phase.
|
||||
- **B, DMA-to-RAM plus a CPU paint**: the record lands in RAM with the window
|
||||
shut, and the 68000 paints it with the packed `movem` blit — **227,553 clocks,
|
||||
27.3% of a slot, MEASURED** (`blit.s` V8, 61.4) and **independent of the
|
||||
medium**. On screen **72.7% of every slot at any rate that delivers the record
|
||||
at all.**
|
||||
|
||||
They are equally visible at a data-phase rate of **2,131 KB/s, which is 3.7x the
|
||||
container's own wire**. Below that — which is every rate anyone has proposed —
|
||||
**the player with the CPU in the loop is on screen longer than the one without
|
||||
it.** 61.5 is not wrong; it ranked them in **clocks**, and this is the column
|
||||
that table does not have:
|
||||
|
||||
| W (clk per delivered byte) | A: DMAC-direct | B: DMA + CPU paint |
|
||||
|---:|---:|---:|
|
||||
| 5 | 31.0% | 58.4% |
|
||||
| **9 — the dual-address floor** | **54.9%** | **82.2%** |
|
||||
| 12 | 72.8% | 100.1% |
|
||||
| 16 | 96.6% | 123.9% |
|
||||
| 19 | 114.5% | 141.8% |
|
||||
|
||||
Both charge the audio DMA at 10,417 clocks (1.25%, from the IPL ROM's own
|
||||
channel-3 setup, 52.5); neither has a decoder in it. **B costs 27.3% of a frame
|
||||
and 99,328 B of RAM** — two record buffers, because at any rate near the wire the
|
||||
delivery of record *i+1* occupies most of the slot the paint of record *i*
|
||||
happens in. On a 2 MB machine that is 4.7% of memory, and memory is the resource
|
||||
the packed branch has spare: the ring it deleted was 256 KB.
|
||||
|
||||
### 64.3 Holding the bus costs the frame clock half its ticks — and the clock cannot tell
|
||||
|
||||
`src/player/clock.i` counts V-DISP interrupts. A held channel **halts the
|
||||
68000**. The MFP's pending bit is **one bit**, so every edge that falls inside a
|
||||
transfer spanning two of them is an edge the machine can **never** count. Nothing
|
||||
in this project had run a transfer and a clock at once, so nothing could have
|
||||
seen it.
|
||||
|
||||
| configuration, 120 frames | V-DISP edges seen | host frames drawn | lost |
|
||||
|---|---:|---:|---:|
|
||||
| **held, paced at 12 fps** | 551 | 1,038 | **487 = 46.9%** |
|
||||
| stealing, paced at 6 fps | 1,105 | 1,112 | 7 = 0.6% |
|
||||
|
||||
**And the player reported ZERO late frames in both.** That is not a reassurance,
|
||||
it is the finding: the pace gate compares the frame index against `PACE`, and
|
||||
`PACE` is advanced by the ISR the held channel stops the CPU from running — so a
|
||||
clock that loses edges loses them from **both sides of the comparison**. The held
|
||||
player believed it was running at 12 fps; the screen was at **6.37**. The only
|
||||
thing in the run that can contradict it is the host's raster count, which is why
|
||||
`packed.lua` reports both and `packed_run.sh` gates on the difference being
|
||||
non-zero.
|
||||
|
||||
The CPU's own account says the same thing from the other end: **held, the 68000
|
||||
went round its transfer wait 120 times in 120 frames — once each, meaning it
|
||||
never executed during a single transfer.** Stealing, it went round 1,100,520
|
||||
times. A player has to keep a clock, read a stick and feed ADPCM; **which of the
|
||||
two configurations can do any of that is a design question, and this is the run
|
||||
that answers it.**
|
||||
|
||||
### 64.4 The channel configuration does not set the transport's time
|
||||
|
||||
Free-running, both configurations delivered the same 49,664 B record within 0.5%
|
||||
of each other: **90.72 ms stealing, 90.27 ms held** — 534.6 and 537.3 KB/s,
|
||||
108.9% and 108.3% of a 12 fps slot.
|
||||
|
||||
**That figure is a property of the apparatus and is not `W` and not a medium.**
|
||||
MAME's device models carry no transfer timing (`docs/BENCHMARK.md`, 42.5). What
|
||||
the pair of runs *does* establish is a **negative that no arithmetic could
|
||||
have given**: the transfer time is **not the DMAC configuration's to set**. What
|
||||
a channel configuration buys is **who owns the CPU**, not when the picture
|
||||
appears. The mechanism behind MAME's own ceiling is **not diagnosed** — it is not
|
||||
the DMAC (the two configurations agree) and not the CPU (held, the CPU is halted
|
||||
throughout) — and no MAME source tree was available on this machine to name it.
|
||||
|
||||
### 64.5 What this does not settle
|
||||
|
||||
- **`W`. Not one clock of it.** Unchanged since 59.
|
||||
- **B2, whether a real board blanks in buffer mode.** Everything in 64.2 is
|
||||
conditional on it, and the condition now decides **which of two packed players
|
||||
is built** rather than how much headroom one has. `probe_bit11_blank.lua` is
|
||||
still written and still wants a board.
|
||||
- **The data-phase rate of any real medium.** This is the session's addition to
|
||||
**B1**, and it is a measurement nobody has planned: throughput and seek time
|
||||
were the two numbers on the list, and **the burst rate during a data phase is a
|
||||
third** that decides whether a DMAC-direct packed player shows a picture.
|
||||
- **Whether B is buildable as described.** It is priced off a measured blit and
|
||||
a measured ladder, and no line of it has been written.
|
||||
|
||||
+76
-12
@@ -56,6 +56,24 @@ pairing. **B2 now decides which packed CONTAINER exists, not only which player**
|
||||
(63.4). The fallback is a flag: `--scene-palette --no-palette` is 30.79 dB, zero
|
||||
churn, 576.0 KB/s and still +2.07 dB on the shipping codec. **K3 is next.**
|
||||
|
||||
Amended end of session 32: **K3 IS DONE, AND IT FOUND THE THING THAT DECIDES
|
||||
THE BRANCH (FINDINGS 64).** `src/player/packed.s` brings up its own display,
|
||||
builds its own 193-entry chain, keeps its own clock off V-DISP and fetches every
|
||||
record itself off a CZ-6BS1: **120 of 120 frames pixel-exact, every one
|
||||
compared, in both palette orders.** Two things came with it. **(1) The write
|
||||
window is the frame.** A packed write needs R20 bit 11, buffer mode blanks the
|
||||
layer, and a DMAC-direct player holds the window open for the whole data phase
|
||||
— so the dark fraction of a slot is `record / (DATA-PHASE rate x slot)` and a
|
||||
medium that exactly meets the 582.0 KB/s sustained requirement **displays none
|
||||
of the frames it delivers on time**. The rate that matters here is the BURST
|
||||
rate, which is a third hardware number **B1 has no test for**. It also
|
||||
**reverses 61.5's ranking**: a packed player that DMAs to RAM and paints with
|
||||
the CPU opens the window only for the measured 27.3% blit, so it is on screen
|
||||
72.7% of every slot at any rate, and the two are equal only at **2,131 KB/s =
|
||||
3.7x the wire**. **(2) A held channel costs the frame clock half its ticks and
|
||||
the clock cannot tell** — 46.9% of V-DISP edges lost, zero late frames reported,
|
||||
the player believing 12 fps while the screen ran at 6.37. **The open item is now
|
||||
K4.**
|
||||
**THE COMPLETION TARGET IS M3, THE VERTICAL SLICE** (USER DECISION): one scene
|
||||
tree — a decision point, two outcomes, a death clip — with audio, streaming from
|
||||
a real SCSI volume on a stock 2 MB machine, playable. That is the point at which
|
||||
@@ -108,7 +126,16 @@ None of these block M2 or M3 software work, because session 18 forced every rate
|
||||
to be an explicit argument. They set constants, and two of them decide how much
|
||||
headroom the finished player has.
|
||||
|
||||
**B1. Measure the BlueSCSI — throughput AND seek time.** **Session 29 gave this
|
||||
**B1. Measure the BlueSCSI — throughput, seek time AND the DATA-PHASE BURST
|
||||
RATE.** The third one is session 32's (FINDINGS 64.2) and it is not a refinement
|
||||
of the first: sustained throughput decides whether record *i* arrives before
|
||||
slot *i*, and the **burst rate during the data phase** decides how much of the
|
||||
slot the picture is on screen, because a DMAC-direct packed player holds the
|
||||
GVRAM write window open for exactly as long as the transfer takes and buffer
|
||||
mode blanks the layer. A drive with a read-ahead cache can pass the first and
|
||||
fail the second. The acceptance test is **`record / (burst x slot)` = the dark
|
||||
fraction**; at the container's own 582.0 KB/s it is 1.0, and the picture is never
|
||||
displayed. **Session 29 gave this
|
||||
a second acceptance test that is not a codec figure at all: 576 KB/s SUSTAINED,
|
||||
which is what a decoder-free packed literal frame costs and cannot be talked down
|
||||
from (FINDINGS 61.5).** A codec's bitrate is a lever; a literal frame's is
|
||||
@@ -286,9 +313,37 @@ frame is not tied to a scene palette the codec's codewords index into. It costs
|
||||
codec's ceiling, so the whole +2.31 dB is the per-frame palette; 90% of that
|
||||
palette changes every frame; and a mismatched paint is 12.8 dB worse, for
|
||||
roughly half of every frame slot, if buffer mode does not blank.
|
||||
- **K3. End to end, off the disc.** Palette, page-1 X-scroll 384, priority
|
||||
`vc1 = 0x0002`, R20 bit 11; one chained DMA a frame. Gated pixel-exact over the
|
||||
same 120-frame window the decoder is gated on.
|
||||
- ~~**K3. End to end, off the disc.**~~ **DONE, session 32 — FINDINGS 64.**
|
||||
`src/player/packed.s`, `tools/bench/packed.lua`, `tools/bench/packed_run.sh`,
|
||||
`tools/bench/verify_packed.py`, `tools/analysis/31_display_duty.py`. Palette,
|
||||
page-1 X-scroll 384, priority `vc1 = 0x0002`, R20 bit 11, one chained DMA a
|
||||
frame, **120 of 120 pixel-exact in both palette orders** — and the gate checks
|
||||
every frame rather than the last, because a packed frame is a literal and the
|
||||
codec's recursion was what made one comparison audit 120.
|
||||
|
||||
**What it found is K4's whole content**, and it is in the amendment above: the
|
||||
window is the frame, the burst rate decides visibility, and a held channel
|
||||
eats the clock.
|
||||
|
||||
- **K4. THE PACKED PLAYER THAT IS ON SCREEN.** 64.2's option B: DMA the record
|
||||
into one of two RAM buffers with the window SHUT, then paint it with the
|
||||
packed `movem` blit (`blit.s` V8, **measured** at 227,553 clocks = 27.3% of a
|
||||
slot). **82.2% of a frame at the 9 clk/B dual-address floor** against A's
|
||||
54.9%, **99,328 B of RAM**, and a picture on screen **72.7% of every slot at
|
||||
any delivery rate** instead of 0% at the container's own wire.
|
||||
|
||||
It is not a rewrite of K3: `packed.s` keeps its display bring-up, its clock,
|
||||
its transport and its record arithmetic, and what changes is the chain's
|
||||
destination and the addition of a paint. **The one thing in it that has never
|
||||
been run is the overlap** — a channel filling buffer *i+1* while the CPU
|
||||
paints buffer *i*, which is the first time in this project that the DMAC and
|
||||
the 68000 have had to want the bus at the same time for a whole scene.
|
||||
|
||||
**K4 is conditional on B2 the same way K3's ranking was**, and the condition
|
||||
now cuts the other way: if buffer mode does NOT blank, A is on screen the whole
|
||||
slot and K4 is 27.3% of a frame spent on nothing. **Do not build K4 before B2
|
||||
is answered** — that is the same rule 61.7 wrote for the codec, applied to the
|
||||
branch that replaced it.
|
||||
|
||||
**What K3 deletes, and why that is a risk and not a win to be banked:** a
|
||||
DMAC-direct packed player has **no ring** — `ring.i`, `xfer.i` and most of
|
||||
@@ -719,17 +774,26 @@ P2 re-encode bundle DONE (60): DLX5, records ARE sectors ───────
|
||||
E7/E4/C1 PARKED session 29: encoder work waits on B2 ───────────┤
|
||||
│
|
||||
K1 palette-register DMA? ─> K2 packed container ─> K3 end to end ──┤
|
||||
(61.9; K1/K2 survive a bad B2, K3 does not) │
|
||||
K1 DONE s30 (62), K2 DONE s31 (63) ── K3 is the open one │
|
||||
K1 DONE s30 (62), K2 DONE s31 (63), K3 DONE s32 (64): │
|
||||
120/120 pixel-exact, both palette orders, off a real volume │
|
||||
└─> and K3 found that the WRITE WINDOW IS THE FRAME, so: │
|
||||
K4 (DMA to RAM + the measured 27.3% paint) is the player │
|
||||
that is ON SCREEN below a 2,131 KB/s BURST rate -- which │
|
||||
is 3.7x the wire, so below every rate anyone has proposed │
|
||||
P4a WIRING (the channel behind ring.i's mailbox) <- THE LAST ITEM ─┤
|
||||
P1 P2 P3 P4b P5 P7, P6 bus cost (52), G1 scene graph (56) ────────┼─> M2 ─>
|
||||
B1 seek+rate (sets HEADROOM, not fit) ─────────────────────────────┘ M3 ─> M4
|
||||
B2 blanking ─┬─ NOT blanked ─> the DECODER-FREE PACKED player is live (61):
|
||||
│ 55.2% of a frame at the 9 clk/B FLOOR, against
|
||||
│ the codec's 110.4% -- and then B1 decides it,
|
||||
│ because it wants 576 KB/s FIXED (61.4, 61.5)
|
||||
└─ blanked ──────> a 27..113% duty strobe; there is no version of
|
||||
it that is merely expensive (48.3, 61.6)
|
||||
B2 blanking ─┬─ NOT blanked ─> K3's DMAC-DIRECT player is the one: 54.9% of a
|
||||
│ frame at the 9 clk/B floor against the codec's
|
||||
│ 110.4%, on screen the whole slot, and K4's paint
|
||||
│ would be 27.3% spent on nothing (61.4, 61.5)
|
||||
└─ blanked ──────> K3's player is on screen for
|
||||
1 - record/(BURST x slot) of every slot, which is
|
||||
ZERO at the container's own wire -- so K4 is the
|
||||
player, at 82.2% of a frame and 99,328 B of RAM
|
||||
(64.2). Neither answer kills the branch and each
|
||||
picks a different player.
|
||||
B1 BURST rate (NEW, 64.2) ──> which of the two K3/K4 wins, if B2 blanks
|
||||
```
|
||||
|
||||
**Read that top-left branch as the project's live question.** Everything else
|
||||
|
||||
+223
-1
@@ -1,4 +1,161 @@
|
||||
# Status & next-session handoff — end of session 31 (2026-08-25)
|
||||
# Status & next-session handoff — end of session 32 (2026-08-25)
|
||||
|
||||
## Session 32: the packed player runs, and the write window turns out to be the frame
|
||||
|
||||
**Green light first and last: `./tools/bench/check.sh` was ALL GREEN before any
|
||||
of this (`tmp/check_s32_start.log`) and ALL GREEN after** — the same stages, plus
|
||||
one new one.
|
||||
|
||||
**FINDINGS 64. ROADMAP K3 is done.** `src/player/packed.s` (2,898 B),
|
||||
`tools/bench/packed.lua`, `tools/bench/prep_packed.py`,
|
||||
`tools/bench/packed_run.sh`, `tools/bench/verify_packed.py`,
|
||||
`tools/analysis/31_display_duty.py`. `tools/bench/mkvol.sh` grew a DLXP arm —
|
||||
for a packed container the file **is** the volume, which is the format's own
|
||||
claim arriving at the disc.
|
||||
|
||||
**Everything below is EMULATED**: MAME 0.277 `x68000`, `-bios ipl10`, stock
|
||||
10 MHz / 2 MB, `-exp1 cz6bs1`, a real CHD volume. No real hardware ran.
|
||||
|
||||
**1. THE PLAYER WORKS, AND IT IS THE STRONGEST END-TO-END RESULT IN THE TREE.**
|
||||
The 68000 brings up its own display (CRTC mode, both scroll register pairs,
|
||||
priority `vc1 = $0002`, R20 bit 11), builds its own **193-entry** DMA chain,
|
||||
keeps its own frame clock off V-DISP, and fetches every record itself with
|
||||
`READ(10)`. The rig writes no picture byte, no palette entry and no CRTC
|
||||
register.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| frames | **120 of 120 pixel-exact**, EVERY ONE COMPARED |
|
||||
| both palette orders | palette FIRST and `--palette-last`, **120/120 each** |
|
||||
| chain | `$E82000`/512 B then 192 rows of 256 B at the 1,024 B stride — one start |
|
||||
| record `i` | LBA `1 + i*97`. Arithmetic. No index, no ring, no decoder |
|
||||
|
||||
The gate had to change shape: `verify_decode.py` checks the LAST frame and that
|
||||
audits 120 because the codec is temporally recursive. **A packed frame is a
|
||||
literal**, so `verify_packed.py` compares all of them, letterbox included.
|
||||
|
||||
**2. THE HEADLINE, AND IT IS NOT THE GOOD HALF.** The free-running run — which
|
||||
is what a 12 fps player becomes once the transfer is longer than the slot —
|
||||
reported a number no budget in this tree has a column for: **the write window
|
||||
was open on 99.5% of the host frames.** Every frame was pixel-exact and almost none of them was
|
||||
visible. It is arithmetic: a packed write needs R20 bit 11, buffer mode blanks
|
||||
the layer, and a DMAC-direct player holds the window open for the whole data
|
||||
phase — there is no second page to hide behind, because the packed layout spends
|
||||
both.
|
||||
|
||||
dark fraction of a slot = record bytes / (DATA-PHASE rate x slot)
|
||||
|
||||
**The rate in that expression is the BURST rate, not the sustained one**, and
|
||||
that is a third hardware number **B1 has no test for**. At the container's own
|
||||
582.0 KB/s the dark fraction is **1.0**: every frame delivered, on time,
|
||||
pixel-exact, and **none of them displayed**.
|
||||
|
||||
**3. AND IT REVERSES 61.5's RANKING.** A packed player that DMAs into RAM with
|
||||
the window SHUT and paints with the measured `movem` blit (27.3% of a slot) is
|
||||
on screen **72.7% of every slot at any rate**. The two are equal only at a
|
||||
data-phase rate of **2,131 KB/s = 3.7x the wire**. Below that — every rate
|
||||
anyone has proposed — **the player with the CPU in the loop is on screen
|
||||
longer.** That is ROADMAP **K4**, and it costs 82.2% of a frame at the 9 clk/B
|
||||
floor against 54.9%, plus 99,328 B of RAM for two record buffers.
|
||||
|
||||
**4. A HELD CHANNEL COSTS THE FRAME CLOCK HALF ITS TICKS, AND THE CLOCK CANNOT
|
||||
TELL.** `clock.i` counts V-DISP interrupts; a held channel halts the 68000; the
|
||||
MFP's pending bit is one bit. Held at 12 fps: **487 of 1,038 edges lost
|
||||
(46.9%)**, and the player reported **zero late frames** — because `PACE` is
|
||||
advanced by the ISR the channel stops from running, so the loss cancels out of
|
||||
the comparison. The player believed 12 fps; the screen was at **6.37**. Only the
|
||||
host's raster count contradicts it, and `packed_run.sh` gates on the difference
|
||||
being non-zero. Stealing at half rate loses 0.6%. Held, the CPU went round its
|
||||
transfer wait **120 times in 120 frames** — once each; it never executed during a
|
||||
transfer.
|
||||
|
||||
**5. THE CHANNEL CONFIGURATION DOES NOT SET THE TRANSPORT'S TIME.** Free-running,
|
||||
held and stealing delivered the same record within **0.5%** (90.27 vs 90.72 ms).
|
||||
That figure is the APPARATUS — MAME's device models carry no transfer timing —
|
||||
but the negative is real and no arithmetic could have given it: **a channel
|
||||
configuration buys who owns the CPU, not when the picture appears.**
|
||||
|
||||
**RISKS IN THIS SESSION'S RESULT, stated rather than left to be found:**
|
||||
|
||||
- **Everything in 2 and 3 is conditional on B2**, and B2 is still unmeasured on
|
||||
a board. What changed is the stakes: it now picks WHICH packed player, and
|
||||
each answer makes the other one's design work wasted.
|
||||
- **MAME's ~535 KB/s transport ceiling is not diagnosed.** It is not the DMAC
|
||||
(both configurations agree) and not the CPU (held, it is halted throughout),
|
||||
and no MAME source tree was on this machine to name it. It bounds nothing in
|
||||
the arithmetic, which is geometry, but it is the reason the gate is paced at
|
||||
half rate and that should not be mistaken for a rig limitation being worked
|
||||
around.
|
||||
- **The cadence sampler is quantised to one host frame** (17.6 ms). The MEAN
|
||||
over 120 frames is not (±0.30 ms); the min/median/max are the sampler's.
|
||||
|
||||
## HANDOFF — start here
|
||||
|
||||
**THE TREE IS ALL GREEN**, session 32's stage included (`tmp/check_s33_start.log`,
|
||||
ALL GREEN, ~6 min).
|
||||
|
||||
### The work, in the order it should be done
|
||||
|
||||
**1. NOT K4 — NOT YET.** ROADMAP K4 is written and priced (82.2% of a frame at
|
||||
the 9 clk/B floor, 99,328 B of RAM, on screen 72.7% of every slot at any rate),
|
||||
and it is **conditional on B2**, which is a board question nobody here can
|
||||
answer. If buffer mode does NOT blank, K3's player is already on screen the
|
||||
whole slot and K4's paint is 27.3% of a frame spent on nothing. 61.7 wrote that
|
||||
rule for the codec; it applies to the branch that replaced it. **Do not open
|
||||
K4 until B2 comes back.**
|
||||
|
||||
**2. WHAT IS NOT BLOCKED IS P6 — AUDIO.** It is the largest unpriced item left
|
||||
in the project and it is on M3's critical path, which is the completion target.
|
||||
The bus half closed in session 20 (52: 1.25%..1.48% of a frame, and the unit
|
||||
error was caught) — **everything else in it is open**: extraction, an encoder,
|
||||
the container interleave, and what a second stream does to `wire` and therefore
|
||||
to 51.3's refill climb. It is independent of B2 in the part that matters: both
|
||||
packed containers and the codec carry the same audio.
|
||||
|
||||
Three things make it a session rather than a chore:
|
||||
- **No ffmpeg ENCODER exists for this format.** `adpcm_ima_oki` is a decoder
|
||||
only, so the encoder is ours to write and ours to gate.
|
||||
- **The two available references DISAGREE, and it was MEASURED not recalled**
|
||||
(session 33, `tmp/adpcm_probe/`). ffmpeg's `adpcm_ima_oki` is
|
||||
`((2*delta+1) * step) >> 3`, 12-bit signal clipped to [-2048, 2047], output
|
||||
x16, **high nibble first** — reproduced sample-exact over 2,268 nibbles. The
|
||||
OKI datasheet form truncates per term (`step/8 + step + step/2 + step/4`,
|
||||
selected by nibble bits) and differs on **445 of those 2,268 samples, by up to
|
||||
4 in 12-bit units**. Which one the machine runs is not a matter of taste, and
|
||||
**MAME has an `okim6258` in the x68000 — so it can be asked.**
|
||||
- **DLXP1 has no room for it.** A packed record is 97 sectors EXACTLY because
|
||||
49,664 B is what a literal frame costs; audio does not fit in the slack,
|
||||
because there is none. What audio does to the packed record's geometry is a
|
||||
container question, not an encoder one.
|
||||
|
||||
### What is PARKED, so it is not re-derived
|
||||
|
||||
**E7, E4 and C1** (61.8), and **P4a's wiring** for the video path (K3 deletes the
|
||||
ring from it). Do not delete any of it — B2 is unanswered.
|
||||
|
||||
### Risks that are OURS, not hardware
|
||||
|
||||
1. **A chained transfer has never run back to back at 12 fps and now it has**,
|
||||
120 times, pixel-exact — but only at HALF rate, because at full rate there is
|
||||
nothing to snapshot (64.2). The gate's pacing is a consequence of the finding.
|
||||
2. **The packed player deletes `ring.i`, `xfer.i` and most of `stream.s`.**
|
||||
Unchanged: a simplification that large usually hides something, and it has
|
||||
now run clean for 120 frames without finding out what.
|
||||
3. **MAME's ~535 KB/s transport ceiling is undiagnosed** (64.4). It bounds
|
||||
nothing in the arithmetic, which is geometry.
|
||||
4. **B2 sits over the branch and now picks WHICH PLAYER**, not how much headroom
|
||||
one has.
|
||||
|
||||
### Reproducing this session
|
||||
|
||||
./tools/bench/check.sh # ALL GREEN
|
||||
bash tools/bench/packed_run.sh tmp/packed_singe.dlxp # all four runs
|
||||
python3 tools/analysis/31_display_duty.py
|
||||
|
||||
**WHAT IS NEXT.** P6: audio, from the disc to a byte count that the container
|
||||
has to carry.
|
||||
|
||||
---
|
||||
|
||||
## Session 31: the packed container, and the palette that buys 2.31 dB has a price
|
||||
|
||||
@@ -1704,6 +1861,39 @@ Green light: `./tools/bench/check.sh` **ALL GREEN**, before and after.
|
||||
|
||||
## NEXT SESSION, in order
|
||||
|
||||
**AS OF SESSION 32 the top of the list is this, and the rest of this section is
|
||||
the session-19 list kept for its detail.** `docs/ROADMAP.md` is the current
|
||||
shape; where the two disagree, ROADMAP is the one that was edited last.
|
||||
|
||||
1. **B2, and it is now the fork rather than a headroom question.**
|
||||
`tools/bench/probe_bit11_blank.lua` is written and settles it in minutes on a
|
||||
real board. FINDINGS 64.2 is why it moved to the top: **if buffer mode
|
||||
blanks, the K3 player that now exists shows nothing at the container's own
|
||||
wire and K4 is the player; if it does not blank, K3 is the player and K4's
|
||||
27.3% paint is spent on nothing.** Each answer makes the other branch's work
|
||||
wasted, so building either before the answer is the one thing to avoid.
|
||||
|
||||
2. **B1 gained a THIRD number: the DATA-PHASE BURST RATE** (64.2). Sustained
|
||||
throughput decides whether record *i* arrives before slot *i*; the burst rate
|
||||
decides how much of the slot the picture is on screen. A drive with a
|
||||
read-ahead cache can pass one and fail the other. The acceptance test is
|
||||
`record / (burst x slot)` = the dark fraction, and it is **1.0 at 582.0 KB/s**
|
||||
and 0.273 — the same as K4's paint — only at **2,131 KB/s**.
|
||||
|
||||
3. **K4, and NOT before item 1.** ROADMAP has the shape. The one thing in it
|
||||
that has never been run is the **overlap**: a channel filling buffer *i+1*
|
||||
while the CPU paints buffer *i*, which is the first time the DMAC and the
|
||||
68000 have had to want the bus at the same time for a whole scene.
|
||||
|
||||
4. **Diagnose MAME's transport ceiling, or stop quoting it.** ~535 KB/s a record
|
||||
in both channel configurations, mechanism unknown, no MAME source on this
|
||||
machine. It bounds nothing — the arithmetic in 64.2 is geometry — but it is
|
||||
why `check.sh`'s packed stage is paced at half rate, and an undiagnosed
|
||||
apparatus constant is exactly the shape of thing FINDINGS 42.1 caught.
|
||||
|
||||
---
|
||||
|
||||
|
||||
**Item 4 is CLOSED (session 19, FINDINGS 51).** The list below is otherwise
|
||||
unchanged, and items 1-3 are all still blocked on hardware this machine does not
|
||||
have. What session 19 adds to item 1 is a SECOND number to measure the medium
|
||||
@@ -2984,6 +3174,38 @@ flattering possible way to state one.
|
||||
**Do not run this alongside another MAME job.** They share `tmp/` logs and
|
||||
snapshot directories and the second run silently truncates the first.
|
||||
|
||||
## Reproducing the packed player (session 32)
|
||||
|
||||
```
|
||||
python3 tools/encoder/pack.py tmp/fr_singe tmp/packed_singe.dlxp --nframes 120
|
||||
./tools/bench/packed_run.sh tmp/packed_singe.dlxp
|
||||
```
|
||||
~4 min, four MAME runs. `DLX_PK_GATE_ONLY=1` runs only the first, which is what
|
||||
`check.sh` takes. Expected: 120 of 120 pixel-exact; 193 chain entries; 0 late
|
||||
frames; 0 frames unsampled; a free-running transport of ~90 ms a record; and
|
||||
**487 of 1,038 V-DISP edges lost in the held configuration**, which is the run's
|
||||
whole point and is asserted rather than admired.
|
||||
|
||||
The palette-LAST container is a flag and not a re-encode:
|
||||
```
|
||||
python3 tools/encoder/pack.py tmp/fr_singe tmp/packed_singe_last.dlxp \
|
||||
--nframes 120 --palette-last
|
||||
./tools/bench/packed_run.sh tmp/packed_singe_last.dlxp
|
||||
```
|
||||
The chain assertion is read out of the container's own flags, so a run that
|
||||
built the array the other way round fails instead of painting 192 rows of
|
||||
picture into the palette registers.
|
||||
|
||||
And the arithmetic underneath the result, which needs no emulator:
|
||||
```
|
||||
python3 tools/analysis/31_display_duty.py tmp/packed_singe.dlxp \
|
||||
--rate 582 700 1164 2131 3000
|
||||
```
|
||||
`--rate` is REQUIRED and has no default, for FINDINGS 50's reason.
|
||||
|
||||
**Do not run this alongside another MAME job.** It shares `tmp/` logs with every
|
||||
other rig here.
|
||||
|
||||
## Reproducing the decoder result (session 7)
|
||||
|
||||
```
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 200 KiB |
Reference in New Issue
Block a user