--- title: "Evergreen" id: 55971 type: "computer_media" slug: "evergreen" url: "http://localhost/computer_media/evergreen/" markdown_url: "http://localhost/computer_media/evergreen.md" published_at: "2024-07-14T13:07:30+00:00" modified_at: "2026-03-30T21:41:50+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/07/SCR-20240714-imal.png" excerpt: "A BASIC rendition of \"Evergreen\" that synchronizes scrolling lyrics with melody and live three-voice chord accompaniment using AY sound chip register programming." 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/" genre: - name: "Music" slug: "music" taxonomy: "genre" url: "http://localhost/type/music/" media_contents: - id: 55923 title: "Timex Sinclair Public Domain Library Tape 2003" type: "computer_media" url: "http://localhost/computer_media/timex-sinclair-public-domain-library-tape-2003/" media_type: "Program" download_url: "https://archive.org/download/timex-sinclair-software-archive/Evergreen%20%28198x%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/07/SCR-20240714-imal.png" media_type_tags: "Music" --- # Evergreen This program performs the song “Evergreen” (the Barbra Streisand ballad) by displaying lyrics syllable-by-syllable while playing the melody and chord accompaniment through the AY-3-8912 sound chip. Each DATA record holds a lyric fragment, a chord subroutine line number, a note duration, and a pitch value used both for BEEP and for horizontal text positioning via TAB. The chord subroutines use SOUND register writes to program the three AY chip tone channels simultaneously, implementing named chords such as C Major, A Minor, G Minor, B-flat Major, and others by poking raw 12-bit frequency divisor values split across paired registers. A dummy chord at line 1900 simply RETURNs immediately, allowing chord changes to be skipped mid-phrase without restructuring the DATA. Lines 1990 and 1995 handle inter-section pauses by silencing and restarting the noise/envelope settings, while POKE 23692,255 suppresses the scroll prompt. *** ## Program Analysis ### Program Structure The program is organized into three distinct regions. Lines 1–35 handle initialization and the main playback loop. Lines 40–230 contain all the song DATA. Lines 300–310 perform a final cleanup and stop. Lines 1000–1999 are a library of subroutines, each setting AY-3-8912 registers for a specific chord or control function. 1. **Init (1–20):** Sets screen colors, prints the title, pauses, clears, and disables scroll with `POKE 23692,255`. 2. **Main loop (25–35):** Reads 150 DATA records and for each one prints a lyric fragment, calls a chord subroutine, sounds a note, and clears the screen if the pitch value equals 30. 3. **Song DATA (40–230):** Each record is a 4-tuple: lyric string, chord subroutine line, BEEP duration, BEEP pitch semitones. 4. **Cleanup (300–310):** Calls the opening chord, pauses, silences the AY chip, then STOPs. 5. **Chord subroutines (1000–1999):** Each writes directly to AY register pairs via `SOUND` statements and RETURNs. ### Data Format and Dual Use of the Pitch Field Each DATA record uses four fields: `L$` (lyric), `C` (chord subroutine address), `D` (BEEP duration in seconds), and `P` (BEEP pitch in semitones above middle A). The field `P` serves double duty: it is also passed to `TAB P*1.2` to position the lyric text horizontally on screen, creating a rough left-to-right visual spread that loosely reflects the melodic pitch range. When `P=30` (a sentinel value used for inter-section silences), the screen is cleared after the beat. ### AY-3-8912 Chord Programming Each chord subroutine programs the three tone channels of the AY chip by writing raw frequency divisor values to register pairs 0/1, 2/3, and 4/5 via the `SOUND` keyword. Because the AY frequency registers are 12 bits wide, each pitch requires a low byte (even register) and a high byte (odd register). For example, line 1065 sets: | Register | Value | Meaning | | --- | --- | --- | | 0 | 23 | Channel A tone low byte | | 1 | 1 | Channel A tone high byte → divisor 279 | | 2 | 234 | Channel B tone low byte | | 3 | 0 | Channel B tone high byte → divisor 234 | | 4 | 186 | Channel C tone low byte | | 5 | 0 | Channel C tone high byte → divisor 186 | This represents a G Minor chord. The opening subroutines at lines 1000 and 1001 also configure registers 7–13 to set the mixer, amplitude, and envelope shape, establishing noise and volume levels that persist for the rest of the song. ### The Dummy Chord Technique Line 1900 is a subroutine that consists of nothing but `RETURN` (with a comment labeling it “DUMMY CHORD”). Wherever a note in the melody should continue the previous chord without change, the DATA record points `C` to 1900. This avoids duplicating chord-setting code and keeps the DATA loop uniform: every record unconditionally executes `GO SUB C`, regardless of whether a chord change is intended. ### Silence and Section Break Subroutines Lines 1990 and 1995 handle inter-section gaps. Both mute all AY channels by writing 63 to register 7 (mixer: all off), pause for 30 or 40 frames respectively, then restore the mixer and amplitude settings. Line 1999, called at the very end of the program, writes 63 to register 7 and zeros channels A, B, and C to fully silence the chip. ### Screen Management `POKE 23692,255` at line 20 writes to the system variable that controls how many lines can be printed before a scroll prompt appears, suppressing the “scroll?” message for 255 lines. The screen is cleared mid-song only when the pitch sentinel value `P=30` is encountered, which coincides with the DATA records at the end of lyric phrases. The TAB expression `TAB P*1.2` will produce values from 0 to roughly 26 across the pitch range used (0–21 semitones), staying within the 32-column display width. ### Notable Idioms and Techniques - Chord subroutine addresses are stored directly as numeric DATA values and called with `GO SUB C`, making the line numbers part of the song’s data structure. - The `FOR B=1 TO 150` loop iterates exactly as many times as there are DATA records, relying on the programmer to keep both counts in sync — there is no runtime check. - The `REM` comments in chord subroutines (e.g., `REM C MAJ`, `REM A MIN`) serve as inline documentation of the musical harmony, which is otherwise encoded entirely as raw register values. - Line 1 initializes the AY chip’s noise register before the title screen appears, likely to silence any residual sound from a previous run. - The `SAVE "EVERGREEN" LINE 1` at line 9998 saves the program with an auto-run directive starting at line 1. ### Potential Issues - The comment at line 15 reads `REM QTR=.4 till 2nd note line 80`, indicating the programmer considered extracting the quarter-note duration as a variable but did not do so; durations are instead hard-coded in each DATA record. - If the DATA count and the loop bound `150` ever fall out of sync, the program will either exhaust its DATA (causing an error) or loop past the last record silently, depending on direction. - The expression `TAB P*1.2` uses a floating-point multiply each iteration; for large `P` values this could in principle exceed column 31, though the pitch range in the data stays well within safe bounds. ## Source Code ``` 1 SOUND 7,63 10 PAPER 4: BRIGHT 1: BORDER 4: INK 0: CLS : PRINT AT 10,6;"E V E R G R E E N" 15 PAUSE 150: CLS : REM QTR=.4 till 2nd note line 80 20 POKE 23692,255 25 FOR B=1 TO 150: READ L$,C,D,P 30 PRINT TAB P*1.2;L$: GO SUB C: BEEP D,P: IF P=30 THEN CLS 35 NEXT B 40 DATA "LOVE,",1001,1.6,9,"SOFT",1030,.4,9 50 DATA "AS",1000,.4,10,"AN",1030,.4,12,"EAS-",1900,.4,12,"Y",1035,.4,14,"CHAIR;",1030,2,7 55 DATA " ",1995,0,0 60 DATA "LOVE,",1900,1.6,7,"FRESH",1015,.4,7,"AS",1900,.4,9,"THE",1900,.4,10,"MORN-",1030,.4,9,"ING",1900,.4,12,"AIR.",1900,2,5 65 DATA " ",1995,0,30 70 DATA "ONE",1050,1.6,17,"LOVE",1900,.4,14,"THAT",1900,.4,16,"IS",1900,.4,17,"SHARED",1055,.4,16,"BY",1900,.4,19,"TWO,",1900,2,12 80 DATA "I",1065,.4,14,"HAVE",1900,.3,17,"FOUND",1900,1.2,10,"WITH",1900,.3,9,"YOU.",1125,1.2,7 85 DATA " ",1995,0,30 90 DATA "LIKE",1015,.3,5,"A",1900,.3,7,"ROSE",1030,1.5,5 100 DATA "UN-",1900,.3,9,"DER",1900,.3,10,"THE",1900,.3,12,"A-",1035,.3,12,"PRIL",1900,.3,14,"SNOW,",1900,1.8,7 105 DATA " ",1990,0,30 110 DATA "I",1065,.6,19,"WAS",1900,.3,17,"AL-",1015,.3,14,"WAYS",1900,.3,12,"CER",1900,.15,8,"-",1900,.15,7,"TAIN",1900,.3,5,"LOVE",1030,.3,7,"WOULD",1900,.3,9,"GROW.",1900,1.8,5 115 DATA " ",1995,0,30 120 DATA "LOVE,",1050,1.2,17,"AGE-",1900,.3,14,"LESS",1900,.3,16,"AND",1900,.3,17,"EV-",1055,.3,16,"ER-",1900,.3,19,"GREEN,",1900,1.8,12 125 DATA " ",1995,0,30 130 DATA "SEL-",1105,.3,14,"DOM",1900,.3,17,"SEEN",1900,.6,14,"BY",1900,.3,9,"TWO.",1130,2.2,12 140 DATA "YOU",1105,.6,17,"AND",1900,.3,16,"I",1900,.6,14,"WILL",1900,.15,16,"MAKE",1900,.15,17,"EACH",1055,.6,16,"NIGHT",1900,.15,12,"A",1900,.15,9,"FIRST,",1900,1.2,12 145 DATA " ",1995,0,30 150 DATA "EV-",1105,.3,17,"RY",1900,.3,16,"DAY",1900,.6,14,"A",1900,.15,16,"BE-",1900,.15,17,"GIN-",1055,.3,16,"NING.",1900,.6,12 155 DATA " ",1995,0,30 160 DATA "SPIR-",1105,.3,17,"ITS",1900,.3,16,"RISE",1025,.9,14,"AND",1900,.15,16,"THEIR",1900,.15,17,"DANCE",1055,.6,21,"IS",1900,.3,22,"UN-",1900,.3,21,"RE-",1900,.3,17,"HEARSED.",1055,1.2,12 165 DATA " ",1990,0,30 170 DATA "THEY",1030,.3,14,"WARM",1900,.3,17,"AND",1900,.15,16,"EX-",1900,.15,17,"CITE",1035,.3,16,"US",1900,.9,14 175 DATA " ",1990,0,30 180 DATA "CAUSE",1105,.6,22,"WE",1900,.3,22,"HAVE",1900,.3,21,"THE",1900,.3,22,"BRIGHT",1015,.3,21,"-",1900,.3,19,"EST",1900,.9,21 185 DATA " ",1995,0,30 190 DATA "LOVE,",1030,1.2,9,"TWO",1900,.3,9,"LIGHTS",1900,.3,10,"THAT",1900,.3,12,"SHINE",1900,.3,12,"AS",1035,.3,14,"ONE,",1900,1.8,7 195 DATA " ",1995,0,30 200 DATA "MORN-",1035,.3,7,"ING",1900,.3,7,"GLO-",1015,.6,7,"RY",1900,.3,7,"AND",1900,.3,9,"THE",1900,.3,10,"MID-",1030,.3,9,"NIGHT",1900,.3,12,"SUN.",1900,1.8,5 205 DATA " ",1995,0,30 210 DATA "TIME,",1050,1.2,17,"WE'VE",1900,.3,14,"LEARNED",1900,.3,16,"TO",1900,.3,17,"SAIL",1055,.3,16,"A-",1900,.3,19,"BOVE;",1900,1.8,12 220 DATA "TIME",1065,.3,14,"WON'T",1900,.3,17,"CHANGE",1900,.6,10,"THE",1900,.3,10,"MEAN-",1900,.3,10,"ING",1900,.3,9,"OF",1125,1.2,7 225 DATA " ",1995,0,30 230 DATA "ONE",1015,.6,7,"LOVE",1030,1.2,5,"AGE-",1900,.3,9,"LESS",1900,.3,10,"AND",1900,.3,12,"EV-",1035,1.2,10,"ER",1900,.9,19,"EV-",1115,.3,10,"ER",1900,1.2,10,"GREEN.",1030,1.8,17 300 GO SUB 1001: PAUSE 90: GO SUB 1999 310 STOP 1000 SOUND 7,56;8,15;9,15;10,16;11,5;12,5;13,13: RETURN : REM ON 1001 SOUND 7,58;8,8;10,8: RETURN : REM channels a & c only 1015 SOUND 0,209;1,0;2,165;3,0;4,139;5,0: RETURN : REM C MAJ 1025 SOUND 0,165;1,0;2,131;3,0;4,110;5,0: RETURN : REM E MAJ 1030 SOUND 0,156;1,0;2,124;3,0;4,104;5,0: RETURN : REM F MAJ 1035 SOUND 0,139;1,0;2,110;3,0;4,93;5,0: RETURN : REM G MAJ 1050 SOUND 0,186;1,0;2,156;3,0;4,124;5,0: RETURN : REM D MINOR D FNATURAL A 1055 SOUND 0,248;1,0;2,209;3,0;4,165;5,0: RETURN : REM A MIN 1065 SOUND 0,23;1,1;2,234;3,0;4,186;5,0: RETURN : REM G MIN 1105 SOUND 0,234;1,0;2,186;3,0;4,156;5,0: RETURN : REM B FLAT MAJ 1115 SOUND 0,147;1,0;2,124;3,0;4,98;5,0: RETURN : REM F# MIN 1125 SOUND 0,165;1,0;2,139;3,0;4,117;5,0: RETURN : REM Eb 1130 SOUND 0,7;1,1;2,209;3,0;4,175;5,0: RETURN : REM Ab 1900 RETURN : REM DUMMY CHORD TO REGULATE DATA 1990 SOUND 7,63: PAUSE 30: SOUND 7,56;8,10;9,10;10,16;11,5;12,5;13,13: RETURN 1995 SOUND 7,63: PAUSE 40: SOUND 7,56;8,10;9,10;10,16;11,5;12,5;13,13: RETURN 1999 SOUND 7,63;8,0;9,0;10,0: RETURN : REM OFF 9997 STOP 9998 CLEAR : SAVE "EVERGREEN" LINE 1 ```