Pulse Width Modulation
Shaping square waves
Pulse width modulation varied the duty cycle of square waves, transforming simple bleeps into rich, evolving tones that defined the sound of chip music.
Overview
A square wave at 50% duty cycle sounds hollow and thin. Change that ratio—make the high portion wider or narrower—and the harmonic content shifts dramatically. The SID chip could sweep through duty cycles in real-time, creating dynamic, living sounds from simple waveforms.
Understanding duty cycle
| Duty cycle | Sound character |
|---|---|
| 12.5% | Thin, nasal, clarinet-like |
| 25% | Hollow, woody |
| 50% | Pure square, hollow |
| 75% | Same as 25% (inverted) |
The duty cycle determines which harmonics are present:
- 50%: Only odd harmonics (1, 3, 5, 7...)
- Other ratios: Mix of odd and even
SID implementation
Register layout
The SID stores pulse width as a 12-bit value across two registers per voice. The high register only uses bits 0-3:
| Register | Bits | Function |
|---|---|---|
$D402 (PWLO1) | 7:0 | Voice 1 pulse width, low 8 bits |
$D403 (PWHI1) | 3:0 | Voice 1 pulse width, high 4 bits |
$D409 (PWLO2) | 7:0 | Voice 2 pulse width, low 8 bits |
$D40A (PWHI2) | 3:0 | Voice 2 pulse width, high 4 bits |
$D410 (PWLO3) | 7:0 | Voice 3 pulse width, low 8 bits |
$D411 (PWHI3) | 3:0 | Voice 3 pulse width, high 4 bits |
The duty cycle is PW / 4096. Values of 0 or 4095 cause the pulse output to stick high or low — silence. Stay clear of the edges if you're modulating right up to the rails.
Setting pulse width
lda #$00
sta $d402 ; PW low byte
lda #$08 ; 50% duty cycle ($800 = 2048 = 4096/2)
sta $d403 ; PW high nibble
Sweeping (PWM)
pulse_sweep:
lda pulse_lo
clc
adc #$10 ; Increment
sta pulse_lo
sta $d402
lda pulse_hi
adc #$00
and #$0f ; Keep in range
sta pulse_hi
sta $d403
rts
Musical applications
Brass sounds
Fast PWM sweep creates brassy attack:
Attack: Sweep 12% → 50% quickly
Sustain: Hold at 50% or slow sweep
String pads
Slow, subtle PWM creates warmth:
Continuous slow sweep: 25% ↔ 50% over 2-4 seconds
Lead sounds
Moderate PWM adds movement and interest to melodic lines.
NES pulse channels
The NES has four fixed duty selections per pulse channel. Hardware-wise the four sequences are 12.5%, 25%, 50%, and "25% negated" — the 25-negated waveform is 1 0 0 1 1 1 1 1, which sounds the same as a 75%-duty pulse (six-eighths high) and is universally referred to as "75%" in tracker UIs.
| DD bits | Sequence (8 steps) | Audible duty |
|---|---|---|
| 00 | 0 1 0 0 0 0 0 0 | 12.5% |
| 01 | 0 1 1 0 0 0 0 0 | 25% |
| 10 | 0 1 1 1 1 0 0 0 | 50% |
| 11 | 1 0 0 1 1 1 1 1 | 25% negated (= 75% audible) |
There is no continuous sweep: the duty selector is two bits in $4000 (or $4004 for pulse 2), so you can only switch between the four presets. Famicom musicians fake PWM-like motion by alternating between 25% and 50% on adjacent rows.
Tracker notation
Pulse-width commands vary by tracker:
| Tracker | Command | Notes |
|---|---|---|
| GoatTracker (C64) | Wxx | Sets pulse width directly (high nibble of PWHI:PWLO) |
| GoatTracker (C64) | 1xx / 2xx | Pulse width slide up / down at speed xx |
| FamiTracker (NES) | Vxy | Sets duty cycle preset (0-3) |
Combining with other effects
PWM layers beautifully with:
| Combination | Result |
|---|---|
| PWM + Filter sweep | Rich, evolving pads |
| PWM + Vibrato | Organic lead sounds |
| PWM + Arpeggio | Shimmering chords |
The "SID sound"
PWM is central to the distinctive SID character:
- Rob Hubbard's brass stabs
- Martin Galway's warm pads
- Tim Follin's aggressive leads