This program plays a two-voice rendition of the Toccata by Leonardo Leo with a real-time echo effect using the SOUND command. Voice 1 carries the main melody loaded from a binary block at address 60003, while Voice 2 plays the same music but offset by a configurable delay stored at address 60000 (initially set to a PAUSE/254 rest). Each note is encoded as a three-byte triplet: pitch, second parameter, and duration, in a format described as MUSIcomp-compatible. The volume of the echo voice (channel 9) is intentionally set lower (12 vs. 15) than the main voice (channel 8) to simulate natural echo decay. The program saves both the BASIC loader and a 895-byte machine code music data block to tape, and reloads them together with a single RUN.
Program Structure
The program is organized into a short initialization phase, a main playback loop, two subroutines for advancing note pointers, and a tape save/load section at the end.
- Lines 6–10: Initialize voice pointers
v1(60003) andv2(60000), and poke a REST byte (254) and duration (1) atv2so the echo starts one note behind. - Lines 15: Prime both voices by calling subroutines 100 and 200 to load the first note of each voice into working variables.
- Lines 35–70: Main loop — checks for end-of-music, sets volumes, plays via
SOUND, pauses for tempo, decrements duration counters, and fetches the next note when a voice’s counter expires. - Lines 100–120 / 200–220: Subroutines that
PEEKthe three-byte note record (pitch, second parameter, duration) and advance the respective pointer by 3. - Lines 9000 / 9999: Tape utilities —
LOAD ""CODE 60003loads the music data thenRUNs the program; line 9999 saves both the BASIC and 895-byte code block.
Note Data Format
The music is stored as a flat array of 3-byte records starting at address 60003, described as MUSIcomp-compatible. Each record contains:
| Offset | Variable | Meaning |
|---|---|---|
| +0 | a1 / b1 | Pitch value passed to SOUND channel register 0 or 1 (or 254=rest, 255=end) |
| +1 | a2 / b2 | Second SOUND parameter (e.g., octave/fine tune) passed to register 1 or 3 |
| +2 | a3 / b3 | Duration in loop ticks (decremented each iteration; fetch next note when zero) |
The 895-byte code block spans addresses 60003 to 60897, accommodating up to 298 three-byte note records for the main melody, preceded by a short echo-delay header at 60000–60002.
Echo Mechanism
The echo is achieved entirely in BASIC without a second melody data set. Voice 2’s pointer v2 is initialized to 60000 — three bytes before the main melody at 60003. The bytes at 60000–60002 are pre-set by lines 10 to 254, ?, 1 (a one-tick rest), so Voice 2 starts one note-event behind Voice 1. As both pointers advance through the same music data at their own rates, Voice 2 always lags Voice 1, producing the echo. The REM at line 5 notes that the delay (pause) duration is controlled by POKE V2+2,1, making the lag adjustable.
SOUND Command Usage
Line 50 issues a single compound SOUND statement addressing seven AY registers in one call:
8,a4— Channel A amplitude (main melody volume, 0–15)9,b4— Channel B amplitude (echo volume, fixed lower at 12 vs. 15)7,60— Mixer register, value 60 (binary 00111100) enables tone on channels A and B, disables noise0,a1; 1,a2— Channel A coarse and fine pitch2,b1; 3,b2— Channel B coarse and fine pitch
Lines 110 and 210 perform a brief volume-duck before each new note (SOUND 8,14 / SOUND 9,11) to introduce a slight gap between consecutive notes of the same pitch, preventing them from blurring together. The REM explicitly notes this is kept non-zero for “smoother delivery.”
Key BASIC Idioms and Techniques
- Compound
LETstatements on a single line (e.g., line 105) minimise line overhead and slightly speed up execution. - Duration counters
a3/b3are decremented in the main loop and subroutines are called only on expiry, avoiding unnecessaryPEEKoperations every tick. - The termination check at line 35 requires both voices to signal end-of-music (255) before stopping, preventing premature exit if the two voices have different lengths.
PAUSE 4at line 55 provides a fixed tempo tick; adjusting this value would change playback speed globally.
Content
Source Code
1 REM REAL ECHO WRITTEN BY E & K BOISVERT ©1986 BYTE POWER TOCCATA BY Leonardo Leo
5 REM V1=ADDRESS OF MAIN MUSIC V2=ADDRESS OF ECHO AT FIRST V2 STARTS AT A PAUSE (DELAY OF ECHO)
6 LET v1=60003: LET v2=60000
9 REM POKE V2,254 FOR THE DELAY (PAUSE) POKE V2+2,1 DURATION OF DELAY
10 POKE V2,254: POKE V2+2,1
14 REM FIND FIRST NOTES OF VOICE 1 & 2
15 GO SUB 100: GO SUB 200
30 REM 255=END OF MUSIC 254=REST (PAUSE) (MUSIcomp COMPATIBLE DATA)
35 IF a1=255 AND B1=255 THEN STOP
39 REM A4 & B4 ARE THE VOLUME OF VOICE 1 & 2
40 LET a4=15: IF a1>=254 THEN LET a4=0
43 REM VOLUME OF VOICE 2 (ECHO) HAS TO BE LOWER TO CREATE EFFECT
45 LET b4=12: IF b1>=254 THEN LET b4=0
49 REM PLAY NOTES
50 SOUND 8,a4;9,b4;7,60;0,a1;1,a2;2,b1;3,b2
54 REM TEMPO (PAUSE 4)
55 PAUSE 4
59 REM A3 & B3 DURATION OF NOTE IF EQUAL 0 THEN FIND NEXT NOTE
60 LET a3=a3-1: IF a3=0 THEN GO SUB 100
65 LET b3=b3-1: IF b3=0 THEN GO SUB 200
69 REM COMPLETE LOOP
70 GO TO 35
100 REM FIND NOTE OF VOICE 1 AND ADD 3 TO V1 (NEXT NOTE OF VOICE 1)
105 LET a1=PEEK v1: LET a2=PEEK (v1+1): LET a3=PEEK (v1+2): LET v1=v1+3
109 REM LOWER VOLUME (IF NOT PAUSE OR END) TO SEPARATE NOTES (NOT 0 FOR SMOOTHER DELIVERY)
110 IF A1<254 THEN SOUND 8,14
120 RETURN
200 REM FIND NOTE OF VOICE 2 AND ADD 3 TO V2 (NEXT NOTE OF VOICE 2)
205 LET b1=PEEK v2: LET b2=PEEK (v2+1): LET b3=PEEK (v2+2): LET v2=v2+3
209 REM LOWER VOLUME (IF NOT PAUSE OR END) TO SEPARATE NOTES (NOT 0 FOR SMOOTHER DELIVERY)
210 IF B1<254 THEN SOUND 9,11
220 RETURN
8999 REM LOAD CODES FOR MUSIC
9000 LOAD ""CODE 60003: RUN
9999 SAVE "REAL ECHO" LINE 9000: SAVE "MUSIC"CODE 60003,895: VERIFY "REAL ECHO": VERIFY "MUSIC"CODE
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
