Skip to content
RetrogamingDeep Dive Published Updated 10 min readViews unavailable

Atari 2600 TIA Audio: Divider Chains, AUDC Waveforms, and Clock-Accurate Sound

Understand Atari 2600 TIA sound from AUDC, AUDF, and AUDV through divider and noise state, mixing, save states, and cycle-level emulator tests.

The Atari 2600’s TIA audio is small enough to look simple and stateful enough to punish shortcuts. It has two independent sound circuits, each controlled by an audio-control register (AUDC), a five-bit frequency divider (AUDF), and a four-bit volume register (AUDV). The control value selects feedback and division behavior; the frequency value changes how often that behavior advances; and the volume value changes the electrical drive level. These are not three independent “waveform, pitch, amplitude” knobs in the modern synthesizer sense.

For accurate emulation, preserve the time evolution of each channel. A program may rewrite the registers while a divider or polynomial sequence is partway through its state. The audible result depends on that history, on how two channels are combined, and on how the console clock is converted into host samples. Recomputing a tone from the latest register tuple for every output sample loses those transitions.

Three registers per channel, two independent circuits

The write-register pairs are AUDC0/AUDC1 at TIA offsets $15/$16, AUDF0/AUDF1 at $17/$18, and AUDV0/AUDV1 at $19/$1A. Each address is decoded by the TIA; the 2600’s partial address decoding means the register names appear through mirrored addresses in the wider CPU address space. An emulator should route a TIA write through its address decoder, not treat the six offsets as ordinary RAM.

The AUDC low four bits choose the noise/tone sequence and additional output division. AUDF is a five-bit divisor control. In the original programmer’s guide, the approximately 30 kHz pulse train from the horizontal-sync circuitry is divided by values 1 through 32: register value zero means divide by one, and value 31 means divide by 32. The resulting tick clocks the sound generator. AUDV is a four-bit control from 0 through 15. The hardware guide describes it in terms of staged output-driver strength, with zero producing no drive and larger values enabling more current.

Treat the two channels as independent state machines until the output stage. They can use different controls, divider phases, and volumes at the same time. The original console could combine their outputs externally for a speaker; an emulator may offer stereo separation as a host feature, but that routing choice must not leak back into emulated register behavior.

AUDC selects a family of sequences, not a sample table

The TIA programmer’s guide describes a nine-bit shift counter whose feedback taps and count lengths are selected by AUDC. The register values combine polynomial sequences and extra divisions. The table summarizes the guide’s register descriptions; “poly” means a repeating pseudo-noise sequence, not random data. The precise resulting pattern depends on the circuit state and clock history.

AUDC Documented sequence or division
$0 Set output to 1
$1 4-bit polynomial
$2 Divide by 15, then 4-bit polynomial
$3 5-bit polynomial feeding 4-bit polynomial
$4, $5 Divide by 2, pure tone
$6, $A Divide by 31, pure tone
$7 5-bit polynomial, then divide by 2
$8 9-bit polynomial (white-noise mode in the guide)
$9 5-bit polynomial
$B Set the last four bits to 1
$C, $D Divide by 6, pure tone
$E Divide by 93, pure tone
$F 5-bit polynomial, then divide by 6

The table is a functional guide, not a guarantee that every audible mode has an intuitive name. “Noise,” “buzz,” and “tone” are convenient descriptions of patterns, not a promise of a particular spectral shape on every console and output circuit. Codes that share a description should be tested with stateful traces; do not infer that writes to one AUDC value always reset the internal counters or immediately align its phase with another value.

An emulator implementation can factor the documented shift behavior into shorter pulse and noise counters, as current Stella source does. That can be more convenient than representing a literal monolithic nine-bit register, as long as the observable sequence and transitions match the hardware. The source is evidence for a working emulator model, not a replacement for hardware documentation when asserting transistor-level design details.

At the first stage, AUDF = n produces one sound-generator clock for every n + 1 reference pulses. If the reference is taken as approximately 30 kHz, the initial clock rate is approximately 30,000 / (n + 1) Hz. This is a useful sanity check, but it is not a universal pitch formula. AUDC can add another divide-by-two, divide-by-six, divide-by-31, divide-by-93, or polynomial sequence before the output bit changes. Even a pure-tone mode can have an additional toggle/division stage between the generator clock and a full audible cycle.

That is why “higher AUDF means lower pitch” is a useful broad observation, while a single formula applied to all 16 AUDC values is not. A robust implementation first advances the selected state machine at the divided clock, then derives the channel’s current output level from its state and AUDV. If an emulator instead maps (AUDC, AUDF) directly to a nominal sine or square-wave frequency, it will miss the noise modes, the mixed polynomial modes, and the phase continuity that makes register writes sound authentic.

