--- title: "Bears 1" id: 56100 type: "computer_media" slug: "bears-1" url: "http://localhost/computer_media/bears-1/" markdown_url: "http://localhost/computer_media/bears-1.md" published_at: "2024-07-19T01:17:57+00:00" modified_at: "2026-03-30T21:41:05+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/07/SCR-20240718-skjr.png" excerpt: "Plays the Chicago Bears Fight Song with an optional scrolling staff display, using custom UDG note symbols and carefully mapped pitch-to-position calculations." 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: "James Brezina" slug: "james-brezina" taxonomy: "indiv" url: "http://localhost/indiv/james-brezina/" genre: - name: "Music" slug: "music" taxonomy: "genre" url: "http://localhost/type/music/" media_type: "Program" programmers: - name: "James Brezina" slug: "james-brezina" taxonomy: "indiv" url: "http://localhost/indiv/james-brezina/" download_url: "https://archive.org/download/timex-sinclair-software-archive/Bears-1%20%28198x%29%28Brezina%2C%20James%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/07/SCR-20240718-skjr.png" related_content: - id: 56102 title: "Bears" type: "computer_media" url: "http://localhost/computer_media/bears/" media_type_tags: "Music" --- # Bears 1 Plays the Chicago Bears Fight Song; can show notation for the tune, too. This program plays the Chicago Bears Fight Song and optionally displays a scrolling musical staff notation on screen. The program uses two parallel arrays — `a()` for note durations and `b()` for pitch semitone offsets — read from DATA statements split across multiple RESTORE points to work around memory or line-length constraints. A subroutine at line 4000 defines 18 custom UDG characters (144 bytes total, starting at USR “\a”) representing note head symbols in various stem orientations, which are then selected in subroutine 5000 based on duration and pitch class. The staff display draws six ledger lines labeled C through F using AT-positioned PRINT statements and plots notes using OVER 1 (XOR) ink mode at row positions calculated from semitone values. After playback, line 899 calls `RANDOMIZE USR 23296`, which jumps to the system restart routine. *** ## Program Analysis ### Program Structure The program is organized into several distinct functional regions: 1. **Lines 2–6:** User prompt asking whether a printed note display is desired, stored in `q$`. 2. **Lines 6–190:** Initialization — sets array size `s=164`, calls UDG setup (line 8), dimensions arrays `a()`, `b()`, `c$()`, `d$()`, and reads pitch and duration DATA into `b()` and `a()` respectively. 3. **Lines 195–500:** Main playback loop iterating from 1 to `s`; optionally draws note symbols on staff, then executes `BEEP a(i),b(i)`. 4. **Lines 899–999:** Post-playback restart via `RANDOMIZE USR 23296` and `STOP`. 5. **Lines 1000–1070:** Pitch DATA for array `b()`, split with multiple `RESTORE` points. 6. **Lines 2000–2075:** Duration DATA for array `a()`, similarly split. 7. **Lines 3000–3210:** Staff drawing subroutines — two staves (upper at rows 3–8, lower at rows 12–17), plus screen-clear/redraw logic. 8. **Lines 4000–4999:** UDG definition subroutine — loads 144 bytes (18 × 8 bytes) into UDG slots starting at `USR "\a"`. 9. **Lines 5000–5100:** Note-symbol assignment subroutine — sets `c$(i)` and `d$(i)` (upper/lower UDG character pair) based on duration `a(i)` and pitch class `b(i)`. 10. **Line 9000:** SAVE and VERIFY command for the tape. ### Data Organization and RESTORE Usage Both pitch and duration data are split across non-contiguous DATA lines with explicit `RESTORE` statements mid-loop. For pitch array `b()`, reading starts at line 1000, switches to line 1005 at `i=83`, and to line 1070 at `i=163`. Duration array `a()` similarly switches at lines 2005 and 2075. This is necessary because the DATA cannot all be placed contiguously — the two DATA regions (1000–1070 and 2000–2075) are interleaved with the code, and this technique allows reading from separate pools without merging them. There is a notable gap in the duration DATA: lines 2015–2075 account for only about 82 entries after the `RESTORE 2005` switch, yet the loop requires 82 values (indices 83–164). Lines 2060 and 2066–2074 are missing from the listing, which may reflect omitted DATA or lines not shown. ### UDG Note Symbols Subroutine 4000 loads 18 custom UDG characters (UDGs \a through \r, occupying 144 bytes) by POKEing into `USR "\a"` through `USR "\a"+143`. The 8-byte patterns encode different note head shapes with stems pointing up or down, and variations for natural (white key) vs. accidental (black key) notes. These are used in pairs: `c$(i)` holds the upper character and `d$(i)` holds the lower character of a two-row note glyph. ### Pitch-to-Staff-Row Mapping The variable `z` is computed in lines 225–295 from the semitone offset `b(i)`, mapping pitch to a vertical screen row offset for placing the note on the staff. The mapping groups chromatic semitones into diatonic staff positions: | b(i) value(s) | z (staff row offset) | | --- | --- | | 0 | 0 | | -1 | -1 | | 1, 2 | 0 | | 3, 4 | 1 | | 5, 6 | 1 | | 7, 8 | 2 | | 9, 10 | 2 | | 11 | 3 | | 12, 13 | 3 | | 14, 15, 16 | 4 | | 17, 18, 19 | 5 | | 20, 21 | 6 | | 22, 23 | 6 | | -2 | -2 | The note is printed at `AT y-z, 1+j` using `INK 3; OVER 1`, placing it in yellow with XOR mode to overlay the staff lines cleanly. ### Horizontal Position Tracking The variable `j` tracks the column position within each staff line. It starts equal to `i` and is decremented in steps of 30 as the note index increases, reflecting that each staff line holds 30 notes. The cascaded IF statements at lines 305–385 perform this modulo-30 calculation explicitly rather than using MOD, which was likely done for speed. At every 30th note (lines 300, 310, 320), subroutines redraw or scroll the staff. ### Duration-to-Symbol Mapping Subroutine 5000 uses a large block of IF statements (lines 5049–5065) to assign UDG character pairs based on both duration (`a(i)`) and pitch class (whether `b(i)` is a natural or accidental). The two groups of pitch values correspond to natural notes (odd/even semitone groupings where accidentals sit) and white-key notes. Duration values used are: 0.1, 0.2, 0.25, 0.3, 0.35, 0.5, 0.7, 1.0, and >1.0 (whole note, rendered as “O” and “#” text characters). ### Notable Techniques - `RANDOMIZE USR 23296` at line 899 and within line 3200 jumps to the system restart entry point, effectively resetting the machine after playback or clearing the screen mid-piece. - `PAUSE 10` at line 455 inserts brief rests after specific note indices (17, 68, 82, 91, 150) to add phrasing to the music. - The two-row note glyph system (arrays `c$` and `d$`) allows note heads with stems to span two character cells vertically, giving a more realistic staff appearance. - The user is warned upfront that loading the note display arrays can take up to 10 minutes (due to the large subroutine 5000 with hundreds of IF evaluations for all 164 notes), and that this mode plays at incorrect tempo because the IF processing adds latency between BEEPs. - Line 10.1 appears in the DATA at line 1035 — this is a floating-point value used as a pitch offset, not a line number, representing a microtonal or intentional slight pitch deviation for expressive effect. ### Anomalies and Observations - Lines 5059 and 5060 both assign `d$(i)="\d"` for durations 0.25 and 0.3 in the natural-note group, making those two durations visually identical on the staff — likely an oversight. - Line 99 (`RESTORE 2000`) immediately follows line 95 which also does `RESTORE 2000`; the duplicate is redundant but harmless. - The DATA for the duration array appears to be missing several lines (around 2060–2074 range) in the listing, which would cause an “out of DATA” error if the printout path is taken — these lines may simply have been omitted from the submitted listing. - Line 3200 calls `RANDOMIZE USR 23296` before `CLS` and `GO TO 3000` — the machine-code call here performs a screen or system reset rather than playback termination. ## Source Code ``` 2 PRINT "Do you desire a note printout? (y/n). A note printout option takes longer to run (in some cases it may take 10 minutes to load the dimension holding the notes). Also the sounds will be at the incorrect tempo. Without the printout the sounds will be at the correct tempo." 3 INPUT q$ 4 IF q$="n" OR q$="N" THEN GO TO 6 6 LET s=164 8 GO SUB 4000 10 DIM a(s): DIM b(s) 11 DIM c$(s): DIM d$(s) 15 RESTORE 1000 20 FOR i=1 TO s: READ b(i) 25 IF i=83 THEN RESTORE 1005 30 IF i=163 THEN RESTORE 1070 90 NEXT i 95 RESTORE 2000 99 RESTORE 2000 100 FOR i=1 TO s: READ a(i) 110 IF i=83 THEN RESTORE 2005 113 IF i=163 THEN RESTORE 2075 190 NEXT i 195 IF q$="n" OR q$="N" THEN GO TO 215 200 GO SUB 5000 205 CLS : GO SUB 3000 210 LET y=8 215 FOR i=1 TO s 217 IF q$="n" OR q$="N" THEN GO TO 450 220 LET j=i 225 IF b(i)=0 THEN LET z=0 230 IF b(i)=-1 THEN LET z=-1 235 IF b(i)=1 OR b(i)=2 THEN LET z=0 240 IF b(i)=3 OR b(i)=4 THEN LET z=1 245 IF b(i)=5 OR b(i)=6 THEN LET z=1 250 IF b(i)=7 OR b(i)=8 THEN LET z=2 255 IF b(i)=9 OR b(i)=10 THEN LET z=2 260 IF b(i)=11 THEN LET z=3 265 IF b(i)=12 OR b(i)=13 THEN LET z=3 270 IF b(i)=14 OR b(i)=15 OR b(i)=16 THEN LET z=4 280 IF b(i)=17 OR b(i)=18 OR b(i)=19 THEN LET z=5 285 IF b(i)=20 OR b(i)=21 THEN LET z=6 290 IF b(i)=22 OR b(i)=23 THEN LET z=6 295 IF b(i)=-2 THEN LET z=-2 300 IF i=31 THEN GO SUB 3030 305 IF i>30 THEN LET j=i-30 310 IF i=61 OR i=121 OR i=181 OR i=241 OR i=301 OR i=361 OR i=421 THEN GO SUB 3200 315 IF i>60 THEN LET j=i-60 320 IF i=91 OR i=151 OR i=211 OR i=271 OR i=331 OR i=391 OR i=451 THEN GO SUB 3210 325 IF i>90 THEN LET j=i-90 330 IF i>120 THEN LET j=i-120 335 IF i>150 THEN LET j=i-150 340 IF i>180 THEN LET j=i-180 345 IF i>210 THEN LET j=i-210 350 IF i>240 THEN LET j=i-240 355 IF i>270 THEN LET j=i-270 360 IF i>300 THEN LET j=i-300 365 IF i>330 THEN LET j=i-330 370 IF i>360 THEN LET j=i-360 375 IF i>390 THEN LET j=i-390 380 IF i>420 THEN LET j=i-420 385 IF i>450 THEN LET j=i-450 400 PRINT INK 3; OVER 1;AT y-z,1+j;c$(i);AT (y-z-1),1+j;d$(i) 450 BEEP a(i),b(i) 455 IF i=17 OR i=68 OR i=82 OR i=91 OR i=150 THEN PAUSE 10 500 NEXT i 899 RANDOMIZE USR 23296 999 STOP 1000 DATA 10 1005 DATA 10,9,12,10,7,9,4,7 1010 DATA 10,9,10,12,10,10,9,8 1015 DATA 12,12,11,14,12,8,0 1020 DATA 5,8,12,11,12,14,12,12,11 1025 DATA 10,12,15,15,15,15,12 1030 DATA 15,12,10,8,7,10 1035 DATA 5,9,12,14,12,10.1,10,10,9 1040 DATA 12,10,7,-1,4,7,10,9,10 1045 DATA 12,10,4,12 1050 DATA 8,7,5,7,8,12,10 1055 DATA 14,15,15,12,15,14 1060 DATA 15 1065 DATA 10 1070 DATA 15 2000 DATA .3 2005 DATA .7,.2,.3,.2,.7,.2,.3,.2 2010 DATA .3,.3,.2,.3,.3,.3,.2,.5 2015 DATA .35,.7,.2,.3,.2,.7,.2,.3 2020 DATA .2,.3,.3,.2,.35,.35,.3 2025 DATA .2,.5,.35,.2,.2,.2,.3,.2 2030 DATA .3,.2,.3,.2,.7,.2 2035 DATA .35,.35,.35,.35,.7,.35,.35,1,.2 2040 DATA .3,.2,1,.2,.3,.2,.35,.3,.2 2045 DATA .35,.3,.2,1 2050 DATA .3,.2,.35,.35,.35,.35,.35 2055 DATA .35,.35,.2,.2,.5,.35 2065 DATA 1 2070 DATA .3 2075 DATA 1.25 2999 STOP 3000 LET y=8: PRINT AT 3,0;"F-------------------------------" 3005 PRINT AT 4,0;"D-------------------------------" 3010 PRINT AT 5,0;"B-------------------------------" 3015 PRINT AT 6,0;"G-------------------------------" 3020 PRINT AT 7,0;"E-------------------------------" 3025 PRINT AT 8,0;"C-------------------------------" 3027 RETURN 3030 LET y=17: PRINT AT 12,0;"F-------------------------------" 3035 PRINT AT 13,0;"D-------------------------------" 3040 PRINT AT 14,0;"B-------------------------------" 3045 PRINT AT 15,0;"G-------------------------------" 3050 PRINT AT 16,0;"E-------------------------------" 3054 PRINT AT 17,0;"C-------------------------------" 3055 RETURN 3200 RANDOMIZE USR 23296: CLS : GO TO 3000 3210 GO TO 3030 3999 STOP 4000 RESTORE 4010: FOR i=0 TO 143: READ byte: POKE USR "\a"+i,byte: NEXT i: RETURN 4005 STOP 4010 DATA 4,28,60,124,124,60,48,0 4015 DATA 0,0,0,7,4,7,4,4 4020 DATA 0,0,0,7,4,4,4,4 4025 DATA 0,0,0,4,4,4,4,4 4030 DATA 124,60,48,0,0,0,0,0 4035 DATA 7,4,7,4,4,28,60,124 4040 DATA 7,4,4,4,4,28,60,124 4045 DATA 4,4,4,4,4,28,60,124 4050 DATA 4,28,36,68,68,36,48,0 4055 DATA 0,48,36,68,68,36,48,0 4060 DATA 68,36,48,0,0,0,0,0 4065 DATA 4,4,4,4,4,60,36,68 4070 DATA 0,0,0,0,0,48,36,68 4075 DATA 4,28,60,125,124,60,48,0 4080 DATA 7,4,4,4,4,28,60,125 4085 DATA 4,4,4,4,4,28,60,125 4090 DATA 4,4,4,4,4,28,38,69 4095 DATA 4,28,36,69,68,36,48,0 4999 STOP 5000 FOR i=1 TO s 5049 IF a(i)=.1 AND (b(i)=-1 OR b(i)=1 OR b(i)=2 OR b(i)=5 OR b(i)=6 OR b(i)=9 OR b(i)=10 OR b(i)=12 OR b(i)=13 OR b(i)=16 OR b(i)=22 OR b(i)=19 OR b(i)=23) THEN LET c$(i)="\e": LET d$(i)="\f" 5050 IF a(i)=.2 AND (b(i)=-1 OR b(i)=1 OR b(i)=2 OR b(i)=5 OR b(i)=6 OR b(i)=9 OR b(i)=10 OR b(i)=12 OR b(i)=13 OR b(i)=19 OR b(i)=16 OR b(i)=22 OR b(i)=23) THEN LET c$(i)="\e": LET d$(i)="\g" 5051 IF a(i)=.3 AND (b(i)=-1 OR b(i)=1 OR b(i)=2 OR b(i)=5 OR b(i)=6 OR b(i)=9 OR b(i)=10 OR b(i)=12 OR b(i)=13 OR b(i)=19 OR b(i)=16 OR b(i)=22 OR b(i)=23) THEN LET c$(i)="\e": LET d$(i)="\h" 5052 IF a(i)=.35 AND (b(i)=-1 OR b(i)=1 OR b(i)=2 OR b(i)=5 OR b(i)=6 OR b(i)=9 OR b(i)=10 OR b(i)=12 OR b(i)=13 OR b(i)=19 OR b(i)=16 OR b(i)=22 OR b(i)=23) THEN LET c$(i)="\e": LET d$(i)="\p" 5053 IF a(i)=.25 AND (b(i)=-1 OR b(i)=1 OR b(i)=2 OR b(i)=5 OR b(i)=6 OR b(i)=9 OR b(i)=10 OR b(i)=12 OR b(i)=13 OR b(i)=19 OR b(i)=16 OR b(i)=22 OR b(i)=23) THEN LET c$(i)="\e": LET d$(i)="\o" 5054 IF a(i)=.5 AND (b(i)=-1 OR b(i)=1 OR b(i)=2 OR b(i)=5 OR b(i)=6 OR b(i)=9 OR b(i)=10 OR b(i)=12 OR b(i)=13 OR b(i)=19 OR b(i)=16 OR b(i)=22 OR b(i)=23) THEN LET c$(i)="\k": LET d$(i)="\l" 5055 IF a(i)=.7 AND (b(i)=-1 OR b(i)=1 OR b(i)=2 OR b(i)=5 OR b(i)=6 OR b(i)=9 OR b(i)=10 OR b(i)=12 OR b(i)=13 OR b(i)=19 OR b(i)=16 OR b(i)=22 OR b(i)=23) THEN LET c$(i)="\k": LET d$(i)="\q" 5056 IF a(i)=1 AND (b(i)=-1 OR b(i)=1 OR b(i)=2 OR b(i)=5 OR b(i)=6 OR b(i)=9 OR b(i)=10 OR b(i)=12 OR b(i)=13 OR b(i)=19 OR b(i)=16 OR b(i)=22 OR b(i)=23) THEN LET c$(i)="\k": LET d$(i)="\m" 5057 IF a(i)=.1 AND (b(i)=-2 OR b(i)=0 OR b(i)=3 OR b(i)=4 OR b(i)=7 OR b(i)=8 OR b(i)=11 OR b(i)=14 OR b(i)=17 OR b(i)=18 OR b(i)=15 OR b(i)=20 OR b(i)=21) THEN LET c$(i)="\a": LET d$(i)="\b" 5058 IF a(i)=.2 AND (b(i)=-2 OR b(i)=0 OR b(i)=3 OR b(i)=4 OR b(i)=7 OR b(i)=8 OR b(i)=11 OR b(i)=14 OR b(i)=17 OR b(i)=18 OR b(i)=15 OR b(i)=20 OR b(i)=21) THEN LET c$(i)="\a": LET d$(i)="\c" 5059 IF a(i)=.25 AND (b(i)=-2 OR b(i)=0 OR b(i)=3 OR b(i)=4 OR b(i)=7 OR b(i)=8 OR b(i)=11 OR b(i)=14 OR b(i)=17 OR b(i)=18 OR b(i)=15 OR b(i)=20 OR b(i)=21) THEN LET c$(i)="\a": LET d$(i)="\d" 5060 IF a(i)=.3 AND (b(i)=-2 OR b(i)=0 OR b(i)=3 OR b(i)=4 OR b(i)=7 OR b(i)=8 OR b(i)=11 OR b(i)=14 OR b(i)=17 OR b(i)=18 OR b(i)=15 OR b(i)=20 OR b(i)=21) THEN LET c$(i)="\a": LET d$(i)="\d" 5061 IF a(i)=.35 AND (b(i)=-2 OR b(i)=0 OR b(i)=3 OR b(i)=4 OR b(i)=7 OR b(i)=8 OR b(i)=11 OR b(i)=14 OR b(i)=17 OR b(i)=18 OR b(i)=15 OR b(i)=20 OR b(i)=21) THEN LET c$(i)="\n": LET d$(i)="\d" 5062 IF a(i)=.5 AND (b(i)=-2 OR b(i)=0 OR b(i)=3 OR b(i)=4 OR b(i)=7 OR b(i)=8 OR b(i)=11 OR b(i)=14 OR b(i)=17 OR b(i)=18 OR b(i)=15 OR b(i)=20 OR b(i)=21) THEN LET c$(i)="\i": LET d$(i)="\d" 5063 IF a(i)=.7 AND (b(i)=-2 OR b(i)=0 OR b(i)=3 OR b(i)=4 OR b(i)=7 OR b(i)=8 OR b(i)=11 OR b(i)=14 OR b(i)=17 OR b(i)=18 OR b(i)=15 OR b(i)=20 OR b(i)=21) THEN LET c$(i)="\r": LET d$(i)="\d" 5064 IF a(i)=1 AND (b(i)=-2 OR b(i)=0 OR b(i)=3 OR b(i)=4 OR b(i)=7 OR b(i)=8 OR b(i)=11 OR b(i)=14 OR b(i)=17 OR b(i)=18 OR b(i)=15 OR b(i)=20 OR b(i)=21) THEN LET c$(i)="\j": LET d$(i)=CHR$ 32 5065 IF a(i)>1 THEN LET c$(i)="O": LET d$(i)="#" 5099 NEXT i 5100 RETURN 8999 STOP 9000 SAVE "BEARS-1": PRINT "Rewind & press ENTER to VERIFY.": PAUSE 0: VERIFY "BEARS-1": PRINT "BEARS-1 VERIFIED." ```