The Machine Speaks Back
The 48K Spectrum has no sound chip. Sound is the CPU's own job: flip one bit of one port to push the speaker, wait, flip it back, wait — over and over. That square wave is the tone, and the CPU does nothing else while it plays. On this machine, you are the sound chip.
Back in Unit 8 the machine learned to hear you, reading the keyboard through a port. This unit is the answer — and here the Spectrum stands apart from every other machine in this course.
The C64 has the SID. The NES has its APU. The Amiga has Paula. The 48K Spectrum has nothing — no sound chip at all. So how does it make a sound? The CPU does it by hand. There is one bit, bit 4 of port $FE, wired straight to the speaker. Write a 1 and the speaker cone pushes out; write a 0 and it pulls back. Do that fast enough, evenly enough, and the in-and-out becomes a tone.
That's the whole trick: flip the bit, wait, flip it back, wait, forever. The waiting sets the pitch — a longer wait is a lower note. And because the CPU is busy counting out those waits, it can do nothing else while the sound plays. On this machine, you aren't programming a sound chip. You are the sound chip.
What you'll hear by the end
A plain, buzzy tone, held steady. It's a square wave — the speaker slammed between its two positions — and that thin, direct sound is the voice of a thousand Spectrum games. The waveform strip above is the real captured output: two flat halves, exactly as the bit goes high then low.
Flip the bit, wait, repeat
; ============================================================================
; Meet the Machine - Unit 15: The Machine Speaks Back
; ============================================================================
; The 48K Spectrum has NO sound chip. Sound is the CPU's own job: flip bit 4 of
; port $FE to push the speaker, wait, flip it back, wait, over and over. That
; square wave IS the tone. The pitch is the length of the wait; the CPU does
; nothing else while it plays - it is the sound chip.
; ============================================================================
org 32768
start:
di ; no interrupts - they would jitter the tone
; --- clear the screen to a flat colour, so the shot is clean ---
ld hl, $4000
ld (hl), 0
ld de, $4001
ld bc, $17ff
ldir ; blank every pixel
ld hl, $5800
ld (hl), $2d ; paper cyan + ink cyan -> a flat field
ld de, $5801
ld bc, $02ff
ldir
; ----------------------------------------------- YOUR CODE START
loop:
ld a, $15 ; speaker bit (4) HIGH, border cyan (bits 0-2)
out ($fe), a
call delay ; hold...
ld a, $05 ; speaker bit LOW, border still cyan
out ($fe), a
call delay ; ...hold
jr loop ; forever - the toggling is the tone
delay:
ld bc, 200 ; the wait length - bigger is a lower note
.wait: dec bc
ld a, b
or c
jr nz, .wait
ret
; ------------------------------------------------- YOUR CODE END
end start
After clearing the screen to a flat colour, the tone is one short loop:
loop:
ld a, $15 ; speaker bit (4) HIGH, border cyan (bits 0-2)
out ($fe), a
call delay ; hold...
ld a, $05 ; speaker bit LOW, border still cyan
out ($fe), a
call delay ; ...hold
jr loop ; forever - the toggling is the tone
out ($fe), a writes a byte to port $FE. Bit 4 is the speaker: $15 has it set (high), $05 has it clear (low) — and both keep bits 0–2 at 5, so the border stays cyan the whole time. Between the two writes is a delay: a loop that counts a register down to zero and does nothing else. That pause is half of one wave. Flip high, wait; flip low, wait; round again — a square wave, built one instruction at a time.
The delay routine is the pitch:
delay:
ld bc, 200 ; the wait length - bigger is a lower note
.wait: dec bc
ld a, b
or c
jr nz, .wait
ret
It loads a number and counts it down. A bigger number is a longer wait, a slower flip, a lower note. There is no frequency register to set — the time the CPU spends counting is the frequency. And note the di at the top of the program: with interrupts off, nothing else steals the CPU's attention, so the waits stay even and the tone stays pure.
Assemble and run
pasmonext --sna beeper.asm beeper.sna
A steady tone from the speaker; a flat cyan screen.
Try this: change the pitch
The wait length is ld bc, 200. Make it ld bc, 100 and the note jumps higher; ld bc, 400 and it drops lower. You're not setting a pitch — you're setting how long the CPU dawdles between flips, and that is the pitch.
Try this: a two-note tune
Wrap the loop in an outer counter so it flips a few hundred times, then change bc and flip a few hundred more. Two waits, two pitches — the start of a melody. This is exactly how Spectrum games played music: not by handing notes to a chip, but by the CPU counting out every single wave of every single note, itself.
If it doesn't work
- Silence. Bit 4 isn't toggling — both
outvalues must differ in bit 4 ($15high,$05low). If they're the same, the speaker never moves. - A faint or harsh click instead of a tone. The two delays aren't equal, or the loop isn't repeating — the wave needs an even high-then-low, over and over, to sound like a note.
- The tone wavers in pitch. Interrupts are firing and stealing time. The
diat the top keeps the CPU's count even; without it the 50-times-a-second interrupt jitters the note.
What you've learnt
The 48K Spectrum has no sound chip. Sound is made by the CPU itself: toggling bit 4 of port $FE to drive the speaker, with a counted delay between flips that sets the pitch. The CPU does nothing else while the tone plays — it is the oscillator. It's the opposite of a sound chip you set going and leave; here, the sound is the program.
What's next
That's the last of what the machine can do — its screen, its colour, its input, and now its sound, all of it driven straight by the CPU. 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.