What it is
The active Tang Primer 20K bitstream instantiates sid6581.vhd — a VHDL
recreation of the MOS 6581 (SID) from the Commodore 64. It exposes the standard
6581 register window at $D400–$D418, so SID players address the chip
exactly as they would on a real C64.
The chip is more or less SID-compatible: most of the 6581 behavior is modelled, with a few analog quirks deliberately approximated (see below). On the Tang build it replaces the older four-voice synthesizer — running both at once exhausted the device's global clock networks and destabilized DDR3 PHY calibration.
Modelled 6581 features
| Feature | Detail |
|---|---|
| Oscillators | 3 voices, 24-bit phase accumulators, ~985 kHz PAL φ2 rate |
| Waveforms | 12-bit triangle, sawtooth, pulse (12-bit width), noise (23-bit LFSR) |
| Combined | multiple waveform bits wire-ANDed (the classic 6581 approximation) |
| ADSR | cycle-accurate: reSID rate-counter periods, linear attack, exponential decay/release divider |
| Filter | 2-pole state-variable multimode (LP/BP/HP), per-voice routing ($D417), 11-bit cutoff |
| Cutoff curve | non-linear "dark 6581" approximation — mid values stay low, the bass sounds rounded |
| Resonance | deliberately weak Q (~0.7…2), like the 6581, avoiding limiter clipping |
| Hard sync | oscillator reset when the previous voice's MSB rises (CONTROL bit 1) |
| Ring modulation | triangle fold bit XORed with the previous voice's MSB (CONTROL bit 2) |
| Master volume | $D418 low nibble; voice-3 disconnect ($D418 bit 7) honoured |
The filter runs once per SID tick over a 3-step internal pipeline (one multiply per clock), so the two serial coefficient multiplies never share a single 54 MHz path; the filter states saturate to keep the SVF from blowing up.
sid6581.vhd for a brighter/darker voicing.
Playing original SID files
This is the centerpiece of the demo. tools/build_native_sid_rom.py turns a
PSID/RSID file into a runnable ROM — and crucially without a lossy 50 Hz register
conversion. Instead it:
- embeds the original 6502 payload of the SID file unchanged,
- copies it to its native load address,
- calls the tune's
initroutine once, - and runs the
playroutine every 20 ms (50 Hz).
As a result every write — frequency, pulse width, control, ADSR and filter — reaches the hardware exactly as the original player produces it. Real C64 player code runs on the real 6502 core, driving a real SID recreation.
What a tune needs
- a real
playaddress (not an IRQ/CIA-driven RSID), - it loads into linear RAM
$0200–$5FFF, - its payload fits the 12 KB ROM window at
$A000, - single-speed PAL (50 Hz
play).
Bundled examples
| ROM | Tune | Note |
|---|---|---|
| soundsid.rom | World_Record_2.sid | no filter/sync/ring |
| sound_commando.rom | Commando.sid | leans on the low-pass filter |
# wrap one tune and play it python tools/build_native_sid_rom.py path/to/tune.sid sw/<name>.s make -C sw upload-sound-commando # build + upload in one step # or wrap a whole collection at once python tools/build_all_sid_roms.py # builds playable ROMs + .bat files
The bulk builder also runs each tune through the bare-metal 6502 SID emulator and skips silent tunes — players that produce no sound without a CIA/VIC/KERNAL to rely on.
SID registers ($D400)
Standard 6581 layout: three voices of 7 registers each, plus global filter/volume registers:
| Address | Register |
|---|---|
| $D400–$D406 | Voice 1: freq, pulse width, control, ADSR |
| $D407–$D40D | Voice 2 (same layout) |
| $D40E–$D414 | Voice 3 (same layout) |
| $D415–$D416 | Filter cutoff (11-bit) |
| $D417 | Resonance & per-voice filter routing |
| $D418 | Filter mode & master volume |
Hardware output
The signed 16-bit sample is shifted out through the I²S serializer in
pt8211_dac.vhd to the dock's PT8211 (TM8211) DAC (~47 kHz).
The power-amp enable pin (PA_EN, R16) is held high, otherwise the headphone
amplifier stays silent.
Alternative synthesizers
The repository keeps two other register-compatible sound sources for other targets:
- Large 4-voice chip (
sound_chip4.vhd) — 4 voices, five waveforms (sine, square, sawtooth, triangle, noise), full ADSR, note duration and a mixer; the full C-emulator model. - Bring-up single voice (
sound_voice.vhd) — one voice, square + noise, no envelope; the minimal variant.
Verification
GHDL testbenches confirm the SID core produces audio (filter off, backward-compatible level), the filter stays bounded and a low cutoff attenuates, and that combined waveforms, ring modulation and hard sync are wired, bounded and non-silent.