Ask the chip which decoder it is, and find four wrong axes where one was expected
ROADMAP P6a, on the machine. 68000 code programs HD63450 channel 3 with the IPL ROM's own ADPCM bytes -- dual address, 8-bit port, cycle steal, external request -- and feeds the MSM6258 a designed 1,678-nibble stream at the chip's own pace: 839 B in 0.1074 s = 7,811.4 B/s against the format's 7,812.5, CER=$00. That transport is P6b's, not scaffolding. Sixteen candidate decoder models, three capture decimations and a searched prologue are fitted to MAME's capture. Exactly one reproduces it sample-exact over all 1,678 samples, and every axis carries a negative control: flip it alone and the closest survivor disagrees on 826, 1,504, 156 and 1,522 samples. The chip runs 'terms', takes the LOW nibble of a byte first, clamps the accumulator at 10 bits and starts it at -2. tools/encoder/adpcm.py defaulted to the opposite of all four, and 65.2 named the wrong axis as the risk: the delta formula is worth -2.88 dB and the NIBBLE ORDER is worth -25.74 dB. 65.1's "high first, measured" was a measurement of ffmpeg, i.e. of the VOX file convention, which is a different question from what a chip does with a byte in its data register. The 10-bit clamp is free on the Singe window and only because that window peaks at 435 of 511 -- 1.4 dB of headroom on a -13.4 dBFS passage, 12.1 dB below where the encoder was clamping, and inside the recursion. So the audio level is an open choice again, downward, and the loudest passage on the disc is unmeasured. Session 33's silence had two ordinary causes: the PPI's port C is an input until control word $92 says otherwise, and $01 is COMMAND_STOP. And a rig fact worth the space: the 8 MHz ADPCM clock is CT1 in the YM2151's $1B, delivered on the sound system's schedule rather than at the store, so a transfer started in the same breath as the setup plays its first ~17 ms at the old clock and no model fits a stream that changed rate part way through. Name the layer: this is MAME 0.277's okim6258 device model measured end to end through the machine's real transport. It settles the rig and not the silicon. Also struck: 64.4's "no MAME source tree is on this machine" -- there is none on disk, but the machine has network and the upstream tag fetches. check.sh ALL GREEN before (tmp/check_s34_start.log) and after (tmp/check_s34_end.log), with one new stage. Claude-Session: https://claude.ai/code/session_01194oWYW8DQXK1SZ2DnChW6
This commit is contained in:
@@ -6949,3 +6949,129 @@ correct because Sharp wrote it.
|
||||
DLXP2 would be built from, and no byte of one has been written.
|
||||
- **What audio does to a scene change.** The slack table is here, but 51.3's
|
||||
refill climb with a second consumer through a real branch point is not.
|
||||
|
||||
---
|
||||
|
||||
## 66. The chip is asked, and it disagrees with the encoder on all four axes — the largest of them is not the one we were worried about (session 34)
|
||||
|
||||
**ROADMAP P6a.** `src/player/adpcm.i`, `src/player/adpcmgate.s`,
|
||||
`tools/bench/prep_adpcm.py`, `tools/bench/adpcm.lua`,
|
||||
`tools/bench/adpcm_run.sh`, `tools/bench/verify_adpcm_chip.py`,
|
||||
`tools/analysis/33_adpcm_model.py`.
|
||||
|
||||
**Name the layer.** 68000 code drives the transport; the thing measured is
|
||||
**MAME 0.277's `okim6258` device model**, end to end through the machine's real
|
||||
DMA path. It settles the **rig** — an emulated audio test encoded against the
|
||||
wrong model is 25 dB of nothing — and it does **not** settle the silicon. What a
|
||||
real MSM6258V does is still a hardware/datasheet item.
|
||||
|
||||
### 66.0 A MAME source tree IS reachable from this machine, and 64.4 is struck
|
||||
|
||||
FINDINGS 64.4 and `tools/encoder/adpcm.py`'s header both record "no MAME source
|
||||
tree is on this machine". There is no tree on disk, but the machine has network
|
||||
and `raw.githubusercontent.com/mamedev/mame/mame0277/...` fetches. That is how
|
||||
this session designed its experiment rather than swept blindly, and it is worth
|
||||
recording because two sessions reasoned about MAME's device model as an
|
||||
unopenable box when it was one `curl` away.
|
||||
|
||||
**It does not replace the measurement and did not become one.** The installed
|
||||
binary is Ubuntu's 0.277 and the tag is upstream's; whether the two are the same
|
||||
bytes is not something a fetch can say. Everything below is read out of a
|
||||
capture from the binary that is actually here.
|
||||
|
||||
### 66.1 The transport is the IPL ROM's own, and it works first time
|
||||
|
||||
`src/player/adpcm.i` programs HD63450 **channel 3** with the bytes
|
||||
`tools/analysis/21_iplrom_dmac.py` decodes out of the IPL ROM at `$FF0C2E` and
|
||||
`$FF9A82`: `DCR = $80` (dual address, 8-bit port, cycle steal without hold),
|
||||
`SCR = $04`, `MFC = DFC = $05`, `CPR = $01`, `DAR = $E92003`, `OCR = $32`
|
||||
(memory→device, byte, **external request**), `CCR = $80`, then command `$02` to
|
||||
`$E92001`. 839 bytes moved in **0.1074 s = 7,811.4 B/s** against the format's own
|
||||
7,812.5, `CSR = $E0`, `CER = $00`, `MTC = 0`. **This is P6b's transport, not
|
||||
scaffolding.**
|
||||
|
||||
**Two things session 33 could not have guessed and did not have to.** 65.5 fed
|
||||
the chip from Lua, got silence, swept control 0..3 × port C 0..15 and stopped.
|
||||
Both reasons are ordinary:
|
||||
|
||||
* **The PPI's port C is an INPUT until it is told otherwise.** ADPCM pan and the
|
||||
sample-rate divider are port C bits; an i8255 out of reset has every port an
|
||||
input, so writes to `$E9A005` move a latch nothing is reading. Control word
|
||||
`$92` first, and only then does `$08` mean *pan both, ÷512*.
|
||||
* **`$01` is COMMAND_STOP.** `$02` is PLAY. The sweep that "covered" 0..3 wrote
|
||||
`$01` in the probe that swept port C, so the chip was never playing in it.
|
||||
|
||||
### 66.2 THE HEADLINE: four axes were wrong, and the expensive one is the NIBBLE ORDER
|
||||
|
||||
The probe is 1,678 nibbles — 16 zero nibbles of prologue, a trigger, an encoded
|
||||
sine, then loud bursts — and `verify_adpcm_chip.py` searches **sixteen candidate
|
||||
decoder models** (nibble feed × delta formula × clamp × initial accumulator)
|
||||
crossed with three capture decimations and a prologue length. **Exactly one
|
||||
reproduces the capture, over 1,678 consecutive samples, sample-exact**, and each
|
||||
axis carries a negative control: flip it alone and the closest surviving
|
||||
alternative disagrees on 826, 1,504, 156 and 1,522 samples respectively.
|
||||
|
||||
| axis | `adpcm.py` default | **the chip** | cost of getting it wrong ALONE |
|
||||
|---|---|---|---:|
|
||||
| nibble order | high first | **LOW first** | **−25.74 dB** |
|
||||
| delta formula | `shift` | **`terms`** | −2.88 dB |
|
||||
| clamp | 12-bit | **10-bit** | 0.00 dB *(on this window)* |
|
||||
| accumulator at PLAY | 0 | **−2** | −0.45 dB |
|
||||
| **all four at once** | | | **−10.38 dB** against 21.97 |
|
||||
|
||||
**65.2 named the wrong axis as the risk.** It priced the delta formula at 25 dB
|
||||
and left nibble order recorded as "HIGH FIRST, **measured**". That measurement
|
||||
was real and it was **against ffmpeg**, i.e. about the Dialogic VOX *file*
|
||||
convention — not about what a chip does with a byte written to its data
|
||||
register. The two are different questions with different answers, and the one
|
||||
this port needs is the second. `verify_adpcm.py` keeps ffmpeg's parameters
|
||||
deliberately: a reference check whose reference has been adjusted to agree is
|
||||
not a check. `adpcm.CHIP` carries the measured set, and anything that encodes
|
||||
**for the machine** passes it explicitly.
|
||||
|
||||
### 66.3 The clamp is 10-bit, it costs nothing here, and that is the finding that will bite
|
||||
|
||||
The MSM6258's D/A is 10-bit and the model clamps the **accumulator** there, so it
|
||||
is inside the recursion rather than an output scaling. On the Singe window it is
|
||||
free — encode for 12 bits or for 10 and the answer is **21.99 dB either way,
|
||||
with zero samples on the clamp** — for one reason only: that window peaks at
|
||||
**435 of 511**, i.e. **1.4 dB of headroom**, and it is a −13.4 dBFS passage.
|
||||
|
||||
A 10-bit accumulator is **12.1 dB smaller** than the 12-bit word the encoder was
|
||||
clamping to. **65.1's "the level is not a lever" survives downward and is now
|
||||
wrong upward**: normalising still buys nothing, and a passage a few dB louder
|
||||
than this one does not fit. **Nothing in this project has measured the loudest
|
||||
passage on the disc**, so the audio level is an open choice, not a settled one.
|
||||
|
||||
### 66.4 A rig fact that cost this session most of its time, and is worth the space
|
||||
|
||||
The 8 MHz ADPCM master clock is **CT1 in the YM2151's port register `$1B`**, in a
|
||||
different device from the divider. MAME delivers that write to the ADPCM chip on
|
||||
the **sound system's own schedule**, not at the instant of the store — so a
|
||||
transfer started in the same breath as the setup plays its first ~17 ms at the
|
||||
previous clock. The symptom is specific and misleading: the capture's first
|
||||
~130 samples arrive in **exact identical pairs**, the rest do not, and **no model
|
||||
fits a stream that changed rate part way through** — which reads exactly like a
|
||||
broken probe. `adpcmgate.s` spins ~100 ms after `ad_setup` and says why. A
|
||||
player sets its clock once at boot and never meets this.
|
||||
|
||||
**The general lesson is the one this tree keeps relearning**: a run that fails to
|
||||
match is not evidence about the thing being measured until the apparatus has
|
||||
been shown to be steady. Three quarters of the diagnosis here was spent
|
||||
disproving hypotheses about the *chip* for a symptom that was about the *clock
|
||||
write*.
|
||||
|
||||
### 66.5 What is still open, stated so it is not read as closed
|
||||
|
||||
* **The silicon.** Every value in 66.2 is MAME's. A real MSM6258V may differ on
|
||||
any of the four, and the two published references already disagree on one.
|
||||
This is a datasheet or a board, and it is cheap on a board: play a known
|
||||
nibble stream and record the line out.
|
||||
* **Nothing has played as audio.** The capture is a measurement instrument, not
|
||||
a listening test, and no ADPCM has reached a speaker on real hardware.
|
||||
* **The encoder is still greedy** (65 risk list, unchanged): exhaustive
|
||||
per-sample search, no lookahead. 21.99 dB is this format's floor here.
|
||||
* **P6b is now unblocked and its bytes are decided**: DLXP2 must be encoded with
|
||||
`adpcm.CHIP`, and 65.3's cadence arithmetic (F=11, A=14, wire 582.0 → 589.6
|
||||
KB/s) is untouched by any of this — it is a byte count, and none of the four
|
||||
axes changes how many bytes a second the format needs.
|
||||
|
||||
+26
-1
@@ -96,6 +96,25 @@ The codec container, which already has an index and variable records, pays
|
||||
branch's simplification, and 64's risk list predicted there would be one without
|
||||
knowing what.
|
||||
|
||||
Amended end of session 34: **P6a IS DONE, AND THE ENCODER WAS WRONG ON FOUR AXES
|
||||
RATHER THAN ONE (FINDINGS 66).** 68000 code programs HD63450 channel 3 with the
|
||||
IPL ROM's own ADPCM bytes — dual address, 8-bit port, cycle steal, EXTERNAL
|
||||
request — feeds the MSM6258 at the chip's own pace (**7,811.4 B/s against
|
||||
7,812.5**), and one of **sixteen** candidate decoder models reproduces MAME's
|
||||
capture **sample-exact over 1,678 consecutive samples**, with a negative control
|
||||
on every axis. The chip runs **`terms`, LOW nibble first, a 10-bit accumulator,
|
||||
starting at −2**, and `tools/encoder/adpcm.py` defaulted to the opposite of all
|
||||
four. **65.2 named the wrong axis as the risk**: the delta formula is worth
|
||||
−2.88 dB and the NIBBLE ORDER is worth **−25.74 dB**, and 65.1's "high first,
|
||||
measured" was a measurement of ffmpeg's VOX file convention rather than of a
|
||||
chip's data register. Two things came with it. **(1) The 10-bit clamp is free on
|
||||
the Singe window and only because that window peaks at 435 of 511** — 1.4 dB of
|
||||
headroom on a −13.4 dBFS passage, where the encoder had been clamping 12.1 dB
|
||||
higher — so **the audio LEVEL is an open choice again**, downward, and the
|
||||
loudest passage on the disc is unmeasured. **(2) The transport is P6b's, not
|
||||
scaffolding**, and it worked first time. **P6b is next and its bytes are
|
||||
decided: DLXP2 encodes with `adpcm.CHIP`.**
|
||||
|
||||
**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
|
||||
@@ -657,7 +676,13 @@ Buildable, and empty until P4: there is nothing to boot from yet.
|
||||
**Exit criterion: one decision point, two outcomes, a death clip, with audio,
|
||||
playing from disc on stock hardware.**
|
||||
|
||||
**P6. Audio — and it is the largest unpriced risk left in the project.**
|
||||
**P6. Audio. P6a DONE, session 34 — FINDINGS 66.** ~~and it is the largest
|
||||
unpriced risk left in the project.~~ Three quarters of P6 closed in session 33
|
||||
and the fourth precondition — which decoder the chip runs — closed in 34, on the
|
||||
machine, through the real DMA channel. **What is left of P6 is P6b (DLXP2, a
|
||||
container with sound in it) and the refill climb with a second consumer.**
|
||||
|
||||
*(original framing, kept because every figure below is still the live one:)*
|
||||
MSM6258 ADPCM, 15.6 kHz mono, **7.8 KB/s**. That figure is in `ratectl.py`'s
|
||||
budget and nowhere else: not extracted, not encoded, not interleaved into the
|
||||
container, and **never priced on the bus**. Two reasons to treat it as a risk
|
||||
|
||||
+119
-1
@@ -1,4 +1,122 @@
|
||||
# Status & next-session handoff — end of session 33 (2026-08-25)
|
||||
# Status & next-session handoff — end of session 34 (2026-08-25)
|
||||
|
||||
## Session 34: the chip is asked, and the encoder was wrong on four axes
|
||||
|
||||
**Green light first and last: `./tools/bench/check.sh` was ALL GREEN before any
|
||||
of this (`tmp/check_s34_start.log`) and ALL GREEN after
|
||||
(`tmp/check_s34_end.log`)** — the same stages, plus one new one.
|
||||
|
||||
**FINDINGS 66. ROADMAP P6a is DONE.** `src/player/adpcm.i`,
|
||||
`src/player/adpcmgate.s`, `tools/bench/prep_adpcm.py`, `tools/bench/adpcm.lua`,
|
||||
`tools/bench/adpcm_run.sh`, `tools/bench/verify_adpcm_chip.py`,
|
||||
`tools/analysis/33_adpcm_model.py`.
|
||||
|
||||
**NAME THE LAYER.** 68000 code drove the transport; the thing measured is
|
||||
**MAME 0.277's `okim6258` device model**, end to end through the machine's own
|
||||
DMA path. No board ran. It settles the RIG and not the silicon.
|
||||
|
||||
**0. A MAME SOURCE TREE IS REACHABLE AND 64.4 IS STRUCK.** There is no tree on
|
||||
disk, but this machine has network and the upstream tag fetches. Two sessions
|
||||
reasoned about MAME's device model as an unopenable box when it was one `curl`
|
||||
away. It designed the experiment; it did not become the result.
|
||||
|
||||
**1. THE TRANSPORT IS THE IPL ROM'S OWN AND IT WORKED FIRST TIME.** HD63450
|
||||
channel 3, `DCR=$80 SCR=$04 MFC=DFC=$05 CPR=$01 DAR=$E92003 OCR=$32 CCR=$80`,
|
||||
then `$02` to `$E92001` — every byte decoded out of the ROM by
|
||||
`21_iplrom_dmac.py`. **839 B in 0.1074 s = 7,811.4 B/s** against the format's
|
||||
7,812.5, `CSR=$E0 CER=$00 MTC=0`. **This is P6b's transport arriving early.**
|
||||
|
||||
Session 33's silence (65.5) had two ordinary causes and neither was a register
|
||||
semantic worth guessing at: **the PPI's port C is an INPUT until control word
|
||||
`$92` says otherwise**, so the pan/divider writes went to a latch nothing read;
|
||||
and **`$01` is COMMAND_STOP**, which is what the port-C sweep wrote every time.
|
||||
|
||||
**2. THE HEADLINE, AND IT IS NOT THE AXIS 65 WAS WORRIED ABOUT.** Sixteen
|
||||
candidate models, three capture decimations, a searched prologue: **exactly one
|
||||
reproduces the capture over 1,678 consecutive samples, sample-exact**, and every
|
||||
axis has a negative control.
|
||||
|
||||
| axis | `adpcm.py` default | the chip | wrong ALONE |
|
||||
|---|---|---|---:|
|
||||
| nibble order | high first | **LOW first** | **−25.74 dB** |
|
||||
| delta formula | `shift` | **`terms`** | −2.88 dB |
|
||||
| clamp | 12-bit | **10-bit** | 0.00 dB (this window) |
|
||||
| accumulator at PLAY | 0 | **−2** | −0.45 dB |
|
||||
| all four | | | **−10.38 dB** against 21.97 |
|
||||
|
||||
**65.1's "nibble order: HIGH FIRST, measured" was true and was about ffmpeg** —
|
||||
the Dialogic VOX *file* convention — not about what a chip does with a byte in
|
||||
its data register. `verify_adpcm.py` keeps ffmpeg's parameters on purpose; the
|
||||
measured set is `adpcm.CHIP` and callers that encode FOR the machine pass it.
|
||||
|
||||
**3. THE 10-BIT CLAMP IS FREE HERE AND ONLY BY LUCK.** Encode for 12 bits or for
|
||||
10 and the Singe window is **21.99 dB either way, zero samples on the clamp** —
|
||||
because it peaks at **435 of 511**, **1.4 dB of headroom**, on a −13.4 dBFS
|
||||
passage. The clamp is **12.1 dB below** where the encoder was clamping and it is
|
||||
INSIDE the recursion. **65.1's "the level is not a lever" survives downward and
|
||||
is now wrong upward**, and **the loudest passage on the disc is unmeasured**.
|
||||
|
||||
**4. THE RIG FACT THAT COST THIS SESSION MOST OF ITS TIME.** The 8 MHz ADPCM
|
||||
clock is CT1 in the YM2151's `$1B`, in a different device from the divider, and
|
||||
MAME delivers it on the sound system's schedule rather than at the store. A
|
||||
transfer started in the same breath as the setup plays its first ~17 ms at the
|
||||
old clock; the capture's first ~130 samples come out in **exact identical
|
||||
pairs** and no model fits a stream that changed rate part way through — which
|
||||
reads exactly like a broken probe. `adpcmgate.s` settles ~100 ms and says why.
|
||||
|
||||
**RISKS IN THIS SESSION'S RESULT:**
|
||||
|
||||
- **The silicon is untouched.** All four values are MAME's. A real MSM6258V may
|
||||
differ on any of them, and the two published references already disagree on
|
||||
one. Cheap on a board: play a known nibble stream, record the line out.
|
||||
- **Nothing has played as audio.** The capture is an instrument, not a listen.
|
||||
- **The encoder is still greedy** — no lookahead, unchanged from 65.
|
||||
- **The 10-bit clamp is a ceiling nobody has measured the disc against.**
|
||||
|
||||
## HANDOFF — start here
|
||||
|
||||
**THE TREE IS ALL GREEN**, session 34's stage included.
|
||||
|
||||
### The work, in the order it should be done
|
||||
|
||||
**1. P6b — DLXP2, a container with sound in it.** Unblocked, and its bytes are
|
||||
now decided: encode with **`adpcm.CHIP`** (`terms`, low nibble first, 10-bit,
|
||||
init −2), not with the defaults. 65.3 is all the arithmetic and none of it
|
||||
moved — cadence **F=11, A=14**, `LBA(i) = LBA0 + i*97 + floor(i/11)*14`,
|
||||
14,336 B held, wire **582.0 → 589.6 KB/s** — because a byte count does not care
|
||||
which decoder reads the bytes.
|
||||
|
||||
**2. THE AUDIO LEVEL, which 66.3 reopened.** The encoder now has 12.1 dB less
|
||||
room than it thought. Measure the loudest passage on the disc before choosing a
|
||||
level; a scene that clips inside the recursion does not merely distort, it
|
||||
drives the predictor.
|
||||
|
||||
**3. WHAT IS LEFT OF P6 AFTER THAT** is the fourth quarter: 51.3's refill climb
|
||||
with a second consumer through a real branch point. The slack table is in
|
||||
`32_audio_wire.py`; nothing has been run.
|
||||
|
||||
### What is still BLOCKED, so it is not picked up by mistake
|
||||
|
||||
**K4 — the packed player that is on screen — is conditional on B2**, a board
|
||||
question. **E7, E4 and C1** are parked (61.8), and **P4a's wiring** is parked
|
||||
with the ring K3 deleted.
|
||||
|
||||
**The hardware list is unchanged and is the user's**: B1 (sustained AND the
|
||||
data-phase BURST rate, 64.2), B2 (blanking — the five-minute half), B3
|
||||
(`#EXREQ`), B4 (a byte write to a palette register). **Session 34 adds a fifth
|
||||
that is cheaper than any of them**: play a known nibble stream on a real
|
||||
MSM6258V and record the line out, which settles 66.2's four axes on silicon.
|
||||
|
||||
### Reproducing this session
|
||||
|
||||
./tools/bench/check.sh # ALL GREEN
|
||||
./tools/bench/adpcm_run.sh # the chip gate on its own
|
||||
python3 tools/analysis/33_adpcm_model.py tmp/au_singe.raw
|
||||
|
||||
**WHAT IS NEXT.** P6b: a container with sound in it, encoded for the chip that
|
||||
was just measured.
|
||||
|
||||
---
|
||||
|
||||
## Session 33: audio gets an encoder, and the packed container's best property gets a bill
|
||||
|
||||
|
||||
Reference in New Issue
Block a user