Minuetz

This file is part of and ISTUG Public Domain Library 5. Download the collection to get this file.
Date: 198x
Type: Program
Platform(s): TS 2068

MINUETZ is a music composition and dice-game program that generates or records musical scores based on dice rolls, implementing an algorithmic composition system reminiscent of Mozart’s Musikalisches Würfelspiel. The program uses extensive machine code routines called via RANDOMIZE USR at addresses such as 61518, 61998, 62276, and 62350, handling music playback, sorting, and display tasks. Numeric constants are stored as single-letter and two-letter variables (B=1, C=2, D=3, E=4, F=6, etc.) initialised at line 3 to save memory and speed up execution. The score display routine at lines 8530–8775 renders musical notation graphically using PLOT, DRAW, and CIRCLE commands, including note stems, flags, ledger lines, and accidentals. A manuscript save/print facility allows compositions to be saved as SCREEN$ files or dumped to a printer via LPRINT and COPY.


Program Analysis

Program Structure

The program is organised into several functional blocks accessed via GO TO and GO SUB:

  1. Initialisation (lines 1–4): Jumps to line 3, sets up all numeric constants and hardware addresses.
  2. Main menu and dice loop (lines 36–44): Offers three modes (gamble, predict, auto-compose), collects 16 dice rolls into array M.
  3. Animation and display (lines 60, 200–205): Shows a brief dice-roll animation via a DATA-driven subroutine at 9000 (not listed but called), then displays the dice record.
  4. Composition and playback (lines 207–254): Calls score-display and playback routines, handles volume/tempo input, adjusts playback speed by POKEing tempo and re-scaling note data.
  5. Score rendering (lines 8530–8775): Draws musical staves and individual notes graphically.
  6. Note-classification subroutine (lines 8660–8732): Classifies notes into rhythmic values using array S.
  7. Notehead stem/flag drawing (lines 8771–8775): Small subroutines for different note types.
  8. Support routines (lines 9800–9930): Playback repeat, OUT-based sound initialisation, dice-record display, staff-line drawing, treble-clef drawing, random data seeding.
  9. Save/load block (lines 9998–9999): Saves the BASIC program and a separate CODE block, then reloads on startup.

Constant-Variable Encoding

Line 3 encodes small integers into single- and two-letter variables to reduce token storage and speed arithmetic throughout the program. The mapping is:

VariableValueUse
B1Unity, loop steps, POKE offsets
A0Zero, BORDER/PAPER black, AT row/col
C2Small counts, array indices
D3Coordinates, note calculations
E4INK colour (cyan), coordinates
F6Dice face count, coordinates
VO2 (default)Volume
KY5/6Playback key transpose ratio
O256 (set line 36)Page size, GO TO targets

Derived targets such as O-F (250), O-E (252), O-D (253) are used in GO TO statements, exploiting the constant O=256 to avoid embedding literal line numbers.

Machine Code Subroutines

Several pre-loaded machine code routines are invoked via RANDOMIZE USR and RANDOMIZE USR PL. Their entry points are stored as named variables:

VariableAddressPurpose
DI61998Dice animation/display render
SORT62276Sort note data
RNDT62209Base of random dice-total table (16 bytes)
DT61518Dice translation table base
VT62161Voice/note data table base
BV62291Playback voice bitmask POKE target
PL62350Music playback engine
RN62281Random/part seed POKE target
LD62159Left-die value POKE address
RD62160 (LD+1)Right-die value POKE address

The code block is stored separately, saved as "minuetz C" CODE at address 58930, length 3642 bytes, and reloaded at line 9999 on each run.

Algorithmic Composition Mode

When mode 3 (auto-compose) is selected, line 36 calls GO SUB 9930, which seeds the 16-entry dice-total table (RNDT) with random values using POKE RNDT+N, RND*10. In modes 1 and 2 the dice totals are entered manually or generated by RND and then POKEd into the same table, providing a uniform interface to the machine code playback engine regardless of input method.

Score Rendering (Lines 8530–8775)

The graphical score renderer iterates over three voices (V=1 TO 3), two groups (G=1 TO 2), and five columns (L=0 TO 3). For each note position it reads pitch data from VT, calls the note-classifier at 8660 to populate array S with rhythmic values (whole, half, quarter, eighth etc.), then uses CIRCLE, PLOT, and DRAW to render noteheads, stems, flags, ledger lines, and accidentals. The variable Z tracks whether a ledger line is needed above or below the staff.

Note Classification (Lines 8660–8732)

The subroutine at 8660 classifies a 7-element note array N into rhythmic values stored in S. A rest is detected when N(2)=0 and assigned S(n)=5. Repeated adjacent pitches trigger the tie/beam logic in subroutine 8730, which walks backwards through preceding notes incrementing their S values. A special case at 8666 detects a three-note repeated chord and assigns value 2 to positions 2, 4, and 6.