When a program changes AUDF, retain a deliberate model of the divider counter. The reference guide says the CPU can load the frequency register at any time; it does not say the entire sound circuit is reconstructed from zero on every write. Test updates at multiple divider phases. A write near counter rollover can expose whether the implementation resets, reloads, or continues the counter incorrectly. Similar care applies when AUDC changes feedback mode while the sequence is in progress.

Volume is channel drive, while host audio needs its own model

AUDV is not a PCM sample or a stored per-channel amplitude envelope. The historical guide describes four graduated output transistors controlled by the four volume bits, producing 16 selectable pull-down drive levels. Software can change that register at any time, but the underlying sound sequence continues to determine whether the output is active. A zero-volume channel can therefore retain timing state even though it contributes no audible signal.

An emulator has to turn the two hardware channel outputs into digital audio samples. This introduces a second clock domain: TIA events happen at the emulated machine’s timing, while the host requests samples at a configured sample rate. A high-quality path integrates or averages channel output over the relevant interval and resamples that result. Simply reading the output state at one instant per host sample can alias short pulses or miss transitions, especially at low pitches, high pitch-divider settings, or low sample rates.

The electrical output and the host speaker path are separate concerns. Filter choices, channel balancing, clipping, and optional stereo routing belong to the emulator’s output model and settings. They should not silently change the emulated channel counters. When comparing against a console recording, account for the recording path and television or speaker response rather than treating every spectral difference as a register-decoding bug.

A minimal 6502 register update

The following DASM-style example starts a channel in the guide’s 9-bit polynomial mode, chooses an AUDF divider value, and sets a nonzero volume. It demonstrates register writes only; it does not schedule an envelope or prove that the resulting sound matches any particular recording.

AUDC0 = $15
AUDF0 = $17
AUDV0 = $19

    lda #$08        ; AUDC: 9-bit polynomial mode
    sta AUDC0
    lda #$0F        ; AUDF: divide reference by 16
    sta AUDF0
    lda #$0A        ; AUDV: nonzero 4-bit output drive
    sta AUDV0

In a 2600 emulator, these stores pass through the TIA’s address decode and update channel zero’s control state. A useful unit test should separately verify that writes to channel one affect only channel one, values are masked to their register widths, and writes through mirrored TIA addresses reach the same register. Do not make a host audio callback the owner of these registers: CPU writes occur on emulated time, and the audio backend may run asynchronously.

Timing, state restoration, and regression tests

To test the divider, hold AUDC and AUDV constant, sweep AUDF from 0 through 31, and record the generator-clock interval. Verify value zero divides by one and value 31 divides by 32. Then select pure-tone and divided-tone controls and count output transitions, not just full periods inferred from a waveform viewer. Repeat with a register write just before, on, and after a divider event to catch off-by-one and reload errors.

For polynomial modes, use deterministic initial state and record a short sequence of output bits for each AUDC. Compare the sequence and its period against a validated hardware test or a reference emulator known to pass the same ROM. Include mode transitions after several different numbers of clocks. A mode that sounds plausibly noisy can still have the wrong feedback tap or repeat period.

For the two-channel mixer, exercise channel 0 alone, channel 1 alone, both at maximum, and both at different levels. Check the expected mono sum or documented emulator stereo routing. If the implementation resamples by accumulating levels, test the same register trace with several host sample rates and verify that event timing does not change when the output format changes. Audio quality should vary smoothly with the host rate, not alter the console’s clocked sequence.

Save states need more than the visible AUDC, AUDF, and AUDV bytes. The divider phase, pulse/noise sequence positions, feedback or hold state, and any partial sample-integration accumulator can affect what comes next. A safe regression test records output for a trace, saves halfway through, restores, and confirms the subsequent emulated channel samples continue identically. Reset and restore paths should be tested separately: a reset may initialize hardware state, while a save-state restore must resume it.

Current Stella source illustrates this separation in practice: its audio subsystem advances channel state, averages recent levels into samples, then converts channel values through a host mixing table. It also serializes channel counters and flags. Those are implementation choices, but they highlight the architectural boundary an emulator needs: deterministic chip state first, sample conversion second. Keep the sample queue itself outside the console’s hardware model unless the save-state format explicitly needs queued host samples.

Finally, verify register timing against CPU/TIA event ordering. A write is an event at a particular point in the 6507/TIA timeline, not merely a new value for the next video frame. Test short routines that rewrite sound registers repeatedly within one frame, and capture both register traces and resulting channel transitions. A one-frame sound-effect test is useful for listening, but it cannot replace cycle-level checks of divider and sequence state.

The TIA’s sound engine is a pair of small, clocked sequence generators followed by an output stage. Accurate emulation comes from preserving that state, respecting the exact register widths and divider semantics, and separating chip timing from host audio delivery. Once those pieces are independently testable, pitch tables and sound effects become a reproducible result of hardware behavior rather than a collection of guessed presets.

Related:

Sources:

Comments