--- title: "Minuetz" id: 55108 type: "computer_media" slug: "minuetz" url: "http://localhost/computer_media/minuetz/" markdown_url: "http://localhost/computer_media/minuetz.md" published_at: "2024-06-16T15:02:25+00:00" modified_at: "2026-03-30T21:44:06+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/06/SCR-20240616-kajj.png" excerpt: "Roll dice or let the computer decide as this algorithmic composition program builds musical scores, renders notation on-screen, and plays them back through machine code routines." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" - name: "Music/Speech" slug: "music-speech" taxonomy: "category" url: "http://localhost/category/software/music-speech/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" genre: - name: "Music" slug: "music" taxonomy: "genre" url: "http://localhost/type/music/" media_contents: - id: 54965 title: "ISTUG Public Domain Library 5" type: "computer_media" url: "http://localhost/computer_media/istug-public-domain-library-5/" media_type: "Program" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/06/SCR-20240616-kajj.png" media_type_tags: "Music" --- # Minuetz 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: | Variable | Value | Use | | --- | --- | --- | | `B` | 1 | Unity, loop steps, POKE offsets | | `A` | 0 | Zero, BORDER/PAPER black, AT row/col | | `C` | 2 | Small counts, array indices | | `D` | 3 | Coordinates, note calculations | | `E` | 4 | INK colour (cyan), coordinates | | `F` | 6 | Dice face count, coordinates | | `VO` | 2 (default) | Volume | | `KY` | 5/6 | Playback key transpose ratio | | `O` | 256 (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: | Variable | Address | Purpose | | --- | --- | --- | | `DI` | 61998 | Dice animation/display render | | `SORT` | 62276 | Sort note data | | `RNDT` | 62209 | Base of random dice-total table (16 bytes) | | `DT` | 61518 | Dice translation table base | | `VT` | 62161 | Voice/note data table base | | `BV` | 62291 | Playback voice bitmask POKE target | | `PL` | 62350 | Music playback engine | | `RN` | 62281 | Random/part seed POKE target | | `LD` | 62159 | Left-die value POKE address | | `RD` | 62160 (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. ## 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 MF THEN GO TO 42 43 IF CO THEN INPUT "No. ON RIGHT DICE ?";L: IF LF 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 LF+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 LF+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 ```