Repository navigation
Lock WriteC64Spans slices to a sample store, and count the stores - #71
Conversation
… stores A sample player on the C64 drives the SID's volume DAC from a CIA2 NMI, and a DMA halt that spans two timer underflows collapses them into one NMI, so the player loses a sample. Slicing bounds each halt but cannot place it: a slice can still straddle an underflow, and the host cannot see where the NMI is. The board can. A new WriteC64Spans flag, LockToStore, names the address the player stores each sample to (the first span-list entry carries it), and each slice starts on the cycle after the next store, where the rest of the period is free. The gap field becomes the longest wait for a store, after which a slice starts anyway, so a write never waits on audio that has stopped. Firmware without the flag refuses it with "Flags must be 0"; a flagged zero-span header tells the two apart with nothing owed. A locked slice is run and released by the PHI2 ISR itself (DMA_AutoRun), since handing each step back to thread mode costs 70-80 cycles of halt per slice, nearly a whole NMI period at 12 kHz. SampleWatch (0x64F9) installs the store watch on the bus snoop hook and reports the stores counted, the time they spanned and the slices that did and did not find a store, so a host can count lost NMIs at the source.
ControlComms.md gains the LockToStore flag, its probe, and the SampleWatch command; Constraints.md lists SampleWatch among the users that borrow the bus snoop slot.
SensoriumEmbedded
left a comment
There was a problem hiding this comment.
Thank you for the locked slices and SampleWatch, no lost NMIs at 12 kHz with 1 KB writes going through is a great result. Looking forward to hearing the results. :)
There's one thing I'd like you to look at before merging.
1. The extension image carries the SampleWatch snoop but never calls it (DMAControl.c:267-396)
VMBoot.ino:28 includes DMAControl.c, so its contents are built into the extension image too. Nothing in the extension reaches WriteC64Spans or SampleWatch, since their only caller is SerUSBIO.ino in the main image. They're still in the extension's ELF, because FASTRUN and FLASHMEM functions go into the shared .fastrun and .flashmem sections, and the linker doesn't drop those one function at a time. SampleStoreSnoop costs the extension 128 B of ITCM, plus about 40 B of DTCM for SampleStats and the watch state. The extension's ITCM code has a hard 64 KB limit, and with this PR its headroom drops from 1,112 B to 920 B.
Suggested fix: the four suggestions inline wrap the store watch (DMAControl.c:267-359) and the locked branch of WriteC64Spans (DMAControl.c:377-396) in #ifndef MinimumBuild. Committing them as a batch makes one commit. That saves the extension 128 B of ITCM and about 40 B of DTCM, plus some flash. It's safe because the main image doesn't define MinimumBuild, and the extension, which does, has no caller. DMA_AutoRun lives in Common_Defs.h, so the interrupt-side reads are unaffected.
First time I've tried the suggested fixes, let me know if I did it wrong... :)
Thank you!
|
Thanks, good catch. Your four suggestions are in 7a01c30, unchanged. Measured with
The main TR+ and TR images are byte-for-byte the same size as before. |
SensoriumEmbedded
left a comment
There was a problem hiding this comment.
corrected suggestions
What this does
A C64 playing samples through
$D418from a CIA2 timer NMI loses a sample whenever one DMA halt spans two timer underflows: the NMI is edge-triggered, so the two collapse into one. WriteC64Spans already slices writes to shorten halts, but each slice still costs 70–80 cycles of thread-mode handshake, which is close to a whole NMI period at 12 kHz (85 cycles).This adds a lock-to-store flag (
0x01) to WriteC64Spans:{ $D418, 0 }), and the gap byte becomes the longest wait for a store.DMA_AutoRun), so the handshake no longer sits inside the halt.It also adds SampleWatch (
0x64F9). It counts the C64's writes to one address and reports intervals, total and longest interval, and locked/unlocked slice counts since the previous call. With the store's period, a host can count lost samples at the source.docs/ControlComms.mddocuments both, including the probe: a flagged zero-span header is refused withWant 1 to 64 spansby this firmware andFlags must be 0by older firmware, with nothing owed either way.Measured
On an NTSC C64 with a 12 kHz NMI and 33-byte locked slices:
Things a reviewer should weigh
isrPHI2.DMAWaitAssertReady()and the end ofDMATransferISR()now select their next state onDMA_AutoRun.docs/Architecture/Constraints.mdrecords this as a deliberate exception, the way the_PreStartone is.fBusSnoopthe wayBusAnalysis()does.Bus snoop in useotherwise, and clears the slot only if it still holds it.DMA_AutoRunis set only for the length of a locked job, by a scope guard. With flags = 0, WriteC64Spans takes the same path as before, and the existing refusal texts are kept.Possible follow-ups (not in this change)
BusAnalysis()checks the snoop slot before masking interrupts, the same race this change closes in SampleWatch.