Composer

Products: Composer
Date: 198x
Type: Cassette
Platform(s): TS 1000
Tags: Music

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:

  1. Lines 1–131: Machine code (REM line 1), initialization entry points, note interpolation subroutines, and frequency precomputation.
  2. Lines 133–181: Playback engine — iterates over the note/octave/duration string arrays and calls the machine code driver.
  3. Lines 183–241: Main loop — draws the staff, handles keyboard input, dispatches to editing subroutines.
  4. Lines 243–287: Cursor movement (left/right, up/down across staff lines).
  5. Lines 289–379: Note entry, array resizing, display update.
  6. Lines 381–427: Initialization subroutine — prompts for duration and octave, zeros variables.
  7. Lines 429–537: Auxiliary subroutines: repeat, duration change, octave shift, speed change.
  8. Lines 663–691: Semitone entry branch (shares tail of note-entry subroutine).
  9. 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\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"

Composer

Products: Composer
Date: 198x
Type: Cassette
Platform(s): TS 1000
Tags: Music

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:

  1. Lines 1–131: Machine code (REM line 1), initialization entry points, note interpolation subroutines, and frequency precomputation.
  2. Lines 133–181: Playback engine — iterates over the note/octave/duration string arrays and calls the machine code driver.
  3. Lines 183–241: Main loop — draws the staff, handles keyboard input, dispatches to editing subroutines.
  4. Lines 243–287: Cursor movement (left/right, up/down across staff lines).
  5. Lines 289–379: Note entry, array resizing, display update.
  6. Lines 381–427: Initialization subroutine — prompts for duration and octave, zeros variables.
  7. Lines 429–537: Auxiliary subroutines: repeat, duration change, octave shift, speed change.
  8. Lines 663–691: Semitone entry branch (shares tail of note-entry subroutine).
  9. 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:

AddressVariablePurpose
16515D (duration × 16)Note duration
16516Low byte of timerSpeaker half-period low
16517High byte of timerSpeaker 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

KeyFunction
5Move cursor left
8Move cursor right / advance
6Move cursor down (lower pitch)
7Move cursor up (higher pitch)
SPlace natural note
APlace semitone (sharp/flat) note
RPlay back the tune (FAST mode)
QSet repeat count
PShift all octaves up or down
FChange overall playback speed
DChange note duration
OChange current octave
MSave 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

Appears On

Related Products

