This program plays a multi-voice musical piece called “My Girl” using the SOUND command to drive up to four simultaneous tone channels (registers 0–5 for tone, 6–7 for noise/mixer, 8–10 for amplitude, and 11–13 for the envelope). The main loop reads groups of 17 data values per note-step: four values set mixer and envelope parameters, then three groups of three set tone frequencies and amplitudes across three PAUSE-separated time slots. A counter variable Z triggers RESTORE statements at counts 7 and 12, redirecting DATA reads to different sections of the song for structural variation. The sentinel value 1000 in the final DATA line at line 1000 signals the end of the piece via the conditional at line 20. Register 11/12/13 envelope values are reloaded each iteration, and register 7 is forced to 63 (all channels muted) at the loop termination point.
Program Analysis
Program Structure
The program is organized into three functional regions: an initialization block (lines 1–5), a main playback loop (lines 10–150), and a large DATA section (lines 160–1000). Line 1 sets up the envelope registers 11, 12, and 13 globally before the loop begins. The loop counter Z is incremented each iteration and tested at lines 11 and 12 to redirect the DATA pointer via RESTORE 200 and RESTORE 400, allowing the song to be divided into three structurally distinct sections without duplicating the playback engine code.
Data Layout per Note-Step
Each iteration of the main loop consumes exactly 17 DATA values across four READ statements:
- Line 15:
A,B,C,D— mixer register 7, noise period register 6, and two tone/amplitude seed values - Line 30:
E,F,G,H,I— amplitude and tone parameters for the first SOUND group - Line 40:
J,K,L,M,N— parameters for the second and third groups plus envelope reload values - Line 50:
O,P,Q— three PAUSE durations that interleave the three SOUND bursts
SOUND Register Usage
The playback loop addresses the AY-style sound chip registers in a specific pattern per step:
| Line | Registers Written | Purpose |
|---|---|---|
| 60 | 7, 6 | Mixer control, noise period |
| 70 | 0, 1, 8 | Channel A tone low, tone high, amplitude |
| 90 | 2, 3, 9 | Channel B tone low, tone high, amplitude |
| 110 | 4, 5, 10 | Channel C tone low, tone high, amplitude |
| 130 | 11, 12, 13 | Envelope period low, period high, shape |
| 135 / 155 | 7 = 63 | Silence all channels (mute) |
The PAUSE values O, P, Q at lines 80, 100, and 120 create timed gaps between the three channel-group writes, giving the impression of rhythmic articulation across the three voices.
Section Jumping via RESTORE
The counter Z (incremented at line 10) acts as a bar counter. At Z=7, RESTORE 200 redirects reads to the second musical phrase. At Z=12, RESTORE 400 redirects to a third section with notably different rhythmic DATA (line 410 contains shorter PAUSE values of 9 and smaller tone steps, suggesting a faster or contrasting passage). Z is never reset, so each section plays exactly once before the sentinel terminates playback.
Termination Sentinel
Line 1000 holds a single DATA record 1000,0,0,0. When A equals 1000 after the READ at line 15, the condition at line 20 branches to line 155 which silences the chip with SOUND 7,63 and halts by falling through to the DATA block (no further executable statements follow). This is a standard sentinel pattern for DATA-driven BASIC loops.
Notable Techniques
- Three PAUSE-separated SOUND writes per loop iteration simulate polyphonic articulation without needing an interrupt-driven approach.
- The envelope registers (11–13) are rewritten every iteration via
L,M,Nfrom the DATA, allowing per-phrase envelope shape changes. - Line 135 forces register 7 to 63 (all tone and noise channels disabled in the mixer) at the end of each note step, acting as a release/gap before the next iteration’s register 7 is set, producing cleaner note separation.
- Compact DATA packing at line 400 onward places a full 17-value record on a single line, contrasting with the earlier style of one record spread across four lines, saving program memory.
Potential Anomalies
The variable Z is never reset to zero after the section transitions, which is intentional — each threshold fires once. However, if the program were looped (e.g., by adding a branch back to line 5 after line 155), Z would never re-trigger the RESTORE calls, so the song would replay only the final DATA section. This is not a bug in the as-written single-play design, but worth noting for anyone modifying the program.
Content
Image Gallery
Source Code
1 SOUND 11,5;12,5;13,13
5 LET Z=0
10 LET Z=Z+1
11 IF Z=7 THEN RESTORE 200
12 IF Z=12 THEN RESTORE 400
15 READ A,B,C,D
20 IF A=1000 THEN GO TO 155
30 READ E,F,G,H,I
40 READ J,K,L,M,N
50 READ O,P,Q
60 SOUND 7,A;6,B
70 SOUND 0,C;1,D;8,E
80 PAUSE O
90 SOUND 2,F;3,G;9,H
100 PAUSE P
110 SOUND 4,I;5,J;10,K
120 PAUSE Q
130 SOUND 11,L;12,M;13,N
135 SOUND 7,63
150 GO TO 10
155 SOUND 7,63
160 DATA 56,0
170 DATA 138,1,30,75,1,30,7,1,30
180 DATA 5,5,13
190 DATA 1,1,144
200 DATA 56,0
210 DATA 248,0,30,197,0,30,75,1,30
220 DATA 5,5,13
230 DATA 1,1,72
240 DATA 56,0
250 DATA 39,1,30,241,1,30,138,1,30
260 DATA 5,5,13
270 DATA 1,1,72
280 DATA 56,0
290 DATA 186,1,30,95,1,30,7,1,30
300 DATA 5,5,13
310 DATA 1,1,36
320 DATA 56,0
330 DATA 95,1,30,39,1,30,221,0,30
340 DATA 5,5,13
350 DATA 1,1,36
360 DATA 56,0
370 DATA 75,1,30,7,1,30,197,0,30
380 DATA 5,5,13
390 DATA 1,1,144
400 DATA 56,0,75,1,30,7,1,30,197,0,30,5,5,13,1,1,36
410 DATA 56,0,42,6,10,7,1,15,7,1,15,2,2,9,36,18,18
420 DATA 56,0,42,6,10,39,1,30,7,1,30,2,2,9,1,18,18
430 DATA 60,0,196,7,15,75,1,15,0,0,0,2,2,9,1,1,144
1000 DATA 1000,0,0,0
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.