Bears 1

Developer(s): James Brezina
Date: 198x
Type: Program
Platform(s): TS 2068
Tags: Music

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)
00
-1-1
1, 20
3, 41
5, 61
7, 82
9, 102
113
12, 133
14, 15, 164
17, 18, 195
20, 216
22, 236
-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.

Content

Appears On

Related Products

Related Articles

Related Content

Image Gallery

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."

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

Scroll to Top