Sound Output

Playback uses direct hardware port writes via OUT R,… and OUT RO,… (ports 245 and 246) to address the sound chip, consistent with the AY-3-8912 interface on the TS2068. Volume is applied by adding VO to the channel level bytes. Tempo is controlled by POKEing address 62385 with 115+L*F (where L is the tempo 1–10 and F=6), giving values 121–175. The playback speed scaling at line 254 multiplies each 16-bit note-duration word in the range 61416–61517 by KY (the key ratio), storing results back as a little-endian word pair.

Keyboard Detection and Joystick Check

Line 4 checks PEEK 61416 <> 148 to detect whether the machine code data has been loaded correctly; if not, it halves KY as a fallback. The PAUSE VAL "4E4" at line 40 waits up to approximately 167 seconds for a keypress to register a dice throw.

Notable Techniques

  • VAL "number" in GO TO/GO SUB targets reduces token storage for long line numbers.
  • The treble-clef drawing at lines 9924–9925 uses a sequence of arc-DRAW commands (with non-zero third parameters for circular arcs) to approximate the clef shape.
  • Staff lines are drawn in a loop at 9923 using horizontal DRAW from x=0 to x=255 (O-B), iterating with a STEP of F (6 pixels) for line spacing.
  • DIM M(16) is used to store the sum of each pair of dice rolls; after the loop it is re-dimensioned to DIM M(1) at line 207 to free memory before composition display.
  • The manuscript print path at line 226 uses LPRINT, COPY, and a second LPRINT to title and hard-copy the screen, while the save path at line 224 jumps to GO TO C (line 2), which executes SAVE A$ SCREEN$.
  • Line 205 scrolls 22 lines using POKE 23692,-B (setting the scroll counter to 255) to suppress the “scroll?” prompt during screen clearing.

Bugs and Anomalies

  • Line 36 references variable e (lowercase) in AT F+E+E,e, but only E (uppercase) is defined. In Sinclair BASIC variable names are case-insensitive for numeric variables, so this resolves correctly to 4.
  • Line 9930 contains RANDOMIZE L*0, which always randomizes with seed 0 regardless of L. This appears to be a typo for RANDOMIZE L*O (capital letter O = 256), mirroring the idiom used at line 36.
  • Line 8570 uses lowercase v, y1 in the condition, mixed with uppercase elsewhere; these resolve to the same variables in Sinclair BASIC.
  • Subroutine 9005 (the dice-face drawing routine called from line 60) references variable s which is not initialised in that scope — it is the loop variable from the enclosing FOR at line 60, relying on the caller having set it correctly.

Content

Appears On

Library tape of the Indiana Sinclair Timex User’s Group.

Related Products

Related Articles

Related Content

Image Gallery

Minuetz

