This program is a music composition utility that displays a staff-like grid on screen, allowing the user to place notes using cursor keys and save the resulting tune. It precomputes note frequencies for eight octaves across seven notes (A through G, including semitones) using the ZX81’s speaker timer formula F = INT(1/(f × 17×10⁻⁶) + 0.5), storing two-byte timer values in the arrays O$ and R$ for natural and sharp/flat notes respectively. A short machine-code routine embedded in REM line 1 drives the speaker directly, called via USR 16518, with timer and duration values POKEd into addresses 16515–16517. The composer supports variable octave selection, note duration, repeat playback, speed adjustment, and scrolling the staff when the cursor reaches the right edge.
Program Analysis
Program Structure
The program is divided into several functional regions:
- Lines 1–131: Machine code (REM line 1), initialization entry points, note interpolation subroutines, and frequency precomputation.
- Lines 133–181: Playback engine — iterates over the note/octave/duration string arrays and calls the machine code driver.
- Lines 183–241: Main loop — draws the staff, handles keyboard input, dispatches to editing subroutines.
- Lines 243–287: Cursor movement (left/right, up/down across staff lines).
- Lines 289–379: Note entry, array resizing, display update.
- Lines 381–427: Initialization subroutine — prompts for duration and octave, zeros variables.
- Lines 429–537: Auxiliary subroutines: repeat, duration change, octave shift, speed change.
- Lines 663–691: Semitone entry branch (shares tail of note-entry subroutine).
- Lines 693–721: Title/splash screen and SAVE routines.
Machine Code Routine (REM Line 1)
The entire sound driver is stored as raw bytes in the REM statement at line 1. The bytes are:
This program is a music composition utility that displays a staff-like grid on screen, allowing the user to place notes using cursor keys and save the resulting tune. It precomputes note frequencies for eight octaves across seven notes (A through G, including semitones) using the ZX81’s speaker timer formula F = INT(1/(f × 17×10⁻⁶) + 0.5), storing two-byte timer values in the arrays O$ and R$ for natural and sharp/flat notes respectively. A short machine-code routine embedded in REM line 1 drives the speaker directly, called via USR 16518, with timer and duration values POKEd into addresses 16515–16517. The composer supports variable octave selection, note duration, repeat playback, speed adjustment, and scrolling the staff when the cursor reaches the right edge. The program is divided into several functional regions: The entire sound driver is stored as raw bytes in the REM statement at line 1. The bytes are: This routine is entered via The routine uses the Z80 Lines 73–119 precompute speaker timer values for seven notes across eight octaves (J=8 down to 1, higher J = lower octave). The formula at line 125 converts a frequency in Hz to a loop-count timer: The seven base frequencies used (relative multiples) are 26⅔, 30, 32, 36, 40, 42⅔, and 48 — corresponding approximately to D, E, F, G, A, B♭, and C in a just-intonation-adjacent scheme, scaled by powers of two for each octave. Natural notes are stored in Three parallel string arrays hold the composition: The arrays are dynamically resized using a The five staff lines are represented by rows of Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters. No people associated with this content. This program is a music composition utility that displays a staff-like grid on screen, allowing the user to place notes using cursor keys and save the resulting tune. It precomputes note frequencies for eight octaves across seven notes (A through G, including semitones) using the ZX81’s speaker timer formula F = INT(1/(f × 17×10⁻⁶) + 0.5), storing two-byte timer values in the arrays O$ and R$ for natural and sharp/flat notes respectively. A short machine-code routine embedded in REM line 1 drives the speaker directly, called via USR 16518, with timer and duration values POKEd into addresses 16515–16517. The composer supports variable octave selection, note duration, repeat playback, speed adjustment, and scrolling the staff when the cursor reaches the right edge. The program is divided into several functional regions: The entire sound driver is stored as raw bytes in the REM statement at line 1. The bytes are: This routine is entered via The routine uses the Z80 Lines 73–119 precompute speaker timer values for seven notes across eight octaves (J=8 down to 1, higher J = lower octave). The formula at line 125 converts a frequency in Hz to a loop-count timer: The seven base frequencies used (relative multiples) are 26⅔, 30, 32, 36, 40, 42⅔, and 48 — corresponding approximately to D, E, F, G, A, B♭, and C in a just-intonation-adjacent scheme, scaled by powers of two for each octave. Natural notes are stored in Three parallel string arrays hold the composition: The arrays are dynamically resized using a The five staff lines are represented by rows of Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters. No people associated with this content. This program is a music composition utility that displays a staff-like grid on screen, allowing the user to place notes using cursor keys and save the resulting tune. It precomputes note frequencies for eight octaves across seven notes (A through G, including semitones) using the ZX81’s speaker timer formula F = INT(1/(f × 17×10⁻⁶) + 0.5), storing two-byte timer values in the arrays O$ and R$ for natural and sharp/flat notes respectively. A short machine-code routine embedded in REM line 1 drives the speaker directly, called via USR 16518, with timer and duration values POKEd into addresses 16515–16517. The composer supports variable octave selection, note duration, repeat playback, speed adjustment, and scrolling the staff when the cursor reaches the right edge. The program is divided into several functional regions: The entire sound driver is stored as raw bytes in the REM statement at line 1. The bytes are: This routine is entered via The routine uses the Z80 Lines 73–119 precompute speaker timer values for seven notes across eight octaves (J=8 down to 1, higher J = lower octave). The formula at line 125 converts a frequency in Hz to a loop-count timer: The seven base frequencies used (relative multiples) are 26⅔, 30, 32, 36, 40, 42⅔, and 48 — corresponding approximately to D, E, F, G, A, B♭, and C in a just-intonation-adjacent scheme, scaled by powers of two for each octave. Natural notes are stored in Three parallel string arrays hold the composition: The arrays are dynamically resized using a The five staff lines are represented by rows of Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters. No people associated with this content. This program is a music composition utility that displays a staff-like grid on screen, allowing the user to place notes using cursor keys and save the resulting tune. It precomputes note frequencies for eight octaves across seven notes (A through G, including semitones) using the ZX81’s speaker timer formula F = INT(1/(f × 17×10⁻⁶) + 0.5), storing two-byte timer values in the arrays O$ and R$ for natural and sharp/flat notes respectively. A short machine-code routine embedded in REM line 1 drives the speaker directly, called via USR 16518, with timer and duration values POKEd into addresses 16515–16517. The composer supports variable octave selection, note duration, repeat playback, speed adjustment, and scrolling the staff when the cursor reaches the right edge. The program is divided into several functional regions: The entire sound driver is stored as raw bytes in the REM statement at line 1. The bytes are: This routine is entered via The routine uses the Z80 Lines 73–119 precompute speaker timer values for seven notes across eight octaves (J=8 down to 1, higher J = lower octave). The formula at line 125 converts a frequency in Hz to a loop-count timer: The seven base frequencies used (relative multiples) are 26⅔, 30, 32, 36, 40, 42⅔, and 48 — corresponding approximately to D, E, F, G, A, B♭, and C in a just-intonation-adjacent scheme, scaled by powers of two for each octave. Natural notes are stored in Three parallel string arrays hold the composition: The arrays are dynamically resized using a The five staff lines are represented by rows of Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters. No people associated with this content.\FF\CC itemtype='https://schema.org/Blog' itemscope='itemscope' class="wp-singular computer_media-template-default single single-computer_media postid-56670 wp-custom-logo wp-embed-responsive wp-theme-astra wp-child-theme-astra-child ast-desktop ast-separate-container ast-left-sidebar astra-4.12.7 group-blog ast-blog-single-style-1 ast-custom-post-type ast-single-post ast-inherit-site-logo-transparent ast-hfb-header ast-full-width-primary-header ast-box-layout ast-normal-title-enabled astra-addon-4.12.5"
Skip to content
Composer
Program Analysis
Program Structure
Machine Code Routine (REM Line 1)
\FF\10\CC\01\2A\82\40\ED\5B\84\40\01\00\00\E5\D3\FF\CD\A2\40\DB\FE\CD\A2\40\E1\B7\ED\52\30\EF\C9\2A\84\40\37\ED\42\30\FB\C9USR 16518 (line 29 / 155 / 169), which points two bytes past the start of the BASIC line header into the REM body. The three parameters are POKEd into fixed addresses before the call:Address Variable Purpose 16515 D (duration × 16) Note duration 16516 Low byte of timer Speaker half-period low 16517 High byte of timer Speaker half-period high OUT (0FFH),A instruction (D3 FF) to toggle the ZX81 speaker port and a delay loop keyed to the 16-bit timer value to set pitch.Frequency Precomputation
F = INT(1 / (F_hz × 17E-6) + 0.5)O$(J,N,1..2); interpolated semitone (midpoint) values are stored in R$(J,N,1..2) via the subroutine at lines 7–27, which averages adjacent timer values.Note and Duration Encoding
N$ — note names (A–G); semitones stored as inverse characters (e.g., %E), which have character codes above 128, detected at line 141 (IF N<=128).X$ — octave per note, encoded as a digit character; decoded with CODE X$(X)-28.D$ — duration per note, encoded similarly with CODE D$(X)-28, then multiplied by 16 at line 140.DIM / copy idiom (lines 307–325): the old contents are saved into a temporary string L$, the array is re-dimensioned one element larger, and the contents are restored with a slice assignment.Staff Display
"-" characters at odd screen rows 1, 3, 5, 7, 9; spaces at even rows and row 11 represent spaces between lines and ledger areas. The array V$(row, col) tracks the current character at each cell so notes can be restored when the cursor moves away. The cursor is displayed as a block graphic character (\##, a solid 2×2 block). When the cursor reaches column 30, the screen scrolls 19 lines and a new staff section is drawn.Key Bindings
Key Function 5 Move cursor left 8 Move cursor right / advance 6 Move cursor down (lower pitch) 7 Move cursor up (higher pitch) S Place natural note A Place semitone (sharp/flat) note R Play back the tune (FAST mode) Q Set repeat count P Shift all octaves up or down F Change overall playback speed D Change note duration O Change current octave M Save composition to tape Notable Techniques and Idioms
GOSUB 069Z at line 3 and GOTO 0037 at line 5 use leading-zero and trailing-letter forms of line numbers — the 069Z target does not exist as a reachable line in the normal flow and acts as a skip; the program entry for BASIC execution falls through to line 37 where data initialization begins after the precomputation jump at line 121 sends control to line 183.GO TO and GO SUB are written with leading zeros (e.g., GOTO 0019), a known memory optimization that stores the number as a string rather than a floating-point value in the token stream.CODE N$(X)-37 yields a value >128 for semitone note names stored as inverse characters.FOR delay loop scaled by the duration value when the decoded note index is negative.IF X<=LEN N$ THEN GOTO 0157) re-enters the playback loop mid-subroutine after a rest, rather than continuing the FOR X loop normally — an unusual flow-control trick to share the NEXT X at line 157.Bugs and Anomalies
"M" at line 215 jumps to line 715, bypassing line 216 which would have been the next sequential check. No functional bug since the jump is explicit.WAIT loop at line 207 (IF INKEY$="" THEN GOTO 0207) waits for a key to be pressed, but the subsequent key tests (lines 209–239) re-read INKEY$ without waiting, meaning a fast typist could miss checks if the key is released between lines. This is a standard ZX81 INKEY$ polling hazard.V$(11) at line 53 is dimensioned to 28 characters with a 30-character initializer — the extra two characters are silently truncated by BASIC.K$(Q+1) for certain staff positions (semitone rows), implicitly raising the octave index — this relies on K$="123456788" having a duplicate “8” at position 9 to avoid an out-of-bounds error at the top octave.Content
Source Code
1 REM \FF\10\CC\01\2A\82\40\ED\5B\84\40\01\00\00\E5\D3\FF\CD\A2\40\DB\FE\CD\A2\40\E1\B7\ED\52\30\EF\C9\2A\84\40\37\ED\42\30\FB\C9\20\22\23\25\1C
3 GOSUB 069Z
5 GOTO 0037
7 IF N=1 AND J>1 THEN GOTO 0033
9 IF N>1 THEN GOTO 0015
11 LET N=N+1
13 RETURN
15 LET F=256*CODE O$(J,N-1,1)+CODE O$(J,N-1,2)
17 LET F1=256*CODE O$(J,N,1)+CODE O$(J,N,2)
19 LET F=INT ((F1+F)/2+.5)
21 LET R$(J,N,2)=CHR$ (F-(INT (F/256)*256))
23 LET R$(J,N,1)=CHR$ (INT (F/256))
25 LET N=N+1
27 RETURN
29 LET C=USR 16518
31 RETURN
33 LET F1=256*CODE O$(J-1,N,1)+CODE O$(J-1,N,2)
35 GOTO 0019
37 DIM V$(11,28)
39 LET V$(1)="---------------------------"
41 FOR I=3 TO 9 STEP 2
43 LET V$(I)=V$(1)
45 NEXT I
47 FOR I=2 TO 10 STEP 2
49 LET V$(I)=" "
51 NEXT I
53 LET V$(11)=" "
55 DIM R$(8,7,2)
57 LET U=0
59 LET G=1
61 GOSUB 0383
63 DIM O$(8,7,2)
65 DIM X$(3)
67 DIM N$(3)
69 DIM D$(3)
71 FAST
73 FOR J=8 TO 1 STEP -1
75 LET N=1
77 LET F=INT ((26*2**(J-1)+2**(J-1)*(2/3))+.5)
79 GOSUB 0125
81 GOSUB 0007
83 LET F=INT (30*2**(J-1)+.5)
85 GOSUB 0125
87 GOSUB 0007
89 LET F=INT (32*2**(J-1)+.5)
91 GOSUB 0125
93 GOSUB 0007
95 LET F=INT (36*2**(J-1)+.5)
97 GOSUB 0125
99 GOSUB 0007
101 LET F=INT (40*2**(J-1)+.5)
103 GOSUB 0125
105 GOSUB 0007
107 LET F=INT ((42*2**(J-1)+2**(J-1)*(2/3))+.5)
109 GOSUB 0125
111 GOSUB 0007
113 LET F=INT (48*2**(J-1)+.5)
115 GOSUB 0125
117 GOSUB 0007
119 NEXT J
121 GOTO 0183
123 REM %F%O%R% %N%O%T%E%S%.
125 LET F=INT ((1/(F*17E-6))+.5)
127 LET O$(J,N,2)=CHR$ (F-(INT (F/256)*256))
129 LET O$(J,N,1)=CHR$ (INT (F/256))
131 RETURN
133 FOR X=1 TO LEN N$
135 LET N=CODE N$(X)-37
137 LET J=CODE X$(X)-28
139 LET D=(CODE D$(X)-28)*16
141 IF N<=128 THEN GOTO 0147
143 GOTO 0161
145 NEXT X
147 IF N<0 THEN GOTO 0175
149 POKE 16516,CODE O$(J,N,2)
151 POKE 16517,CODE O$(J,N,1)
153 POKE 16515,D
155 LET C=USR 16518
157 NEXT X
159 RETURN
161 LET N=N-128
163 POKE 16516,CODE R$(J,N,2)
165 POKE 16517,CODE R$(J,N,1)
167 POKE 16515,D
169 LET C=USR 16518
171 NEXT X
173 RETURN
175 FOR T=1 TO D*8
177 NEXT T
179 IF X<=LEN N$ THEN GOTO 0157
181 RETURN
183 REM %P%R%I%N%T%S% %B%A%R%S%.
185 FOR I=1 TO 30
187 PRINT AT 1,I;"-";AT 3,I;"-";AT 5,I;"-"
189 PRINT AT 7,I;"-";AT 9,I;"-"
191 NEXT I
193 FOR I=1 TO 9
195 PRINT AT I,1;"\: ";AT I,2;"\ :"
197 PRINT AT 14,1;"%D";AT 16,1;"%C%U%R%R%E%N%T %O%C%T%A%V%E%: "
199 NEXT I
201 SLOW
203 IF TT+T>=30 THEN GOTO 0457
205 PRINT AT A,TT+T;"\##"
207 IF INKEY$="" THEN GOTO 0207
209 IF INKEY$="O" THEN GOTO 0451
213 IF INKEY$="5" THEN GOTO 0243
215 IF INKEY$="M" THEN GOTO 715
217 IF INKEY$<>"A" THEN GOTO 0223
219 LET L=1
221 GOSUB 0307
223 IF INKEY$="Q" THEN GOSUB 0431
225 IF INKEY$="P" THEN GOSUB 0491
227 IF INKEY$="6" THEN GOSUB 0255
229 IF INKEY$="7" THEN GOSUB 0267
231 IF INKEY$="8" THEN GOTO 0279
233 IF INKEY$="D" THEN GOTO 0445
235 IF INKEY$="R" THEN GOSUB 0289
237 IF INKEY$="F" THEN GOSUB 0517
239 IF INKEY$="S" THEN GOSUB 0307
241 GOTO 0203
243 IF TT+T>3 THEN LET T=T-1
245 PRINT AT A,TT+T+1;V$(A,T+1)
247 PRINT AT A,TT+T;"\##"
249 LET PP=1
251 GOTO 0207
253 RETURN
255 PRINT AT A,TT+T;" "
257 IF A=10 OR A=11 OR A=2 OR A=4 OR A=6 OR A=8 THEN GOTO 0261
259 PRINT AT A,TT+T;"-"
261 IF A<11 THEN LET A=A+1
263 LET W=130
265 RETURN
267 PRINT AT A,TT+T;" "
269 IF A=10 OR A=11 OR A=2 OR A=4 OR A=6 OR A=8 THEN GOTO 0273
271 PRINT AT A,TT+T;"-"
273 IF A>1 THEN LET A=A-1
275 LET W=130
277 RETURN
279 PRINT AT A,TT+T;V$(A,T)
281 LET PP=0
283 IF TT+T<30 THEN LET T=T+1
285 PRINT AT A,TT+T;"\##"
287 GOTO 0207
289 FAST
291 LET U=T
293 GOSUB 0133
295 FOR I=1 TO REP-1
297 GOSUB 0133
299 NEXT I
301 SLOW
303 LET T=U
305 GOTO 0205
307 IF B+(T+1)<=LEN N$ THEN GOTO 0327
309 LET L$=N$
311 DIM N$(B+(T+1))
313 LET N$(1 TO LEN N$-2)=L$
315 LET L$=X$
317 DIM X$(B+(T+1))
319 LET X$(1 TO LEN X$-2)=L$
321 LET L$=D$
323 DIM D$(B+(T+1))
325 LET D$(1 TO LEN D$-2)=L$
327 IF L=1 THEN GOTO 0665
329 LET SEMI=6
331 IF A=1 THEN LET N$(B+(T))="F"
333 IF A=2 THEN LET N$(B+(T))="E"
335 IF A=3 THEN LET N$(B+(T))="D"
337 IF A=4 THEN LET N$(B+(T))="C"
339 IF A=5 THEN LET N$(B+T)="B"
341 IF A=6 THEN LET N$(B+T)="A"
343 IF A=7 THEN LET N$(B+T)="G"
345 IF A=8 THEN LET N$(B+T)="F"
347 IF A=9 THEN LET N$(B+T)="E"
349 IF A=10 THEN LET N$(B+T)="D"
351 IF A=11 THEN LET N$(B+(T))="C"
353 LET V$(A,T)="$"
355 LET D$(B+(T))=J$(G)
357 LET X$(B+(T))=K$(Q)
359 IF A=SEMI OR A=5 OR A=2 OR A=1 OR A=3 OR A=4 THEN LET X$(B+(T))=K$(Q+1)
361 IF PP=0 THEN GOTO 0371
363 FOR I=1 TO 11
365 IF I=1 OR I=3 OR I=5 OR I=7 OR I=9 THEN PRINT AT I,TT+T;"-"
367 IF I=2 OR I=4 OR I=6 OR I=8 OR I=10 OR I=11 THEN PRINT AT I,TT+T;" "
369 NEXT I
371 PRINT AT A,TT+T;V$(A,T)
373 LET T=T+1
375 PRINT AT 14,(TT+T)-1;G
377 PRINT AT 16,16;Q
379 RETURN
381 REM %I%N%I%T%I%A%L%I%Z%A%T%I%O%N% %S%U%B
383 PRINT AT 10,2;"INPUT NOTE DURATION(1 TO 8)"
385 LET B=0
387 LET L=0
389 LET Q=0
391 LET W=128
393 LET H=9000
395 LET A=11
397 LET C=1
399 LET T=1
401 LET TT=2
403 LET PP=0
405 LET REP=0
407 IF INKEY$="" THEN GOTO 0407
409 LET G=VAL INKEY$
411 LET J$="12345678"
413 CLS
415 PRINT AT 10,5;"INPUT OCTAVE(1 TO 6)"
417 IF INKEY$="" THEN GOTO 0417
419 LET Y=(VAL INKEY$)+1
421 LET K$="123456788"
423 LET Q=VAL K$(Y+1)
425 CLS
427 RETURN
429 REM %S%U%B% %T%O% %R%E%P%E%A%T% %T%U%N%E%.
431 PRINT AT 18,1;" YOU WANT THE TUNE TO REPEAT..."
433 PRINT AT 20,5;"HOW MANY TIMES?"
435 INPUT Y
437 LET REP=Y
439 PRINT AT 18,1;" "
441 PRINT AT 20,1;" "
443 RETURN
445 INPUT Y
447 IF Y>0 AND Y<9 THEN LET G=Y
449 GOTO 0205
451 INPUT Y
453 IF Y>0 AND Y<9 THEN LET Q=Y
455 GOTO 0205
457 REM %S%U%B%.% %T%O% %P%R%I%N%T% %2%N%D% %S%E%T% %O%F% %B%A%R%S
459 FOR I=1 TO 19
461 SCROLL
463 NEXT I
465 LET A=11
467 LET B=B+T-1
469 LET T=1
471 LET V$(1)="-----------------------------"
473 LET V$(2)=" "
475 FOR I=1 TO 9 STEP 2
477 LET V$(I)=V$(1)
479 LET V$(I+1)=V$(2)
481 NEXT I
483 LET V$(11)=V$(2)
485 PRINT AT 14,1;"%D";AT 16,1;"%C%U%R%R%E%N%T %O%C%T%A%V%E%: "
487 GOTO 0183
489 REM %S%U%B% %T%O% %C%H%A%N%G%E% %A%L%L% %O%C%T%A%V%E%S\:
491 PRINT AT 20,2;"INCREASE OR DECREASE?"
493 LET P$="12345678"
495 INPUT Y
497 FOR S=1 TO LEN X$-1
499 LET V=VAL X$(S)
501 IF Y=7 AND V<8 THEN LET V=V+1
503 IF Y=6 AND V>1 THEN LET V=V-1
505 LET X$(S)=P$(V)
507 NEXT S
509 PRINT AT 16,16;X$(LEN X$-1)
511 PRINT AT 20,2;" "
513 RETURN
515 REM %S%U%B% %T%O% %C%H%A%N%G%E% %O%V%E%R%A%L%L% % % %S%P%E%E%D% %O%F% %P%L%A%Y%.
517 PRINT AT 20,2;"INCREASE OR DECREASE?"
519 INPUT Y
521 FOR S=1 TO LEN D$-1
523 LET V=VAL D$(S)
525 IF Y=7 AND V<9 THEN LET V=V+1
527 IF Y=6 AND V>1 THEN LET V=V-1
529 LET D$(S)=J$(V)
531 NEXT S
533 PRINT AT 14,3;D$(B+1 TO )
535 PRINT AT 20,2;" "
537 RETURN
663 REM %S%E%M%I%T%O%N%E% %S%U%B%.
665 IF A=1 THEN LET N$(B+T)="F"
667 IF A=2 THEN LET N$(B+T)="%E"
669 IF A=3 THEN LET N$(B+T)="%D"
671 IF A=4 THEN LET N$(B+T)="%C"
673 IF A=5 THEN LET N$(B+T)="%B"
675 IF A=6 THEN LET N$(B+T)="%A"
677 IF A=7 THEN LET N$(B+T)="%G"
679 IF A=8 THEN LET N$(B+T)="%F"
681 IF A=9 THEN LET N$(B+T)="%E"
683 IF A=10 THEN LET N$(B+T)="%D"
685 IF A=11 THEN LET N$(B+T)="%C"
687 LET L=0
689 LET SEMI=5
691 GOTO 0353
693 PRINT AT 5,8;"% % % % % % % % % % % % "
695 PRINT AT 6,8;"% % % % "
697 PRINT AT 7,8;"% % % % "
699 PRINT AT 8,8;"% % % % % % % % % % % % "
701 PRINT AT 9,8;"% % % "
703 PRINT AT 10,8;"% % % % % % % % % "
705 PRINT AT 12,12;"PRESENT"
707 PRINT AT 15,11;"%C%O%M%P%O%S%E%R% "
709 FOR F=1 TO 15
711 NEXT F
713 CLS
714 RETURN
715 PRINT AT 20,2;"START TAPE AND PRESS ""G"""
716 IF INKEY$<>"G" THEN GOTO 716
717 PRINT AT 20,2;" "
718 SAVE "COMPOSE%R"
719 GOTO 0203
721 SAVE "COMPOSE%R"
723 GOTO 1People
Composer
Program Analysis
Program Structure
Machine Code Routine (REM Line 1)
\FF\10\CC\01\2A\82\40\ED\5B\84\40\01\00\00\E5\D3\FF\CD\A2\40\DB\FE\CD\A2\40\E1\B7\ED\52\30\EF\C9\2A\84\40\37\ED\42\30\FB\C9USR 16518 (line 29 / 155 / 169), which points two bytes past the start of the BASIC line header into the REM body. The three parameters are POKEd into fixed addresses before the call:Address Variable Purpose 16515 D (duration × 16) Note duration 16516 Low byte of timer Speaker half-period low 16517 High byte of timer Speaker half-period high OUT (0FFH),A instruction (D3 FF) to toggle the ZX81 speaker port and a delay loop keyed to the 16-bit timer value to set pitch.Frequency Precomputation
F = INT(1 / (F_hz × 17E-6) + 0.5)O$(J,N,1..2); interpolated semitone (midpoint) values are stored in R$(J,N,1..2) via the subroutine at lines 7–27, which averages adjacent timer values.Note and Duration Encoding
N$ — note names (A–G); semitones stored as inverse characters (e.g., %E), which have character codes above 128, detected at line 141 (IF N<=128).X$ — octave per note, encoded as a digit character; decoded with CODE X$(X)-28.D$ — duration per note, encoded similarly with CODE D$(X)-28, then multiplied by 16 at line 140.DIM / copy idiom (lines 307–325): the old contents are saved into a temporary string L$, the array is re-dimensioned one element larger, and the contents are restored with a slice assignment.Staff Display
"-" characters at odd screen rows 1, 3, 5, 7, 9; spaces at even rows and row 11 represent spaces between lines and ledger areas. The array V$(row, col) tracks the current character at each cell so notes can be restored when the cursor moves away. The cursor is displayed as a block graphic character (\##, a solid 2×2 block). When the cursor reaches column 30, the screen scrolls 19 lines and a new staff section is drawn.Key Bindings
Key Function 5 Move cursor left 8 Move cursor right / advance 6 Move cursor down (lower pitch) 7 Move cursor up (higher pitch) S Place natural note A Place semitone (sharp/flat) note R Play back the tune (FAST mode) Q Set repeat count P Shift all octaves up or down F Change overall playback speed D Change note duration O Change current octave M Save composition to tape Notable Techniques and Idioms
GOSUB 069Z at line 3 and GOTO 0037 at line 5 use leading-zero and trailing-letter forms of line numbers — the 069Z target does not exist as a reachable line in the normal flow and acts as a skip; the program entry for BASIC execution falls through to line 37 where data initialization begins after the precomputation jump at line 121 sends control to line 183.GO TO and GO SUB are written with leading zeros (e.g., GOTO 0019), a known memory optimization that stores the number as a string rather than a floating-point value in the token stream.CODE N$(X)-37 yields a value >128 for semitone note names stored as inverse characters.FOR delay loop scaled by the duration value when the decoded note index is negative.IF X<=LEN N$ THEN GOTO 0157) re-enters the playback loop mid-subroutine after a rest, rather than continuing the FOR X loop normally — an unusual flow-control trick to share the NEXT X at line 157.Bugs and Anomalies
"M" at line 215 jumps to line 715, bypassing line 216 which would have been the next sequential check. No functional bug since the jump is explicit.WAIT loop at line 207 (IF INKEY$="" THEN GOTO 0207) waits for a key to be pressed, but the subsequent key tests (lines 209–239) re-read INKEY$ without waiting, meaning a fast typist could miss checks if the key is released between lines. This is a standard ZX81 INKEY$ polling hazard.V$(11) at line 53 is dimensioned to 28 characters with a 30-character initializer — the extra two characters are silently truncated by BASIC.K$(Q+1) for certain staff positions (semitone rows), implicitly raising the octave index — this relies on K$="123456788" having a duplicate “8” at position 9 to avoid an out-of-bounds error at the top octave.Content
Source Code
1 REM \FF\10\CC\01\2A\82\40\ED\5B\84\40\01\00\00\E5\D3\FF\CD\A2\40\DB\FE\CD\A2\40\E1\B7\ED\52\30\EF\C9\2A\84\40\37\ED\42\30\FB\C9\20\22\23\25\1C
3 GOSUB 069Z
5 GOTO 0037
7 IF N=1 AND J>1 THEN GOTO 0033
9 IF N>1 THEN GOTO 0015
11 LET N=N+1
13 RETURN
15 LET F=256*CODE O$(J,N-1,1)+CODE O$(J,N-1,2)
17 LET F1=256*CODE O$(J,N,1)+CODE O$(J,N,2)
19 LET F=INT ((F1+F)/2+.5)
21 LET R$(J,N,2)=CHR$ (F-(INT (F/256)*256))
23 LET R$(J,N,1)=CHR$ (INT (F/256))
25 LET N=N+1
27 RETURN
29 LET C=USR 16518
31 RETURN
33 LET F1=256*CODE O$(J-1,N,1)+CODE O$(J-1,N,2)
35 GOTO 0019
37 DIM V$(11,28)
39 LET V$(1)="---------------------------"
41 FOR I=3 TO 9 STEP 2
43 LET V$(I)=V$(1)
45 NEXT I
47 FOR I=2 TO 10 STEP 2
49 LET V$(I)=" "
51 NEXT I
53 LET V$(11)=" "
55 DIM R$(8,7,2)
57 LET U=0
59 LET G=1
61 GOSUB 0383
63 DIM O$(8,7,2)
65 DIM X$(3)
67 DIM N$(3)
69 DIM D$(3)
71 FAST
73 FOR J=8 TO 1 STEP -1
75 LET N=1
77 LET F=INT ((26*2**(J-1)+2**(J-1)*(2/3))+.5)
79 GOSUB 0125
81 GOSUB 0007
83 LET F=INT (30*2**(J-1)+.5)
85 GOSUB 0125
87 GOSUB 0007
89 LET F=INT (32*2**(J-1)+.5)
91 GOSUB 0125
93 GOSUB 0007
95 LET F=INT (36*2**(J-1)+.5)
97 GOSUB 0125
99 GOSUB 0007
101 LET F=INT (40*2**(J-1)+.5)
103 GOSUB 0125
105 GOSUB 0007
107 LET F=INT ((42*2**(J-1)+2**(J-1)*(2/3))+.5)
109 GOSUB 0125
111 GOSUB 0007
113 LET F=INT (48*2**(J-1)+.5)
115 GOSUB 0125
117 GOSUB 0007
119 NEXT J
121 GOTO 0183
123 REM %F%O%R% %N%O%T%E%S%.
125 LET F=INT ((1/(F*17E-6))+.5)
127 LET O$(J,N,2)=CHR$ (F-(INT (F/256)*256))
129 LET O$(J,N,1)=CHR$ (INT (F/256))
131 RETURN
133 FOR X=1 TO LEN N$
135 LET N=CODE N$(X)-37
137 LET J=CODE X$(X)-28
139 LET D=(CODE D$(X)-28)*16
141 IF N<=128 THEN GOTO 0147
143 GOTO 0161
145 NEXT X
147 IF N<0 THEN GOTO 0175
149 POKE 16516,CODE O$(J,N,2)
151 POKE 16517,CODE O$(J,N,1)
153 POKE 16515,D
155 LET C=USR 16518
157 NEXT X
159 RETURN
161 LET N=N-128
163 POKE 16516,CODE R$(J,N,2)
165 POKE 16517,CODE R$(J,N,1)
167 POKE 16515,D
169 LET C=USR 16518
171 NEXT X
173 RETURN
175 FOR T=1 TO D*8
177 NEXT T
179 IF X<=LEN N$ THEN GOTO 0157
181 RETURN
183 REM %P%R%I%N%T%S% %B%A%R%S%.
185 FOR I=1 TO 30
187 PRINT AT 1,I;"-";AT 3,I;"-";AT 5,I;"-"
189 PRINT AT 7,I;"-";AT 9,I;"-"
191 NEXT I
193 FOR I=1 TO 9
195 PRINT AT I,1;"\: ";AT I,2;"\ :"
197 PRINT AT 14,1;"%D";AT 16,1;"%C%U%R%R%E%N%T %O%C%T%A%V%E%: "
199 NEXT I
201 SLOW
203 IF TT+T>=30 THEN GOTO 0457
205 PRINT AT A,TT+T;"\##"
207 IF INKEY$="" THEN GOTO 0207
209 IF INKEY$="O" THEN GOTO 0451
213 IF INKEY$="5" THEN GOTO 0243
215 IF INKEY$="M" THEN GOTO 715
217 IF INKEY$<>"A" THEN GOTO 0223
219 LET L=1
221 GOSUB 0307
223 IF INKEY$="Q" THEN GOSUB 0431
225 IF INKEY$="P" THEN GOSUB 0491
227 IF INKEY$="6" THEN GOSUB 0255
229 IF INKEY$="7" THEN GOSUB 0267
231 IF INKEY$="8" THEN GOTO 0279
233 IF INKEY$="D" THEN GOTO 0445
235 IF INKEY$="R" THEN GOSUB 0289
237 IF INKEY$="F" THEN GOSUB 0517
239 IF INKEY$="S" THEN GOSUB 0307
241 GOTO 0203
243 IF TT+T>3 THEN LET T=T-1
245 PRINT AT A,TT+T+1;V$(A,T+1)
247 PRINT AT A,TT+T;"\##"
249 LET PP=1
251 GOTO 0207
253 RETURN
255 PRINT AT A,TT+T;" "
257 IF A=10 OR A=11 OR A=2 OR A=4 OR A=6 OR A=8 THEN GOTO 0261
259 PRINT AT A,TT+T;"-"
261 IF A<11 THEN LET A=A+1
263 LET W=130
265 RETURN
267 PRINT AT A,TT+T;" "
269 IF A=10 OR A=11 OR A=2 OR A=4 OR A=6 OR A=8 THEN GOTO 0273
271 PRINT AT A,TT+T;"-"
273 IF A>1 THEN LET A=A-1
275 LET W=130
277 RETURN
279 PRINT AT A,TT+T;V$(A,T)
281 LET PP=0
283 IF TT+T<30 THEN LET T=T+1
285 PRINT AT A,TT+T;"\##"
287 GOTO 0207
289 FAST
291 LET U=T
293 GOSUB 0133
295 FOR I=1 TO REP-1
297 GOSUB 0133
299 NEXT I
301 SLOW
303 LET T=U
305 GOTO 0205
307 IF B+(T+1)<=LEN N$ THEN GOTO 0327
309 LET L$=N$
311 DIM N$(B+(T+1))
313 LET N$(1 TO LEN N$-2)=L$
315 LET L$=X$
317 DIM X$(B+(T+1))
319 LET X$(1 TO LEN X$-2)=L$
321 LET L$=D$
323 DIM D$(B+(T+1))
325 LET D$(1 TO LEN D$-2)=L$
327 IF L=1 THEN GOTO 0665
329 LET SEMI=6
331 IF A=1 THEN LET N$(B+(T))="F"
333 IF A=2 THEN LET N$(B+(T))="E"
335 IF A=3 THEN LET N$(B+(T))="D"
337 IF A=4 THEN LET N$(B+(T))="C"
339 IF A=5 THEN LET N$(B+T)="B"
341 IF A=6 THEN LET N$(B+T)="A"
343 IF A=7 THEN LET N$(B+T)="G"
345 IF A=8 THEN LET N$(B+T)="F"
347 IF A=9 THEN LET N$(B+T)="E"
349 IF A=10 THEN LET N$(B+T)="D"
351 IF A=11 THEN LET N$(B+(T))="C"
353 LET V$(A,T)="$"
355 LET D$(B+(T))=J$(G)
357 LET X$(B+(T))=K$(Q)
359 IF A=SEMI OR A=5 OR A=2 OR A=1 OR A=3 OR A=4 THEN LET X$(B+(T))=K$(Q+1)
361 IF PP=0 THEN GOTO 0371
363 FOR I=1 TO 11
365 IF I=1 OR I=3 OR I=5 OR I=7 OR I=9 THEN PRINT AT I,TT+T;"-"
367 IF I=2 OR I=4 OR I=6 OR I=8 OR I=10 OR I=11 THEN PRINT AT I,TT+T;" "
369 NEXT I
371 PRINT AT A,TT+T;V$(A,T)
373 LET T=T+1
375 PRINT AT 14,(TT+T)-1;G
377 PRINT AT 16,16;Q
379 RETURN
381 REM %I%N%I%T%I%A%L%I%Z%A%T%I%O%N% %S%U%B
383 PRINT AT 10,2;"INPUT NOTE DURATION(1 TO 8)"
385 LET B=0
387 LET L=0
389 LET Q=0
391 LET W=128
393 LET H=9000
395 LET A=11
397 LET C=1
399 LET T=1
401 LET TT=2
403 LET PP=0
405 LET REP=0
407 IF INKEY$="" THEN GOTO 0407
409 LET G=VAL INKEY$
411 LET J$="12345678"
413 CLS
415 PRINT AT 10,5;"INPUT OCTAVE(1 TO 6)"
417 IF INKEY$="" THEN GOTO 0417
419 LET Y=(VAL INKEY$)+1
421 LET K$="123456788"
423 LET Q=VAL K$(Y+1)
425 CLS
427 RETURN
429 REM %S%U%B% %T%O% %R%E%P%E%A%T% %T%U%N%E%.
431 PRINT AT 18,1;" YOU WANT THE TUNE TO REPEAT..."
433 PRINT AT 20,5;"HOW MANY TIMES?"
435 INPUT Y
437 LET REP=Y
439 PRINT AT 18,1;" "
441 PRINT AT 20,1;" "
443 RETURN
445 INPUT Y
447 IF Y>0 AND Y<9 THEN LET G=Y
449 GOTO 0205
451 INPUT Y
453 IF Y>0 AND Y<9 THEN LET Q=Y
455 GOTO 0205
457 REM %S%U%B%.% %T%O% %P%R%I%N%T% %2%N%D% %S%E%T% %O%F% %B%A%R%S
459 FOR I=1 TO 19
461 SCROLL
463 NEXT I
465 LET A=11
467 LET B=B+T-1
469 LET T=1
471 LET V$(1)="-----------------------------"
473 LET V$(2)=" "
475 FOR I=1 TO 9 STEP 2
477 LET V$(I)=V$(1)
479 LET V$(I+1)=V$(2)
481 NEXT I
483 LET V$(11)=V$(2)
485 PRINT AT 14,1;"%D";AT 16,1;"%C%U%R%R%E%N%T %O%C%T%A%V%E%: "
487 GOTO 0183
489 REM %S%U%B% %T%O% %C%H%A%N%G%E% %A%L%L% %O%C%T%A%V%E%S\:
491 PRINT AT 20,2;"INCREASE OR DECREASE?"
493 LET P$="12345678"
495 INPUT Y
497 FOR S=1 TO LEN X$-1
499 LET V=VAL X$(S)
501 IF Y=7 AND V<8 THEN LET V=V+1
503 IF Y=6 AND V>1 THEN LET V=V-1
505 LET X$(S)=P$(V)
507 NEXT S
509 PRINT AT 16,16;X$(LEN X$-1)
511 PRINT AT 20,2;" "
513 RETURN
515 REM %S%U%B% %T%O% %C%H%A%N%G%E% %O%V%E%R%A%L%L% % % %S%P%E%E%D% %O%F% %P%L%A%Y%.
517 PRINT AT 20,2;"INCREASE OR DECREASE?"
519 INPUT Y
521 FOR S=1 TO LEN D$-1
523 LET V=VAL D$(S)
525 IF Y=7 AND V<9 THEN LET V=V+1
527 IF Y=6 AND V>1 THEN LET V=V-1
529 LET D$(S)=J$(V)
531 NEXT S
533 PRINT AT 14,3;D$(B+1 TO )
535 PRINT AT 20,2;" "
537 RETURN
663 REM %S%E%M%I%T%O%N%E% %S%U%B%.
665 IF A=1 THEN LET N$(B+T)="F"
667 IF A=2 THEN LET N$(B+T)="%E"
669 IF A=3 THEN LET N$(B+T)="%D"
671 IF A=4 THEN LET N$(B+T)="%C"
673 IF A=5 THEN LET N$(B+T)="%B"
675 IF A=6 THEN LET N$(B+T)="%A"
677 IF A=7 THEN LET N$(B+T)="%G"
679 IF A=8 THEN LET N$(B+T)="%F"
681 IF A=9 THEN LET N$(B+T)="%E"
683 IF A=10 THEN LET N$(B+T)="%D"
685 IF A=11 THEN LET N$(B+T)="%C"
687 LET L=0
689 LET SEMI=5
691 GOTO 0353
693 PRINT AT 5,8;"% % % % % % % % % % % % "
695 PRINT AT 6,8;"% % % % "
697 PRINT AT 7,8;"% % % % "
699 PRINT AT 8,8;"% % % % % % % % % % % % "
701 PRINT AT 9,8;"% % % "
703 PRINT AT 10,8;"% % % % % % % % % "
705 PRINT AT 12,12;"PRESENT"
707 PRINT AT 15,11;"%C%O%M%P%O%S%E%R% "
709 FOR F=1 TO 15
711 NEXT F
713 CLS
714 RETURN
715 PRINT AT 20,2;"START TAPE AND PRESS ""G"""
716 IF INKEY$<>"G" THEN GOTO 716
717 PRINT AT 20,2;" "
718 SAVE "COMPOSE%R"
719 GOTO 0203
721 SAVE "COMPOSE%R"
723 GOTO 1People
Composer
Program Analysis
Program Structure
Machine Code Routine (REM Line 1)
\FF\10\CC\01\2A\82\40\ED\5B\84\40\01\00\00\E5\D3\FF\CD\A2\40\DB\FE\CD\A2\40\E1\B7\ED\52\30\EF\C9\2A\84\40\37\ED\42\30\FB\C9USR 16518 (line 29 / 155 / 169), which points two bytes past the start of the BASIC line header into the REM body. The three parameters are POKEd into fixed addresses before the call:Address Variable Purpose 16515 D (duration × 16) Note duration 16516 Low byte of timer Speaker half-period low 16517 High byte of timer Speaker half-period high OUT (0FFH),A instruction (D3 FF) to toggle the ZX81 speaker port and a delay loop keyed to the 16-bit timer value to set pitch.Frequency Precomputation
F = INT(1 / (F_hz × 17E-6) + 0.5)O$(J,N,1..2); interpolated semitone (midpoint) values are stored in R$(J,N,1..2) via the subroutine at lines 7–27, which averages adjacent timer values.Note and Duration Encoding
N$ — note names (A–G); semitones stored as inverse characters (e.g., %E), which have character codes above 128, detected at line 141 (IF N<=128).X$ — octave per note, encoded as a digit character; decoded with CODE X$(X)-28.D$ — duration per note, encoded similarly with CODE D$(X)-28, then multiplied by 16 at line 140.DIM / copy idiom (lines 307–325): the old contents are saved into a temporary string L$, the array is re-dimensioned one element larger, and the contents are restored with a slice assignment.Staff Display
"-" characters at odd screen rows 1, 3, 5, 7, 9; spaces at even rows and row 11 represent spaces between lines and ledger areas. The array V$(row, col) tracks the current character at each cell so notes can be restored when the cursor moves away. The cursor is displayed as a block graphic character (\##, a solid 2×2 block). When the cursor reaches column 30, the screen scrolls 19 lines and a new staff section is drawn.Key Bindings
Key Function 5 Move cursor left 8 Move cursor right / advance 6 Move cursor down (lower pitch) 7 Move cursor up (higher pitch) S Place natural note A Place semitone (sharp/flat) note R Play back the tune (FAST mode) Q Set repeat count P Shift all octaves up or down F Change overall playback speed D Change note duration O Change current octave M Save composition to tape Notable Techniques and Idioms
GOSUB 069Z at line 3 and GOTO 0037 at line 5 use leading-zero and trailing-letter forms of line numbers — the 069Z target does not exist as a reachable line in the normal flow and acts as a skip; the program entry for BASIC execution falls through to line 37 where data initialization begins after the precomputation jump at line 121 sends control to line 183.GO TO and GO SUB are written with leading zeros (e.g., GOTO 0019), a known memory optimization that stores the number as a string rather than a floating-point value in the token stream.CODE N$(X)-37 yields a value >128 for semitone note names stored as inverse characters.FOR delay loop scaled by the duration value when the decoded note index is negative.IF X<=LEN N$ THEN GOTO 0157) re-enters the playback loop mid-subroutine after a rest, rather than continuing the FOR X loop normally — an unusual flow-control trick to share the NEXT X at line 157.Bugs and Anomalies
"M" at line 215 jumps to line 715, bypassing line 216 which would have been the next sequential check. No functional bug since the jump is explicit.WAIT loop at line 207 (IF INKEY$="" THEN GOTO 0207) waits for a key to be pressed, but the subsequent key tests (lines 209–239) re-read INKEY$ without waiting, meaning a fast typist could miss checks if the key is released between lines. This is a standard ZX81 INKEY$ polling hazard.V$(11) at line 53 is dimensioned to 28 characters with a 30-character initializer — the extra two characters are silently truncated by BASIC.K$(Q+1) for certain staff positions (semitone rows), implicitly raising the octave index — this relies on K$="123456788" having a duplicate “8” at position 9 to avoid an out-of-bounds error at the top octave.Content
Source Code
1 REM \FF\10\CC\01\2A\82\40\ED\5B\84\40\01\00\00\E5\D3\FF\CD\A2\40\DB\FE\CD\A2\40\E1\B7\ED\52\30\EF\C9\2A\84\40\37\ED\42\30\FB\C9\20\22\23\25\1C
3 GOSUB 069Z
5 GOTO 0037
7 IF N=1 AND J>1 THEN GOTO 0033
9 IF N>1 THEN GOTO 0015
11 LET N=N+1
13 RETURN
15 LET F=256*CODE O$(J,N-1,1)+CODE O$(J,N-1,2)
17 LET F1=256*CODE O$(J,N,1)+CODE O$(J,N,2)
19 LET F=INT ((F1+F)/2+.5)
21 LET R$(J,N,2)=CHR$ (F-(INT (F/256)*256))
23 LET R$(J,N,1)=CHR$ (INT (F/256))
25 LET N=N+1
27 RETURN
29 LET C=USR 16518
31 RETURN
33 LET F1=256*CODE O$(J-1,N,1)+CODE O$(J-1,N,2)
35 GOTO 0019
37 DIM V$(11,28)
39 LET V$(1)="---------------------------"
41 FOR I=3 TO 9 STEP 2
43 LET V$(I)=V$(1)
45 NEXT I
47 FOR I=2 TO 10 STEP 2
49 LET V$(I)=" "
51 NEXT I
53 LET V$(11)=" "
55 DIM R$(8,7,2)
57 LET U=0
59 LET G=1
61 GOSUB 0383
63 DIM O$(8,7,2)
65 DIM X$(3)
67 DIM N$(3)
69 DIM D$(3)
71 FAST
73 FOR J=8 TO 1 STEP -1
75 LET N=1
77 LET F=INT ((26*2**(J-1)+2**(J-1)*(2/3))+.5)
79 GOSUB 0125
81 GOSUB 0007
83 LET F=INT (30*2**(J-1)+.5)
85 GOSUB 0125
87 GOSUB 0007
89 LET F=INT (32*2**(J-1)+.5)
91 GOSUB 0125
93 GOSUB 0007
95 LET F=INT (36*2**(J-1)+.5)
97 GOSUB 0125
99 GOSUB 0007
101 LET F=INT (40*2**(J-1)+.5)
103 GOSUB 0125
105 GOSUB 0007
107 LET F=INT ((42*2**(J-1)+2**(J-1)*(2/3))+.5)
109 GOSUB 0125
111 GOSUB 0007
113 LET F=INT (48*2**(J-1)+.5)
115 GOSUB 0125
117 GOSUB 0007
119 NEXT J
121 GOTO 0183
123 REM %F%O%R% %N%O%T%E%S%.
125 LET F=INT ((1/(F*17E-6))+.5)
127 LET O$(J,N,2)=CHR$ (F-(INT (F/256)*256))
129 LET O$(J,N,1)=CHR$ (INT (F/256))
131 RETURN
133 FOR X=1 TO LEN N$
135 LET N=CODE N$(X)-37
137 LET J=CODE X$(X)-28
139 LET D=(CODE D$(X)-28)*16
141 IF N<=128 THEN GOTO 0147
143 GOTO 0161
145 NEXT X
147 IF N<0 THEN GOTO 0175
149 POKE 16516,CODE O$(J,N,2)
151 POKE 16517,CODE O$(J,N,1)
153 POKE 16515,D
155 LET C=USR 16518
157 NEXT X
159 RETURN
161 LET N=N-128
163 POKE 16516,CODE R$(J,N,2)
165 POKE 16517,CODE R$(J,N,1)
167 POKE 16515,D
169 LET C=USR 16518
171 NEXT X
173 RETURN
175 FOR T=1 TO D*8
177 NEXT T
179 IF X<=LEN N$ THEN GOTO 0157
181 RETURN
183 REM %P%R%I%N%T%S% %B%A%R%S%.
185 FOR I=1 TO 30
187 PRINT AT 1,I;"-";AT 3,I;"-";AT 5,I;"-"
189 PRINT AT 7,I;"-";AT 9,I;"-"
191 NEXT I
193 FOR I=1 TO 9
195 PRINT AT I,1;"\: ";AT I,2;"\ :"
197 PRINT AT 14,1;"%D";AT 16,1;"%C%U%R%R%E%N%T %O%C%T%A%V%E%: "
199 NEXT I
201 SLOW
203 IF TT+T>=30 THEN GOTO 0457
205 PRINT AT A,TT+T;"\##"
207 IF INKEY$="" THEN GOTO 0207
209 IF INKEY$="O" THEN GOTO 0451
213 IF INKEY$="5" THEN GOTO 0243
215 IF INKEY$="M" THEN GOTO 715
217 IF INKEY$<>"A" THEN GOTO 0223
219 LET L=1
221 GOSUB 0307
223 IF INKEY$="Q" THEN GOSUB 0431
225 IF INKEY$="P" THEN GOSUB 0491
227 IF INKEY$="6" THEN GOSUB 0255
229 IF INKEY$="7" THEN GOSUB 0267
231 IF INKEY$="8" THEN GOTO 0279
233 IF INKEY$="D" THEN GOTO 0445
235 IF INKEY$="R" THEN GOSUB 0289
237 IF INKEY$="F" THEN GOSUB 0517
239 IF INKEY$="S" THEN GOSUB 0307
241 GOTO 0203
243 IF TT+T>3 THEN LET T=T-1
245 PRINT AT A,TT+T+1;V$(A,T+1)
247 PRINT AT A,TT+T;"\##"
249 LET PP=1
251 GOTO 0207
253 RETURN
255 PRINT AT A,TT+T;" "
257 IF A=10 OR A=11 OR A=2 OR A=4 OR A=6 OR A=8 THEN GOTO 0261
259 PRINT AT A,TT+T;"-"
261 IF A<11 THEN LET A=A+1
263 LET W=130
265 RETURN
267 PRINT AT A,TT+T;" "
269 IF A=10 OR A=11 OR A=2 OR A=4 OR A=6 OR A=8 THEN GOTO 0273
271 PRINT AT A,TT+T;"-"
273 IF A>1 THEN LET A=A-1
275 LET W=130
277 RETURN
279 PRINT AT A,TT+T;V$(A,T)
281 LET PP=0
283 IF TT+T<30 THEN LET T=T+1
285 PRINT AT A,TT+T;"\##"
287 GOTO 0207
289 FAST
291 LET U=T
293 GOSUB 0133
295 FOR I=1 TO REP-1
297 GOSUB 0133
299 NEXT I
301 SLOW
303 LET T=U
305 GOTO 0205
307 IF B+(T+1)<=LEN N$ THEN GOTO 0327
309 LET L$=N$
311 DIM N$(B+(T+1))
313 LET N$(1 TO LEN N$-2)=L$
315 LET L$=X$
317 DIM X$(B+(T+1))
319 LET X$(1 TO LEN X$-2)=L$
321 LET L$=D$
323 DIM D$(B+(T+1))
325 LET D$(1 TO LEN D$-2)=L$
327 IF L=1 THEN GOTO 0665
329 LET SEMI=6
331 IF A=1 THEN LET N$(B+(T))="F"
333 IF A=2 THEN LET N$(B+(T))="E"
335 IF A=3 THEN LET N$(B+(T))="D"
337 IF A=4 THEN LET N$(B+(T))="C"
339 IF A=5 THEN LET N$(B+T)="B"
341 IF A=6 THEN LET N$(B+T)="A"
343 IF A=7 THEN LET N$(B+T)="G"
345 IF A=8 THEN LET N$(B+T)="F"
347 IF A=9 THEN LET N$(B+T)="E"
349 IF A=10 THEN LET N$(B+T)="D"
351 IF A=11 THEN LET N$(B+(T))="C"
353 LET V$(A,T)="$"
355 LET D$(B+(T))=J$(G)
357 LET X$(B+(T))=K$(Q)
359 IF A=SEMI OR A=5 OR A=2 OR A=1 OR A=3 OR A=4 THEN LET X$(B+(T))=K$(Q+1)
361 IF PP=0 THEN GOTO 0371
363 FOR I=1 TO 11
365 IF I=1 OR I=3 OR I=5 OR I=7 OR I=9 THEN PRINT AT I,TT+T;"-"
367 IF I=2 OR I=4 OR I=6 OR I=8 OR I=10 OR I=11 THEN PRINT AT I,TT+T;" "
369 NEXT I
371 PRINT AT A,TT+T;V$(A,T)
373 LET T=T+1
375 PRINT AT 14,(TT+T)-1;G
377 PRINT AT 16,16;Q
379 RETURN
381 REM %I%N%I%T%I%A%L%I%Z%A%T%I%O%N% %S%U%B
383 PRINT AT 10,2;"INPUT NOTE DURATION(1 TO 8)"
385 LET B=0
387 LET L=0
389 LET Q=0
391 LET W=128
393 LET H=9000
395 LET A=11
397 LET C=1
399 LET T=1
401 LET TT=2
403 LET PP=0
405 LET REP=0
407 IF INKEY$="" THEN GOTO 0407
409 LET G=VAL INKEY$
411 LET J$="12345678"
413 CLS
415 PRINT AT 10,5;"INPUT OCTAVE(1 TO 6)"
417 IF INKEY$="" THEN GOTO 0417
419 LET Y=(VAL INKEY$)+1
421 LET K$="123456788"
423 LET Q=VAL K$(Y+1)
425 CLS
427 RETURN
429 REM %S%U%B% %T%O% %R%E%P%E%A%T% %T%U%N%E%.
431 PRINT AT 18,1;" YOU WANT THE TUNE TO REPEAT..."
433 PRINT AT 20,5;"HOW MANY TIMES?"
435 INPUT Y
437 LET REP=Y
439 PRINT AT 18,1;" "
441 PRINT AT 20,1;" "
443 RETURN
445 INPUT Y
447 IF Y>0 AND Y<9 THEN LET G=Y
449 GOTO 0205
451 INPUT Y
453 IF Y>0 AND Y<9 THEN LET Q=Y
455 GOTO 0205
457 REM %S%U%B%.% %T%O% %P%R%I%N%T% %2%N%D% %S%E%T% %O%F% %B%A%R%S
459 FOR I=1 TO 19
461 SCROLL
463 NEXT I
465 LET A=11
467 LET B=B+T-1
469 LET T=1
471 LET V$(1)="-----------------------------"
473 LET V$(2)=" "
475 FOR I=1 TO 9 STEP 2
477 LET V$(I)=V$(1)
479 LET V$(I+1)=V$(2)
481 NEXT I
483 LET V$(11)=V$(2)
485 PRINT AT 14,1;"%D";AT 16,1;"%C%U%R%R%E%N%T %O%C%T%A%V%E%: "
487 GOTO 0183
489 REM %S%U%B% %T%O% %C%H%A%N%G%E% %A%L%L% %O%C%T%A%V%E%S\:
491 PRINT AT 20,2;"INCREASE OR DECREASE?"
493 LET P$="12345678"
495 INPUT Y
497 FOR S=1 TO LEN X$-1
499 LET V=VAL X$(S)
501 IF Y=7 AND V<8 THEN LET V=V+1
503 IF Y=6 AND V>1 THEN LET V=V-1
505 LET X$(S)=P$(V)
507 NEXT S
509 PRINT AT 16,16;X$(LEN X$-1)
511 PRINT AT 20,2;" "
513 RETURN
515 REM %S%U%B% %T%O% %C%H%A%N%G%E% %O%V%E%R%A%L%L% % % %S%P%E%E%D% %O%F% %P%L%A%Y%.
517 PRINT AT 20,2;"INCREASE OR DECREASE?"
519 INPUT Y
521 FOR S=1 TO LEN D$-1
523 LET V=VAL D$(S)
525 IF Y=7 AND V<9 THEN LET V=V+1
527 IF Y=6 AND V>1 THEN LET V=V-1
529 LET D$(S)=J$(V)
531 NEXT S
533 PRINT AT 14,3;D$(B+1 TO )
535 PRINT AT 20,2;" "
537 RETURN
663 REM %S%E%M%I%T%O%N%E% %S%U%B%.
665 IF A=1 THEN LET N$(B+T)="F"
667 IF A=2 THEN LET N$(B+T)="%E"
669 IF A=3 THEN LET N$(B+T)="%D"
671 IF A=4 THEN LET N$(B+T)="%C"
673 IF A=5 THEN LET N$(B+T)="%B"
675 IF A=6 THEN LET N$(B+T)="%A"
677 IF A=7 THEN LET N$(B+T)="%G"
679 IF A=8 THEN LET N$(B+T)="%F"
681 IF A=9 THEN LET N$(B+T)="%E"
683 IF A=10 THEN LET N$(B+T)="%D"
685 IF A=11 THEN LET N$(B+T)="%C"
687 LET L=0
689 LET SEMI=5
691 GOTO 0353
693 PRINT AT 5,8;"% % % % % % % % % % % % "
695 PRINT AT 6,8;"% % % % "
697 PRINT AT 7,8;"% % % % "
699 PRINT AT 8,8;"% % % % % % % % % % % % "
701 PRINT AT 9,8;"% % % "
703 PRINT AT 10,8;"% % % % % % % % % "
705 PRINT AT 12,12;"PRESENT"
707 PRINT AT 15,11;"%C%O%M%P%O%S%E%R% "
709 FOR F=1 TO 15
711 NEXT F
713 CLS
714 RETURN
715 PRINT AT 20,2;"START TAPE AND PRESS ""G"""
716 IF INKEY$<>"G" THEN GOTO 716
717 PRINT AT 20,2;" "
718 SAVE "COMPOSE%R"
719 GOTO 0203
721 SAVE "COMPOSE%R"
723 GOTO 1People
Composer
Program Analysis
Program Structure
Machine Code Routine (REM Line 1)
\FF\10\CC\01\2A\82\40\ED\5B\84\40\01\00\00\E5\D3\FF\CD\A2\40\DB\FE\CD\A2\40\E1\B7\ED\52\30\EF\C9\2A\84\40\37\ED\42\30\FB\C9USR 16518 (line 29 / 155 / 169), which points two bytes past the start of the BASIC line header into the REM body. The three parameters are POKEd into fixed addresses before the call:Address Variable Purpose 16515 D (duration × 16) Note duration 16516 Low byte of timer Speaker half-period low 16517 High byte of timer Speaker half-period high OUT (0FFH),A instruction (D3 FF) to toggle the ZX81 speaker port and a delay loop keyed to the 16-bit timer value to set pitch.Frequency Precomputation
F = INT(1 / (F_hz × 17E-6) + 0.5)O$(J,N,1..2); interpolated semitone (midpoint) values are stored in R$(J,N,1..2) via the subroutine at lines 7–27, which averages adjacent timer values.Note and Duration Encoding
N$ — note names (A–G); semitones stored as inverse characters (e.g., %E), which have character codes above 128, detected at line 141 (IF N<=128).X$ — octave per note, encoded as a digit character; decoded with CODE X$(X)-28.D$ — duration per note, encoded similarly with CODE D$(X)-28, then multiplied by 16 at line 140.DIM / copy idiom (lines 307–325): the old contents are saved into a temporary string L$, the array is re-dimensioned one element larger, and the contents are restored with a slice assignment.Staff Display
"-" characters at odd screen rows 1, 3, 5, 7, 9; spaces at even rows and row 11 represent spaces between lines and ledger areas. The array V$(row, col) tracks the current character at each cell so notes can be restored when the cursor moves away. The cursor is displayed as a block graphic character (\##, a solid 2×2 block). When the cursor reaches column 30, the screen scrolls 19 lines and a new staff section is drawn.Key Bindings
Key Function 5 Move cursor left 8 Move cursor right / advance 6 Move cursor down (lower pitch) 7 Move cursor up (higher pitch) S Place natural note A Place semitone (sharp/flat) note R Play back the tune (FAST mode) Q Set repeat count P Shift all octaves up or down F Change overall playback speed D Change note duration O Change current octave M Save composition to tape Notable Techniques and Idioms
GOSUB 069Z at line 3 and GOTO 0037 at line 5 use leading-zero and trailing-letter forms of line numbers — the 069Z target does not exist as a reachable line in the normal flow and acts as a skip; the program entry for BASIC execution falls through to line 37 where data initialization begins after the precomputation jump at line 121 sends control to line 183.GO TO and GO SUB are written with leading zeros (e.g., GOTO 0019), a known memory optimization that stores the number as a string rather than a floating-point value in the token stream.CODE N$(X)-37 yields a value >128 for semitone note names stored as inverse characters.FOR delay loop scaled by the duration value when the decoded note index is negative.IF X<=LEN N$ THEN GOTO 0157) re-enters the playback loop mid-subroutine after a rest, rather than continuing the FOR X loop normally — an unusual flow-control trick to share the NEXT X at line 157.Bugs and Anomalies
"M" at line 215 jumps to line 715, bypassing line 216 which would have been the next sequential check. No functional bug since the jump is explicit.WAIT loop at line 207 (IF INKEY$="" THEN GOTO 0207) waits for a key to be pressed, but the subsequent key tests (lines 209–239) re-read INKEY$ without waiting, meaning a fast typist could miss checks if the key is released between lines. This is a standard ZX81 INKEY$ polling hazard.V$(11) at line 53 is dimensioned to 28 characters with a 30-character initializer — the extra two characters are silently truncated by BASIC.K$(Q+1) for certain staff positions (semitone rows), implicitly raising the octave index — this relies on K$="123456788" having a duplicate “8” at position 9 to avoid an out-of-bounds error at the top octave.Content
Source Code
1 REM \FF\10\CC\01\2A\82\40\ED\5B\84\40\01\00\00\E5\D3\FF\CD\A2\40\DB\FE\CD\A2\40\E1\B7\ED\52\30\EF\C9\2A\84\40\37\ED\42\30\FB\C9\20\22\23\25\1C
3 GOSUB 069Z
5 GOTO 0037
7 IF N=1 AND J>1 THEN GOTO 0033
9 IF N>1 THEN GOTO 0015
11 LET N=N+1
13 RETURN
15 LET F=256*CODE O$(J,N-1,1)+CODE O$(J,N-1,2)
17 LET F1=256*CODE O$(J,N,1)+CODE O$(J,N,2)
19 LET F=INT ((F1+F)/2+.5)
21 LET R$(J,N,2)=CHR$ (F-(INT (F/256)*256))
23 LET R$(J,N,1)=CHR$ (INT (F/256))
25 LET N=N+1
27 RETURN
29 LET C=USR 16518
31 RETURN
33 LET F1=256*CODE O$(J-1,N,1)+CODE O$(J-1,N,2)
35 GOTO 0019
37 DIM V$(11,28)
39 LET V$(1)="---------------------------"
41 FOR I=3 TO 9 STEP 2
43 LET V$(I)=V$(1)
45 NEXT I
47 FOR I=2 TO 10 STEP 2
49 LET V$(I)=" "
51 NEXT I
53 LET V$(11)=" "
55 DIM R$(8,7,2)
57 LET U=0
59 LET G=1
61 GOSUB 0383
63 DIM O$(8,7,2)
65 DIM X$(3)
67 DIM N$(3)
69 DIM D$(3)
71 FAST
73 FOR J=8 TO 1 STEP -1
75 LET N=1
77 LET F=INT ((26*2**(J-1)+2**(J-1)*(2/3))+.5)
79 GOSUB 0125
81 GOSUB 0007
83 LET F=INT (30*2**(J-1)+.5)
85 GOSUB 0125
87 GOSUB 0007
89 LET F=INT (32*2**(J-1)+.5)
91 GOSUB 0125
93 GOSUB 0007
95 LET F=INT (36*2**(J-1)+.5)
97 GOSUB 0125
99 GOSUB 0007
101 LET F=INT (40*2**(J-1)+.5)
103 GOSUB 0125
105 GOSUB 0007
107 LET F=INT ((42*2**(J-1)+2**(J-1)*(2/3))+.5)
109 GOSUB 0125
111 GOSUB 0007
113 LET F=INT (48*2**(J-1)+.5)
115 GOSUB 0125
117 GOSUB 0007
119 NEXT J
121 GOTO 0183
123 REM %F%O%R% %N%O%T%E%S%.
125 LET F=INT ((1/(F*17E-6))+.5)
127 LET O$(J,N,2)=CHR$ (F-(INT (F/256)*256))
129 LET O$(J,N,1)=CHR$ (INT (F/256))
131 RETURN
133 FOR X=1 TO LEN N$
135 LET N=CODE N$(X)-37
137 LET J=CODE X$(X)-28
139 LET D=(CODE D$(X)-28)*16
141 IF N<=128 THEN GOTO 0147
143 GOTO 0161
145 NEXT X
147 IF N<0 THEN GOTO 0175
149 POKE 16516,CODE O$(J,N,2)
151 POKE 16517,CODE O$(J,N,1)
153 POKE 16515,D
155 LET C=USR 16518
157 NEXT X
159 RETURN
161 LET N=N-128
163 POKE 16516,CODE R$(J,N,2)
165 POKE 16517,CODE R$(J,N,1)
167 POKE 16515,D
169 LET C=USR 16518
171 NEXT X
173 RETURN
175 FOR T=1 TO D*8
177 NEXT T
179 IF X<=LEN N$ THEN GOTO 0157
181 RETURN
183 REM %P%R%I%N%T%S% %B%A%R%S%.
185 FOR I=1 TO 30
187 PRINT AT 1,I;"-";AT 3,I;"-";AT 5,I;"-"
189 PRINT AT 7,I;"-";AT 9,I;"-"
191 NEXT I
193 FOR I=1 TO 9
195 PRINT AT I,1;"\: ";AT I,2;"\ :"
197 PRINT AT 14,1;"%D";AT 16,1;"%C%U%R%R%E%N%T %O%C%T%A%V%E%: "
199 NEXT I
201 SLOW
203 IF TT+T>=30 THEN GOTO 0457
205 PRINT AT A,TT+T;"\##"
207 IF INKEY$="" THEN GOTO 0207
209 IF INKEY$="O" THEN GOTO 0451
213 IF INKEY$="5" THEN GOTO 0243
215 IF INKEY$="M" THEN GOTO 715
217 IF INKEY$<>"A" THEN GOTO 0223
219 LET L=1
221 GOSUB 0307
223 IF INKEY$="Q" THEN GOSUB 0431
225 IF INKEY$="P" THEN GOSUB 0491
227 IF INKEY$="6" THEN GOSUB 0255
229 IF INKEY$="7" THEN GOSUB 0267
231 IF INKEY$="8" THEN GOTO 0279
233 IF INKEY$="D" THEN GOTO 0445
235 IF INKEY$="R" THEN GOSUB 0289
237 IF INKEY$="F" THEN GOSUB 0517
239 IF INKEY$="S" THEN GOSUB 0307
241 GOTO 0203
243 IF TT+T>3 THEN LET T=T-1
245 PRINT AT A,TT+T+1;V$(A,T+1)
247 PRINT AT A,TT+T;"\##"
249 LET PP=1
251 GOTO 0207
253 RETURN
255 PRINT AT A,TT+T;" "
257 IF A=10 OR A=11 OR A=2 OR A=4 OR A=6 OR A=8 THEN GOTO 0261
259 PRINT AT A,TT+T;"-"
261 IF A<11 THEN LET A=A+1
263 LET W=130
265 RETURN
267 PRINT AT A,TT+T;" "
269 IF A=10 OR A=11 OR A=2 OR A=4 OR A=6 OR A=8 THEN GOTO 0273
271 PRINT AT A,TT+T;"-"
273 IF A>1 THEN LET A=A-1
275 LET W=130
277 RETURN
279 PRINT AT A,TT+T;V$(A,T)
281 LET PP=0
283 IF TT+T<30 THEN LET T=T+1
285 PRINT AT A,TT+T;"\##"
287 GOTO 0207
289 FAST
291 LET U=T
293 GOSUB 0133
295 FOR I=1 TO REP-1
297 GOSUB 0133
299 NEXT I
301 SLOW
303 LET T=U
305 GOTO 0205
307 IF B+(T+1)<=LEN N$ THEN GOTO 0327
309 LET L$=N$
311 DIM N$(B+(T+1))
313 LET N$(1 TO LEN N$-2)=L$
315 LET L$=X$
317 DIM X$(B+(T+1))
319 LET X$(1 TO LEN X$-2)=L$
321 LET L$=D$
323 DIM D$(B+(T+1))
325 LET D$(1 TO LEN D$-2)=L$
327 IF L=1 THEN GOTO 0665
329 LET SEMI=6
331 IF A=1 THEN LET N$(B+(T))="F"
333 IF A=2 THEN LET N$(B+(T))="E"
335 IF A=3 THEN LET N$(B+(T))="D"
337 IF A=4 THEN LET N$(B+(T))="C"
339 IF A=5 THEN LET N$(B+T)="B"
341 IF A=6 THEN LET N$(B+T)="A"
343 IF A=7 THEN LET N$(B+T)="G"
345 IF A=8 THEN LET N$(B+T)="F"
347 IF A=9 THEN LET N$(B+T)="E"
349 IF A=10 THEN LET N$(B+T)="D"
351 IF A=11 THEN LET N$(B+(T))="C"
353 LET V$(A,T)="$"
355 LET D$(B+(T))=J$(G)
357 LET X$(B+(T))=K$(Q)
359 IF A=SEMI OR A=5 OR A=2 OR A=1 OR A=3 OR A=4 THEN LET X$(B+(T))=K$(Q+1)
361 IF PP=0 THEN GOTO 0371
363 FOR I=1 TO 11
365 IF I=1 OR I=3 OR I=5 OR I=7 OR I=9 THEN PRINT AT I,TT+T;"-"
367 IF I=2 OR I=4 OR I=6 OR I=8 OR I=10 OR I=11 THEN PRINT AT I,TT+T;" "
369 NEXT I
371 PRINT AT A,TT+T;V$(A,T)
373 LET T=T+1
375 PRINT AT 14,(TT+T)-1;G
377 PRINT AT 16,16;Q
379 RETURN
381 REM %I%N%I%T%I%A%L%I%Z%A%T%I%O%N% %S%U%B
383 PRINT AT 10,2;"INPUT NOTE DURATION(1 TO 8)"
385 LET B=0
387 LET L=0
389 LET Q=0
391 LET W=128
393 LET H=9000
395 LET A=11
397 LET C=1
399 LET T=1
401 LET TT=2
403 LET PP=0
405 LET REP=0
407 IF INKEY$="" THEN GOTO 0407
409 LET G=VAL INKEY$
411 LET J$="12345678"
413 CLS
415 PRINT AT 10,5;"INPUT OCTAVE(1 TO 6)"
417 IF INKEY$="" THEN GOTO 0417
419 LET Y=(VAL INKEY$)+1
421 LET K$="123456788"
423 LET Q=VAL K$(Y+1)
425 CLS
427 RETURN
429 REM %S%U%B% %T%O% %R%E%P%E%A%T% %T%U%N%E%.
431 PRINT AT 18,1;" YOU WANT THE TUNE TO REPEAT..."
433 PRINT AT 20,5;"HOW MANY TIMES?"
435 INPUT Y
437 LET REP=Y
439 PRINT AT 18,1;" "
441 PRINT AT 20,1;" "
443 RETURN
445 INPUT Y
447 IF Y>0 AND Y<9 THEN LET G=Y
449 GOTO 0205
451 INPUT Y
453 IF Y>0 AND Y<9 THEN LET Q=Y
455 GOTO 0205
457 REM %S%U%B%.% %T%O% %P%R%I%N%T% %2%N%D% %S%E%T% %O%F% %B%A%R%S
459 FOR I=1 TO 19
461 SCROLL
463 NEXT I
465 LET A=11
467 LET B=B+T-1
469 LET T=1
471 LET V$(1)="-----------------------------"
473 LET V$(2)=" "
475 FOR I=1 TO 9 STEP 2
477 LET V$(I)=V$(1)
479 LET V$(I+1)=V$(2)
481 NEXT I
483 LET V$(11)=V$(2)
485 PRINT AT 14,1;"%D";AT 16,1;"%C%U%R%R%E%N%T %O%C%T%A%V%E%: "
487 GOTO 0183
489 REM %S%U%B% %T%O% %C%H%A%N%G%E% %A%L%L% %O%C%T%A%V%E%S\:
491 PRINT AT 20,2;"INCREASE OR DECREASE?"
493 LET P$="12345678"
495 INPUT Y
497 FOR S=1 TO LEN X$-1
499 LET V=VAL X$(S)
501 IF Y=7 AND V<8 THEN LET V=V+1
503 IF Y=6 AND V>1 THEN LET V=V-1
505 LET X$(S)=P$(V)
507 NEXT S
509 PRINT AT 16,16;X$(LEN X$-1)
511 PRINT AT 20,2;" "
513 RETURN
515 REM %S%U%B% %T%O% %C%H%A%N%G%E% %O%V%E%R%A%L%L% % % %S%P%E%E%D% %O%F% %P%L%A%Y%.
517 PRINT AT 20,2;"INCREASE OR DECREASE?"
519 INPUT Y
521 FOR S=1 TO LEN D$-1
523 LET V=VAL D$(S)
525 IF Y=7 AND V<9 THEN LET V=V+1
527 IF Y=6 AND V>1 THEN LET V=V-1
529 LET D$(S)=J$(V)
531 NEXT S
533 PRINT AT 14,3;D$(B+1 TO )
535 PRINT AT 20,2;" "
537 RETURN
663 REM %S%E%M%I%T%O%N%E% %S%U%B%.
665 IF A=1 THEN LET N$(B+T)="F"
667 IF A=2 THEN LET N$(B+T)="%E"
669 IF A=3 THEN LET N$(B+T)="%D"
671 IF A=4 THEN LET N$(B+T)="%C"
673 IF A=5 THEN LET N$(B+T)="%B"
675 IF A=6 THEN LET N$(B+T)="%A"
677 IF A=7 THEN LET N$(B+T)="%G"
679 IF A=8 THEN LET N$(B+T)="%F"
681 IF A=9 THEN LET N$(B+T)="%E"
683 IF A=10 THEN LET N$(B+T)="%D"
685 IF A=11 THEN LET N$(B+T)="%C"
687 LET L=0
689 LET SEMI=5
691 GOTO 0353
693 PRINT AT 5,8;"% % % % % % % % % % % % "
695 PRINT AT 6,8;"% % % % "
697 PRINT AT 7,8;"% % % % "
699 PRINT AT 8,8;"% % % % % % % % % % % % "
701 PRINT AT 9,8;"% % % "
703 PRINT AT 10,8;"% % % % % % % % % "
705 PRINT AT 12,12;"PRESENT"
707 PRINT AT 15,11;"%C%O%M%P%O%S%E%R% "
709 FOR F=1 TO 15
711 NEXT F
713 CLS
714 RETURN
715 PRINT AT 20,2;"START TAPE AND PRESS ""G"""
716 IF INKEY$<>"G" THEN GOTO 716
717 PRINT AT 20,2;" "
718 SAVE "COMPOSE%R"
719 GOTO 0203
721 SAVE "COMPOSE%R"
723 GOTO 1People
This routine is entered via USR 16518 (line 29 / 155 / 169), which points two bytes past the start of the BASIC line header into the REM body. The three parameters are POKEd into fixed addresses before the call:
| Address | Variable | Purpose |
|---|---|---|
| 16515 | D (duration × 16) | Note duration |
| 16516 | Low byte of timer | Speaker half-period low |
| 16517 | High byte of timer | Speaker half-period high |
The routine uses the Z80 OUT (0FFH),A instruction (D3 FF) to toggle the ZX81 speaker port and a delay loop keyed to the 16-bit timer value to set pitch.
Frequency Precomputation
Lines 73–119 precompute speaker timer values for seven notes across eight octaves (J=8 down to 1, higher J = lower octave). The formula at line 125 converts a frequency in Hz to a loop-count timer:
F = INT(1 / (F_hz × 17E-6) + 0.5)
The seven base frequencies used (relative multiples) are 26⅔, 30, 32, 36, 40, 42⅔, and 48 — corresponding approximately to D, E, F, G, A, B♭, and C in a just-intonation-adjacent scheme, scaled by powers of two for each octave.
Natural notes are stored in O$(J,N,1..2); interpolated semitone (midpoint) values are stored in R$(J,N,1..2) via the subroutine at lines 7–27, which averages adjacent timer values.
Note and Duration Encoding
Three parallel string arrays hold the composition:
N$— note names (A–G); semitones stored as inverse characters (e.g.,%E), which have character codes above 128, detected at line 141 (IF N<=128).X$— octave per note, encoded as a digit character; decoded withCODE X$(X)-28.D$— duration per note, encoded similarly withCODE D$(X)-28, then multiplied by 16 at line 140.
The arrays are dynamically resized using a DIM / copy idiom (lines 307–325): the old contents are saved into a temporary string L$, the array is re-dimensioned one element larger, and the contents are restored with a slice assignment.
Staff Display
The five staff lines are represented by rows of "-" characters at odd screen rows 1, 3, 5, 7, 9; spaces at even rows and row 11 represent spaces between lines and ledger areas. The array V$(row, col) tracks the current character at each cell so notes can be restored when the cursor moves away. The cursor is displayed as a block graphic character (\##, a solid 2×2 block). When the cursor reaches column 30, the screen scrolls 19 lines and a new staff section is drawn.
Key Bindings
| Key | Function |
|---|---|
| 5 | Move cursor left |
| 8 | Move cursor right / advance |
| 6 | Move cursor down (lower pitch) |
| 7 | Move cursor up (higher pitch) |
| S | Place natural note |
| A | Place semitone (sharp/flat) note |
| R | Play back the tune (FAST mode) |
| Q | Set repeat count |
| P | Shift all octaves up or down |
| F | Change overall playback speed |
| D | Change note duration |
| O | Change current octave |
| M | Save composition to tape |
Notable Techniques and Idioms
- The
GOSUB 069Zat line 3 andGOTO 0037at line 5 use leading-zero and trailing-letter forms of line numbers — the069Ztarget does not exist as a reachable line in the normal flow and acts as a skip; the program entry for BASIC execution falls through to line 37 where data initialization begins after the precomputation jump at line 121 sends control to line 183. - All numeric literals in
GO TOandGO SUBare written with leading zeros (e.g.,GOTO 0019), a known memory optimization that stores the number as a string rather than a floating-point value in the token stream. - The semitone detection at line 141 exploits the fact that inverse-video characters in ZX81 BASIC have codes ≥128 (128 + normal code), so
CODE N$(X)-37yields a value >128 for semitone note names stored as inverse characters. - The silence/rest mechanism (line 147 branch to 175) uses a simple
FORdelay loop scaled by the duration value when the decoded note index is negative. - Line 179 (
IF X<=LEN N$ THEN GOTO 0157) re-enters the playback loop mid-subroutine after a rest, rather than continuing theFOR Xloop normally — an unusual flow-control trick to share theNEXT Xat line 157. - The splash/title screen code at lines 693–713 is never called from the main program flow visible in this listing; it appears to be a leftover or an entry point used only on initial tape load.
Bugs and Anomalies
- Line 215 is followed immediately by line 217, with line 216 absent — the key test for
"M"at line 215 jumps to line 715, bypassing line 216 which would have been the next sequential check. No functional bug since the jump is explicit. - The
WAITloop at line 207 (IF INKEY$="" THEN GOTO 0207) waits for a key to be pressed, but the subsequent key tests (lines 209–239) re-readINKEY$without waiting, meaning a fast typist could miss checks if the key is released between lines. This is a standard ZX81 INKEY$ polling hazard. - The
V$(11)at line 53 is dimensioned to 28 characters with a 30-character initializer — the extra two characters are silently truncated by BASIC. - Line 359 adjusts the octave stored for a note by using
K$(Q+1)for certain staff positions (semitone rows), implicitly raising the octave index — this relies onK$="123456788"having a duplicate “8” at position 9 to avoid an out-of-bounds error at the top octave.
Content
Source Code
1 REM \FF\CC itemtype='https://schema.org/Blog' itemscope='itemscope' class="wp-singular computer_media-template-default single single-computer_media postid-56670 wp-custom-logo wp-embed-responsive wp-theme-astra wp-child-theme-astra-child ast-desktop ast-separate-container ast-left-sidebar astra-4.12.7 group-blog ast-blog-single-style-1 ast-custom-post-type ast-single-post ast-inherit-site-logo-transparent ast-hfb-header ast-full-width-primary-header ast-box-layout ast-normal-title-enabled astra-addon-4.12.5"
Skip to content
Composer
This program is a music composition utility that displays a staff-like grid on screen, allowing the user to place notes using cursor keys and save the resulting tune. It precomputes note frequencies for eight octaves across seven notes (A through G, including semitones) using the ZX81’s speaker timer formula F = INT(1/(f × 17×10⁻⁶) + 0.5), storing two-byte timer values in the arrays O$ and R$ for natural and sharp/flat notes respectively. A short machine-code routine embedded in REM line 1 drives the speaker directly, called via USR 16518, with timer and duration values POKEd into addresses 16515–16517. The composer supports variable octave selection, note duration, repeat playback, speed adjustment, and scrolling the staff when the cursor reaches the right edge.
Program Analysis
Program Structure
The program is divided into several functional regions:
- Lines 1–131: Machine code (REM line 1), initialization entry points, note interpolation subroutines, and frequency precomputation.
- Lines 133–181: Playback engine — iterates over the note/octave/duration string arrays and calls the machine code driver.
- Lines 183–241: Main loop — draws the staff, handles keyboard input, dispatches to editing subroutines.
- Lines 243–287: Cursor movement (left/right, up/down across staff lines).
- Lines 289–379: Note entry, array resizing, display update.
- Lines 381–427: Initialization subroutine — prompts for duration and octave, zeros variables.
- Lines 429–537: Auxiliary subroutines: repeat, duration change, octave shift, speed change.
- Lines 663–691: Semitone entry branch (shares tail of note-entry subroutine).
- Lines 693–721: Title/splash screen and SAVE routines.
Machine Code Routine (REM Line 1)
The entire sound driver is stored as raw bytes in the REM statement at line 1. The bytes are:
\FF\10\CC\01\2A\82\40\ED\5B\84\40\01\00\00\E5\D3\FF\CD\A2\40\DB\FE\CD\A2\40\E1\B7\ED\52\30\EF\C9\2A\84\40\37\ED\42\30\FB\C9
This routine is entered via USR 16518 (line 29 / 155 / 169), which points two bytes past the start of the BASIC line header into the REM body. The three parameters are POKEd into fixed addresses before the call:
Address Variable Purpose 16515 D (duration × 16) Note duration 16516 Low byte of timer Speaker half-period low 16517 High byte of timer Speaker half-period high
The routine uses the Z80 OUT (0FFH),A instruction (D3 FF) to toggle the ZX81 speaker port and a delay loop keyed to the 16-bit timer value to set pitch.
Frequency Precomputation
Lines 73–119 precompute speaker timer values for seven notes across eight octaves (J=8 down to 1, higher J = lower octave). The formula at line 125 converts a frequency in Hz to a loop-count timer:
F = INT(1 / (F_hz × 17E-6) + 0.5)
The seven base frequencies used (relative multiples) are 26⅔, 30, 32, 36, 40, 42⅔, and 48 — corresponding approximately to D, E, F, G, A, B♭, and C in a just-intonation-adjacent scheme, scaled by powers of two for each octave.
Natural notes are stored in O$(J,N,1..2); interpolated semitone (midpoint) values are stored in R$(J,N,1..2) via the subroutine at lines 7–27, which averages adjacent timer values.
Note and Duration Encoding
Three parallel string arrays hold the composition:
N$ — note names (A–G); semitones stored as inverse characters (e.g., %E), which have character codes above 128, detected at line 141 (IF N<=128).
X$ — octave per note, encoded as a digit character; decoded with CODE X$(X)-28.
D$ — duration per note, encoded similarly with CODE D$(X)-28, then multiplied by 16 at line 140.
The arrays are dynamically resized using a DIM / copy idiom (lines 307–325): the old contents are saved into a temporary string L$, the array is re-dimensioned one element larger, and the contents are restored with a slice assignment.
Staff Display
The five staff lines are represented by rows of "-" characters at odd screen rows 1, 3, 5, 7, 9; spaces at even rows and row 11 represent spaces between lines and ledger areas. The array V$(row, col) tracks the current character at each cell so notes can be restored when the cursor moves away. The cursor is displayed as a block graphic character (\##, a solid 2×2 block). When the cursor reaches column 30, the screen scrolls 19 lines and a new staff section is drawn.
Key Bindings
Key Function 5 Move cursor left 8 Move cursor right / advance 6 Move cursor down (lower pitch) 7 Move cursor up (higher pitch) S Place natural note A Place semitone (sharp/flat) note R Play back the tune (FAST mode) Q Set repeat count P Shift all octaves up or down F Change overall playback speed D Change note duration O Change current octave M Save composition to tape
Notable Techniques and Idioms
- The
GOSUB 069Z at line 3 and GOTO 0037 at line 5 use leading-zero and trailing-letter forms of line numbers — the 069Z target does not exist as a reachable line in the normal flow and acts as a skip; the program entry for BASIC execution falls through to line 37 where data initialization begins after the precomputation jump at line 121 sends control to line 183.
- All numeric literals in
GO TO and GO SUB are written with leading zeros (e.g., GOTO 0019), a known memory optimization that stores the number as a string rather than a floating-point value in the token stream.
- The semitone detection at line 141 exploits the fact that inverse-video characters in ZX81 BASIC have codes ≥128 (128 + normal code), so
CODE N$(X)-37 yields a value >128 for semitone note names stored as inverse characters.
- The silence/rest mechanism (line 147 branch to 175) uses a simple
FOR delay loop scaled by the duration value when the decoded note index is negative.
- Line 179 (
IF X<=LEN N$ THEN GOTO 0157) re-enters the playback loop mid-subroutine after a rest, rather than continuing the FOR X loop normally — an unusual flow-control trick to share the NEXT X at line 157.
- The splash/title screen code at lines 693–713 is never called from the main program flow visible in this listing; it appears to be a leftover or an entry point used only on initial tape load.
Bugs and Anomalies
- Line 215 is followed immediately by line 217, with line 216 absent — the key test for
"M" at line 215 jumps to line 715, bypassing line 216 which would have been the next sequential check. No functional bug since the jump is explicit.
- The
WAIT loop at line 207 (IF INKEY$="" THEN GOTO 0207) waits for a key to be pressed, but the subsequent key tests (lines 209–239) re-read INKEY$ without waiting, meaning a fast typist could miss checks if the key is released between lines. This is a standard ZX81 INKEY$ polling hazard.
- The
V$(11) at line 53 is dimensioned to 28 characters with a 30-character initializer — the extra two characters are silently truncated by BASIC.
- Line 359 adjusts the octave stored for a note by using
K$(Q+1) for certain staff positions (semitone rows), implicitly raising the octave index — this relies on K$="123456788" having a duplicate “8” at position 9 to avoid an out-of-bounds error at the top octave.
Content
Source Code
1 REM \FF\10\CC\01\2A\82\40\ED\5B\84\40\01\00\00\E5\D3\FF\CD\A2\40\DB\FE\CD\A2\40\E1\B7\ED\52\30\EF\C9\2A\84\40\37\ED\42\30\FB\C9\20\22\23\25\1C
3 GOSUB 069Z
5 GOTO 0037
7 IF N=1 AND J>1 THEN GOTO 0033
9 IF N>1 THEN GOTO 0015
11 LET N=N+1
13 RETURN
15 LET F=256*CODE O$(J,N-1,1)+CODE O$(J,N-1,2)
17 LET F1=256*CODE O$(J,N,1)+CODE O$(J,N,2)
19 LET F=INT ((F1+F)/2+.5)
21 LET R$(J,N,2)=CHR$ (F-(INT (F/256)*256))
23 LET R$(J,N,1)=CHR$ (INT (F/256))
25 LET N=N+1
27 RETURN
29 LET C=USR 16518
31 RETURN
33 LET F1=256*CODE O$(J-1,N,1)+CODE O$(J-1,N,2)
35 GOTO 0019
37 DIM V$(11,28)
39 LET V$(1)="---------------------------"
41 FOR I=3 TO 9 STEP 2
43 LET V$(I)=V$(1)
45 NEXT I
47 FOR I=2 TO 10 STEP 2
49 LET V$(I)=" "
51 NEXT I
53 LET V$(11)=" "
55 DIM R$(8,7,2)
57 LET U=0
59 LET G=1
61 GOSUB 0383
63 DIM O$(8,7,2)
65 DIM X$(3)
67 DIM N$(3)
69 DIM D$(3)
71 FAST
73 FOR J=8 TO 1 STEP -1
75 LET N=1
77 LET F=INT ((26*2**(J-1)+2**(J-1)*(2/3))+.5)
79 GOSUB 0125
81 GOSUB 0007
83 LET F=INT (30*2**(J-1)+.5)
85 GOSUB 0125
87 GOSUB 0007
89 LET F=INT (32*2**(J-1)+.5)
91 GOSUB 0125
93 GOSUB 0007
95 LET F=INT (36*2**(J-1)+.5)
97 GOSUB 0125
99 GOSUB 0007
101 LET F=INT (40*2**(J-1)+.5)
103 GOSUB 0125
105 GOSUB 0007
107 LET F=INT ((42*2**(J-1)+2**(J-1)*(2/3))+.5)
109 GOSUB 0125
111 GOSUB 0007
113 LET F=INT (48*2**(J-1)+.5)
115 GOSUB 0125
117 GOSUB 0007
119 NEXT J
121 GOTO 0183
123 REM %F%O%R% %N%O%T%E%S%.
125 LET F=INT ((1/(F*17E-6))+.5)
127 LET O$(J,N,2)=CHR$ (F-(INT (F/256)*256))
129 LET O$(J,N,1)=CHR$ (INT (F/256))
131 RETURN
133 FOR X=1 TO LEN N$
135 LET N=CODE N$(X)-37
137 LET J=CODE X$(X)-28
139 LET D=(CODE D$(X)-28)*16
141 IF N<=128 THEN GOTO 0147
143 GOTO 0161
145 NEXT X
147 IF N<0 THEN GOTO 0175
149 POKE 16516,CODE O$(J,N,2)
151 POKE 16517,CODE O$(J,N,1)
153 POKE 16515,D
155 LET C=USR 16518
157 NEXT X
159 RETURN
161 LET N=N-128
163 POKE 16516,CODE R$(J,N,2)
165 POKE 16517,CODE R$(J,N,1)
167 POKE 16515,D
169 LET C=USR 16518
171 NEXT X
173 RETURN
175 FOR T=1 TO D*8
177 NEXT T
179 IF X<=LEN N$ THEN GOTO 0157
181 RETURN
183 REM %P%R%I%N%T%S% %B%A%R%S%.
185 FOR I=1 TO 30
187 PRINT AT 1,I;"-";AT 3,I;"-";AT 5,I;"-"
189 PRINT AT 7,I;"-";AT 9,I;"-"
191 NEXT I
193 FOR I=1 TO 9
195 PRINT AT I,1;"\: ";AT I,2;"\ :"
197 PRINT AT 14,1;"%D";AT 16,1;"%C%U%R%R%E%N%T %O%C%T%A%V%E%: "
199 NEXT I
201 SLOW
203 IF TT+T>=30 THEN GOTO 0457
205 PRINT AT A,TT+T;"\##"
207 IF INKEY$="" THEN GOTO 0207
209 IF INKEY$="O" THEN GOTO 0451
213 IF INKEY$="5" THEN GOTO 0243
215 IF INKEY$="M" THEN GOTO 715
217 IF INKEY$<>"A" THEN GOTO 0223
219 LET L=1
221 GOSUB 0307
223 IF INKEY$="Q" THEN GOSUB 0431
225 IF INKEY$="P" THEN GOSUB 0491
227 IF INKEY$="6" THEN GOSUB 0255
229 IF INKEY$="7" THEN GOSUB 0267
231 IF INKEY$="8" THEN GOTO 0279
233 IF INKEY$="D" THEN GOTO 0445
235 IF INKEY$="R" THEN GOSUB 0289
237 IF INKEY$="F" THEN GOSUB 0517
239 IF INKEY$="S" THEN GOSUB 0307
241 GOTO 0203
243 IF TT+T>3 THEN LET T=T-1
245 PRINT AT A,TT+T+1;V$(A,T+1)
247 PRINT AT A,TT+T;"\##"
249 LET PP=1
251 GOTO 0207
253 RETURN
255 PRINT AT A,TT+T;" "
257 IF A=10 OR A=11 OR A=2 OR A=4 OR A=6 OR A=8 THEN GOTO 0261
259 PRINT AT A,TT+T;"-"
261 IF A<11 THEN LET A=A+1
263 LET W=130
265 RETURN
267 PRINT AT A,TT+T;" "
269 IF A=10 OR A=11 OR A=2 OR A=4 OR A=6 OR A=8 THEN GOTO 0273
271 PRINT AT A,TT+T;"-"
273 IF A>1 THEN LET A=A-1
275 LET W=130
277 RETURN
279 PRINT AT A,TT+T;V$(A,T)
281 LET PP=0
283 IF TT+T<30 THEN LET T=T+1
285 PRINT AT A,TT+T;"\##"
287 GOTO 0207
289 FAST
291 LET U=T
293 GOSUB 0133
295 FOR I=1 TO REP-1
297 GOSUB 0133
299 NEXT I
301 SLOW
303 LET T=U
305 GOTO 0205
307 IF B+(T+1)<=LEN N$ THEN GOTO 0327
309 LET L$=N$
311 DIM N$(B+(T+1))
313 LET N$(1 TO LEN N$-2)=L$
315 LET L$=X$
317 DIM X$(B+(T+1))
319 LET X$(1 TO LEN X$-2)=L$
321 LET L$=D$
323 DIM D$(B+(T+1))
325 LET D$(1 TO LEN D$-2)=L$
327 IF L=1 THEN GOTO 0665
329 LET SEMI=6
331 IF A=1 THEN LET N$(B+(T))="F"
333 IF A=2 THEN LET N$(B+(T))="E"
335 IF A=3 THEN LET N$(B+(T))="D"
337 IF A=4 THEN LET N$(B+(T))="C"
339 IF A=5 THEN LET N$(B+T)="B"
341 IF A=6 THEN LET N$(B+T)="A"
343 IF A=7 THEN LET N$(B+T)="G"
345 IF A=8 THEN LET N$(B+T)="F"
347 IF A=9 THEN LET N$(B+T)="E"
349 IF A=10 THEN LET N$(B+T)="D"
351 IF A=11 THEN LET N$(B+(T))="C"
353 LET V$(A,T)="$"
355 LET D$(B+(T))=J$(G)
357 LET X$(B+(T))=K$(Q)
359 IF A=SEMI OR A=5 OR A=2 OR A=1 OR A=3 OR A=4 THEN LET X$(B+(T))=K$(Q+1)
361 IF PP=0 THEN GOTO 0371
363 FOR I=1 TO 11
365 IF I=1 OR I=3 OR I=5 OR I=7 OR I=9 THEN PRINT AT I,TT+T;"-"
367 IF I=2 OR I=4 OR I=6 OR I=8 OR I=10 OR I=11 THEN PRINT AT I,TT+T;" "
369 NEXT I
371 PRINT AT A,TT+T;V$(A,T)
373 LET T=T+1
375 PRINT AT 14,(TT+T)-1;G
377 PRINT AT 16,16;Q
379 RETURN
381 REM %I%N%I%T%I%A%L%I%Z%A%T%I%O%N% %S%U%B
383 PRINT AT 10,2;"INPUT NOTE DURATION(1 TO 8)"
385 LET B=0
387 LET L=0
389 LET Q=0
391 LET W=128
393 LET H=9000
395 LET A=11
397 LET C=1
399 LET T=1
401 LET TT=2
403 LET PP=0
405 LET REP=0
407 IF INKEY$="" THEN GOTO 0407
409 LET G=VAL INKEY$
411 LET J$="12345678"
413 CLS
415 PRINT AT 10,5;"INPUT OCTAVE(1 TO 6)"
417 IF INKEY$="" THEN GOTO 0417
419 LET Y=(VAL INKEY$)+1
421 LET K$="123456788"
423 LET Q=VAL K$(Y+1)
425 CLS
427 RETURN
429 REM %S%U%B% %T%O% %R%E%P%E%A%T% %T%U%N%E%.
431 PRINT AT 18,1;" YOU WANT THE TUNE TO REPEAT..."
433 PRINT AT 20,5;"HOW MANY TIMES?"
435 INPUT Y
437 LET REP=Y
439 PRINT AT 18,1;" "
441 PRINT AT 20,1;" "
443 RETURN
445 INPUT Y
447 IF Y>0 AND Y<9 THEN LET G=Y
449 GOTO 0205
451 INPUT Y
453 IF Y>0 AND Y<9 THEN LET Q=Y
455 GOTO 0205
457 REM %S%U%B%.% %T%O% %P%R%I%N%T% %2%N%D% %S%E%T% %O%F% %B%A%R%S
459 FOR I=1 TO 19
461 SCROLL
463 NEXT I
465 LET A=11
467 LET B=B+T-1
469 LET T=1
471 LET V$(1)="-----------------------------"
473 LET V$(2)=" "
475 FOR I=1 TO 9 STEP 2
477 LET V$(I)=V$(1)
479 LET V$(I+1)=V$(2)
481 NEXT I
483 LET V$(11)=V$(2)
485 PRINT AT 14,1;"%D";AT 16,1;"%C%U%R%R%E%N%T %O%C%T%A%V%E%: "
487 GOTO 0183
489 REM %S%U%B% %T%O% %C%H%A%N%G%E% %A%L%L% %O%C%T%A%V%E%S\:
491 PRINT AT 20,2;"INCREASE OR DECREASE?"
493 LET P$="12345678"
495 INPUT Y
497 FOR S=1 TO LEN X$-1
499 LET V=VAL X$(S)
501 IF Y=7 AND V<8 THEN LET V=V+1
503 IF Y=6 AND V>1 THEN LET V=V-1
505 LET X$(S)=P$(V)
507 NEXT S
509 PRINT AT 16,16;X$(LEN X$-1)
511 PRINT AT 20,2;" "
513 RETURN
515 REM %S%U%B% %T%O% %C%H%A%N%G%E% %O%V%E%R%A%L%L% % % %S%P%E%E%D% %O%F% %P%L%A%Y%.
517 PRINT AT 20,2;"INCREASE OR DECREASE?"
519 INPUT Y
521 FOR S=1 TO LEN D$-1
523 LET V=VAL D$(S)
525 IF Y=7 AND V<9 THEN LET V=V+1
527 IF Y=6 AND V>1 THEN LET V=V-1
529 LET D$(S)=J$(V)
531 NEXT S
533 PRINT AT 14,3;D$(B+1 TO )
535 PRINT AT 20,2;" "
537 RETURN
663 REM %S%E%M%I%T%O%N%E% %S%U%B%.
665 IF A=1 THEN LET N$(B+T)="F"
667 IF A=2 THEN LET N$(B+T)="%E"
669 IF A=3 THEN LET N$(B+T)="%D"
671 IF A=4 THEN LET N$(B+T)="%C"
673 IF A=5 THEN LET N$(B+T)="%B"
675 IF A=6 THEN LET N$(B+T)="%A"
677 IF A=7 THEN LET N$(B+T)="%G"
679 IF A=8 THEN LET N$(B+T)="%F"
681 IF A=9 THEN LET N$(B+T)="%E"
683 IF A=10 THEN LET N$(B+T)="%D"
685 IF A=11 THEN LET N$(B+T)="%C"
687 LET L=0
689 LET SEMI=5
691 GOTO 0353
693 PRINT AT 5,8;"% % % % % % % % % % % % "
695 PRINT AT 6,8;"% % % % "
697 PRINT AT 7,8;"% % % % "
699 PRINT AT 8,8;"% % % % % % % % % % % % "
701 PRINT AT 9,8;"% % % "
703 PRINT AT 10,8;"% % % % % % % % % "
705 PRINT AT 12,12;"PRESENT"
707 PRINT AT 15,11;"%C%O%M%P%O%S%E%R% "
709 FOR F=1 TO 15
711 NEXT F
713 CLS
714 RETURN
715 PRINT AT 20,2;"START TAPE AND PRESS ""G"""
716 IF INKEY$<>"G" THEN GOTO 716
717 PRINT AT 20,2;" "
718 SAVE "COMPOSE%R"
719 GOTO 0203
721 SAVE "COMPOSE%R"
723 GOTO 1
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
People
No people associated with this content.
A\EDB itemtype='https://schema.org/Blog' itemscope='itemscope' class="wp-singular computer_media-template-default single single-computer_media postid-56670 wp-custom-logo wp-embed-responsive wp-theme-astra wp-child-theme-astra-child ast-desktop ast-separate-container ast-left-sidebar astra-4.12.7 group-blog ast-blog-single-style-1 ast-custom-post-type ast-single-post ast-inherit-site-logo-transparent ast-hfb-header ast-full-width-primary-header ast-box-layout ast-normal-title-enabled astra-addon-4.12.5"
Skip to content
Composer
This program is a music composition utility that displays a staff-like grid on screen, allowing the user to place notes using cursor keys and save the resulting tune. It precomputes note frequencies for eight octaves across seven notes (A through G, including semitones) using the ZX81’s speaker timer formula F = INT(1/(f × 17×10⁻⁶) + 0.5), storing two-byte timer values in the arrays O$ and R$ for natural and sharp/flat notes respectively. A short machine-code routine embedded in REM line 1 drives the speaker directly, called via USR 16518, with timer and duration values POKEd into addresses 16515–16517. The composer supports variable octave selection, note duration, repeat playback, speed adjustment, and scrolling the staff when the cursor reaches the right edge.
Program Analysis
Program Structure
The program is divided into several functional regions:
- Lines 1–131: Machine code (REM line 1), initialization entry points, note interpolation subroutines, and frequency precomputation.
- Lines 133–181: Playback engine — iterates over the note/octave/duration string arrays and calls the machine code driver.
- Lines 183–241: Main loop — draws the staff, handles keyboard input, dispatches to editing subroutines.
- Lines 243–287: Cursor movement (left/right, up/down across staff lines).
- Lines 289–379: Note entry, array resizing, display update.
- Lines 381–427: Initialization subroutine — prompts for duration and octave, zeros variables.
- Lines 429–537: Auxiliary subroutines: repeat, duration change, octave shift, speed change.
- Lines 663–691: Semitone entry branch (shares tail of note-entry subroutine).
- Lines 693–721: Title/splash screen and SAVE routines.
Machine Code Routine (REM Line 1)
The entire sound driver is stored as raw bytes in the REM statement at line 1. The bytes are:
\FF\10\CC\01\2A\82\40\ED\5B\84\40\01\00\00\E5\D3\FF\CD\A2\40\DB\FE\CD\A2\40\E1\B7\ED\52\30\EF\C9\2A\84\40\37\ED\42\30\FB\C9
This routine is entered via USR 16518 (line 29 / 155 / 169), which points two bytes past the start of the BASIC line header into the REM body. The three parameters are POKEd into fixed addresses before the call:
Address Variable Purpose 16515 D (duration × 16) Note duration 16516 Low byte of timer Speaker half-period low 16517 High byte of timer Speaker half-period high
The routine uses the Z80 OUT (0FFH),A instruction (D3 FF) to toggle the ZX81 speaker port and a delay loop keyed to the 16-bit timer value to set pitch.
Frequency Precomputation
Lines 73–119 precompute speaker timer values for seven notes across eight octaves (J=8 down to 1, higher J = lower octave). The formula at line 125 converts a frequency in Hz to a loop-count timer:
F = INT(1 / (F_hz × 17E-6) + 0.5)
The seven base frequencies used (relative multiples) are 26⅔, 30, 32, 36, 40, 42⅔, and 48 — corresponding approximately to D, E, F, G, A, B♭, and C in a just-intonation-adjacent scheme, scaled by powers of two for each octave.
Natural notes are stored in O$(J,N,1..2); interpolated semitone (midpoint) values are stored in R$(J,N,1..2) via the subroutine at lines 7–27, which averages adjacent timer values.
Note and Duration Encoding
Three parallel string arrays hold the composition:
N$ — note names (A–G); semitones stored as inverse characters (e.g., %E), which have character codes above 128, detected at line 141 (IF N<=128).
X$ — octave per note, encoded as a digit character; decoded with CODE X$(X)-28.
D$ — duration per note, encoded similarly with CODE D$(X)-28, then multiplied by 16 at line 140.
The arrays are dynamically resized using a DIM / copy idiom (lines 307–325): the old contents are saved into a temporary string L$, the array is re-dimensioned one element larger, and the contents are restored with a slice assignment.
Staff Display
The five staff lines are represented by rows of "-" characters at odd screen rows 1, 3, 5, 7, 9; spaces at even rows and row 11 represent spaces between lines and ledger areas. The array V$(row, col) tracks the current character at each cell so notes can be restored when the cursor moves away. The cursor is displayed as a block graphic character (\##, a solid 2×2 block). When the cursor reaches column 30, the screen scrolls 19 lines and a new staff section is drawn.
Key Bindings
Key Function 5 Move cursor left 8 Move cursor right / advance 6 Move cursor down (lower pitch) 7 Move cursor up (higher pitch) S Place natural note A Place semitone (sharp/flat) note R Play back the tune (FAST mode) Q Set repeat count P Shift all octaves up or down F Change overall playback speed D Change note duration O Change current octave M Save composition to tape
Notable Techniques and Idioms
- The
GOSUB 069Z at line 3 and GOTO 0037 at line 5 use leading-zero and trailing-letter forms of line numbers — the 069Z target does not exist as a reachable line in the normal flow and acts as a skip; the program entry for BASIC execution falls through to line 37 where data initialization begins after the precomputation jump at line 121 sends control to line 183.
- All numeric literals in
GO TO and GO SUB are written with leading zeros (e.g., GOTO 0019), a known memory optimization that stores the number as a string rather than a floating-point value in the token stream.
- The semitone detection at line 141 exploits the fact that inverse-video characters in ZX81 BASIC have codes ≥128 (128 + normal code), so
CODE N$(X)-37 yields a value >128 for semitone note names stored as inverse characters.
- The silence/rest mechanism (line 147 branch to 175) uses a simple
FOR delay loop scaled by the duration value when the decoded note index is negative.
- Line 179 (
IF X<=LEN N$ THEN GOTO 0157) re-enters the playback loop mid-subroutine after a rest, rather than continuing the FOR X loop normally — an unusual flow-control trick to share the NEXT X at line 157.
- The splash/title screen code at lines 693–713 is never called from the main program flow visible in this listing; it appears to be a leftover or an entry point used only on initial tape load.
Bugs and Anomalies
- Line 215 is followed immediately by line 217, with line 216 absent — the key test for
"M" at line 215 jumps to line 715, bypassing line 216 which would have been the next sequential check. No functional bug since the jump is explicit.
- The
WAIT loop at line 207 (IF INKEY$="" THEN GOTO 0207) waits for a key to be pressed, but the subsequent key tests (lines 209–239) re-read INKEY$ without waiting, meaning a fast typist could miss checks if the key is released between lines. This is a standard ZX81 INKEY$ polling hazard.
- The
V$(11) at line 53 is dimensioned to 28 characters with a 30-character initializer — the extra two characters are silently truncated by BASIC.
- Line 359 adjusts the octave stored for a note by using
K$(Q+1) for certain staff positions (semitone rows), implicitly raising the octave index — this relies on K$="123456788" having a duplicate “8” at position 9 to avoid an out-of-bounds error at the top octave.
Content
Source Code
1 REM \FF\10\CC\01\2A\82\40\ED\5B\84\40\01\00\00\E5\D3\FF\CD\A2\40\DB\FE\CD\A2\40\E1\B7\ED\52\30\EF\C9\2A\84\40\37\ED\42\30\FB\C9\20\22\23\25\1C
3 GOSUB 069Z
5 GOTO 0037
7 IF N=1 AND J>1 THEN GOTO 0033
9 IF N>1 THEN GOTO 0015
11 LET N=N+1
13 RETURN
15 LET F=256*CODE O$(J,N-1,1)+CODE O$(J,N-1,2)
17 LET F1=256*CODE O$(J,N,1)+CODE O$(J,N,2)
19 LET F=INT ((F1+F)/2+.5)
21 LET R$(J,N,2)=CHR$ (F-(INT (F/256)*256))
23 LET R$(J,N,1)=CHR$ (INT (F/256))
25 LET N=N+1
27 RETURN
29 LET C=USR 16518
31 RETURN
33 LET F1=256*CODE O$(J-1,N,1)+CODE O$(J-1,N,2)
35 GOTO 0019
37 DIM V$(11,28)
39 LET V$(1)="---------------------------"
41 FOR I=3 TO 9 STEP 2
43 LET V$(I)=V$(1)
45 NEXT I
47 FOR I=2 TO 10 STEP 2
49 LET V$(I)=" "
51 NEXT I
53 LET V$(11)=" "
55 DIM R$(8,7,2)
57 LET U=0
59 LET G=1
61 GOSUB 0383
63 DIM O$(8,7,2)
65 DIM X$(3)
67 DIM N$(3)
69 DIM D$(3)
71 FAST
73 FOR J=8 TO 1 STEP -1
75 LET N=1
77 LET F=INT ((26*2**(J-1)+2**(J-1)*(2/3))+.5)
79 GOSUB 0125
81 GOSUB 0007
83 LET F=INT (30*2**(J-1)+.5)
85 GOSUB 0125
87 GOSUB 0007
89 LET F=INT (32*2**(J-1)+.5)
91 GOSUB 0125
93 GOSUB 0007
95 LET F=INT (36*2**(J-1)+.5)
97 GOSUB 0125
99 GOSUB 0007
101 LET F=INT (40*2**(J-1)+.5)
103 GOSUB 0125
105 GOSUB 0007
107 LET F=INT ((42*2**(J-1)+2**(J-1)*(2/3))+.5)
109 GOSUB 0125
111 GOSUB 0007
113 LET F=INT (48*2**(J-1)+.5)
115 GOSUB 0125
117 GOSUB 0007
119 NEXT J
121 GOTO 0183
123 REM %F%O%R% %N%O%T%E%S%.
125 LET F=INT ((1/(F*17E-6))+.5)
127 LET O$(J,N,2)=CHR$ (F-(INT (F/256)*256))
129 LET O$(J,N,1)=CHR$ (INT (F/256))
131 RETURN
133 FOR X=1 TO LEN N$
135 LET N=CODE N$(X)-37
137 LET J=CODE X$(X)-28
139 LET D=(CODE D$(X)-28)*16
141 IF N<=128 THEN GOTO 0147
143 GOTO 0161
145 NEXT X
147 IF N<0 THEN GOTO 0175
149 POKE 16516,CODE O$(J,N,2)
151 POKE 16517,CODE O$(J,N,1)
153 POKE 16515,D
155 LET C=USR 16518
157 NEXT X
159 RETURN
161 LET N=N-128
163 POKE 16516,CODE R$(J,N,2)
165 POKE 16517,CODE R$(J,N,1)
167 POKE 16515,D
169 LET C=USR 16518
171 NEXT X
173 RETURN
175 FOR T=1 TO D*8
177 NEXT T
179 IF X<=LEN N$ THEN GOTO 0157
181 RETURN
183 REM %P%R%I%N%T%S% %B%A%R%S%.
185 FOR I=1 TO 30
187 PRINT AT 1,I;"-";AT 3,I;"-";AT 5,I;"-"
189 PRINT AT 7,I;"-";AT 9,I;"-"
191 NEXT I
193 FOR I=1 TO 9
195 PRINT AT I,1;"\: ";AT I,2;"\ :"
197 PRINT AT 14,1;"%D";AT 16,1;"%C%U%R%R%E%N%T %O%C%T%A%V%E%: "
199 NEXT I
201 SLOW
203 IF TT+T>=30 THEN GOTO 0457
205 PRINT AT A,TT+T;"\##"
207 IF INKEY$="" THEN GOTO 0207
209 IF INKEY$="O" THEN GOTO 0451
213 IF INKEY$="5" THEN GOTO 0243
215 IF INKEY$="M" THEN GOTO 715
217 IF INKEY$<>"A" THEN GOTO 0223
219 LET L=1
221 GOSUB 0307
223 IF INKEY$="Q" THEN GOSUB 0431
225 IF INKEY$="P" THEN GOSUB 0491
227 IF INKEY$="6" THEN GOSUB 0255
229 IF INKEY$="7" THEN GOSUB 0267
231 IF INKEY$="8" THEN GOTO 0279
233 IF INKEY$="D" THEN GOTO 0445
235 IF INKEY$="R" THEN GOSUB 0289
237 IF INKEY$="F" THEN GOSUB 0517
239 IF INKEY$="S" THEN GOSUB 0307
241 GOTO 0203
243 IF TT+T>3 THEN LET T=T-1
245 PRINT AT A,TT+T+1;V$(A,T+1)
247 PRINT AT A,TT+T;"\##"
249 LET PP=1
251 GOTO 0207
253 RETURN
255 PRINT AT A,TT+T;" "
257 IF A=10 OR A=11 OR A=2 OR A=4 OR A=6 OR A=8 THEN GOTO 0261
259 PRINT AT A,TT+T;"-"
261 IF A<11 THEN LET A=A+1
263 LET W=130
265 RETURN
267 PRINT AT A,TT+T;" "
269 IF A=10 OR A=11 OR A=2 OR A=4 OR A=6 OR A=8 THEN GOTO 0273
271 PRINT AT A,TT+T;"-"
273 IF A>1 THEN LET A=A-1
275 LET W=130
277 RETURN
279 PRINT AT A,TT+T;V$(A,T)
281 LET PP=0
283 IF TT+T<30 THEN LET T=T+1
285 PRINT AT A,TT+T;"\##"
287 GOTO 0207
289 FAST
291 LET U=T
293 GOSUB 0133
295 FOR I=1 TO REP-1
297 GOSUB 0133
299 NEXT I
301 SLOW
303 LET T=U
305 GOTO 0205
307 IF B+(T+1)<=LEN N$ THEN GOTO 0327
309 LET L$=N$
311 DIM N$(B+(T+1))
313 LET N$(1 TO LEN N$-2)=L$
315 LET L$=X$
317 DIM X$(B+(T+1))
319 LET X$(1 TO LEN X$-2)=L$
321 LET L$=D$
323 DIM D$(B+(T+1))
325 LET D$(1 TO LEN D$-2)=L$
327 IF L=1 THEN GOTO 0665
329 LET SEMI=6
331 IF A=1 THEN LET N$(B+(T))="F"
333 IF A=2 THEN LET N$(B+(T))="E"
335 IF A=3 THEN LET N$(B+(T))="D"
337 IF A=4 THEN LET N$(B+(T))="C"
339 IF A=5 THEN LET N$(B+T)="B"
341 IF A=6 THEN LET N$(B+T)="A"
343 IF A=7 THEN LET N$(B+T)="G"
345 IF A=8 THEN LET N$(B+T)="F"
347 IF A=9 THEN LET N$(B+T)="E"
349 IF A=10 THEN LET N$(B+T)="D"
351 IF A=11 THEN LET N$(B+(T))="C"
353 LET V$(A,T)="$"
355 LET D$(B+(T))=J$(G)
357 LET X$(B+(T))=K$(Q)
359 IF A=SEMI OR A=5 OR A=2 OR A=1 OR A=3 OR A=4 THEN LET X$(B+(T))=K$(Q+1)
361 IF PP=0 THEN GOTO 0371
363 FOR I=1 TO 11
365 IF I=1 OR I=3 OR I=5 OR I=7 OR I=9 THEN PRINT AT I,TT+T;"-"
367 IF I=2 OR I=4 OR I=6 OR I=8 OR I=10 OR I=11 THEN PRINT AT I,TT+T;" "
369 NEXT I
371 PRINT AT A,TT+T;V$(A,T)
373 LET T=T+1
375 PRINT AT 14,(TT+T)-1;G
377 PRINT AT 16,16;Q
379 RETURN
381 REM %I%N%I%T%I%A%L%I%Z%A%T%I%O%N% %S%U%B
383 PRINT AT 10,2;"INPUT NOTE DURATION(1 TO 8)"
385 LET B=0
387 LET L=0
389 LET Q=0
391 LET W=128
393 LET H=9000
395 LET A=11
397 LET C=1
399 LET T=1
401 LET TT=2
403 LET PP=0
405 LET REP=0
407 IF INKEY$="" THEN GOTO 0407
409 LET G=VAL INKEY$
411 LET J$="12345678"
413 CLS
415 PRINT AT 10,5;"INPUT OCTAVE(1 TO 6)"
417 IF INKEY$="" THEN GOTO 0417
419 LET Y=(VAL INKEY$)+1
421 LET K$="123456788"
423 LET Q=VAL K$(Y+1)
425 CLS
427 RETURN
429 REM %S%U%B% %T%O% %R%E%P%E%A%T% %T%U%N%E%.
431 PRINT AT 18,1;" YOU WANT THE TUNE TO REPEAT..."
433 PRINT AT 20,5;"HOW MANY TIMES?"
435 INPUT Y
437 LET REP=Y
439 PRINT AT 18,1;" "
441 PRINT AT 20,1;" "
443 RETURN
445 INPUT Y
447 IF Y>0 AND Y<9 THEN LET G=Y
449 GOTO 0205
451 INPUT Y
453 IF Y>0 AND Y<9 THEN LET Q=Y
455 GOTO 0205
457 REM %S%U%B%.% %T%O% %P%R%I%N%T% %2%N%D% %S%E%T% %O%F% %B%A%R%S
459 FOR I=1 TO 19
461 SCROLL
463 NEXT I
465 LET A=11
467 LET B=B+T-1
469 LET T=1
471 LET V$(1)="-----------------------------"
473 LET V$(2)=" "
475 FOR I=1 TO 9 STEP 2
477 LET V$(I)=V$(1)
479 LET V$(I+1)=V$(2)
481 NEXT I
483 LET V$(11)=V$(2)
485 PRINT AT 14,1;"%D";AT 16,1;"%C%U%R%R%E%N%T %O%C%T%A%V%E%: "
487 GOTO 0183
489 REM %S%U%B% %T%O% %C%H%A%N%G%E% %A%L%L% %O%C%T%A%V%E%S\:
491 PRINT AT 20,2;"INCREASE OR DECREASE?"
493 LET P$="12345678"
495 INPUT Y
497 FOR S=1 TO LEN X$-1
499 LET V=VAL X$(S)
501 IF Y=7 AND V<8 THEN LET V=V+1
503 IF Y=6 AND V>1 THEN LET V=V-1
505 LET X$(S)=P$(V)
507 NEXT S
509 PRINT AT 16,16;X$(LEN X$-1)
511 PRINT AT 20,2;" "
513 RETURN
515 REM %S%U%B% %T%O% %C%H%A%N%G%E% %O%V%E%R%A%L%L% % % %S%P%E%E%D% %O%F% %P%L%A%Y%.
517 PRINT AT 20,2;"INCREASE OR DECREASE?"
519 INPUT Y
521 FOR S=1 TO LEN D$-1
523 LET V=VAL D$(S)
525 IF Y=7 AND V<9 THEN LET V=V+1
527 IF Y=6 AND V>1 THEN LET V=V-1
529 LET D$(S)=J$(V)
531 NEXT S
533 PRINT AT 14,3;D$(B+1 TO )
535 PRINT AT 20,2;" "
537 RETURN
663 REM %S%E%M%I%T%O%N%E% %S%U%B%.
665 IF A=1 THEN LET N$(B+T)="F"
667 IF A=2 THEN LET N$(B+T)="%E"
669 IF A=3 THEN LET N$(B+T)="%D"
671 IF A=4 THEN LET N$(B+T)="%C"
673 IF A=5 THEN LET N$(B+T)="%B"
675 IF A=6 THEN LET N$(B+T)="%A"
677 IF A=7 THEN LET N$(B+T)="%G"
679 IF A=8 THEN LET N$(B+T)="%F"
681 IF A=9 THEN LET N$(B+T)="%E"
683 IF A=10 THEN LET N$(B+T)="%D"
685 IF A=11 THEN LET N$(B+T)="%C"
687 LET L=0
689 LET SEMI=5
691 GOTO 0353
693 PRINT AT 5,8;"% % % % % % % % % % % % "
695 PRINT AT 6,8;"% % % % "
697 PRINT AT 7,8;"% % % % "
699 PRINT AT 8,8;"% % % % % % % % % % % % "
701 PRINT AT 9,8;"% % % "
703 PRINT AT 10,8;"% % % % % % % % % "
705 PRINT AT 12,12;"PRESENT"
707 PRINT AT 15,11;"%C%O%M%P%O%S%E%R% "
709 FOR F=1 TO 15
711 NEXT F
713 CLS
714 RETURN
715 PRINT AT 20,2;"START TAPE AND PRESS ""G"""
716 IF INKEY$<>"G" THEN GOTO 716
717 PRINT AT 20,2;" "
718 SAVE "COMPOSE%R"
719 GOTO 0203
721 SAVE "COMPOSE%R"
723 GOTO 1
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
People
No people associated with this content.
Skip to content
Composer
This program is a music composition utility that displays a staff-like grid on screen, allowing the user to place notes using cursor keys and save the resulting tune. It precomputes note frequencies for eight octaves across seven notes (A through G, including semitones) using the ZX81’s speaker timer formula F = INT(1/(f × 17×10⁻⁶) + 0.5), storing two-byte timer values in the arrays O$ and R$ for natural and sharp/flat notes respectively. A short machine-code routine embedded in REM line 1 drives the speaker directly, called via USR 16518, with timer and duration values POKEd into addresses 16515–16517. The composer supports variable octave selection, note duration, repeat playback, speed adjustment, and scrolling the staff when the cursor reaches the right edge.
Program Analysis
Program Structure
The program is divided into several functional regions:
- Lines 1–131: Machine code (REM line 1), initialization entry points, note interpolation subroutines, and frequency precomputation.
- Lines 133–181: Playback engine — iterates over the note/octave/duration string arrays and calls the machine code driver.
- Lines 183–241: Main loop — draws the staff, handles keyboard input, dispatches to editing subroutines.
- Lines 243–287: Cursor movement (left/right, up/down across staff lines).
- Lines 289–379: Note entry, array resizing, display update.
- Lines 381–427: Initialization subroutine — prompts for duration and octave, zeros variables.
- Lines 429–537: Auxiliary subroutines: repeat, duration change, octave shift, speed change.
- Lines 663–691: Semitone entry branch (shares tail of note-entry subroutine).
- Lines 693–721: Title/splash screen and SAVE routines.
Machine Code Routine (REM Line 1)
The entire sound driver is stored as raw bytes in the REM statement at line 1. The bytes are:
\FF\10\CC\01\2A\82\40\ED\5B\84\40\01\00\00\E5\D3\FF\CD\A2\40\DB\FE\CD\A2\40\E1\B7\ED\52\30\EF\C9\2A\84\40\37\ED\42\30\FB\C9
This routine is entered via USR 16518 (line 29 / 155 / 169), which points two bytes past the start of the BASIC line header into the REM body. The three parameters are POKEd into fixed addresses before the call:
Address Variable Purpose 16515 D (duration × 16) Note duration 16516 Low byte of timer Speaker half-period low 16517 High byte of timer Speaker half-period high
The routine uses the Z80 OUT (0FFH),A instruction (D3 FF) to toggle the ZX81 speaker port and a delay loop keyed to the 16-bit timer value to set pitch.
Frequency Precomputation
Lines 73–119 precompute speaker timer values for seven notes across eight octaves (J=8 down to 1, higher J = lower octave). The formula at line 125 converts a frequency in Hz to a loop-count timer:
F = INT(1 / (F_hz × 17E-6) + 0.5)
The seven base frequencies used (relative multiples) are 26⅔, 30, 32, 36, 40, 42⅔, and 48 — corresponding approximately to D, E, F, G, A, B♭, and C in a just-intonation-adjacent scheme, scaled by powers of two for each octave.
Natural notes are stored in O$(J,N,1..2); interpolated semitone (midpoint) values are stored in R$(J,N,1..2) via the subroutine at lines 7–27, which averages adjacent timer values.
Note and Duration Encoding
Three parallel string arrays hold the composition:
N$ — note names (A–G); semitones stored as inverse characters (e.g., %E), which have character codes above 128, detected at line 141 (IF N<=128).
X$ — octave per note, encoded as a digit character; decoded with CODE X$(X)-28.
D$ — duration per note, encoded similarly with CODE D$(X)-28, then multiplied by 16 at line 140.
The arrays are dynamically resized using a DIM / copy idiom (lines 307–325): the old contents are saved into a temporary string L$, the array is re-dimensioned one element larger, and the contents are restored with a slice assignment.
Staff Display
The five staff lines are represented by rows of "-" characters at odd screen rows 1, 3, 5, 7, 9; spaces at even rows and row 11 represent spaces between lines and ledger areas. The array V$(row, col) tracks the current character at each cell so notes can be restored when the cursor moves away. The cursor is displayed as a block graphic character (\##, a solid 2×2 block). When the cursor reaches column 30, the screen scrolls 19 lines and a new staff section is drawn.
Key Bindings
Key Function 5 Move cursor left 8 Move cursor right / advance 6 Move cursor down (lower pitch) 7 Move cursor up (higher pitch) S Place natural note A Place semitone (sharp/flat) note R Play back the tune (FAST mode) Q Set repeat count P Shift all octaves up or down F Change overall playback speed D Change note duration O Change current octave M Save composition to tape
Notable Techniques and Idioms
- The
GOSUB 069Z at line 3 and GOTO 0037 at line 5 use leading-zero and trailing-letter forms of line numbers — the 069Z target does not exist as a reachable line in the normal flow and acts as a skip; the program entry for BASIC execution falls through to line 37 where data initialization begins after the precomputation jump at line 121 sends control to line 183.
- All numeric literals in
GO TO and GO SUB are written with leading zeros (e.g., GOTO 0019), a known memory optimization that stores the number as a string rather than a floating-point value in the token stream.
- The semitone detection at line 141 exploits the fact that inverse-video characters in ZX81 BASIC have codes ≥128 (128 + normal code), so
CODE N$(X)-37 yields a value >128 for semitone note names stored as inverse characters.
- The silence/rest mechanism (line 147 branch to 175) uses a simple
FOR delay loop scaled by the duration value when the decoded note index is negative.
- Line 179 (
IF X<=LEN N$ THEN GOTO 0157) re-enters the playback loop mid-subroutine after a rest, rather than continuing the FOR X loop normally — an unusual flow-control trick to share the NEXT X at line 157.
- The splash/title screen code at lines 693–713 is never called from the main program flow visible in this listing; it appears to be a leftover or an entry point used only on initial tape load.
Bugs and Anomalies
- Line 215 is followed immediately by line 217, with line 216 absent — the key test for
"M" at line 215 jumps to line 715, bypassing line 216 which would have been the next sequential check. No functional bug since the jump is explicit.
- The
WAIT loop at line 207 (IF INKEY$="" THEN GOTO 0207) waits for a key to be pressed, but the subsequent key tests (lines 209–239) re-read INKEY$ without waiting, meaning a fast typist could miss checks if the key is released between lines. This is a standard ZX81 INKEY$ polling hazard.
- The
V$(11) at line 53 is dimensioned to 28 characters with a 30-character initializer — the extra two characters are silently truncated by BASIC.
- Line 359 adjusts the octave stored for a note by using
K$(Q+1) for certain staff positions (semitone rows), implicitly raising the octave index — this relies on K$="123456788" having a duplicate “8” at position 9 to avoid an out-of-bounds error at the top octave.
Content
Source Code
1 REM \FF\10\CC\01\2A\82\40\ED\5B\84\40\01\00\00\E5\D3\FF\CD\A2\40\DB\FE\CD\A2\40\E1\B7\ED\52\30\EF\C9\2A\84\40\37\ED\42\30\FB\C9\20\22\23\25\1C
3 GOSUB 069Z
5 GOTO 0037
7 IF N=1 AND J>1 THEN GOTO 0033
9 IF N>1 THEN GOTO 0015
11 LET N=N+1
13 RETURN
15 LET F=256*CODE O$(J,N-1,1)+CODE O$(J,N-1,2)
17 LET F1=256*CODE O$(J,N,1)+CODE O$(J,N,2)
19 LET F=INT ((F1+F)/2+.5)
21 LET R$(J,N,2)=CHR$ (F-(INT (F/256)*256))
23 LET R$(J,N,1)=CHR$ (INT (F/256))
25 LET N=N+1
27 RETURN
29 LET C=USR 16518
31 RETURN
33 LET F1=256*CODE O$(J-1,N,1)+CODE O$(J-1,N,2)
35 GOTO 0019
37 DIM V$(11,28)
39 LET V$(1)="---------------------------"
41 FOR I=3 TO 9 STEP 2
43 LET V$(I)=V$(1)
45 NEXT I
47 FOR I=2 TO 10 STEP 2
49 LET V$(I)=" "
51 NEXT I
53 LET V$(11)=" "
55 DIM R$(8,7,2)
57 LET U=0
59 LET G=1
61 GOSUB 0383
63 DIM O$(8,7,2)
65 DIM X$(3)
67 DIM N$(3)
69 DIM D$(3)
71 FAST
73 FOR J=8 TO 1 STEP -1
75 LET N=1
77 LET F=INT ((26*2**(J-1)+2**(J-1)*(2/3))+.5)
79 GOSUB 0125
81 GOSUB 0007
83 LET F=INT (30*2**(J-1)+.5)
85 GOSUB 0125
87 GOSUB 0007
89 LET F=INT (32*2**(J-1)+.5)
91 GOSUB 0125
93 GOSUB 0007
95 LET F=INT (36*2**(J-1)+.5)
97 GOSUB 0125
99 GOSUB 0007
101 LET F=INT (40*2**(J-1)+.5)
103 GOSUB 0125
105 GOSUB 0007
107 LET F=INT ((42*2**(J-1)+2**(J-1)*(2/3))+.5)
109 GOSUB 0125
111 GOSUB 0007
113 LET F=INT (48*2**(J-1)+.5)
115 GOSUB 0125
117 GOSUB 0007
119 NEXT J
121 GOTO 0183
123 REM %F%O%R% %N%O%T%E%S%.
125 LET F=INT ((1/(F*17E-6))+.5)
127 LET O$(J,N,2)=CHR$ (F-(INT (F/256)*256))
129 LET O$(J,N,1)=CHR$ (INT (F/256))
131 RETURN
133 FOR X=1 TO LEN N$
135 LET N=CODE N$(X)-37
137 LET J=CODE X$(X)-28
139 LET D=(CODE D$(X)-28)*16
141 IF N<=128 THEN GOTO 0147
143 GOTO 0161
145 NEXT X
147 IF N<0 THEN GOTO 0175
149 POKE 16516,CODE O$(J,N,2)
151 POKE 16517,CODE O$(J,N,1)
153 POKE 16515,D
155 LET C=USR 16518
157 NEXT X
159 RETURN
161 LET N=N-128
163 POKE 16516,CODE R$(J,N,2)
165 POKE 16517,CODE R$(J,N,1)
167 POKE 16515,D
169 LET C=USR 16518
171 NEXT X
173 RETURN
175 FOR T=1 TO D*8
177 NEXT T
179 IF X<=LEN N$ THEN GOTO 0157
181 RETURN
183 REM %P%R%I%N%T%S% %B%A%R%S%.
185 FOR I=1 TO 30
187 PRINT AT 1,I;"-";AT 3,I;"-";AT 5,I;"-"
189 PRINT AT 7,I;"-";AT 9,I;"-"
191 NEXT I
193 FOR I=1 TO 9
195 PRINT AT I,1;"\: ";AT I,2;"\ :"
197 PRINT AT 14,1;"%D";AT 16,1;"%C%U%R%R%E%N%T %O%C%T%A%V%E%: "
199 NEXT I
201 SLOW
203 IF TT+T>=30 THEN GOTO 0457
205 PRINT AT A,TT+T;"\##"
207 IF INKEY$="" THEN GOTO 0207
209 IF INKEY$="O" THEN GOTO 0451
213 IF INKEY$="5" THEN GOTO 0243
215 IF INKEY$="M" THEN GOTO 715
217 IF INKEY$<>"A" THEN GOTO 0223
219 LET L=1
221 GOSUB 0307
223 IF INKEY$="Q" THEN GOSUB 0431
225 IF INKEY$="P" THEN GOSUB 0491
227 IF INKEY$="6" THEN GOSUB 0255
229 IF INKEY$="7" THEN GOSUB 0267
231 IF INKEY$="8" THEN GOTO 0279
233 IF INKEY$="D" THEN GOTO 0445
235 IF INKEY$="R" THEN GOSUB 0289
237 IF INKEY$="F" THEN GOSUB 0517
239 IF INKEY$="S" THEN GOSUB 0307
241 GOTO 0203
243 IF TT+T>3 THEN LET T=T-1
245 PRINT AT A,TT+T+1;V$(A,T+1)
247 PRINT AT A,TT+T;"\##"
249 LET PP=1
251 GOTO 0207
253 RETURN
255 PRINT AT A,TT+T;" "
257 IF A=10 OR A=11 OR A=2 OR A=4 OR A=6 OR A=8 THEN GOTO 0261
259 PRINT AT A,TT+T;"-"
261 IF A<11 THEN LET A=A+1
263 LET W=130
265 RETURN
267 PRINT AT A,TT+T;" "
269 IF A=10 OR A=11 OR A=2 OR A=4 OR A=6 OR A=8 THEN GOTO 0273
271 PRINT AT A,TT+T;"-"
273 IF A>1 THEN LET A=A-1
275 LET W=130
277 RETURN
279 PRINT AT A,TT+T;V$(A,T)
281 LET PP=0
283 IF TT+T<30 THEN LET T=T+1
285 PRINT AT A,TT+T;"\##"
287 GOTO 0207
289 FAST
291 LET U=T
293 GOSUB 0133
295 FOR I=1 TO REP-1
297 GOSUB 0133
299 NEXT I
301 SLOW
303 LET T=U
305 GOTO 0205
307 IF B+(T+1)<=LEN N$ THEN GOTO 0327
309 LET L$=N$
311 DIM N$(B+(T+1))
313 LET N$(1 TO LEN N$-2)=L$
315 LET L$=X$
317 DIM X$(B+(T+1))
319 LET X$(1 TO LEN X$-2)=L$
321 LET L$=D$
323 DIM D$(B+(T+1))
325 LET D$(1 TO LEN D$-2)=L$
327 IF L=1 THEN GOTO 0665
329 LET SEMI=6
331 IF A=1 THEN LET N$(B+(T))="F"
333 IF A=2 THEN LET N$(B+(T))="E"
335 IF A=3 THEN LET N$(B+(T))="D"
337 IF A=4 THEN LET N$(B+(T))="C"
339 IF A=5 THEN LET N$(B+T)="B"
341 IF A=6 THEN LET N$(B+T)="A"
343 IF A=7 THEN LET N$(B+T)="G"
345 IF A=8 THEN LET N$(B+T)="F"
347 IF A=9 THEN LET N$(B+T)="E"
349 IF A=10 THEN LET N$(B+T)="D"
351 IF A=11 THEN LET N$(B+(T))="C"
353 LET V$(A,T)="$"
355 LET D$(B+(T))=J$(G)
357 LET X$(B+(T))=K$(Q)
359 IF A=SEMI OR A=5 OR A=2 OR A=1 OR A=3 OR A=4 THEN LET X$(B+(T))=K$(Q+1)
361 IF PP=0 THEN GOTO 0371
363 FOR I=1 TO 11
365 IF I=1 OR I=3 OR I=5 OR I=7 OR I=9 THEN PRINT AT I,TT+T;"-"
367 IF I=2 OR I=4 OR I=6 OR I=8 OR I=10 OR I=11 THEN PRINT AT I,TT+T;" "
369 NEXT I
371 PRINT AT A,TT+T;V$(A,T)
373 LET T=T+1
375 PRINT AT 14,(TT+T)-1;G
377 PRINT AT 16,16;Q
379 RETURN
381 REM %I%N%I%T%I%A%L%I%Z%A%T%I%O%N% %S%U%B
383 PRINT AT 10,2;"INPUT NOTE DURATION(1 TO 8)"
385 LET B=0
387 LET L=0
389 LET Q=0
391 LET W=128
393 LET H=9000
395 LET A=11
397 LET C=1
399 LET T=1
401 LET TT=2
403 LET PP=0
405 LET REP=0
407 IF INKEY$="" THEN GOTO 0407
409 LET G=VAL INKEY$
411 LET J$="12345678"
413 CLS
415 PRINT AT 10,5;"INPUT OCTAVE(1 TO 6)"
417 IF INKEY$="" THEN GOTO 0417
419 LET Y=(VAL INKEY$)+1
421 LET K$="123456788"
423 LET Q=VAL K$(Y+1)
425 CLS
427 RETURN
429 REM %S%U%B% %T%O% %R%E%P%E%A%T% %T%U%N%E%.
431 PRINT AT 18,1;" YOU WANT THE TUNE TO REPEAT..."
433 PRINT AT 20,5;"HOW MANY TIMES?"
435 INPUT Y
437 LET REP=Y
439 PRINT AT 18,1;" "
441 PRINT AT 20,1;" "
443 RETURN
445 INPUT Y
447 IF Y>0 AND Y<9 THEN LET G=Y
449 GOTO 0205
451 INPUT Y
453 IF Y>0 AND Y<9 THEN LET Q=Y
455 GOTO 0205
457 REM %S%U%B%.% %T%O% %P%R%I%N%T% %2%N%D% %S%E%T% %O%F% %B%A%R%S
459 FOR I=1 TO 19
461 SCROLL
463 NEXT I
465 LET A=11
467 LET B=B+T-1
469 LET T=1
471 LET V$(1)="-----------------------------"
473 LET V$(2)=" "
475 FOR I=1 TO 9 STEP 2
477 LET V$(I)=V$(1)
479 LET V$(I+1)=V$(2)
481 NEXT I
483 LET V$(11)=V$(2)
485 PRINT AT 14,1;"%D";AT 16,1;"%C%U%R%R%E%N%T %O%C%T%A%V%E%: "
487 GOTO 0183
489 REM %S%U%B% %T%O% %C%H%A%N%G%E% %A%L%L% %O%C%T%A%V%E%S\:
491 PRINT AT 20,2;"INCREASE OR DECREASE?"
493 LET P$="12345678"
495 INPUT Y
497 FOR S=1 TO LEN X$-1
499 LET V=VAL X$(S)
501 IF Y=7 AND V<8 THEN LET V=V+1
503 IF Y=6 AND V>1 THEN LET V=V-1
505 LET X$(S)=P$(V)
507 NEXT S
509 PRINT AT 16,16;X$(LEN X$-1)
511 PRINT AT 20,2;" "
513 RETURN
515 REM %S%U%B% %T%O% %C%H%A%N%G%E% %O%V%E%R%A%L%L% % % %S%P%E%E%D% %O%F% %P%L%A%Y%.
517 PRINT AT 20,2;"INCREASE OR DECREASE?"
519 INPUT Y
521 FOR S=1 TO LEN D$-1
523 LET V=VAL D$(S)
525 IF Y=7 AND V<9 THEN LET V=V+1
527 IF Y=6 AND V>1 THEN LET V=V-1
529 LET D$(S)=J$(V)
531 NEXT S
533 PRINT AT 14,3;D$(B+1 TO )
535 PRINT AT 20,2;" "
537 RETURN
663 REM %S%E%M%I%T%O%N%E% %S%U%B%.
665 IF A=1 THEN LET N$(B+T)="F"
667 IF A=2 THEN LET N$(B+T)="%E"
669 IF A=3 THEN LET N$(B+T)="%D"
671 IF A=4 THEN LET N$(B+T)="%C"
673 IF A=5 THEN LET N$(B+T)="%B"
675 IF A=6 THEN LET N$(B+T)="%A"
677 IF A=7 THEN LET N$(B+T)="%G"
679 IF A=8 THEN LET N$(B+T)="%F"
681 IF A=9 THEN LET N$(B+T)="%E"
683 IF A=10 THEN LET N$(B+T)="%D"
685 IF A=11 THEN LET N$(B+T)="%C"
687 LET L=0
689 LET SEMI=5
691 GOTO 0353
693 PRINT AT 5,8;"% % % % % % % % % % % % "
695 PRINT AT 6,8;"% % % % "
697 PRINT AT 7,8;"% % % % "
699 PRINT AT 8,8;"% % % % % % % % % % % % "
701 PRINT AT 9,8;"% % % "
703 PRINT AT 10,8;"% % % % % % % % % "
705 PRINT AT 12,12;"PRESENT"
707 PRINT AT 15,11;"%C%O%M%P%O%S%E%R% "
709 FOR F=1 TO 15
711 NEXT F
713 CLS
714 RETURN
715 PRINT AT 20,2;"START TAPE AND PRESS ""G"""
716 IF INKEY$<>"G" THEN GOTO 716
717 PRINT AT 20,2;" "
718 SAVE "COMPOSE%R"
719 GOTO 0203
721 SAVE "COMPOSE%R"
723 GOTO 1
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
People
No people associated with this content.
\E5\D3\FF\CD\A2\DB\FE\CD\A2\E1\B7\ED\EF\C9
Skip to content
Composer
This program is a music composition utility that displays a staff-like grid on screen, allowing the user to place notes using cursor keys and save the resulting tune. It precomputes note frequencies for eight octaves across seven notes (A through G, including semitones) using the ZX81’s speaker timer formula F = INT(1/(f × 17×10⁻⁶) + 0.5), storing two-byte timer values in the arrays O$ and R$ for natural and sharp/flat notes respectively. A short machine-code routine embedded in REM line 1 drives the speaker directly, called via USR 16518, with timer and duration values POKEd into addresses 16515–16517. The composer supports variable octave selection, note duration, repeat playback, speed adjustment, and scrolling the staff when the cursor reaches the right edge.
Program Analysis
Program Structure
The program is divided into several functional regions:
- Lines 1–131: Machine code (REM line 1), initialization entry points, note interpolation subroutines, and frequency precomputation.
- Lines 133–181: Playback engine — iterates over the note/octave/duration string arrays and calls the machine code driver.
- Lines 183–241: Main loop — draws the staff, handles keyboard input, dispatches to editing subroutines.
- Lines 243–287: Cursor movement (left/right, up/down across staff lines).
- Lines 289–379: Note entry, array resizing, display update.
- Lines 381–427: Initialization subroutine — prompts for duration and octave, zeros variables.
- Lines 429–537: Auxiliary subroutines: repeat, duration change, octave shift, speed change.
- Lines 663–691: Semitone entry branch (shares tail of note-entry subroutine).
- Lines 693–721: Title/splash screen and SAVE routines.
Machine Code Routine (REM Line 1)
The entire sound driver is stored as raw bytes in the REM statement at line 1. The bytes are:
\FF\10\CC\01\2A\82\40\ED\5B\84\40\01\00\00\E5\D3\FF\CD\A2\40\DB\FE\CD\A2\40\E1\B7\ED\52\30\EF\C9\2A\84\40\37\ED\42\30\FB\C9
This routine is entered via USR 16518 (line 29 / 155 / 169), which points two bytes past the start of the BASIC line header into the REM body. The three parameters are POKEd into fixed addresses before the call:
Address Variable Purpose 16515 D (duration × 16) Note duration 16516 Low byte of timer Speaker half-period low 16517 High byte of timer Speaker half-period high
The routine uses the Z80 OUT (0FFH),A instruction (D3 FF) to toggle the ZX81 speaker port and a delay loop keyed to the 16-bit timer value to set pitch.
Frequency Precomputation
Lines 73–119 precompute speaker timer values for seven notes across eight octaves (J=8 down to 1, higher J = lower octave). The formula at line 125 converts a frequency in Hz to a loop-count timer:
F = INT(1 / (F_hz × 17E-6) + 0.5)
The seven base frequencies used (relative multiples) are 26⅔, 30, 32, 36, 40, 42⅔, and 48 — corresponding approximately to D, E, F, G, A, B♭, and C in a just-intonation-adjacent scheme, scaled by powers of two for each octave.
Natural notes are stored in O$(J,N,1..2); interpolated semitone (midpoint) values are stored in R$(J,N,1..2) via the subroutine at lines 7–27, which averages adjacent timer values.
Note and Duration Encoding
Three parallel string arrays hold the composition:
N$ — note names (A–G); semitones stored as inverse characters (e.g., %E), which have character codes above 128, detected at line 141 (IF N<=128).
X$ — octave per note, encoded as a digit character; decoded with CODE X$(X)-28.
D$ — duration per note, encoded similarly with CODE D$(X)-28, then multiplied by 16 at line 140.
The arrays are dynamically resized using a DIM / copy idiom (lines 307–325): the old contents are saved into a temporary string L$, the array is re-dimensioned one element larger, and the contents are restored with a slice assignment.
Staff Display
The five staff lines are represented by rows of "-" characters at odd screen rows 1, 3, 5, 7, 9; spaces at even rows and row 11 represent spaces between lines and ledger areas. The array V$(row, col) tracks the current character at each cell so notes can be restored when the cursor moves away. The cursor is displayed as a block graphic character (\##, a solid 2×2 block). When the cursor reaches column 30, the screen scrolls 19 lines and a new staff section is drawn.
Key Bindings
Key Function 5 Move cursor left 8 Move cursor right / advance 6 Move cursor down (lower pitch) 7 Move cursor up (higher pitch) S Place natural note A Place semitone (sharp/flat) note R Play back the tune (FAST mode) Q Set repeat count P Shift all octaves up or down F Change overall playback speed D Change note duration O Change current octave M Save composition to tape
Notable Techniques and Idioms
- The
GOSUB 069Z at line 3 and GOTO 0037 at line 5 use leading-zero and trailing-letter forms of line numbers — the 069Z target does not exist as a reachable line in the normal flow and acts as a skip; the program entry for BASIC execution falls through to line 37 where data initialization begins after the precomputation jump at line 121 sends control to line 183.
- All numeric literals in
GO TO and GO SUB are written with leading zeros (e.g., GOTO 0019), a known memory optimization that stores the number as a string rather than a floating-point value in the token stream.
- The semitone detection at line 141 exploits the fact that inverse-video characters in ZX81 BASIC have codes ≥128 (128 + normal code), so
CODE N$(X)-37 yields a value >128 for semitone note names stored as inverse characters.
- The silence/rest mechanism (line 147 branch to 175) uses a simple
FOR delay loop scaled by the duration value when the decoded note index is negative.
- Line 179 (
IF X<=LEN N$ THEN GOTO 0157) re-enters the playback loop mid-subroutine after a rest, rather than continuing the FOR X loop normally — an unusual flow-control trick to share the NEXT X at line 157.
- The splash/title screen code at lines 693–713 is never called from the main program flow visible in this listing; it appears to be a leftover or an entry point used only on initial tape load.
Bugs and Anomalies
- Line 215 is followed immediately by line 217, with line 216 absent — the key test for
"M" at line 215 jumps to line 715, bypassing line 216 which would have been the next sequential check. No functional bug since the jump is explicit.
- The
WAIT loop at line 207 (IF INKEY$="" THEN GOTO 0207) waits for a key to be pressed, but the subsequent key tests (lines 209–239) re-read INKEY$ without waiting, meaning a fast typist could miss checks if the key is released between lines. This is a standard ZX81 INKEY$ polling hazard.
- The
V$(11) at line 53 is dimensioned to 28 characters with a 30-character initializer — the extra two characters are silently truncated by BASIC.
- Line 359 adjusts the octave stored for a note by using
K$(Q+1) for certain staff positions (semitone rows), implicitly raising the octave index — this relies on K$="123456788" having a duplicate “8” at position 9 to avoid an out-of-bounds error at the top octave.
Content
Source Code
1 REM \FF\10\CC\01\2A\82\40\ED\5B\84\40\01\00\00\E5\D3\FF\CD\A2\40\DB\FE\CD\A2\40\E1\B7\ED\52\30\EF\C9\2A\84\40\37\ED\42\30\FB\C9\20\22\23\25\1C
3 GOSUB 069Z
5 GOTO 0037
7 IF N=1 AND J>1 THEN GOTO 0033
9 IF N>1 THEN GOTO 0015
11 LET N=N+1
13 RETURN
15 LET F=256*CODE O$(J,N-1,1)+CODE O$(J,N-1,2)
17 LET F1=256*CODE O$(J,N,1)+CODE O$(J,N,2)
19 LET F=INT ((F1+F)/2+.5)
21 LET R$(J,N,2)=CHR$ (F-(INT (F/256)*256))
23 LET R$(J,N,1)=CHR$ (INT (F/256))
25 LET N=N+1
27 RETURN
29 LET C=USR 16518
31 RETURN
33 LET F1=256*CODE O$(J-1,N,1)+CODE O$(J-1,N,2)
35 GOTO 0019
37 DIM V$(11,28)
39 LET V$(1)="---------------------------"
41 FOR I=3 TO 9 STEP 2
43 LET V$(I)=V$(1)
45 NEXT I
47 FOR I=2 TO 10 STEP 2
49 LET V$(I)=" "
51 NEXT I
53 LET V$(11)=" "
55 DIM R$(8,7,2)
57 LET U=0
59 LET G=1
61 GOSUB 0383
63 DIM O$(8,7,2)
65 DIM X$(3)
67 DIM N$(3)
69 DIM D$(3)
71 FAST
73 FOR J=8 TO 1 STEP -1
75 LET N=1
77 LET F=INT ((26*2**(J-1)+2**(J-1)*(2/3))+.5)
79 GOSUB 0125
81 GOSUB 0007
83 LET F=INT (30*2**(J-1)+.5)
85 GOSUB 0125
87 GOSUB 0007
89 LET F=INT (32*2**(J-1)+.5)
91 GOSUB 0125
93 GOSUB 0007
95 LET F=INT (36*2**(J-1)+.5)
97 GOSUB 0125
99 GOSUB 0007
101 LET F=INT (40*2**(J-1)+.5)
103 GOSUB 0125
105 GOSUB 0007
107 LET F=INT ((42*2**(J-1)+2**(J-1)*(2/3))+.5)
109 GOSUB 0125
111 GOSUB 0007
113 LET F=INT (48*2**(J-1)+.5)
115 GOSUB 0125
117 GOSUB 0007
119 NEXT J
121 GOTO 0183
123 REM %F%O%R% %N%O%T%E%S%.
125 LET F=INT ((1/(F*17E-6))+.5)
127 LET O$(J,N,2)=CHR$ (F-(INT (F/256)*256))
129 LET O$(J,N,1)=CHR$ (INT (F/256))
131 RETURN
133 FOR X=1 TO LEN N$
135 LET N=CODE N$(X)-37
137 LET J=CODE X$(X)-28
139 LET D=(CODE D$(X)-28)*16
141 IF N<=128 THEN GOTO 0147
143 GOTO 0161
145 NEXT X
147 IF N<0 THEN GOTO 0175
149 POKE 16516,CODE O$(J,N,2)
151 POKE 16517,CODE O$(J,N,1)
153 POKE 16515,D
155 LET C=USR 16518
157 NEXT X
159 RETURN
161 LET N=N-128
163 POKE 16516,CODE R$(J,N,2)
165 POKE 16517,CODE R$(J,N,1)
167 POKE 16515,D
169 LET C=USR 16518
171 NEXT X
173 RETURN
175 FOR T=1 TO D*8
177 NEXT T
179 IF X<=LEN N$ THEN GOTO 0157
181 RETURN
183 REM %P%R%I%N%T%S% %B%A%R%S%.
185 FOR I=1 TO 30
187 PRINT AT 1,I;"-";AT 3,I;"-";AT 5,I;"-"
189 PRINT AT 7,I;"-";AT 9,I;"-"
191 NEXT I
193 FOR I=1 TO 9
195 PRINT AT I,1;"\: ";AT I,2;"\ :"
197 PRINT AT 14,1;"%D";AT 16,1;"%C%U%R%R%E%N%T %O%C%T%A%V%E%: "
199 NEXT I
201 SLOW
203 IF TT+T>=30 THEN GOTO 0457
205 PRINT AT A,TT+T;"\##"
207 IF INKEY$="" THEN GOTO 0207
209 IF INKEY$="O" THEN GOTO 0451
213 IF INKEY$="5" THEN GOTO 0243
215 IF INKEY$="M" THEN GOTO 715
217 IF INKEY$<>"A" THEN GOTO 0223
219 LET L=1
221 GOSUB 0307
223 IF INKEY$="Q" THEN GOSUB 0431
225 IF INKEY$="P" THEN GOSUB 0491
227 IF INKEY$="6" THEN GOSUB 0255
229 IF INKEY$="7" THEN GOSUB 0267
231 IF INKEY$="8" THEN GOTO 0279
233 IF INKEY$="D" THEN GOTO 0445
235 IF INKEY$="R" THEN GOSUB 0289
237 IF INKEY$="F" THEN GOSUB 0517
239 IF INKEY$="S" THEN GOSUB 0307
241 GOTO 0203
243 IF TT+T>3 THEN LET T=T-1
245 PRINT AT A,TT+T+1;V$(A,T+1)
247 PRINT AT A,TT+T;"\##"
249 LET PP=1
251 GOTO 0207
253 RETURN
255 PRINT AT A,TT+T;" "
257 IF A=10 OR A=11 OR A=2 OR A=4 OR A=6 OR A=8 THEN GOTO 0261
259 PRINT AT A,TT+T;"-"
261 IF A<11 THEN LET A=A+1
263 LET W=130
265 RETURN
267 PRINT AT A,TT+T;" "
269 IF A=10 OR A=11 OR A=2 OR A=4 OR A=6 OR A=8 THEN GOTO 0273
271 PRINT AT A,TT+T;"-"
273 IF A>1 THEN LET A=A-1
275 LET W=130
277 RETURN
279 PRINT AT A,TT+T;V$(A,T)
281 LET PP=0
283 IF TT+T<30 THEN LET T=T+1
285 PRINT AT A,TT+T;"\##"
287 GOTO 0207
289 FAST
291 LET U=T
293 GOSUB 0133
295 FOR I=1 TO REP-1
297 GOSUB 0133
299 NEXT I
301 SLOW
303 LET T=U
305 GOTO 0205
307 IF B+(T+1)<=LEN N$ THEN GOTO 0327
309 LET L$=N$
311 DIM N$(B+(T+1))
313 LET N$(1 TO LEN N$-2)=L$
315 LET L$=X$
317 DIM X$(B+(T+1))
319 LET X$(1 TO LEN X$-2)=L$
321 LET L$=D$
323 DIM D$(B+(T+1))
325 LET D$(1 TO LEN D$-2)=L$
327 IF L=1 THEN GOTO 0665
329 LET SEMI=6
331 IF A=1 THEN LET N$(B+(T))="F"
333 IF A=2 THEN LET N$(B+(T))="E"
335 IF A=3 THEN LET N$(B+(T))="D"
337 IF A=4 THEN LET N$(B+(T))="C"
339 IF A=5 THEN LET N$(B+T)="B"
341 IF A=6 THEN LET N$(B+T)="A"
343 IF A=7 THEN LET N$(B+T)="G"
345 IF A=8 THEN LET N$(B+T)="F"
347 IF A=9 THEN LET N$(B+T)="E"
349 IF A=10 THEN LET N$(B+T)="D"
351 IF A=11 THEN LET N$(B+(T))="C"
353 LET V$(A,T)="$"
355 LET D$(B+(T))=J$(G)
357 LET X$(B+(T))=K$(Q)
359 IF A=SEMI OR A=5 OR A=2 OR A=1 OR A=3 OR A=4 THEN LET X$(B+(T))=K$(Q+1)
361 IF PP=0 THEN GOTO 0371
363 FOR I=1 TO 11
365 IF I=1 OR I=3 OR I=5 OR I=7 OR I=9 THEN PRINT AT I,TT+T;"-"
367 IF I=2 OR I=4 OR I=6 OR I=8 OR I=10 OR I=11 THEN PRINT AT I,TT+T;" "
369 NEXT I
371 PRINT AT A,TT+T;V$(A,T)
373 LET T=T+1
375 PRINT AT 14,(TT+T)-1;G
377 PRINT AT 16,16;Q
379 RETURN
381 REM %I%N%I%T%I%A%L%I%Z%A%T%I%O%N% %S%U%B
383 PRINT AT 10,2;"INPUT NOTE DURATION(1 TO 8)"
385 LET B=0
387 LET L=0
389 LET Q=0
391 LET W=128
393 LET H=9000
395 LET A=11
397 LET C=1
399 LET T=1
401 LET TT=2
403 LET PP=0
405 LET REP=0
407 IF INKEY$="" THEN GOTO 0407
409 LET G=VAL INKEY$
411 LET J$="12345678"
413 CLS
415 PRINT AT 10,5;"INPUT OCTAVE(1 TO 6)"
417 IF INKEY$="" THEN GOTO 0417
419 LET Y=(VAL INKEY$)+1
421 LET K$="123456788"
423 LET Q=VAL K$(Y+1)
425 CLS
427 RETURN
429 REM %S%U%B% %T%O% %R%E%P%E%A%T% %T%U%N%E%.
431 PRINT AT 18,1;" YOU WANT THE TUNE TO REPEAT..."
433 PRINT AT 20,5;"HOW MANY TIMES?"
435 INPUT Y
437 LET REP=Y
439 PRINT AT 18,1;" "
441 PRINT AT 20,1;" "
443 RETURN
445 INPUT Y
447 IF Y>0 AND Y<9 THEN LET G=Y
449 GOTO 0205
451 INPUT Y
453 IF Y>0 AND Y<9 THEN LET Q=Y
455 GOTO 0205
457 REM %S%U%B%.% %T%O% %P%R%I%N%T% %2%N%D% %S%E%T% %O%F% %B%A%R%S
459 FOR I=1 TO 19
461 SCROLL
463 NEXT I
465 LET A=11
467 LET B=B+T-1
469 LET T=1
471 LET V$(1)="-----------------------------"
473 LET V$(2)=" "
475 FOR I=1 TO 9 STEP 2
477 LET V$(I)=V$(1)
479 LET V$(I+1)=V$(2)
481 NEXT I
483 LET V$(11)=V$(2)
485 PRINT AT 14,1;"%D";AT 16,1;"%C%U%R%R%E%N%T %O%C%T%A%V%E%: "
487 GOTO 0183
489 REM %S%U%B% %T%O% %C%H%A%N%G%E% %A%L%L% %O%C%T%A%V%E%S\:
491 PRINT AT 20,2;"INCREASE OR DECREASE?"
493 LET P$="12345678"
495 INPUT Y
497 FOR S=1 TO LEN X$-1
499 LET V=VAL X$(S)
501 IF Y=7 AND V<8 THEN LET V=V+1
503 IF Y=6 AND V>1 THEN LET V=V-1
505 LET X$(S)=P$(V)
507 NEXT S
509 PRINT AT 16,16;X$(LEN X$-1)
511 PRINT AT 20,2;" "
513 RETURN
515 REM %S%U%B% %T%O% %C%H%A%N%G%E% %O%V%E%R%A%L%L% % % %S%P%E%E%D% %O%F% %P%L%A%Y%.
517 PRINT AT 20,2;"INCREASE OR DECREASE?"
519 INPUT Y
521 FOR S=1 TO LEN D$-1
523 LET V=VAL D$(S)
525 IF Y=7 AND V<9 THEN LET V=V+1
527 IF Y=6 AND V>1 THEN LET V=V-1
529 LET D$(S)=J$(V)
531 NEXT S
533 PRINT AT 14,3;D$(B+1 TO )
535 PRINT AT 20,2;" "
537 RETURN
663 REM %S%E%M%I%T%O%N%E% %S%U%B%.
665 IF A=1 THEN LET N$(B+T)="F"
667 IF A=2 THEN LET N$(B+T)="%E"
669 IF A=3 THEN LET N$(B+T)="%D"
671 IF A=4 THEN LET N$(B+T)="%C"
673 IF A=5 THEN LET N$(B+T)="%B"
675 IF A=6 THEN LET N$(B+T)="%A"
677 IF A=7 THEN LET N$(B+T)="%G"
679 IF A=8 THEN LET N$(B+T)="%F"
681 IF A=9 THEN LET N$(B+T)="%E"
683 IF A=10 THEN LET N$(B+T)="%D"
685 IF A=11 THEN LET N$(B+T)="%C"
687 LET L=0
689 LET SEMI=5
691 GOTO 0353
693 PRINT AT 5,8;"% % % % % % % % % % % % "
695 PRINT AT 6,8;"% % % % "
697 PRINT AT 7,8;"% % % % "
699 PRINT AT 8,8;"% % % % % % % % % % % % "
701 PRINT AT 9,8;"% % % "
703 PRINT AT 10,8;"% % % % % % % % % "
705 PRINT AT 12,12;"PRESENT"
707 PRINT AT 15,11;"%C%O%M%P%O%S%E%R% "
709 FOR F=1 TO 15
711 NEXT F
713 CLS
714 RETURN
715 PRINT AT 20,2;"START TAPE AND PRESS ""G"""
716 IF INKEY$<>"G" THEN GOTO 716
717 PRINT AT 20,2;" "
718 SAVE "COMPOSE%R"
719 GOTO 0203
721 SAVE "COMPOSE%R"
723 GOTO 1
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
People
No people associated with this content.
A\ED\FB\C9 itemtype='https://schema.org/Blog' itemscope='itemscope' class="wp-singular computer_media-template-default single single-computer_media postid-56670 wp-custom-logo wp-embed-responsive wp-theme-astra wp-child-theme-astra-child ast-desktop ast-separate-container ast-left-sidebar astra-4.12.7 group-blog ast-blog-single-style-1 ast-custom-post-type ast-single-post ast-inherit-site-logo-transparent ast-hfb-header ast-full-width-primary-header ast-box-layout ast-normal-title-enabled astra-addon-4.12.5"C
3 GOSUB 069Z
5 GOTO 0037
7 IF N=1 AND J>1 THEN GOTO 0033
9 IF N>1 THEN GOTO 0015
11 LET N=N+1
13 RETURN
15 LET F=256*CODE O$(J,N-1,1)+CODE O$(J,N-1,2)
17 LET F1=256*CODE O$(J,N,1)+CODE O$(J,N,2)
19 LET F=INT ((F1+F)/2+.5)
21 LET R$(J,N,2)=CHR$ (F-(INT (F/256)*256))
23 LET R$(J,N,1)=CHR$ (INT (F/256))
25 LET N=N+1
27 RETURN
29 LET C=USR 16518
31 RETURN
33 LET F1=256*CODE O$(J-1,N,1)+CODE O$(J-1,N,2)
35 GOTO 0019
37 DIM V$(11,28)
39 LET V$(1)="---------------------------"
41 FOR I=3 TO 9 STEP 2
43 LET V$(I)=V$(1)
45 NEXT I
47 FOR I=2 TO 10 STEP 2
49 LET V$(I)=" "
51 NEXT I
53 LET V$(11)=" "
55 DIM R$(8,7,2)
57 LET U=0
59 LET G=1
61 GOSUB 0383
63 DIM O$(8,7,2)
65 DIM X$(3)
67 DIM N$(3)
69 DIM D$(3)
71 FAST
73 FOR J=8 TO 1 STEP -1
75 LET N=1
77 LET F=INT ((26*2**(J-1)+2**(J-1)*(2/3))+.5)
79 GOSUB 0125
81 GOSUB 0007
83 LET F=INT (30*2**(J-1)+.5)
85 GOSUB 0125
87 GOSUB 0007
89 LET F=INT (32*2**(J-1)+.5)
91 GOSUB 0125
93 GOSUB 0007
95 LET F=INT (36*2**(J-1)+.5)
97 GOSUB 0125
99 GOSUB 0007
101 LET F=INT (40*2**(J-1)+.5)
103 GOSUB 0125
105 GOSUB 0007
107 LET F=INT ((42*2**(J-1)+2**(J-1)*(2/3))+.5)
109 GOSUB 0125
111 GOSUB 0007
113 LET F=INT (48*2**(J-1)+.5)
115 GOSUB 0125
117 GOSUB 0007
119 NEXT J
121 GOTO 0183
123 REM %F%O%R% %N%O%T%E%S%.
125 LET F=INT ((1/(F*17E-6))+.5)
127 LET O$(J,N,2)=CHR$ (F-(INT (F/256)*256))
129 LET O$(J,N,1)=CHR$ (INT (F/256))
131 RETURN
133 FOR X=1 TO LEN N$
135 LET N=CODE N$(X)-37
137 LET J=CODE X$(X)-28
139 LET D=(CODE D$(X)-28)*16
141 IF N<=128 THEN GOTO 0147
143 GOTO 0161
145 NEXT X
147 IF N<0 THEN GOTO 0175
149 POKE 16516,CODE O$(J,N,2)
151 POKE 16517,CODE O$(J,N,1)
153 POKE 16515,D
155 LET C=USR 16518
157 NEXT X
159 RETURN
161 LET N=N-128
163 POKE 16516,CODE R$(J,N,2)
165 POKE 16517,CODE R$(J,N,1)
167 POKE 16515,D
169 LET C=USR 16518
171 NEXT X
173 RETURN
175 FOR T=1 TO D*8
177 NEXT T
179 IF X<=LEN N$ THEN GOTO 0157
181 RETURN
183 REM %P%R%I%N%T%S% %B%A%R%S%.
185 FOR I=1 TO 30
187 PRINT AT 1,I;"-";AT 3,I;"-";AT 5,I;"-"
189 PRINT AT 7,I;"-";AT 9,I;"-"
191 NEXT I
193 FOR I=1 TO 9
195 PRINT AT I,1;"\: ";AT I,2;"\ :"
197 PRINT AT 14,1;"%D";AT 16,1;"%C%U%R%R%E%N%T %O%C%T%A%V%E%: "
199 NEXT I
201 SLOW
203 IF TT+T>=30 THEN GOTO 0457
205 PRINT AT A,TT+T;"\##"
207 IF INKEY$="" THEN GOTO 0207
209 IF INKEY$="O" THEN GOTO 0451
213 IF INKEY$="5" THEN GOTO 0243
215 IF INKEY$="M" THEN GOTO 715
217 IF INKEY$<>"A" THEN GOTO 0223
219 LET L=1
221 GOSUB 0307
223 IF INKEY$="Q" THEN GOSUB 0431
225 IF INKEY$="P" THEN GOSUB 0491
227 IF INKEY$="6" THEN GOSUB 0255
229 IF INKEY$="7" THEN GOSUB 0267
231 IF INKEY$="8" THEN GOTO 0279
233 IF INKEY$="D" THEN GOTO 0445
235 IF INKEY$="R" THEN GOSUB 0289
237 IF INKEY$="F" THEN GOSUB 0517
239 IF INKEY$="S" THEN GOSUB 0307
241 GOTO 0203
243 IF TT+T>3 THEN LET T=T-1
245 PRINT AT A,TT+T+1;V$(A,T+1)
247 PRINT AT A,TT+T;"\##"
249 LET PP=1
251 GOTO 0207
253 RETURN
255 PRINT AT A,TT+T;" "
257 IF A=10 OR A=11 OR A=2 OR A=4 OR A=6 OR A=8 THEN GOTO 0261
259 PRINT AT A,TT+T;"-"
261 IF A<11 THEN LET A=A+1
263 LET W=130
265 RETURN
267 PRINT AT A,TT+T;" "
269 IF A=10 OR A=11 OR A=2 OR A=4 OR A=6 OR A=8 THEN GOTO 0273
271 PRINT AT A,TT+T;"-"
273 IF A>1 THEN LET A=A-1
275 LET W=130
277 RETURN
279 PRINT AT A,TT+T;V$(A,T)
281 LET PP=0
283 IF TT+T<30 THEN LET T=T+1
285 PRINT AT A,TT+T;"\##"
287 GOTO 0207
289 FAST
291 LET U=T
293 GOSUB 0133
295 FOR I=1 TO REP-1
297 GOSUB 0133
299 NEXT I
301 SLOW
303 LET T=U
305 GOTO 0205
307 IF B+(T+1)<=LEN N$ THEN GOTO 0327
309 LET L$=N$
311 DIM N$(B+(T+1))
313 LET N$(1 TO LEN N$-2)=L$
315 LET L$=X$
317 DIM X$(B+(T+1))
319 LET X$(1 TO LEN X$-2)=L$
321 LET L$=D$
323 DIM D$(B+(T+1))
325 LET D$(1 TO LEN D$-2)=L$
327 IF L=1 THEN GOTO 0665
329 LET SEMI=6
331 IF A=1 THEN LET N$(B+(T))="F"
333 IF A=2 THEN LET N$(B+(T))="E"
335 IF A=3 THEN LET N$(B+(T))="D"
337 IF A=4 THEN LET N$(B+(T))="C"
339 IF A=5 THEN LET N$(B+T)="B"
341 IF A=6 THEN LET N$(B+T)="A"
343 IF A=7 THEN LET N$(B+T)="G"
345 IF A=8 THEN LET N$(B+T)="F"
347 IF A=9 THEN LET N$(B+T)="E"
349 IF A=10 THEN LET N$(B+T)="D"
351 IF A=11 THEN LET N$(B+(T))="C"
353 LET V$(A,T)="$"
355 LET D$(B+(T))=J$(G)
357 LET X$(B+(T))=K$(Q)
359 IF A=SEMI OR A=5 OR A=2 OR A=1 OR A=3 OR A=4 THEN LET X$(B+(T))=K$(Q+1)
361 IF PP=0 THEN GOTO 0371
363 FOR I=1 TO 11
365 IF I=1 OR I=3 OR I=5 OR I=7 OR I=9 THEN PRINT AT I,TT+T;"-"
367 IF I=2 OR I=4 OR I=6 OR I=8 OR I=10 OR I=11 THEN PRINT AT I,TT+T;" "
369 NEXT I
371 PRINT AT A,TT+T;V$(A,T)
373 LET T=T+1
375 PRINT AT 14,(TT+T)-1;G
377 PRINT AT 16,16;Q
379 RETURN
381 REM %I%N%I%T%I%A%L%I%Z%A%T%I%O%N% %S%U%B
383 PRINT AT 10,2;"INPUT NOTE DURATION(1 TO 8)"
385 LET B=0
387 LET L=0
389 LET Q=0
391 LET W=128
393 LET H=9000
395 LET A=11
397 LET C=1
399 LET T=1
401 LET TT=2
403 LET PP=0
405 LET REP=0
407 IF INKEY$="" THEN GOTO 0407
409 LET G=VAL INKEY$
411 LET J$="12345678"
413 CLS
415 PRINT AT 10,5;"INPUT OCTAVE(1 TO 6)"
417 IF INKEY$="" THEN GOTO 0417
419 LET Y=(VAL INKEY$)+1
421 LET K$="123456788"
423 LET Q=VAL K$(Y+1)
425 CLS
427 RETURN
429 REM %S%U%B% %T%O% %R%E%P%E%A%T% %T%U%N%E%.
431 PRINT AT 18,1;" YOU WANT THE TUNE TO REPEAT..."
433 PRINT AT 20,5;"HOW MANY TIMES?"
435 INPUT Y
437 LET REP=Y
439 PRINT AT 18,1;" "
441 PRINT AT 20,1;" "
443 RETURN
445 INPUT Y
447 IF Y>0 AND Y<9 THEN LET G=Y
449 GOTO 0205
451 INPUT Y
453 IF Y>0 AND Y<9 THEN LET Q=Y
455 GOTO 0205
457 REM %S%U%B%.% %T%O% %P%R%I%N%T% %2%N%D% %S%E%T% %O%F% %B%A%R%S
459 FOR I=1 TO 19
461 SCROLL
463 NEXT I
465 LET A=11
467 LET B=B+T-1
469 LET T=1
471 LET V$(1)="-----------------------------"
473 LET V$(2)=" "
475 FOR I=1 TO 9 STEP 2
477 LET V$(I)=V$(1)
479 LET V$(I+1)=V$(2)
481 NEXT I
483 LET V$(11)=V$(2)
485 PRINT AT 14,1;"%D";AT 16,1;"%C%U%R%R%E%N%T %O%C%T%A%V%E%: "
487 GOTO 0183
489 REM %S%U%B% %T%O% %C%H%A%N%G%E% %A%L%L% %O%C%T%A%V%E%S\:
491 PRINT AT 20,2;"INCREASE OR DECREASE?"
493 LET P$="12345678"
495 INPUT Y
497 FOR S=1 TO LEN X$-1
499 LET V=VAL X$(S)
501 IF Y=7 AND V<8 THEN LET V=V+1
503 IF Y=6 AND V>1 THEN LET V=V-1
505 LET X$(S)=P$(V)
507 NEXT S
509 PRINT AT 16,16;X$(LEN X$-1)
511 PRINT AT 20,2;" "
513 RETURN
515 REM %S%U%B% %T%O% %C%H%A%N%G%E% %O%V%E%R%A%L%L% % % %S%P%E%E%D% %O%F% %P%L%A%Y%.
517 PRINT AT 20,2;"INCREASE OR DECREASE?"
519 INPUT Y
521 FOR S=1 TO LEN D$-1
523 LET V=VAL D$(S)
525 IF Y=7 AND V<9 THEN LET V=V+1
527 IF Y=6 AND V>1 THEN LET V=V-1
529 LET D$(S)=J$(V)
531 NEXT S
533 PRINT AT 14,3;D$(B+1 TO )
535 PRINT AT 20,2;" "
537 RETURN
663 REM %S%E%M%I%T%O%N%E% %S%U%B%.
665 IF A=1 THEN LET N$(B+T)="F"
667 IF A=2 THEN LET N$(B+T)="%E"
669 IF A=3 THEN LET N$(B+T)="%D"
671 IF A=4 THEN LET N$(B+T)="%C"
673 IF A=5 THEN LET N$(B+T)="%B"
675 IF A=6 THEN LET N$(B+T)="%A"
677 IF A=7 THEN LET N$(B+T)="%G"
679 IF A=8 THEN LET N$(B+T)="%F"
681 IF A=9 THEN LET N$(B+T)="%E"
683 IF A=10 THEN LET N$(B+T)="%D"
685 IF A=11 THEN LET N$(B+T)="%C"
687 LET L=0
689 LET SEMI=5
691 GOTO 0353
693 PRINT AT 5,8;"% % % % % % % % % % % % "
695 PRINT AT 6,8;"% % % % "
697 PRINT AT 7,8;"% % % % "
699 PRINT AT 8,8;"% % % % % % % % % % % % "
701 PRINT AT 9,8;"% % % "
703 PRINT AT 10,8;"% % % % % % % % % "
705 PRINT AT 12,12;"PRESENT"
707 PRINT AT 15,11;"%C%O%M%P%O%S%E%R% "
709 FOR F=1 TO 15
711 NEXT F
713 CLS
714 RETURN
715 PRINT AT 20,2;"START TAPE AND PRESS ""G"""
716 IF INKEY$<>"G" THEN GOTO 716
717 PRINT AT 20,2;" "
718 SAVE "COMPOSE%R"
719 GOTO 0203
721 SAVE "COMPOSE%R"
723 GOTO 1
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.



