DACs and Sample Playback

đźš§

This chapter is a placeholder. The full text is being written.

Synopsis

Square waves are fine for chip music. But the Next can also play actual sampled sound — speech, drums, wave-tables, anything you can put in memory as 8-bit PCM. The trick is the four built-in 8-bit DACs (left A, left B, right A, right B), each controlled by one I/O port. Write a byte → that byte becomes a voltage. Do it 8000 times a second and you have a wavetable synthesiser.

This chapter is where two earlier chapters pay off in a single application: we use the CTC to keep accurate sample-rate timing, and the zxnDMA to do the actual byte transfer with zero CPU overhead. We use both, side by side, and compare them.

Topics:

  • The four DAC ports. $FB (left A), $B3 (left B), $0F (right A), $DF (right B), and the eight-bit unsigned PCM convention.
  • Routing. NextReg $08 bits — which DAC goes to which output, including “all four to both speakers” for max volume.
  • Sample formats. Raw 8-bit signed/unsigned, the 11025 Hz / 22050 Hz / 44100 Hz convention, and how to convert WAV in advance.
  • CPU-driven playback. A CTC channel ticks at the sample rate; its ISR does an OUT (DAC),A and advances a pointer. Costs ~150 T-states per sample at 11 kHz.
  • DMA-driven playback. A CTC channel is wired to the DMA via NextReg $CD. The DMA copies one byte from a sample buffer to the DAC port on every CTC tick. The CPU does literally nothing during playback. This is how the Next does professional-quality audio.
  • Looping, ping-pong, and end-of-buffer. DMA modes that automatically restart, plus status polling or a separate timing interrupt when you need to queue the next buffer.
  • Mixing AY and DAC. Both go to the same output stage; the chip music continues underneath the samples.

What you should know first

Planned exercises

  • CPU-paced sample. Play an 8 kHz speech sample using a CTC ISR.
  • DMA-paced sample. Replay the same sample using DMA. Compare CPU load (use the CTC profiling utility from chapter 5 to measure how much main-loop time you actually win back).
  • CTC-paced DMA streaming. Stream a small unsigned 8-bit sample buffer to a DAC port using burst-mode DMA paced by a CTC channel. The DMA transfers one byte per CTC trigger, releases the bus between samples, and lets the CPU continue running a small animation or counter in the foreground. The foreground activity proves the CPU is not trapped in the sample-output loop.
  • Drum + chip music. AY tracker tune playing in the background, DMA-driven drum hits triggered on key press.

Planned Demo: Audio Streaming with CTC Timing

This demo belongs here rather than in the zxnDMA chapter because the interesting question is not how to upload a DMA table; it is how to produce a stable audio sample rate. The CTC supplies the sample clock, and zxnDMA moves one byte from the sample buffer to a DAC port on each CTC trigger.

The DMA program is close to an ordinary memory-to-port burst transfer, but WR2 has no prescaler. NextReg $CD connects a CTC channel’s ZC/TO pulse to the DMA trigger path:

CTC_CH3     equ $1B3B        ; Use channel 3 for audio timing
NR_REG      equ $243B
NR_DAT      equ $253B
 
; --- Configure CTC Channel 3 as a timer at about 22 kHz.
;     28 MHz / 16 / 80 = 21,875 Hz
    ld bc,CTC_CH3
    ld a,%00000101          ; Timer, prescaler /16, TC follows
    out (c),a
    ld a,80
    out (c),a
 
; --- Arm the CTC -> DMA trigger: NR $CD bit 3 = CTC channel 3.
    ld a,$CD
    ld bc,NR_REG
    out (c),a
    ld a,%00001000
    ld bc,NR_DAT
    out (c),a
 
; --- DMA program: burst mode, no WR2 prescaler.
audioCTCDMAProgram:
    .dma reset
    .dma wr0 a_to_b, transfer
audioCTCSrc:  .dw 0                     ; patch: source buffer address
audioCTCLen:  .dw 0                     ; patch: sample count
    .dma wr1 memory, increment
    .dma wr2 io, fixed                  ; no prescaler: CTC handles timing
    .dma wr4 burst
    .dw $00DF                           ; Port B = DAC port $DF
    .dma wr5
    .dma load
    .dma enable
audioCTCDMAProgram_end:
 
; Kick off streaming: HL = buffer address, BC = sample count
    ld (audioCTCSrc),hl
    ld (audioCTCLen),bc
    ld hl,audioCTCDMAProgram
    ld b,audioCTCDMAProgram_end - audioCTCDMAProgram
    ld c,$6B
    otir

The CPU runs between CTC-triggered burst transfers. Each CTC tick delivers one sample to the DAC, and the foreground code can keep animating, polling input, or preparing the next buffer.