Digital Audio on the ZX Spectrum's 1-Bit Beeper
A developer implemented 16kHz, 4-bit digital audio playback on the ZX Spectrum's primitive 1-bit beeper using cycle-exact Z80 assembly and jump tables.
read it The detailed breakdown of Z80 assembly tricks, cycle counting, and jump-table macros makes for a rewarding read for low-level programmers.
summary
Michael Martin details how he overcame severe hardware and timing constraints to play digital audio through the ZX Spectrum's 1-bit speaker. Operating on a 3.5 MHz Z80 processor with no hardware timers, he used a computed jump table and cycle-exact NOP macros to vary pulse widths at a 16kHz sample rate. The approach required careful register management, shadow register swapping, and inline waveform amplification. The resulting routine successfully streams audio on both emulators and real period hardware.
key points
- The ZX Spectrum lacks hardware timers or dedicated audio chips, requiring the CPU to strictly baby-sit the 1-bit beeper using cycle-counted assembly code.
- A computed jump table coupled with finely-tuned NOP macros allowed for precise pulse-width modulation (PWM) to achieve 16kHz audio.
- Memory constraints and register pressure forced the use of Z80 shadow registers and built-in table amplification to handle 4-bit PCM data.
who should read it
Low-level systems programmers, retro-computing enthusiasts, and assembly language hobbyists.
discussion positive
consensus
Commenters note that while achieving digital audio on 8-bit hardware is impressive, it monopolizes the entire CPU, illustrating why dedicated sound and video chips revolutionized 16-bit systems.
The computed jump into a NOP sled is a nice trick for getting a fine-grained delay without spending registers on a counter. I'm curious how much contended memory got in the way: on the 48K, code or sample data sitting in the lower 16K gets stalled by the ULA during screen fetch, which seems like it would wreck cycle-exact pulse widths unless everything lives above 0x8000. Did you end up having to place things carefully for that, and did the 128K/+2/+3 contention differences show up in the hardware tests? It's also interesting that the first attempts mostly produced the 8kHz carrier, since …
It raises a sharp technical question about ULA memory contention on the 48K Spectrum and how it impacts cycle-exact pulse widths.
Generated by an LLM from the article and thread. Check the sources before relying on it.