Overview
A held tone is a note; a sound effect moves. The APU has hardware to move it for you: an envelope that fades a channel’s volume from full to nothing, a sweep unit that slides a pulse channel’s pitch up or down, and a noise channel of pseudo-random hiss for anything that isn’t a pitch — footsteps, explosions, drums. Set them up, write the trigger register once, and the effect plays and stops itself. No per-frame code, no interrupt: the classic NES “pew” and “boom” are each a few register writes and then silence.
Code
; =============================================================================
; SOUND EFFECTS - NES (APU)
; A pitch-swept pulse (laser) and a decaying noise burst (explosion)
; Each fires once and silences itself via its length counter
; =============================================================================
APUSTATUS = $4015
PULSE2_VOL = $4004 ; DDLC VVVV
PULSE2_SWEEP = $4005 ; EPPP NSSS - enable, period, negate, shift
PULSE2_LO = $4006
PULSE2_HI = $4007 ; timer high + length load (writing this fires it)
NOISE_VOL = $400C ; --LC VVVV
NOISE_PERIOD = $400E ; M--- PPPP - mode + period index (pitch/timbre)
NOISE_LEN = $400F ; LLLL L--- - length load (writing this fires it)
init_sfx:
lda #%00001111 ; all four tone channels enabled
sta APUSTATUS
rts
; --- A laser: pulse 2, pitch sweeping down, volume decaying ---
sfx_laser:
lda #%01000111 ; 25% duty, length runs, ENVELOPE, decay rate 7
sta PULSE2_VOL
lda #%10010100 ; sweep ON, period 1, add-to-timer, shift 4 -> pitch falls
sta PULSE2_SWEEP
lda #$40
sta PULSE2_LO ; starting timer -> starting pitch
lda #%00011000 ; length load + timer high 0 -> TRIGGER
sta PULSE2_HI
rts
; --- An explosion: noise channel, volume decaying to nothing ---
sfx_explode:
lda #%00000110 ; length runs, ENVELOPE, decay rate 6
sta NOISE_VOL
lda #%00000101 ; noise mode 0, period index 5 (a low rumble)
sta NOISE_PERIOD
lda #%00110000 ; length load -> TRIGGER
sta NOISE_LEN
rts
Trade-offs
| Aspect | Cost |
|---|---|
| CPU | None after the trigger — the hardware runs the effect |
| Memory | A short routine per effect |
| Limitation | An effect steals its channel from the music while it plays |
When to use: Shots, jumps, hits, explosions — anything short and reactive.
When to avoid: Sustained instrument tones for music; those want the length counter halted, not running.
The envelope: fade for free
For an effect you want the opposite of a sustained tone: leave bit 5 clear (length counter runs, so the sound ends) and bit 4 clear (envelope, not constant volume). Now the low nibble stops being a volume and becomes a decay rate — the channel starts at volume 15 and steps down to 0 on its own. A small rate decays fast (a tight tick), a large rate slow (a lingering booom). That single nibble is the difference between a laser and a cannon.
The sweep: pitch that slides
The sweep unit ($4001/$4005) bends a pulse channel’s pitch every few frames without any code:
EPPP NSSS
E enable
PPP how often it steps (0 = fastest)
N negate — 0 adds to the timer (pitch falls), 1 subtracts (pitch rises)
SSS shift — how big each step is
%10010100 sweeps the timer upward, so the note glides down — a descending “pew”. Flip the negate bit (%10011100) and it rises instead. The sweep can drive the timer out of range and mute the channel — which, for a one-shot effect that’s already ending, is exactly what you want.
Noise for the un-pitched
The noise channel has no timer table; its $400E period index (0–15) picks one of sixteen pseudo-random rates — low indices give a deep rumble, high ones a bright hiss. Bit 7 switches to a shorter, more metallic pattern. Paired with a fast envelope it’s a snare or a hi-hat; with a slow one, an explosion. Writing $400F loads its length counter and fires it, exactly like a pulse channel’s $4003.
Related
Patterns: A Square Wave
Vault: APU | Nintendo Entertainment System