Amiga Paula Audio DMA: Channel Reloads, Periods, and Sample Ownership
Trace Amiga Paula audio from chip-RAM DMA through channel reloads and period timing, with practical emulator state, trace, and regression-test boundaries.
The Amiga’s Paula audio block is often summarized as four-channel, 8-bit PCM. That description is useful but incomplete for an emulator engineer. A channel is a timed hardware consumer of chip-memory words, not a host audio stream that can be read whenever a mixer callback happens to run. The location, length, period, volume, DMA enable, and channel state together decide what sample is fetched, when it becomes audible, and when the next block begins.
Keeping those responsibilities separate is essential. Paula’s DMA engine fetches data; the audio channel’s timing presents samples; the host mixer resamples and combines the resulting signal. An implementation that jumps directly from a guest buffer to a host callback can sound plausible while missing register writes made during playback, DMA starvation, loop boundaries, and interactions with the shared chip-memory bus.
A channel is a small state machine
Each of the four channels has a programmed location and length, a period and volume control, and a data register used by programmed-I/O paths. The location and length registers describe a sequence of 16-bit words in chip memory. Audio DMA is enabled through the custom-chip DMA control mechanism; the relevant channel’s DMA request is then serviced as the chipset makes bus slots available. This is distinct from a CPU writing a word directly to the channel data register.
The length is expressed in words, not samples. Each fetched word supplies two adjacent 8-bit sample values, and the channel consumes them in order. A model should make that word-to-byte relationship explicit so that pointer advancement, remaining length, and an odd-sized conceptual byte buffer are never conflated. Software generally programs a sample buffer as an even number of bytes because the hardware fetch unit is a word.
When a DMA block reaches its end, the channel’s documented reload behavior uses the programmed location and length again. Treat this as a hardware state transition rather than as an audio-library loop. A software routine may update the registers before the next reload, and the exact moment those writes take effect matters. Therefore, a single immutable host-side loop object is not a faithful substitute for the custom-chip register file.
Period timing and sample presentation
The period register controls the time between sample advances. Lower period values mean more frequent sample consumption, subject to the chipset’s clocking and timing constraints. It is tempting to translate the register directly into a floating-point frequency once during channel setup. That loses the time domain: later register writes, PAL/NTSC timing, CPU/chip bus scheduling, and save-state restoration can all invalidate the cached rate.
Instead, derive a channel’s next sample event from the emulated clock and the active period. If an implementation stores a deadline, it should retain enough integer or fixed-point precision to avoid accumulating host floating-point drift over long sessions. A guest write that changes the period should affect future sample events at the hardware-defined boundary; it should not retroactively move samples that have already been fetched or emitted.
The audio sample value and its presentation time are separate facts. A sample can be waiting in a channel latch while the output mixer advances at a different rate. Keep a deterministic emulated-time queue or equivalent phase accumulator, then let the host audio callback consume a resampled view of that signal. This allows different host output rates without changing guest-visible DMA timing.
DMA, chip RAM, and ownership
Audio buffers used by DMA must be accessible to the chipset. On classic Amiga configurations, chip RAM is not simply interchangeable with every CPU-visible memory region: custom-chip DMA arbitration and memory visibility constrain what Paula can fetch. A sound that works after moving data into a convenient host buffer may still be architecturally wrong if the guest source resides in memory the audio DMA engine cannot access.
For an emulator, model the fetch as a bus-master read with a channel identity and timestamp. The memory subsystem should decide whether the address is reachable and how arbitration affects the operation. Do not silently make every memory region DMA-visible unless that behavior is explicitly part of the selected machine configuration. Log the source address, channel, word count, and emulated cycle when debugging a missing or repeated sample.
DMA and programmed-I/O playback are different paths. In DMA mode the chipset advances through the block according to its internal transfer state. In direct mode the processor writes data into the audio data register and must meet the channel’s consumption cadence. Treating both as a single circular host buffer masks timing errors and makes software that mixes the mechanisms behave unpredictably.
The bus also connects sound timing to display and blitter activity. Audio fetches are a regular consumer of chipset bandwidth, while other DMA clients have their own slot patterns and priorities. A cycle-exact emulator may need to schedule each request against the chipset’s arbitration rules; a less detailed emulator should still preserve ordering and observable channel completion instead of draining all requests in a zero-time batch.
Modulation and register writes
The audio channels include hardware interactions beyond four independent PCM voices. Paula supports channel-pair modulation modes that let one channel’s output influence another channel’s period or volume behavior. The control bits and sequencing are specific to the Amiga audio hardware; an emulator should implement them from the hardware reference rather than infer them from a modern synthesizer’s modulation model.
This is a strong reason to store raw channel registers separately from derived mixer values. A control write can change whether the next data is interpreted as ordinary sample data or contributes to another channel’s modulation path. The event log should record the write, its target channel, the previous and next control state, and the first affected sample event. This is more actionable than a waveform screenshot alone.
Volume is also a hardware field with a defined range and behavior, not an arbitrary host gain knob. Preserve its quantization before converting the signed sample to a host mixer value. Apply panning and system-wide output gain after emulated channel behavior, in the host output layer. That separation makes it possible to compare the guest signal with known hardware or a reference emulator.
A small scheduling model for tests
The following Python fragment is an intentionally small event-ordering invariant, not a register emulator. It makes explicit that period changes schedule the next sample from the time of the write, while already emitted events remain immutable:
from dataclasses import dataclass
@dataclass
class ChannelClock:
period: int
next_tick: int
last_tick: int
def set_period(self, period: int, now: int) -> None:
if period <= 0:
raise ValueError("period must be positive")
self.period = period
self.next_tick = max(now, self.last_tick) + period
def emit_due(self, now: int) -> int:
emitted = 0
while self.next_tick <= now:
self.last_tick = self.next_tick
self.next_tick += self.period
emitted += 1
return emitted
An actual machine model must substitute the documented Amiga clock conversion and account for register latching, DMA slot timing, and the selected chipset generation. The helper only protects a unit-test invariant: changing a period must not produce time-traveling samples or an infinite loop.
Save states and diagnosis
A sound save state must include more than the host output buffer. Serialize the channel’s programmed and current location, programmed and remaining word length, period, volume, data latch, control/mode bits, DMA enable and pending request, current byte phase within the fetched word, next emulated sample deadline, and any modulation dependency. Restore this state before resuming the global event scheduler. Otherwise, the first restored frame may sound correct and then diverge when a block reload or modulation event occurs.
For debugging, capture a short trace with guest register writes, DMA grants, source words, sample-consumption timestamps, and interrupt or completion state. Compare the trace before comparing waveforms. A shifted waveform can be caused by an incorrect initial period, a missed register write, a word/byte count error, or a DMA grant at the wrong bus slot. Those errors need different fixes.
Regression cases should include a two-word buffer with recognizable high and low bytes, a one-block completion followed by reload, an updated location immediately before reload, a period change during playback, direct data-register writes, invalid or inaccessible memory, and a save/restore just before the final sample. Add modulation cases only after ordinary DMA playback is stable. Record which chipset revision and clock mode each test targets, because one universal timing constant is not an adequate specification.
Operational acceptance criteria
A robust Paula implementation can explain every emitted sample by pointing to the register state, memory word, DMA event, channel phase, and emulated timestamp that produced it. It preserves chip-memory visibility, word accounting, reload behavior, period changes, and the distinction between DMA and direct writes. It also keeps host resampling outside guest hardware state.
The practical rule is to treat Paula as an event-driven chipset device. Four channels are a convenient mental model for the output, but the implementation contract includes bus ownership, paired-channel behavior, data-latch sequencing, and machine timing. Preserving that contract produces both more faithful audio and far better diagnostics when a game or demo depends on a borderline register transition.
Related:
- Amiga Blitter Minterms and Overlapping Copies: DMA Semantics That Matter
- Amiga Copper and Bitplane DMA: Beam-Synchronized Display Lists
Sources: