--- title: "Jingle Bells" id: 64748 type: "computer_media" slug: "jingle-bells" url: "http://localhost/computer_media/jingle-bells/" markdown_url: "http://localhost/computer_media/jingle-bells.md" published_at: "2026-03-07T14:53:22+00:00" modified_at: "2026-03-30T21:43:22+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/03/jingle-bells.png" excerpt: "A BASIC rendition of \"Jingle Bells\" that layers AY chip chords beneath a BEEP melody using computed GO SUB dispatch and clever data encoding tricks." 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: "Patrick Spera" slug: "patrick-spera" taxonomy: "indiv" url: "http://localhost/indiv/patrick-spera/" genre: - name: "Music" slug: "music" taxonomy: "genre" url: "http://localhost/type/music/" media_contents: - id: 64715 title: "Miscellaneous Programs" type: "computer_media" url: "http://localhost/computer_media/miscellaneous-programs/" media_type: "Program" programmers: - name: "Patrick Spera" slug: "patrick-spera" taxonomy: "indiv" url: "http://localhost/indiv/patrick-spera/" mediadate: "1982" images: - url: "http://localhost/wp-content/uploads/2026/03/jingle-bells.png" media_type_tags: "Music" --- # Jingle Bells This program plays “Jingle Bells” using the TS2068’s AY-3-8912 sound chip, combining BEEP for the melody with SOUND commands that program the AY chip’s three tone channels to produce harmonic chords underneath. Song data is stored as triplets (duration, pitch, chord) in DATA statements beginning at line 1000, with each chord value dispatching via computed GO SUB to one of eight chord subroutines (D major, G major, F major, B♭ major, C major, C7, G7, A major) at lines 9010–9082. Negative duration values are used as PAUSE signals, and a sentinel value of -99 triggers a loop back to restart the song continuously. The AY chip registers are set directly: registers 0–5 control the three channel tone periods, register 7 sets the mixer, and registers 8–10 set channel amplitudes. *** ## Program Structure The program is divided into four clearly delineated sections: 1. **Main loop (lines 100–170):** RESTOREs to line 1000 and iterates through DATA triplets, dispatching chord subroutines and issuing BEEP calls. 2. **Song data (lines 1000–1120):** Packed DATA statements encoding every note as a `(duration, pitch, chord)` triplet. 3. **Chord subroutines (lines 9000–9084):** Eight named subroutines each programming the AY-3-8912 via `SOUND` for a specific chord. 4. **Stop-chords subroutine (line 9900):** Silences the AY chip by setting register 7 to 63 (all channels off) and register 13 to 0. ## Data Encoding Each note is represented as a triplet read into `duration`, `pitch`, and `chord`. The encoding uses several conventions: - A `chord` value of `0` means no chord change; values 10–90 (multiples of 10) select a chord subroutine via `GO SUB 9000+chord`. - A negative `duration` that is not `-99` triggers a `PAUSE` of `ABS duration` interrupt units — used here for rests. - The sentinel triplet `-99,-99,900` restarts the song (line 140 catches `duration=-99`) while the `chord` value of `900` calls the stop-chords subroutine at line 9900 first, cleanly silencing the AY before looping. ## Computed GO SUB Dispatch The idiom `GO SUB 9000+chord` at line 130 routes execution to the appropriate chord subroutine purely arithmetically, avoiding a lookup table or IF/THEN chain. Chord index values used in the data are multiples of 10 (10, 30, 40, 60, 70, 80, 90), each mapping directly to a subroutine at lines 9010, 9030, 9040, 9060, 9070, 9080, 9090 respectively. The value 900 dispatches to the stop-chords subroutine at line 9900. ## AY-3-8912 Register Programming Each chord subroutine uses `SOUND` to write directly to AY chip registers. The register assignments are consistent across all chord subroutines: | Registers | Function | | --- | --- | | `0,1` | Channel A tone period (low/high byte) | | `2,3` | Channel B tone period (low/high byte) | | `4,5` | Channel C tone period (low/high byte) | | `7` | Mixer control — value 56 (binary 00111000) enables tone on all three channels, noise off | | `8,9,10` | Amplitude for channels A, B, C — all set to 12 (fixed level, no envelope) | The stop-chords routine sets register 7 to 63 (binary 00111111), disabling all tone and noise output, and clears register 13. ## Melody via BEEP The melody is played through the built-in speaker using the standard `BEEP duration,pitch` statement. Pitch is expressed as semitones above middle C. While `SOUND` sustains the AY chord in the background, `BEEP` provides the foreground melodic line, producing a two-layer musical texture. ## Notable Techniques and Idioms - Using `RESTORE 1000` at line 110 rather than a bare `RESTORE` targets the data section precisely, allowing the song loop to restart cleanly. - Fractional duration constants (e.g., `.125`, `.128`) are used directly in DATA — `BEEP` accepts floating-point duration in seconds, so `.128` ≈ one thirty-second note at a typical tempo and `.125` = exactly one-eighth of a second. - The chord value `900` in the terminal sentinel record cleverly reuses the dispatch mechanism to call the silence subroutine before the loop restarts, avoiding a separate cleanup step. ## Source Code ``` 10 REM Jingle Bells 20 REM Spera (P.M.) style! 100 REM ==> MAIN PROGRAM <== 110 RESTORE 1000 120 READ duration,pitch,chord 130 IF chord>0 THEN GO SUB 9000+chord 140 IF duration=-99 THEN GO TO 100 150 IF duration<0 THEN PAUSE (ABS duration): GO TO 120 160 BEEP duration,pitch 170 GO TO 120 1000 REM ==> SONG DATA <== 1010 DATA .25,0,0,.25,0,30,.25,9,0,.25,7,0,.25,5,0,.75,0,0,.128,0,0,.128,0,0 1020 DATA .25,0,30,.25,9,0,.25,7,0,.25,5,0,.75,2,40,.25,2,0,.25,2,0,.25,10,0,.25,9,0,.25,7,0 1030 DATA .75,4,60,.25,12,0,.25,14,0,.25,12,0,.25,10,0,.25,7,0,.75,9,30,.25,0,0 1040 DATA .25,0,30,.25,9,0,.25,7,0,.25,5,0,.75,0,0,.128,0,0,.128,0,0 1050 DATA .25,0,30,.25,9,0,.25,7,0,.25,5,0,.75,2,40,.25,2,0,.25,2,0,.25,10,0,.25,9,0,.25,7,0 1060 DATA .25,12,60,.25,12,0,.25,12,0,.25,12,0,.25,14,0,.25,12,0,.25,10,0,.25,7,0,1,5,30 1070 DATA .25,9,30,.25,9,0,.50,9,0,.25,9,0,.25,9,0,.50,9,0,.25,9,0,.25,12,0,.25,5,0,.25,7,0,1,9,0 1080 DATA .25,10,60,.25,10,0,.25,10,0,.25,10,0,.25,10,30,.25,9,0,.25,9,0,.125,9,0,.125,9,0 1090 DATA .25,9,70,.25,7,0,.25,7,0,.25,9,0,.50,7,60,.50,12,0 1100 DATA .25,9,30,.25,9,0,.50,9,0,.25,9,0,.25,9,0,.50,9,0,.25,9,0,.25,12,0,.25,5,0,.25,7,0,1,9,0 1110 DATA .25,10,60,.25,10,0,.25,10,0,.25,10,0,.25,10,30,.25,9,0,.25,9,0,.125,9,0,.125,9,0 1120 DATA .25,12,60,.25,12,0,.25,10,0,.25,7,0,1,5,30,-99,-99,900 9000 REM ==> CHORDS <== 9010 REM D maj. chord 9012 SOUND 0,248;1,0;2,186;3,0;4,147;5,0;7,56;8,12;9,12;10,12 9014 RETURN 9020 REM G maj. chord 9022 SOUND 0,23;1,1;2,221;3,0;4,139;5,0;7,56;8,12;9,12;10,12 9024 RETURN 9030 REM F maj. chord 9032 SOUND 0,248;1,0;2,209;3,0;4,156;5,0;7,56;8,12;9,12;10,12 9034 RETURN 9040 REM Bb maj. chord 9042 SOUND 0,234;1,0;2,186;3,0;4,156;5,0;7,56;8,12;9,12;10,12 9044 RETURN 9050 REM C maj. chord 9052 SOUND 0,23;1,1;2,209;3,0;4,165;5,0;7,56;8,12;9,12;10,12 9054 RETURN 9060 REM C 7th chord 9062 SOUND 0,234;1,0;2,209;3,0;4,165;5,0;7,56;8,12;9,12;10,12 9064 RETURN 9070 REM G 7th chord 9072 SOUND 0,23;1,1;2,221;3,0;4,156;5,0;7,56;8,12;9,12;10,12 9074 RETURN 9080 REM A maj. chord 9082 SOUND 0,248;1,0;2,197;3,0;4,165;5,0;7,56;8,12;9,12;10,12 9084 RETURN 9900 REM ==> STOP CHORDS <== 9902 SOUND 7,63;13,0 9904 RETURN 9990 STOP 9999 SAVE "jingle" LINE 100 ```