Source Code

    1 GO TO VAL "3"
    2 SAVE A$ SCREEN$ : GO TO 230
    3 LET B=VAL "1": LET A=B-B: LET C=B+B: LET D=C+B:: LET E=D+B: LET F=E+C: LET VO=C: LET KY=(E+B)/F: LET R=VAL "245": LET RO=VAL "246": LET LD=VAL "62159": LET RD=LD+B: LET DT=VAL "61518": LET DI=VAL "61998": LET VT=VAL "62161": LET BV=VAL "62291": LET RN=VAL "62281": LET SORT=VAL "62276": LET RNDT=VAL "62209"
    4 LET PL=VAL "62350": IF PEEK 61416<>148 THEN LET KY=B/KY
   36 LET O=VAL "256": LET L=RND*O: RANDOMIZE L*O: LET KY=B/KY: BORDER A: PAPER A: INK E+D: CLS : LET AU=A: LET CO=A: POKE VAL "23658",F+F: PLOT E*F,40: DRAW A,120: DRAW 220,A: DRAW A,-120: DRAW -220,A: OVER B: PRINT AT D,E;"DO YOU WISH TO :";AT F,E;" 1. GAMBLE WITH THE DICE ?          or,";AT F+E,E;" 2. PREDICT THE FALL OF             THE DICE ?   or";AT F+E+E,e;" 3. COMPOSE AUTOMATICALLY?": INPUT " ENTER 1,2 OR 3 ";A$: IF A$="3" THEN LET AU=B: GO SUB 9930: GO TO 207
   38 OVER A: IF A$="2" THEN LET CO=B
   40 INVERSE A: DIM M(16): FOR Q=A TO 15: PRINT AT 21,A;"THROW [ ENTER ]": PAUSE VAL "4E4"
   41 LET M=INT (RND*F)+B: RANDOMIZE RND*5E4: LET L=INT (RND*F)+B
   42 IF co THEN INPUT "No. ON LEFT DICE ?";M: IF M<B OR M>F THEN GO TO 42
   43 IF CO THEN INPUT "No. ON RIGHT DICE ?";L: IF L<B OR L>F THEN GO TO 43
   44 POKE LD,M: POKE RD,L: POKE RNDT+Q,L+M-C: LET M(Q+B)=L+M
   50 DATA 235,160,180,165,170,125,217,110
   60 CLS : RESTORE 50: FOR s=10 TO -17 STEP -27: FOR n=B TO C: READ x,y: GO SUB 9000: NEXT n: CLS : NEXT s
  200 PRINT AT A,A;: RANDOMIZE USR DI: PLOT E,171: DRAW 22,A: DRAW A,-14: DRAW -22,A: DRAW A,14: PRINT AT B,B; INVERSE B;Q+B: INVERSE A: NEXT Q: INPUT "'ENTER' TO CONTINUE ";A$
  205 FOR n=B TO 22: RANDOMIZE USR 2361: POKE 23692,-B: PRINT " ": NEXT n
  207 GO SUB VAL "9890": DIM M(1): IF AU THEN GO TO O-F
  210 LET P1=b: INPUT "Manuscript ?(Y/N)";a$: IF A$<>"Y" THEN GO TO O-F
  220 POKE RN,209: GO SUB VAL "9916": PRINT AT A,A;"PART ";P1: POKE RN,209+(P1-B)*8: GO SUB 8500: INPUT " SAVE manuscript ?(Y/N)";A$: IF A$<>"Y" THEN GO TO 230
  222 INPUT "TITLE ? ";A$: IF LEN A$>F+E THEN LET A$=A$( TO F+E)
  223 INPUT "Save SCREEN$ ? or Copy to       PRINTER ? (S/P) ";F$: IF F$="P" THEN GO TO 225
  224 IF F$="S" THEN GO TO C: GO TO 223
  226 LPRINT A$: LPRINT : COPY : LPRINT 
  230 LET P1=P1+B: IF P1=C THEN GO TO 220
  252 INPUT "ENTER VOLUME(1-10) ";VO: IF VO>F+4 THEN GO TO O-E
  253 INPUT "TEMPO (1-10) ";L: IF L<B OR L>F+4 THEN GO TO O-D
  254 POKE VAL "62385",115+L*F: INPUT "'ENTER' TO CONTINUE ";A$: FOR N=VAL "61416" TO VAL "61517" STEP C: LET L=(PEEK N+O*PEEK (N+B))*KY: POKE N,L-(INT (L/O)*O): POKE N+B,INT (L/O): NEXT N: GO SUB VAL "9820": CLS : GO TO F
 8530 LET Z=A: POKE BV,240: DIM N(7): FOR V=B TO D: RANDOMIZE USR SORT: LET TS=VT: LET YS=CODE "P": FOR G=B TO C: FOR L=A TO D
 8540 DIM A(7): FOR T=C TO F+B: LET N(T)=PEEK TS: LET TS=TS+B: IF N(T)>127 THEN LET A(T)=B: LET N(T)=N(T)-128
 8548 NEXT T: GO SUB 8660
 8550 LET P=18+L*60: FOR T=C TO F+B
 8560 LET U=B: IF NOT S(T) THEN GO TO 8630
 8570 LET X=P+(T-C)*10: LET Y1=(N(T)-B)*D: IF (v=D AND y1>14) OR (V=B AND Y1>68) OR (V=C AND Y1>50) THEN LET U=-B
 8575 IF NOT Y1 OR Y1=F OR Y1>72 AND V=B AND INT (Y1/F)=Y1/F THEN LET Z=B
 8585 LET Y=YS+Y1: IF S(T)<>5 THEN GO TO 8610
 8590 IF V=C THEN GO TO 8630
 8600 LET Y=YS+32: IF V=B THEN LET Y=Y+32
 8605 CIRCLE X,Y,B: GO SUB 8775: GO TO 8630
 8620 CIRCLE X,Y,B: CIRCLE X,Y,C: PLOT X+C*U,Y: DRAW A,11*U: GO SUB 8770+S(T)
 8625 IF Z THEN PLOT X-E,Y: DRAW 8,A: IF V=Z THEN PLOT X-E,Y-F: DRAW 8,A
 8626 IF z AND v=D THEN PLOT x-E,y+F: DRAW F,A
 8627 IF A(T) THEN PLOT X-8,Y: DRAW F,A: PLOT X-8,Y+C: DRAW F,A: PLOT X-7,Y-C: DRAW A,F: PLOT X-E,Y-C: DRAW A,F
 8630 LET Z=A: NEXT T: NEXT L: LET YS=C: NEXT G: POKE BV,240+V: NEXT V: RETURN 
 8660 DIM S(7): IF NOT N(C) THEN GO TO 8670
 8665 LET Q=A: FOR N=C TO F: IF N(N)<>N(N+B) THEN LET Q=B
 8666 NEXT N: IF NOT Q THEN LET S(C)=C: LET S(E)=C: LET S(F)=C: RETURN 
 8670 FOR N=C TO 7: IF NOT n(n) THEN LET s(n)=5: LET n=n+B: GO TO 8710
 8690 IF n(n)=n(n-B) THEN GO SUB 8730: GO TO 8710
 8700 LET s(n)=B
 8710 NEXT n: RETURN 
 8730 FOR Q=B TO D: IF N(N-Q-B)<>N(N-Q) THEN LET S(N-Q)=S(N-Q)+B: RETURN 
 8732 NEXT Q: LET S(N-E)=S(N-E)+B: RETURN 
 8771 DRAW D,-E*U: DRAW -D,E*U: DRAW A,-E: DRAW D,-E*U: RETURN 
 8772 DRAW D,-E*U: RETURN 
 8773 DRAW D,-E*U: CIRCLE X+E,Y,B: RETURN 
 8774 RETURN 
 8775 DRAW 5,C,PI: DRAW -D,-8: RETURN 
 9005 PLOT X,Y: DRAW s,s*D/E: DRAW s,-s/C: DRAW -s,-s: DRAW -s,s*D/E: DRAW A,-s: DRAW s,-s*D/E: DRAW OVER B;A,s: DRAW A,-s: DRAW s,s: DRAW A,s: RETURN 
 9800 POKE VAL "62291",240: INPUT "Repeat ?",a$: IF A$<>"Y" THEN RETURN 
 9810 INPUT "ENTER VOLUME(1-10) ";VO: IF VO>F+4 THEN GO TO 9810
 9815 INPUT "TEMPO(1-10) ";L: IF L<B OR L>F+4 THEN GO TO 9815
 9816 POKE VAL "62385",115+L*F
 9830 POKE VAL "62291",240: FOR N=A TO E+B: OUT R,N: OUT RO,A: NEXT N: OUT R,E+E: OUT RO,E+VO: OUT R,F+D: OUT RO,E+VO: OUT R,F+E: OUT RO,E+VO: OUT R,F+B: OUT RO,248
 9835 RANDOMIZE USR PL: OUT R,F+B: OUT RO,-B: GO TO VAL "9800"
 9890 LET p=DT-11: FOR n=A TO 15: LET p=p+11: POKE RNDT+N,PEEK (P+PEEK (RNDT+N)): NEXT N: IF AU THEN RETURN 
 9900 CLS : PRINT AT C,F+E;"Dice Record": PLOT A,145: DRAW O-B,A: DRAW A,-70: DRAW -O+B,A: DRAW A,70: PLOT 60,75: DRAW A,70: OVER B: FOR K=A TO B: PRINT AT F+K*E,B;"PART ";K+B;: FOR N=B TO 8: PRINT TAB 5+N*D;M(K*8+N);: NEXT n: NEXT K: OVER A: RETURN 
 9916 BORDER D+E: PAPER D+E: INK F+D: CLS : LET Q=9923: LET L=14: GO SUB Q: LET L=N+F: GO SUB Q: LET L=N+C*F: GO SUB Q: LET L=N+F: GO SUB Q: LET L=44: GO SUB Q+B: LET L=122: GO SUB Q+B: LET M=F*F: PLOT C,M:: GO SUB Q+C: LET M=114: PLOT C,M: GO SUB Q+C
 9917 FOR N=O-B TO 45 STEP -61: PLOT N,14: DRAW A,60: PLOT N,92: DRAW A,60: NEXT N: PLOT O-E,14: DRAW A,60: LET L=O-F: CIRCLE L,23,B: CIRCLE L,29,B: CIRCLE L,59,B: CIRCLE L,65,B: RETURN 
 9923 FOR N=L TO L+E*F STEP F: PLOT A,N: DRAW O-B,A: NEXT N: RETURN 
 9924 PLOT E,L: DRAW E,A,C: DRAW -D,32,-.1: DRAW E,A,-C: DRAW -9,-F*D,-.6: DRAW F+F,A,3.8: DRAW -F-C,-E,D: CIRCLE E,L,B: RETURN 
 9925 DRAW F,A,-C: DRAW -F,-C*F,-1.2: CIRCLE C,M-B,B: CIRCLE 11,M,B: CIRCLE 11,M-F,B: RETURN 
 9930 LET L=RND*O: RANDOMIZE L*0: FOR N=A TO 15: POKE RNDT+N,RND*10: NEXT N: RETURN 
 9998 SAVE "MINUETZ" LINE 9999: BEEP .4,15: SAVE "minuetz C"CODE 58930,3642: STOP 
 9999 PRINT AT 10,1;"Leave recorder running","  MINUETZ data now loading": LOAD "minuetz C"CODE : GO TO 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