Limitations of the Beeper (Square Wave)
The simple beeper (Part 28) can only produce a square wave (a harsh, buzzy tone) because it only switches the speaker ON or OFF. To create richer sounds, like music that sounds closer to a sine wave, or complex speech effects, we need to simulate varying amplitude (volume).
Pulse-Width Modulation (PWM) Synthesis
Since the Z80 cannot vary the voltage to the speaker, we simulate varying amplitude using Pulse-Width Modulation (PWM).
The Principle: We keep the speaker ON for varying amounts of time within a fixed sample period.
- #1. Loud Sound: Keep the speaker ON for almost the entire period.
- #2. Quiet Sound: Keep the speaker ON for only a tiny fraction of the period.
- #3. Silence: Keep the speaker OFF for the entire period.
By rapidly switching the pulse width according to a wave table, the speaker averages the sound output, creating the illusion of smooth volume changes.
The PWM Output Routine
This routine replaces the simple ON/OFF delay loop. It relies on a sample value (0-255) that controls the pulse width.
The Steps:
SAMPLE_VALUE EQU 80H ; Example: 8-bit sample value (0-255)
SPEAKER_PORT EQU 0FEH ; Example I/O port address
PWM_LOOP:
LD A, SAMPLE_VALUE ; A ← The sample amplitude (e.g., 100)
; 1. Turn Speaker ON
OUT (SPEAKER_PORT), A ; (Sets the bit ON)
; Delay based on the sample value (Output ON duration)
CALL DELAY_ROUTINE_A ; Delay for (Sample Value) cycles
; 2. Turn Speaker OFF
LD A, 0 ; A ← 0
OUT (SPEAKER_PORT), A ; (Sets the bit OFF)
; Delay based on the inverse of the sample value
; (Total Period - Sample Value)
CALL DELAY_ROUTINE_B ; Delay for (255 - Sample Value) cycles
; Repeat this loop 5000+ times per second (fast)
JP PWM_LOOP
Complex Synthesis (Wave Tables)
To produce actual music or speech, you read samples from a Wave Table (a large block of memory holding pre-calculated amplitude values) and feed them into the PWM routine above.
Wave Table Logic:
- Use a pointer (HL) to track the current position in the Wave Table.
- In the V-Blank interrupt (Part 25), read the byte at
(HL)
intoSAMPLE_VALUE
. - Increment the pointer
HL
. - The main loop then plays the PWM sound according to the new sample value.
By changing the step size of the pointer, you can change the pitch of the synthesized sound.