Files
Dragon-s-Lair-X68k/src/player/adpcmgate.s
T
prosolis 6dd3fb3597 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
2026-08-25 09:50:03 -07:00

113 lines
4.4 KiB
ArmAsm

; Front-end for the ADPCM transport (ROADMAP P6a), for the rig.
;
; It plays ONE buffer of nibbles the host pushed into RAM and reports what the
; channel did. What is being measured is not this code -- it is the CHIP: which
; delta formula, which nibble of a byte first, where the accumulator clamps, and
; what it starts at. tools/bench/verify_adpcm_chip.py reads all four out of
; MAME's own -wavwrite capture.
;
; THE ONE THING THIS FILE HAS TO GET RIGHT is the order of the two starts. The
; chip resets its accumulator, its step index AND its nibble select when it is
; told to PLAY, and it begins consuming immediately out of whatever its data
; register holds. So the channel goes first and the CPU waits for MTC to move
; -- proof that a byte has actually been taken -- before the PLAY. The stream
; still has a prologue of unknown length, because the gap between PLAY and the
; NEXT #DRQ3 is not ours to set; the prologue is 16 zero nibbles for exactly
; that reason and the verifier reads its length off the capture.
include "src/player/geom.i"
AD_FLAG = $18600 ; u32 0 idle, 1 armed, 2 playing, $FF done, $EE error
AD_BUF = $18604 ; u32 where the nibble bytes are
AD_LEN = $18608 ; u32 how many bytes
AD_MTC0 = $1860C ; u32 MTC at the instant PLAY was written
AD_CSRF = $18610 ; u32 CSR at completion
AD_CERF = $18614 ; u32 CER with it
AD_MTCF = $18618 ; u32 MTC with it
AD_MARF = $1861C ; u32 MAR with it -- where the channel stopped
AD_SPIN = $18620 ; u32 trips round the wait loop
AD_STAT = $18624 ; u32 the chip's own status byte while playing
AD_PATIENCE = 60000000
AD_SETTLE = 60000 ; ~100 ms at 10 MHz, 18 clocks a trip ; the wait is bounded like every other
org $10000
start:
bsr ad_setup
; ---- SETTLE, and it is not superstition. The 8 MHz ADPCM clock is
; CT1 in the YM2151's port register, and this machine delivers that
; write to the ADPCM chip on the SOUND system's own schedule rather
; than at the instant of the store -- so a transfer started in the same
; breath as ad_setup plays its first ~17 ms at the PREVIOUS clock. The
; symptom is exact: the capture's first 130-odd samples come out in
; identical PAIRS, because the chip is clocking half as fast as the
; capture, and every model then fails to fit a stream that changed rate
; part way through. ~100 ms of nothing costs the gate nothing and a
; player sets its clock once at boot.
move.l #AD_SETTLE,d0
.settle:subq.l #1,d0
bne.s .settle
movea.l AD_BUF.l,a1
move.l AD_LEN.l,d1
bsr ad_arm
move.l #1,AD_FLAG.l
; ---- wait for the channel to actually take byte 0. Not a delay loop:
; the condition is MTC having moved, which is the channel's own account.
move.l #AD_PATIENCE,d3
.first: move.w A3_MTC,d0
andi.l #$FFFF,d0
cmp.l AD_LEN.l,d0
bne.s .go
subq.l #1,d3
bne.s .first
bra bad
.go: move.l d0,AD_MTC0.l
bsr ad_play
move.l #2,AD_FLAG.l
moveq #0,d0
move.b AD_CTRLR,d0 ; bit 7 clear = the chip says it is playing
move.l d0,AD_STAT.l
clr.l AD_SPIN.l
move.l #AD_PATIENCE,d3
.wait: addq.l #1,AD_SPIN.l
move.b A3_CSR,d4
btst #4,d4 ; ERR -- CER says which
bne.s bad
btst #7,d4 ; COC
bne.s .fin
subq.l #1,d3
bne.s .wait
bra.s bad
.fin: bsr report
; The chip is left PLAYING deliberately: it goes on replaying the last
; byte it was given, which the verifier ignores. Stopping here would
; put a silence in the capture at a point the host would then have to
; find, and the capture already has a length it knows.
move.l #$FF,AD_FLAG.l
hold: bra.s hold
bad: bsr report
move.l #$EE,AD_FLAG.l
bra.s hold
report:
moveq #0,d0
move.b A3_CSR,d0
move.l d0,AD_CSRF.l
moveq #0,d0
move.b A3_CER,d0
move.l d0,AD_CERF.l
move.w A3_MTC,d0
andi.l #$FFFF,d0
move.l d0,AD_MTCF.l
move.l A3_MAR,d0
move.l d0,AD_MARF.l
rts
include "src/player/adpcm.i"