Write your own tunes in standard notation. You specify tempo, rhythm, note duration and pitch (within a range of six...

Related Articles

Related Content

Image Gallery

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.

Scroll to Top
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"

Composer

Products: Composer
Date: 198x
Type: Cassette
Platform(s): TS 1000
Tags: Music

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:

  1. Lines 1–131: Machine code (REM line 1), initialization entry points, note interpolation subroutines, and frequency precomputation.
  2. Lines 133–181: Playback engine — iterates over the note/octave/duration string arrays and calls the machine code driver.
  3. Lines 183–241: Main loop — draws the staff, handles keyboard input, dispatches to editing subroutines.
  4. Lines 243–287: Cursor movement (left/right, up/down across staff lines).
  5. Lines 289–379: Note entry, array resizing, display update.
  6. Lines 381–427: Initialization subroutine — prompts for duration and octave, zeros variables.
  7. Lines 429–537: Auxiliary subroutines: repeat, duration change, octave shift, speed change.
  8. Lines 663–691: Semitone entry branch (shares tail of note-entry subroutine).
  9. 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:

AddressVariablePurpose
16515D (duration × 16)Note duration
16516Low byte of timerSpeaker half-period low
16517High byte of timerSpeaker 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

KeyFunction
5Move cursor left
8Move cursor right / advance
6Move cursor down (lower pitch)
7Move cursor up (higher pitch)
SPlace natural note
APlace semitone (sharp/flat) note
RPlay back the tune (FAST mode)
QSet repeat count
PShift all octaves up or down
FChange overall playback speed
DChange note duration
OChange current octave
MSave 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

Appears On

Related Products

Write your own tunes in standard notation. You specify tempo, rhythm, note duration and pitch (within a range of six...

Related Articles

Related Content

Image Gallery

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.

Scroll to Top

Composer

Products: Composer
Date: 198x
Type: Cassette
Platform(s): TS 1000
Tags: Music

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:

  1. Lines 1–131: Machine code (REM line 1), initialization entry points, note interpolation subroutines, and frequency precomputation.
  2. Lines 133–181: Playback engine — iterates over the note/octave/duration string arrays and calls the machine code driver.
  3. Lines 183–241: Main loop — draws the staff, handles keyboard input, dispatches to editing subroutines.
  4. Lines 243–287: Cursor movement (left/right, up/down across staff lines).
  5. Lines 289–379: Note entry, array resizing, display update.
  6. Lines 381–427: Initialization subroutine — prompts for duration and octave, zeros variables.
  7. Lines 429–537: Auxiliary subroutines: repeat, duration change, octave shift, speed change.
  8. Lines 663–691: Semitone entry branch (shares tail of note-entry subroutine).
  9. 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:

AddressVariablePurpose
16515D (duration × 16)Note duration
16516Low byte of timerSpeaker half-period low
16517High byte of timerSpeaker 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

KeyFunction
5Move cursor left
8Move cursor right / advance
6Move cursor down (lower pitch)
7Move cursor up (higher pitch)
SPlace natural note
APlace semitone (sharp/flat) note
RPlay back the tune (FAST mode)
QSet repeat count
PShift all octaves up or down
FChange overall playback speed
DChange note duration
OChange current octave
MSave 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

Appears On

Related Products

Write your own tunes in standard notation. You specify tempo, rhythm, note duration and pitch (within a range of six...

Related Articles

Related Content

Image Gallery

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.

Scroll to Top
\E5\D3\FF\CD\A2\DB\FE\CD\A2\E1\B7\ED\EF\C9

Composer

Products: Composer
Date: 198x
Type: Cassette
Platform(s): TS 1000
Tags: Music

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:

  1. Lines 1–131: Machine code (REM line 1), initialization entry points, note interpolation subroutines, and frequency precomputation.
  2. Lines 133–181: Playback engine — iterates over the note/octave/duration string arrays and calls the machine code driver.
  3. Lines 183–241: Main loop — draws the staff, handles keyboard input, dispatches to editing subroutines.
  4. Lines 243–287: Cursor movement (left/right, up/down across staff lines).
  5. Lines 289–379: Note entry, array resizing, display update.
  6. Lines 381–427: Initialization subroutine — prompts for duration and octave, zeros variables.
  7. Lines 429–537: Auxiliary subroutines: repeat, duration change, octave shift, speed change.
  8. Lines 663–691: Semitone entry branch (shares tail of note-entry subroutine).
  9. 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:

AddressVariablePurpose
16515D (duration × 16)Note duration
16516Low byte of timerSpeaker half-period low
16517High byte of timerSpeaker 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

KeyFunction
5Move cursor left
8Move cursor right / advance
6Move cursor down (lower pitch)
7Move cursor up (higher pitch)
SPlace natural note
APlace semitone (sharp/flat) note
RPlay back the tune (FAST mode)
QSet repeat count
PShift all octaves up or down
FChange overall playback speed
DChange note duration
OChange current octave
MSave 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

Appears On

Related Products

Write your own tunes in standard notation. You specify tempo, rhythm, note duration and pitch (within a range of six...

Related Articles

Related Content

Image Gallery

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.

Scroll to Top
A\ED\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:

AddressVariablePurpose
16515D (duration × 16)Note duration
16516Low byte of timerSpeaker half-period low
16517High byte of timerSpeaker 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

KeyFunction
5Move cursor left
8Move cursor right / advance
6Move cursor down (lower pitch)
7Move cursor up (higher pitch)
SPlace natural note
APlace semitone (sharp/flat) note
RPlay back the tune (FAST mode)
QSet repeat count
PShift all octaves up or down
FChange overall playback speed
DChange note duration
OChange current octave
MSave 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

Appears On

Related Products

Write your own tunes in standard notation. You specify tempo, rhythm, note duration and pitch (within a range of six...

Related Articles

Related Content

Image Gallery

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"



Composer

Products: Composer
Date: 198x
Type: Cassette
Platform(s): TS 1000
Tags: Music

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:

  1. Lines 1–131: Machine code (REM line 1), initialization entry points, note interpolation subroutines, and frequency precomputation.
  2. Lines 133–181: Playback engine — iterates over the note/octave/duration string arrays and calls the machine code driver.
  3. Lines 183–241: Main loop — draws the staff, handles keyboard input, dispatches to editing subroutines.
  4. Lines 243–287: Cursor movement (left/right, up/down across staff lines).
  5. Lines 289–379: Note entry, array resizing, display update.
  6. Lines 381–427: Initialization subroutine — prompts for duration and octave, zeros variables.
  7. Lines 429–537: Auxiliary subroutines: repeat, duration change, octave shift, speed change.
  8. Lines 663–691: Semitone entry branch (shares tail of note-entry subroutine).
  9. 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:

AddressVariablePurpose
16515D (duration × 16)Note duration
16516Low byte of timerSpeaker half-period low
16517High byte of timerSpeaker 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

KeyFunction
5Move cursor left
8Move cursor right / advance
6Move cursor down (lower pitch)
7Move cursor up (higher pitch)
SPlace natural note
APlace semitone (sharp/flat) note
RPlay back the tune (FAST mode)
QSet repeat count
PShift all octaves up or down
FChange overall playback speed
DChange note duration
OChange current octave
MSave 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

Appears On

Related Products

Write your own tunes in standard notation. You specify tempo, rhythm, note duration and pitch (within a range of six...

Related Articles

Related Content

Image Gallery

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.

Scroll to Top
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"

Composer

Products: Composer
Date: 198x
Type: Cassette
Platform(s): TS 1000
Tags: Music

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:

  1. Lines 1–131: Machine code (REM line 1), initialization entry points, note interpolation subroutines, and frequency precomputation.
  2. Lines 133–181: Playback engine — iterates over the note/octave/duration string arrays and calls the machine code driver.
  3. Lines 183–241: Main loop — draws the staff, handles keyboard input, dispatches to editing subroutines.
  4. Lines 243–287: Cursor movement (left/right, up/down across staff lines).
  5. Lines 289–379: Note entry, array resizing, display update.
  6. Lines 381–427: Initialization subroutine — prompts for duration and octave, zeros variables.
  7. Lines 429–537: Auxiliary subroutines: repeat, duration change, octave shift, speed change.
  8. Lines 663–691: Semitone entry branch (shares tail of note-entry subroutine).
  9. 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:

AddressVariablePurpose
16515D (duration × 16)Note duration
16516Low byte of timerSpeaker half-period low
16517High byte of timerSpeaker 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

KeyFunction
5Move cursor left
8Move cursor right / advance
6Move cursor down (lower pitch)
7Move cursor up (higher pitch)
SPlace natural note
APlace semitone (sharp/flat) note
RPlay back the tune (FAST mode)
QSet repeat count
PShift all octaves up or down
FChange overall playback speed
DChange note duration
OChange current octave
MSave 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

Appears On

Related Products

Write your own tunes in standard notation. You specify tempo, rhythm, note duration and pitch (within a range of six...

Related Articles

Related Content

Image Gallery

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.

Scroll to Top

Composer

Products: Composer
Date: 198x
Type: Cassette
Platform(s): TS 1000
Tags: Music

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:

  1. Lines 1–131: Machine code (REM line 1), initialization entry points, note interpolation subroutines, and frequency precomputation.
  2. Lines 133–181: Playback engine — iterates over the note/octave/duration string arrays and calls the machine code driver.
  3. Lines 183–241: Main loop — draws the staff, handles keyboard input, dispatches to editing subroutines.
  4. Lines 243–287: Cursor movement (left/right, up/down across staff lines).
  5. Lines 289–379: Note entry, array resizing, display update.
  6. Lines 381–427: Initialization subroutine — prompts for duration and octave, zeros variables.
  7. Lines 429–537: Auxiliary subroutines: repeat, duration change, octave shift, speed change.
  8. Lines 663–691: Semitone entry branch (shares tail of note-entry subroutine).
  9. 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:

AddressVariablePurpose
16515D (duration × 16)Note duration
16516Low byte of timerSpeaker half-period low
16517High byte of timerSpeaker 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

KeyFunction
5Move cursor left
8Move cursor right / advance
6Move cursor down (lower pitch)
7Move cursor up (higher pitch)
SPlace natural note
APlace semitone (sharp/flat) note
RPlay back the tune (FAST mode)
QSet repeat count
PShift all octaves up or down
FChange overall playback speed
DChange note duration
OChange current octave
MSave 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

Appears On

Related Products

Write your own tunes in standard notation. You specify tempo, rhythm, note duration and pitch (within a range of six...

Related Articles

Related Content

Image Gallery

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.

Scroll to Top
\E5\D3\FF\CD\A2\DB\FE\CD\A2\E1\B7\ED\EF\C9

Composer

Products: Composer
Date: 198x
Type: Cassette
Platform(s): TS 1000
Tags: Music

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:

  1. Lines 1–131: Machine code (REM line 1), initialization entry points, note interpolation subroutines, and frequency precomputation.
  2. Lines 133–181: Playback engine — iterates over the note/octave/duration string arrays and calls the machine code driver.
  3. Lines 183–241: Main loop — draws the staff, handles keyboard input, dispatches to editing subroutines.
  4. Lines 243–287: Cursor movement (left/right, up/down across staff lines).
  5. Lines 289–379: Note entry, array resizing, display update.
  6. Lines 381–427: Initialization subroutine — prompts for duration and octave, zeros variables.
  7. Lines 429–537: Auxiliary subroutines: repeat, duration change, octave shift, speed change.
  8. Lines 663–691: Semitone entry branch (shares tail of note-entry subroutine).
  9. 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:

AddressVariablePurpose
16515D (duration × 16)Note duration
16516Low byte of timerSpeaker half-period low
16517High byte of timerSpeaker 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

KeyFunction
5Move cursor left
8Move cursor right / advance
6Move cursor down (lower pitch)
7Move cursor up (higher pitch)
SPlace natural note
APlace semitone (sharp/flat) note
RPlay back the tune (FAST mode)
QSet repeat count
PShift all octaves up or down
FChange overall playback speed
DChange note duration
OChange current octave
MSave 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

Appears On

Related Products

Write your own tunes in standard notation. You specify tempo, rhythm, note duration and pitch (within a range of six...

Related Articles

Related Content

Image Gallery

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.

Scroll to Top
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.

People

No people associated with this content.

Scroll to Top