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
113 lines
4.4 KiB
ArmAsm
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"
|