--- title: "TS-2068 Piano" id: 72275 type: "computer_media" slug: "ts-2068-piano" url: "http://localhost/computer_media/ts-2068-piano/" markdown_url: "http://localhost/computer_media/ts-2068-piano.md" published_at: "2026-10-23T07:47:22+00:00" modified_at: "2026-10-23T07:47:22+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/10/piano-1.png" excerpt: "Turns the keyboard into a two-octave, three-voice AY piano with a drawn keyboard, and can record up to 1,000 events, play them back at four speeds and save them." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" 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/" indiv: - name: "Mike Rhees" slug: "mike-rhees" taxonomy: "indiv" url: "http://localhost/indiv/mike-rhees/" genre: - name: "Music" slug: "music" taxonomy: "genre" url: "http://localhost/type/music/" media_contents: - id: 72439 title: "SINCUS Exchange Tape 104" type: "computer_media" url: "http://localhost/computer_media/sincus-exchange-tape-104/" media_type: "Program" programmers: - name: "Mike Rhees" slug: "mike-rhees" taxonomy: "indiv" url: "http://localhost/indiv/mike-rhees/" download_url: "https://archive.org/download/timex-sinclair-software-archive/TS-2068%20Piano%20%28198x%29%28Rhees%2C%20Mike%29%28TS2068%29%28US%29%28Program%29.zip" tsrun_member: "TS-2068 Piano (198x)(Rhees, Mike)(TS2068)(US)(Program)/TS-2068 Piano (198x)(Rhees, Mike)(TS2068)(US)(Program).tap" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2026/10/piano-1.png" article_media: - id: 44618 title: "Piano" type: "article" url: "http://localhost/article/piano-2/" related_content: - id: 24804 title: "Timex/Sinclair User Group of Las Vegas" type: "usergroup" url: "http://localhost/usergroup/timex-sinclair-user-group-of-las-vegas/" media_type_tags: "Music" license: false --- # TS-2068 Piano Turns the computer keyboard into a two-octave piano played through the TS2068’s AY sound chip. The screen shows two rows of piano keys, each labelled with the computer key that plays it: the Q and number rows for the upper octave and the Z and A rows for the lower one. Up to three notes can sound at once, and each fades away after you release it. A mode menu offers Play, Record (up to 1,000 events), Playback at speed 1 to 4, and Save, which writes the program together with your recording to tape so it reloads ready to play back. The REM in line 10 reads “BASICpiano – Mike Rhess TSUG-LV”, the Timex/Sinclair User Group of Las Vegas; the site’s spelling of his name is Mike Rhees, who wrote the “Piano” article in the *CATS Newsletter* of January 1988. ## Program Analysis ### Structure - Lines 100–400: the play loop: call the keyboard scanner, set the AY tone and volume registers, and record events when in record mode. - Lines 1000–1090: playback, timed by a machine code delay at 65425. - Lines 6500–6550: save. Lines 7000–7110: the mode menu, with `ON ERR` so BREAK returns to it. - Lines 8000–8130: set up the pitch tables `a()`/`b()` from DATA and POKE 167 bytes of machine code to 65280–65446. - Lines 8700–8890: draw the keyboard and its key labels. ### Machine Code The 167 bytes are not documented in the listing. From the BASIC side, the routine at 65286 is called on every pass of the play loop and leaves up to three note numbers in 65280–65282 (0 for none). Playback calls 65442 once before starting, then calls 65425 and repeats the sound update while it returns a non-zero value, which paces the recorded events; the chosen speed is POKEd into 65433. ## Notable Techniques - Three AY channels are used: A and B for the first two notes with their own decaying volumes `e` and `f`, and channel C for a third note using the envelope generator (`SOUND 13,0`). - Each recorded event stores the three note numbers as one three-character string in `a$(i)` and the time in `c(i)`. - The key labels come from short DATA strings, decoded with `VAL i$(1)` and `VAL i$(2)` to place each black key and its letter. - Saving with `LINE 8700` skips the table setup, so a reloaded recording is still in memory. ## Potential Issues - Line 140 decays the second voice with `f-SGN e` instead of `f-SGN f` (the playback version in line 1040 has it right), so in play mode that voice’s fade can stall or go negative. - Recording more than 1,000 events stops with a subscript error, which the error trap turns into a return to the menu. This program came from an Oliger (JLO SAFE) disk and was converted to load from tape. Only the slash in its `SAVE /` had to be removed. It was tested in ZEsarUX. ## Source Code ``` 10 REM "BASICpiano" Mike Rhess TSUG-LV 20 GO TO 8000 100 LET a=USR 65286 110 LET b=PEEK 65280: LET c=PEEK 65281: LET d=PEEK 65282 120 IF NOT c THEN LET e=e-SGN e: GO TO 140 130 LET e=15: SOUND 1,a(c);0,b(c) 140 IF NOT d THEN LET f=f-SGN e: GO TO 160 150 LET f=15: SOUND 3,a(d);2,b(d) 160 IF b THEN SOUND 5,a(b);4,b(b);13,0 170 SOUND 8,e;9,f 200 IF r AND a THEN LET c(i)=a: LET a$(i)=CHR$ b+CHR$ c+CHR$ d: LET i=i+1 400 GO TO 100 1000 SOUND 7,56;10,16;12,25: RANDOMIZE USR 65442: FOR i=1 TO x: RANDOMIZE c(i+1) 1010 LET b=CODE a$(i,1): LET c=CODE a$(i,2): LET d=CODE a$(i,3) 1020 IF NOT c THEN LET e=e-SGN e: GO TO 1040 1030 LET e=15: SOUND 1,a(c);0,b(c) 1040 IF NOT d THEN LET f=f-SGN f: GO TO 1060 1050 LET f=15: SOUND 3,a(d);2,b(d) 1060 IF b THEN SOUND 5,a(b);4,b(b);13,0 1070 SOUND 8,e;9,f 1080 IF USR 65425 THEN GO TO 1020 1090 NEXT i: SOUND 8,0;9,0: PAUSE 60: GO TO 1000 6000 PRINT AT 2,0;"Press ""BREAK and SHIFT""","to Exit",,,,,,: RETURN 6500 REM tape 6510 GO SUB 6000: PRINT AT 3,8;"SAVE" 6520 INPUT "Enter SAVE name "; LINE i$: IF LEN i$>10 THEN GO TO 6520 6530 IF i$="" THEN LET i$="piano/data" 6540 PRINT AT 4,0;"SAVEing data under the name"'"""";i$;"""" 6550 SAVE i$ LINE 8700: STOP 7000 SOUND 8,0;9,0: LET e=0: LET f=e: IF r THEN LET x=i-1: REM error trap 7010 IF INKEY$<>"" THEN GO TO 7010 7020 LET i=1: LET r=0: ON ERR RESET : PRINT AT 2,0;"Select Mode: 1=PLAY",,"2=RECORD",,"3=PLAYBACK",,"4=SAVE", 7030 LET i$=INKEY$: IF i$<"1" OR i$>"4" THEN GO TO 7030 7040 GO SUB 6000: ON ERR GO TO 7000: IF i$="1" THEN PRINT AT 3,8;"PLAY mode": GO TO 7100 7045 IF i$="4" THEN GO TO 6500 7050 IF i$="2" THEN LET r=1: PRINT AT 3,8;"RECORD mode": GO TO 7100 7055 IF INKEY$<>"" THEN GO TO 7055 7060 PRINT AT 3,8;"PLAYBACK mode"'"Speed"; FLASH 1;"?"; FLASH 0;" (1 thru 4)": LET i$=INKEY$: IF i$<"1" OR i$>"4" THEN GO TO 7060 7070 PRINT AT 4,0;" ": POKE 65433,VAL i$: GO TO 1000 7100 IF INKEY$<>"" THEN GO TO 7100 7110 GO TO 100 8000 REM setup tables 8010 CLEAR 65280: LET r=0: DIM a(40): DIM b(40) 8030 SOUND 7,56;10,16;12,25 8040 DIM a$(1000,3): DIM c(1000) 8100 RESTORE 8110 FOR a=1 TO 40: READ a(a),b(a): NEXT a 8130 RESTORE 8600: FOR a=65280 TO 65446: READ b: POKE a,b: NEXT a 8500 DATA 2,114,2,46,1,241,1,186,1,162,2,79,2,14,1,213,1,186,1,138 8510 DATA 0,221,0,209,0,186,0,165,0,156,0,234,0,221,0,197,0,175,0,165,0,98 8520 DATA 0,110,0,117,0,131,0,147,0,93,0,104,0,110,0,124,0,139,0,234 8530 DATA 1,7,1,39,1,75,1,95,0,248,1,23,1,57,1,75,1,116 8600 DATA 0,0,0,0,0,0,175,33,3,255,6,3,119,35,16,252,6,254,72,60,50,32,255,30,5,237 8610 DATA 80,203,10,56,33,62,0,33,0,255,197,6,3,190,40,69,35,16,250,33,3,255,175,6,3,190,40,5,35,16,250,24,18,58,32,255,119,193 8620 DATA 33,32,255,52,29,32,212,203,0,56,204,24,1,193,1,0,3,33,0,255,17,3,255,26,190,40,1,12,119,35,19,16,246,175,185,200,237 8630 DATA 75,120,92,50,120,92,50,121,92,201,35,35,35,175,190,40,197,229,33,3,255,175,6,3,190,40,6,35,16,250,225,24,199,227,126 8640 DATA 245,58,32,255,119,241,225,24,173,42,118,92,237,91,120,92,6,1,237,82,16,252,1,1,0,240,11,175,24,194,0 8650 DATA "14G","17K","034","067","10A","056","15H","001","11S","19 ","023","090","12D","18L","078" 8660 DATA "Qb f","WcZg","EdXa","ReCb","TfVc","YgBd","UaNe","IbMf","Oc g","Pd a" 8700 REM screen 8710 BORDER 4: PAPER 4: CLS : INK 9 8720 PRINT TAB 7; PAPER 2;" TS-2068 PIANO " 8730 LET i$=" ": FOR a=7 TO 13: PRINT PAPER 7;AT a,1;i$;AT a+8,0;i$: NEXT a 8740 FOR a=0 TO 250 STEP 24: PLOT a,0: DRAW 0,56: PLOT a+8,64: DRAW 0,56: NEXT a 8750 PLOT 0,0: DRAW 240,0: PLOT 8,64: DRAW 240,0 8760 RESTORE 8650: FOR a=0 TO 14: READ i$ 8770 LET c=56+64*NOT VAL i$(1): LET b=4+24*VAL i$(2)+16*VAL i$(1): FOR b=b TO b+12: PLOT b,c: DRAW 0,-24: NEXT b 8780 PRINT AT 8+8*VAL i$(1),1+3*VAL i$(2)+2*VAL i$(1); PAPER 1;i$(3) 8790 NEXT a 8800 FOR a=1 TO 30 STEP 3: READ i$ 8810 PRINT PAPER 6;AT 12,a+1;i$(1);AT 20,a;i$(3); PAPER 7;AT 10,a+1;i$(2);AT 18,a;i$(4) 8820 NEXT a 8830 PAPER 1 8890 GO TO 7000 9999 SAVE "BASICpiano" LINE 0 ```