--- title: "Sound Experimentor" id: 55032 type: "computer_media" slug: "sound-experimentor" url: "http://localhost/computer_media/sound-experimentor/" markdown_url: "http://localhost/computer_media/sound-experimentor.md" published_at: "2024-06-10T08:07:07+00:00" modified_at: "2026-04-01T11:24:04+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/06/Rheesware-Sound-Experimentor.png" excerpt: "Interactively explore all 14 registers of a programmable sound chip with a full-screen editor, live waveform display, and real-time audio output." 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: "Rheesware" slug: "rheesware" taxonomy: "post_tag" url: "http://localhost/tag/rheesware/" - 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: "Sound" slug: "sound" taxonomy: "genre" url: "http://localhost/type/sound/" media_type: "Program" download_url: "https://archive.org/download/timex-sinclair-software-archive/Rheesware%20Sound%20Experimentor%20%28198x%29%28Rheeseware%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "198x" producer_company: - id: 11227 title: "Rheesware" type: "company" url: "http://localhost/company/rheesware/" images: - url: "http://localhost/wp-content/uploads/2024/06/Rheesware-Sound-Experimentor.png" related_products: - id: 54954 title: "Sound Experimentor" type: "product" url: "http://localhost/product/sound-experimentor/" media_type_tags: "Sound" --- Sound Experimentor is an interactive editor for the AY-3-8912 (or compatible) programmable sound generator, allowing users to explore and set all 14 sound chip registers in real time. A cursor navigates a grid-based screen layout using keys 5, 6, 7, and 8 for movement, with “1” and “0” toggling binary fields and ENTER prompting numeric input for frequency, amplitude, noise, and envelope period values. The SOUND keyword sends all register values to the chip at once via a single statement at line 40, using a user-defined function `FN a(x)` to map screen row and column to register array indices. An envelope shape visualizer at the bottom of the screen draws waveforms using PLOT and DRAW commands, selecting among eight named shapes (attack, hold, alternate, continue, etc.) via a computed GO TO at line 910. Register values are mirrored in both a numeric display array `a()` and a raw storage array `b()`, with binary enable fields managed as substrings of `b$` evaluated via BIN literals. A printer dump routine at line 3000 reads register values from a DATA statement at line 6200 and outputs them via LPRINT. *** ## Program Analysis ### Program Structure The program is organized into well-delineated functional regions: - Lines 10–40: Initialization dispatch and core SOUND output subroutine - Lines 100–130: Main cursor-movement and keypress loop - Lines 300–410: Input routing and binary envelope field editing - Lines 500–810: Context-sensitive value entry (enable bits, frequency, amplitude, noise) - Lines 900–935: Envelope shape chart renderer (computed GO TO dispatch) - Lines 1000–2090: Two-digit and five-digit value input handlers - Lines 3000–3030: LPRINT register dump - Lines 5000–5900: Screen initialization and register value display subroutines - Lines 6000–6200: Low-level cell display helpers and DATA statement - Line 9999: Save/verify routine ### Register Mapping and Data Model All 14 AY-3-8912 registers are controlled through two parallel data structures. The array `a(11)` holds cooked register values in order: tone periods for channels A/B/C (split into low/high bytes, occupying `a(1)` through `a(6)`), amplitude values `a(7)`–`a(9)`, and envelope period bytes `a(10)`–`a(11)`. Separate scalar variables hold the noise register (`n`), mixer enable byte (`e`), and envelope shape (`v`). A secondary numeric array `b(8)` stores raw user-entered values for screen display, while two 7-character string variables `b$(1)` and `b$(2)` store binary representations of the enable and envelope shape bit fields. The user-defined function at line 30, `DEF FN a(x)=(x-6)*3+y/8+1`, maps the screen cursor row `x` (6–10) and column `y` (0, 8, 16, 24) to an index into `b()`, cleanly encoding the two-dimensional register grid into a one-dimensional array offset. ### SOUND Statement Usage Line 40 contains the entire register-write operation as a single, long SOUND statement addressing all 14 registers (0 through 13) simultaneously. This is called as a subroutine (`GO SUB 40`) whenever register state changes, ensuring the chip always reflects the current editing state. The statement uses `FN a(x)` inline to dereference array values, making it compact but dependent on the global cursor state variables `x` and `y` being set correctly before the call—which they are, since the cursor position is preserved across the main loop. ### Cursor Navigation The main loop at lines 100–130 implements cursor movement using arithmetic rather than conditionals where possible. Key “5” moves left (decrements `y` by 8), key “8” moves right (increments `y` by 8), with boundary clamping embedded in the expressions at lines 120–122. Line 121 enforces a row constraint when `y` reaches 24 (the last column, which has fewer valid rows), and line 122 handles downward movement with a special case for the last column. A flashing, white-paper highlight at the cursor cell is drawn using `FLASH 1; PAPER 7; SCREEN$` to preserve the existing character. ### ON ERR Usage Input routines throughout the program use `ON ERR GO TO` to trap invalid INPUT entries—looping back to the INPUT prompt on error—then immediately follow with `ON ERR RESET` to restore normal error handling once a valid value is received. This pattern appears at lines 620/630, 700/710, 1020/1030, and 2010/2020, providing robust user input validation without crashing on non-numeric entry. ### Envelope Shape Visualizer Lines 900–935 implement a graphical waveform display for the eight AY envelope shapes. Line 910 clears the chart area and then executes `GO TO 920+v`, a computed GO TO where `v` is the envelope shape register value (0–15, though only shapes 8–15 are distinct). Since shapes 0–7 are aliases for shapes 8 and 11, lines 929 and 935 redirect to 923 and 927 respectively. Each shape subroutine draws the characteristic waveform (sawtooth, triangle, ramp, etc.) using PLOT and a sequence of DRAW commands with relative coordinates, then returns. | Line | Shape value(s) | Waveform description | | --- | --- | --- | | 923 | 0–7, 9 (via 929) | Single decay ramp, then silence | | 927 | 11, 15 (via 935) | Single attack ramp, then silence | | 928 | 8 | Repeating decay sawtooth | | 930 | 10 | Triangle wave (decay-attack alternating) | | 931 | 12 | Single decay, then hold high | | 932 | 14 | Repeating attack sawtooth | | 933 | 13 | Single attack, then hold high | | 934 | 16 | Triangle wave (attack-decay alternating) | ### Enable Bits via BIN Strings The mixer enable register (`e`) and envelope shape bits are stored as human-readable binary strings in `b$(1)` and `b$(2)`, initialized to `"BIN 000000"`. Individual characters within these strings are set by direct substring assignment (e.g., `LET b$(1,7-y/8-3*(x=10))=a$`), and the numeric value is recovered with `VAL b$(1)`, which evaluates the BIN literal embedded in the string. This is a clever technique that avoids explicit bit-manipulation arithmetic while still storing the state compactly. ### Printer Dump The printer routine at lines 3000–3030 uses `RESTORE` followed by `READ` to iterate through the DATA statement at line 6200, which lists the variable names as string tokens. When READ assigns these to `i`, they are evaluated as expressions, yielding the current register values. This allows the DATA line to serve as a symbolic register manifest that is self-documenting. Output is formatted with tab-spaced columns via LPRINT. ## Source Code ``` 10 REM sound experimentor 20 GO SUB 5000: GO TO 100 30 DEF FN a(x)=(x-6)*3+y/8+1 40 SOUND 0,a(1);1,a(2);2,a(3);3,a(4);4,a(5);5,a(6);6,n;7,e;8,a(7);9,a(8);10,a(9);11,a(10);12,a(11);13,v: RETURN 100 LET x1=6: LET y1=0: LET x=x1: LET y=y1 110 PRINT AT x1,y1;SCREEN$ (X1,Y1): LET x1=x: LET y1=y: PRINT AT x,y; FLASH 1; PAPER 7;SCREEN$ (x,y) 115 LET a$=INKEY$: IF a$="" THEN GO TO 115 120 LET y=y+(-8*(y>0 AND a$="5")+8*(y<24 AND a$="8")) 121 LET x=x-(x=11 AND y<24) 122 LET x=x+(x<10+(y=24) AND a$="6")-(x>6 AND a$="7") 125 IF a$="r" OR a$="R" THEN GO SUB 5100: GO SUB 40: GO TO 100 127 IF a$="f" OR a$="F" THEN GO SUB 40 128 IF a$="p" OR a$="P" THEN GO SUB 3000 130 IF a$<>"1" AND a$<>"0" AND a$<>CHR$ 13 THEN GO TO 110 300 IF x=6 THEN GO TO 600 310 IF x=7 THEN GO TO 1000 315 IF a$=CHR$ 13 THEN GO TO 110 320 IF x=8 AND y<24 THEN GO TO 500 330 IF x>8 AND y<24 THEN GO TO 800 400 REM env 410 LET b$(2,15-x)=a$: LET v=VAL b$(2): PRINT AT x,y;a$: GO SUB 40: GO SUB 910: PRINT AT 17,11;" ";AT 17,11;v: GO TO 110 500 REM env enables 510 PRINT AT x,y;a$: LET a(7+y/8)=a(7+y/8)-16*(a(7+y/8)>15)+16*(a$="1"): GO SUB 40: GO SUB 5220: GO TO 110 600 REM 4 digit 610 IF y>16 THEN GO TO 2000 620 ON ERR GO TO 620: INPUT "Freq. (-4095 to 0) -";a: IF a>4095 OR a<-4095 THEN STOP 630 ON ERR RESET : LET a=ABS INT a: LET b(FN a(x))=0-a: GO SUB 6000: LET a(y/4+1)=a-256*INT (a/256): LET a(y/4+2)=INT (a/256): GO SUB 40: GO SUB 5200 640 GO TO 110 700 ON ERR GO TO 700: INPUT "Noise val. (0 to 31) ";a: IF a<0 OR a>31 THEN STOP 710 ON ERR RESET : LET n=31-INT a: LET b(FN a(x))=a: GO SUB 6100: GO SUB 40: GO SUB 5220: GO TO 110 800 REM enables 810 LET b$(1,7-y/8-3*(x=10))=a$: LET e=63-VAL b$(1): PRINT AT x,y;a$: GO SUB 40: PRINT AT 15,27;" ";AT 15,27;e: GO TO 110 900 REM chart 910 PRINT AT 19,14; PAPER 1;TAB 32;AT 20,14;TAB 32: INK 7: GO TO 920+v 923 PLOT 112,21: DRAW 23,-11: DRAW 117,0: INK 0: RETURN 927 PLOT 112,10: DRAW 23,11: DRAW 0,-11: DRAW 117,0: INK 0: RETURN 928 PLOT 112,21: FOR i=0 TO 5: DRAW 23,-11: DRAW 0,11: NEXT i: INK 0: RETURN 929 GO TO 923 930 PLOT 112,21: FOR i=0 TO 2: DRAW 23,-11: DRAW 23,11: NEXT i: INK 0: RETURN 931 PLOT 112,21: DRAW 23,-11: DRAW 0,11: DRAW 117,0: INK 0: RETURN 932 PLOT 112,10: FOR i=0 TO 5: DRAW 23,11: DRAW 0,-11: NEXT i: INK 0: RETURN 933 PLOT 112,10: DRAW 23,11: DRAW 117,0: INK 0: RETURN 934 PLOT 112,10: FOR i=0 TO 2: DRAW 23,11: DRAW 23,-11: NEXT i: INK 0: RETURN 935 GO TO 927 1000 REM 2 digit 1010 IF y>16 THEN GO TO 700 1020 ON ERR GO TO 1020: INPUT "Amplitude. (0 to 15) ";a: IF a<0 OR a>15 THEN STOP 1030 ON ERR RESET : LET a=INT a: LET b(FN a(x))=a: GO SUB 6100: LET a(7+y/8)=a+16*(a(7+y/8)>15): GO SUB 40: GO SUB 5220 1900 GO TO 110 2000 REM 5 digit 2010 ON ERR GO TO 2010: INPUT "Period len.(0 to 65535)";a: IF a<0 OR a>65535 THEN STOP 2020 ON ERR RESET : LET a=INT a: LET b(FN a(x))=a: GO SUB 6000: LET a(10)=a-256*INT (a/256): LET a(11)=INT (a/256): GO SUB 40: GO SUB 5200 2090 GO TO 110 3000 REM Printer 3010 LPRINT : LPRINT "RHEESWARE SOUND EXPERIMENTOR REGISTER VALUES" 3015 RESTORE 3020 LPRINT "Reg.# Val. Reg.# Val. ": FOR a=0 TO 13: READ i: LPRINT a;".......";i,: NEXT a 3030 LPRINT : LPRINT : LPRINT : RETURN 5000 REM screen init 5020 BORDER 0: PAPER 6: CLS 5030 PRINT TAB 2; INK 7; PAPER 2;"RHEESWARE Sound Experimentor": PLOT 0,141: DRAW 255,0 5040 PRINT INVERSE 1; INK 1;"ARROW keys select parameter. 1=ON, 0=OFF. R= reset, P= print""ENTER"" inputs vals. F =refeed" 5050 PRINT ' BRIGHT 1;"chan A chan B chan C ": PRINT "0000 fr 0000 fr 0000 fr 00000env00 AMPL 00 AMPL 00 AMPL 00 N. fr0 ENVEL 0 ENVEL 0 ENVEL 0 HOLD 0 ENAB 0 ENAB 0 ENAB 0 ALTERN0 NOISE 0 NOISE 0 NOISE 0 ATTACK 0 CONT " 5060 PLOT 0,76: DRAW 255,0: PRINT ' BRIGHT 1;" Sound register values " 5070 PRINT AT 14,0;: FOR i=0 TO 13: PRINT TAB i*8; PAPER 2; INK 7;i;: NEXT i 5080 PLOT 0,29: DRAW 255,0: PRINT '' BRIGHT 1;"Envelope Shape"'' BRIGHT 0;" env. period>": PLOT 111,23: DRAW 0,-21 5090 PLOT 111,3: DRAW 144,0: FOR i=135 TO 255 STEP 23: PLOT i,2: DRAW 0,5: NEXT i 5100 REM reset 5105 DIM a(11): DIM b$(2,7): LET n=31: LET b$(1)="BIN 000000": LET b$(2)="BIN 000000": LET e=63: DIM b(8) 5110 LET v=0 5120 LET x=6: FOR y=0 TO 24 STEP 8: GO SUB 6000: NEXT y 5130 LET x=7: FOR y=0 TO 24 STEP 8: GO SUB 6100: NEXT y 5140 FOR x=8 TO 10: FOR y=0 TO 24 STEP 8: PRINT AT x,y;"0": NEXT y: NEXT x 5150 PRINT AT x,24;"0" 5160 GO SUB 900 5200 REM register write 5210 FOR i=0 TO 5: PRINT AT INT (14+i/4),i*8+3-32*INT (i/4);" ";AT INT (14+i/4),i*8+3-32*INT (i/4);a(i+1): NEXT i 5220 PRINT AT 15,19;" ";AT 15,19;n;AT 15,27;" ";AT 15,27;e: FOR i=0 TO 3: PRINT AT 16,i*8+3;" ";AT 16,i*8+3;a(i+7): NEXT i: PRINT AT 17,3;" ";AT 17,3;a(11): PRINT AT 17,11;" ";AT 17,11;v 5900 RETURN 6000 PRINT AT x,y;" ";AT x,y;b(FN a(x)): RETURN 6100 PRINT AT x,y;" ";AT x,y;b(FN a(x)): RETURN 6200 DATA a(1),a(2),a(3),a(4),a(5),a(6),n,e,a(7),a(8),a(9),a(10),a(11),v 9999 CLEAR : SAVE "sound": SAVE "sound": VERIFY "": VERIFY "": GO TO 9999 10 REM sound experimentor 20 GO SUB 5000: GO TO 100 30 DEF FN a(x)=(x-6)*3+y/8+1 40 SOUND 0,a(1);1,a(2);2,a(3);3,a(4);4,a(5);5,a(6);6,n;7,e;8,a(7);9,a(8);10,a(9);11,a(10);12,a(11);13,v: RETURN 100 LET x1=6: LET y1=0: LET x=x1: LET y=y1 110 PRINT AT x1,y1;SCREEN$ (X1,Y1): LET x1=x: LET y1=y: PRINT AT x,y; FLASH 1; PAPER 7;SCREEN$ (x,y) 115 LET a$=INKEY$: IF a$="" THEN GO TO 115 120 LET y=y+(-8*(y>0 AND a$="5")+8*(y<24 AND a$="8")) 121 LET x=x-(x=11 AND y<24) 122 LET x=x+(x<10+(y=24) AND a$="6")-(x>6 AND a$="7") 125 IF a$="r" OR a$="R" THEN GO SUB 5100: GO SUB 40: GO TO 100 127 IF a$="f" OR a$="F" THEN GO SUB 40 128 IF a$="p" OR a$="P" THEN GO SUB 3000 130 IF a$<>"1" AND a$<>"0" AND a$<>CHR$ 13 THEN GO TO 110 300 IF x=6 THEN GO TO 600 310 IF x=7 THEN GO TO 1000 315 IF a$=CHR$ 13 THEN GO TO 110 320 IF x=8 AND y<24 THEN GO TO 500 330 IF x>8 AND y<24 THEN GO TO 800 400 REM env 410 LET b$(2,15-x)=a$: LET v=VAL b$(2): PRINT AT x,y;a$: GO SUB 40: GO SUB 910: PRINT AT 17,11;" ";AT 17,11;v: GO TO 110 500 REM env enables 510 PRINT AT x,y;a$: LET a(7+y/8)=a(7+y/8)-16*(a(7+y/8)>15)+16*(a$="1"): GO SUB 40: GO SUB 5220: GO TO 110 600 REM 4 digit 610 IF y>16 THEN GO TO 2000 620 ON ERR GO TO 620: INPUT "Freq. (-4095 to 0) -";a: IF a>4095 OR a<-4095 THEN STOP 630 ON ERR RESET : LET a=ABS INT a: LET b(FN a(x))=0-a: GO SUB 6000: LET a(y/4+1)=a-256*INT (a/256): LET a(y/4+2)=INT (a/256): GO SUB 40: GO SUB 5200 640 GO TO 110 700 ON ERR GO TO 700: INPUT "Noise val. (0 to 31) ";a: IF a<0 OR a>31 THEN STOP 710 ON ERR RESET : LET n=31-INT a: LET b(FN a(x))=a: GO SUB 6100: GO SUB 40: GO SUB 5220: GO TO 110 800 REM enables 810 LET b$(1,7-y/8-3*(x=10))=a$: LET e=63-VAL b$(1): PRINT AT x,y;a$: GO SUB 40: PRINT AT 15,27;" ";AT 15,27;e: GO TO 110 900 REM chart 910 PRINT AT 19,14; PAPER 1;TAB 32;AT 20,14;TAB 32: INK 7: GO TO 920+v 923 PLOT 112,21: DRAW 23,-11: DRAW 117,0: INK 0: RETURN 927 PLOT 112,10: DRAW 23,11: DRAW 0,-11: DRAW 117,0: INK 0: RETURN 928 PLOT 112,21: FOR i=0 TO 5: DRAW 23,-11: DRAW 0,11: NEXT i: INK 0: RETURN 929 GO TO 923 930 PLOT 112,21: FOR i=0 TO 2: DRAW 23,-11: DRAW 23,11: NEXT i: INK 0: RETURN 931 PLOT 112,21: DRAW 23,-11: DRAW 0,11: DRAW 117,0: INK 0: RETURN 932 PLOT 112,10: FOR i=0 TO 5: DRAW 23,11: DRAW 0,-11: NEXT i: INK 0: RETURN 933 PLOT 112,10: DRAW 23,11: DRAW 117,0: INK 0: RETURN 934 PLOT 112,10: FOR i=0 TO 2: DRAW 23,11: DRAW 23,-11: NEXT i: INK 0: RETURN 935 GO TO 927 1000 REM 2 digit 1010 IF y>16 THEN GO TO 700 1020 ON ERR GO TO 1020: INPUT "Amplitude. (0 to 15) ";a: IF a<0 OR a>15 THEN STOP 1030 ON ERR RESET : LET a=INT a: LET b(FN a(x))=a: GO SUB 6100: LET a(7+y/8)=a+16*(a(7+y/8)>15): GO SUB 40: GO SUB 5220 1900 GO TO 110 2000 REM 5 digit 2010 ON ERR GO TO 2010: INPUT "Period len.(0 to 65535)";a: IF a<0 OR a>65535 THEN STOP 2020 ON ERR RESET : LET a=INT a: LET b(FN a(x))=a: GO SUB 6000: LET a(10)=a-256*INT (a/256): LET a(11)=INT (a/256): GO SUB 40: GO SUB 5200 2090 GO TO 110 3000 REM Printer 3010 LPRINT : LPRINT "RHEESWARE SOUND EXPERIMENTOR REGISTER VALUES" 3015 RESTORE 3020 LPRINT "Reg.# Val. Reg.# Val. ": FOR a=0 TO 13: READ i: LPRINT a;".......";i,: NEXT a 3030 LPRINT : LPRINT : LPRINT : RETURN 5000 REM screen init 5020 BORDER 0: PAPER 6: CLS 5030 PRINT TAB 2; INK 7; PAPER 2;"RHEESWARE Sound Experimentor": PLOT 0,141: DRAW 255,0 5040 PRINT INVERSE 1; INK 1;"ARROW keys select parameter. 1=ON, 0=OFF. R= reset, P= print""ENTER"" inputs vals. F =refeed" 5050 PRINT ' BRIGHT 1;"chan A chan B chan C ": PRINT "0000 fr 0000 fr 0000 fr 00000env00 AMPL 00 AMPL 00 AMPL 00 N. fr0 ENVEL 0 ENVEL 0 ENVEL 0 HOLD 0 ENAB 0 ENAB 0 ENAB 0 ALTERN0 NOISE 0 NOISE 0 NOISE 0 ATTACK 0 CONT " 5060 PLOT 0,76: DRAW 255,0: PRINT ' BRIGHT 1;" Sound register values " 5070 PRINT AT 14,0;: FOR i=0 TO 13: PRINT TAB i*8; PAPER 2; INK 7;i;: NEXT i 5080 PLOT 0,29: DRAW 255,0: PRINT '' BRIGHT 1;"Envelope Shape"'' BRIGHT 0;" env. period>": PLOT 111,23: DRAW 0,-21 5090 PLOT 111,3: DRAW 144,0: FOR i=135 TO 255 STEP 23: PLOT i,2: DRAW 0,5: NEXT i 5100 REM reset 5105 DIM a(11): DIM b$(2,7): LET n=31: LET b$(1)="BIN 000000": LET b$(2)="BIN 000000": LET e=63: DIM b(8) 5110 LET v=0 5120 LET x=6: FOR y=0 TO 24 STEP 8: GO SUB 6000: NEXT y 5130 LET x=7: FOR y=0 TO 24 STEP 8: GO SUB 6100: NEXT y 5140 FOR x=8 TO 10: FOR y=0 TO 24 STEP 8: PRINT AT x,y;"0": NEXT y: NEXT x 5150 PRINT AT x,24;"0" 5160 GO SUB 900 5200 REM register write 5210 FOR i=0 TO 5: PRINT AT INT (14+i/4),i*8+3-32*INT (i/4);" ";AT INT (14+i/4),i*8+3-32*INT (i/4);a(i+1): NEXT i 5220 PRINT AT 15,19;" ";AT 15,19;n;AT 15,27;" ";AT 15,27;e: FOR i=0 TO 3: PRINT AT 16,i*8+3;" ";AT 16,i*8+3;a(i+7): NEXT i: PRINT AT 17,3;" ";AT 17,3;a(11): PRINT AT 17,11;" ";AT 17,11;v 5900 RETURN 6000 PRINT AT x,y;" ";AT x,y;b(FN a(x)): RETURN 6100 PRINT AT x,y;" ";AT x,y;b(FN a(x)): RETURN 6200 DATA a(1),a(2),a(3),a(4),a(5),a(6),n,e,a(7),a(8),a(9),a(10),a(11),v 9999 CLEAR : SAVE "sound": SAVE "sound": VERIFY "": VERIFY "": GO TO 9999 ```