-- Play one buffer of ADPCM nibbles on the emulated MSM6258V, from 68000 code -- (ROADMAP P6a). The Lua here does what Lua is allowed to do in this tree: -- push bytes in, start the CPU, read the mailbox out. It is NOT in the feed -- path -- session 33's probe was, and a host that writes the data register at -- host-frame rate is not feeding a chip that consumes at 15,625 Hz (65.5). -- -- WHAT THE MEASUREMENT IS. MAME's -wavwrite capture, at a sample rate chosen -- to EQUAL the chip's stream rate so nothing resamples it, is the chip's own -- output. tools/bench/verify_adpcm_chip.py reads the four model axes out of it. M = manager.machine SP = M.devices[":maincpu"].spaces["program"] local META = loadfile("adpcm_meta.lua")() local AD_FLAG, AD_BUF, AD_LEN = 0x18600, 0x18604, 0x18608 local AD_MTC0, AD_CSRF, AD_CERF = 0x1860C, 0x18610, 0x18614 local AD_MTCF, AD_MARF, AD_SPIN, AD_STAT = 0x18618, 0x1861C, 0x18620, 0x18624 local code do local f=io.open("adpcmgate.bin","rb"); code=f:read("a"); f:close() end local data do local f=io.open("adpcm_data.bin","rb"); data=f:read("a"); f:close() end local function P(s) print("[ADP] "..s) end local function T() local t=M.time; return t.seconds + t.attoseconds/1e18 end local st, t0, tplay = "boot", nil, nil SUB = emu.add_machine_frame_notifier(function() local ok, err = pcall(function() local t = T() if st == "boot" then if t < 3.0 then return end for i = 1, #data do SP:write_u8(META.buf + i - 1, string.byte(data, i)) end for i = 1, #code do SP:write_u8(0x10000 + i - 1, string.byte(code, i)) end SP:write_u32(AD_FLAG, 0) SP:write_u32(AD_BUF, META.buf) SP:write_u32(AD_LEN, META.nbytes) local cpu = M.devices[":maincpu"] cpu.state["SR"].value = 0x2700 -- supervisor, ALL interrupts masked cpu.state["SP"].value = 0x8000 cpu.state["PC"].value = 0x10000 P(string.format("pushed %d B of code and %d B of nibbles at 0x%X", #code, META.nbytes, META.buf)) -- THE CAPTURE'S OWN CLOCK. The wav starts at t=0 of the run, so the host -- has to know when PLAY happened to find the stream in it -- but it is -- NOT used as the alignment: the verifier searches a small window around -- it, because a host frame is 17.6 ms and a sample is 64 us. st, t0 = "running", t return end if st == "running" then local fl = SP:read_u32(AD_FLAG) if fl == 2 and not tplay then tplay = t P(string.format("PLAY at t=%.4f s, chip status $%02X (bit7 clear = playing), " .."MTC then = %d of %d", t, SP:read_u32(AD_STAT), SP:read_u32(AD_MTC0), META.nbytes)) end if fl == 0xFF or fl == 0xEE then P(string.format("channel finished: CSR=$%02X CER=$%02X MTC=%d MAR=$%06X " .."spin=%d", SP:read_u32(AD_CSRF), SP:read_u32(AD_CERF), SP:read_u32(AD_MTCF), SP:read_u32(AD_MARF), SP:read_u32(AD_SPIN))) local dt = t - (tplay or t) P(string.format("%d bytes took %.4f s = %.1f B/s " .."(15,625 nibbles/s wants 7,812.5)", META.nbytes, dt, META.nbytes/dt)) if fl == 0xEE then P("ERROR: the gate flagged a channel error or a timeout") end local f = io.open("adpcm_run.lua", "w") f:write(string.format("return { tplay = %.9f, ok = %s, nbytes = %d,\n" .." csr = %d, cer = %d, mtc = %d, spin = %d }\n", tplay or -1, tostring(fl == 0xFF), META.nbytes, SP:read_u32(AD_CSRF), SP:read_u32(AD_CERF), SP:read_u32(AD_MTCF), SP:read_u32(AD_SPIN))) f:close() st = "drain"; t0 = t return end if t - t0 > 60 then P("TIMEOUT flag="..string.format("%08X", fl)); M:exit() end return end if st == "drain" then -- let the capture run past the end of the stream, so a truncated wav is -- never mistaken for a short stream if t - t0 < 0.3 then return end P("done") M:exit() end end) if not ok then print("[ADP] LUA ERROR: "..tostring(err)); M:exit() end end)