The Machine Speaks Back
Unit 10 taught the machine to hear you. This one teaches it to answer. The NES has a sound chip — the APU — built into the same 2A03 as the CPU. Set up a pulse channel, switch it on, and it holds the note on its own while your code does nothing.
Back in Unit 10 the machine learned to hear you, reading the joypad through a register. This unit is the answer: the machine speaking back. On the NES, the sound comes from the APU.
The APU (Audio Processing Unit) lives inside the same chip as the CPU — the 2A03 is a 6502 with a sound generator bolted on. It has five voices: two pulse channels, a triangle, a noise channel, and a sample player. You don't generate the waveform yourself; you set a channel's registers — volume, pitch, length — switch it on, and the APU produces the sound while the CPU is free.
This unit plays one steady tone on pulse channel 1. Its registers live from $4000, with the master switch for all channels at $4015.
What you'll hear by the end
A steady square-ish tone, held for as long as the machine runs. It's a pulse wave at 50% duty — the bright, hollow voice behind countless NES melodies. The waveform strip above is the real captured output.
Set the registers, switch it on
; ============================================================================
; Meet the Machine (NES) - Unit 17: The Machine Speaks Back
;
; Unit 10 taught the machine to hear you. This one teaches it to answer. The NES
; has a sound chip - the APU - built into the same 2A03 as the CPU. You set a
; pulse channel's volume, pitch and length, switch it on, and it holds the note
; on its own while the CPU does nothing.
; ============================================================================
.segment "HEADER"
.byte "NES", $1a
.byte 2
.byte 1
.byte $00, $00
.segment "CODE"
reset:
sei
cld
ldx #$40
stx $4017 ; silence the frame-counter IRQ
ldx #$ff
txs
inx
stx $2000 ; rendering + NMI off while we set up
stx $2001
stx $4010
warm1:
bit $2002
bpl warm1
warm2:
bit $2002
bpl warm2
; --- backdrop colour: blue, so the screen shows it's running ---
bit $2002
lda #$3f
sta $2006
lda #$00
sta $2006
lda #$11 ; blue
sta $2007
; --- clear the nametable to the blank tile (a flat screen) ---
bit $2002
lda #$20
sta $2006
lda #$00
sta $2006
ldx #0
ldy #4 ; 4 pages = 1024 bytes (960 tiles + attributes)
clear:
lda #$00
sta $2007
inx
bne clear
dey
bne clear
; ----------------------------------------------- YOUR CODE START
; --- play a tone on pulse channel 1 ---
lda #$01
sta $4015 ; switch pulse 1 on
lda #$bf
sta $4000 ; 50% duty, hold the note, constant volume 15
lda #$00
sta $4001 ; sweep off (leave the pitch alone)
lda #$fd
sta $4002 ; timer low byte -> the pitch
lda #$08
sta $4003 ; timer high + length -> the note starts
; ------------------------------------------------- YOUR CODE END
; --- turn the picture on so the blue backdrop shows ---
bit $2002
lda #$00
sta $2006
sta $2006
sta $2005
sta $2005
lda #$0a
sta $2001
forever:
jmp forever
nmi:
rti
irq:
rti
.segment "VECTORS"
.word nmi
.word reset
.word irq
.segment "CHARS"
.res 8192, $00
The sound is one block of stores, after the usual reset and a blue backdrop:
lda #$01
sta $4015 ; switch pulse 1 on
lda #$bf
sta $4000 ; 50% duty, hold the note, constant volume 15
lda #$00
sta $4001 ; sweep off (leave the pitch alone)
lda #$fd
sta $4002 ; timer low byte -> the pitch
lda #$08
sta $4003 ; timer high + length -> the note starts
$4015 is the master enable — bit 0 switches pulse 1 on. $4000 packs several controls into one byte: the top bits choose the duty (the shape of the square), and the rest set a constant volume of 15 and tell the channel to hold its note instead of counting itself down. $4001 is the sweep unit, which can slide the pitch — we switch it off so the note stays put.
$4002 and $4003 set the pitch as an 11-bit timer, low byte then high. Writing $4003 is the trigger: it loads the note's length and restarts the channel, so the tone begins the instant that store lands. Because the channel is set to hold, it rings on while the CPU sits in forever. You set the registers and walked away; the APU does the rest.
Assemble and run
ca65 apu.asm -o apu.o && ld65 -C nes.cfg apu.o -o apu.nes
A steady pulse tone; a blank blue screen.
Try this: change the pitch
$4002/$4003 are the timer — a smaller number is a higher note (the timer counts down, so fewer counts means a faster wave). Lower the timer low byte toward $80 and the note rises; raise it and the note falls. The number is the pitch.
Try this: a different duty
The top two bits of $4000 set the duty cycle. Try $3f (duty 0 — a thin, nasal 12.5%), $7f (25%), or back to $bf (50%). Same channel, same pitch; a different duty gives the note a different colour. That choice is most of what makes one NES instrument sound unlike another.
If it doesn't work
- Silence. Pulse 1 isn't enabled —
$4015needs bit 0 set ($01). And a sweep with the negate bit set can mute the channel even when it looks off; keep$4001at$00. - A click, then nothing. The channel counted its length down to zero. Bit 5 of
$4000tells it to hold — set it (the$bfhere does) so the note sustains. - A tone that drifts in pitch. The sweep unit is running.
$4001at$00leaves the pitch alone.
What you've learnt
The APU is the NES's sound chip, inside the 2A03 itself. You set a pulse channel's duty and volume ($4000), its pitch ($4002/$4003), switch it on at $4015, and the write to $4003 starts the note. Told to hold, the channel rings on its own while the CPU is free. You set the registers; the chip plays.
What's next
That's the last of what the machine can do — its screen, its colour, its shapes, its input, and now its sound. One unit remains, and it isn't about a new instruction. Next — The Machine Trusts You — what it means to program a machine that does exactly what you wrote, even when you're wrong.