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:
- Lines 2–6: User prompt asking whether a printed note display is desired, stored in
q$. - Lines 6–190: Initialization — sets array size
s=164, calls UDG setup (line 8), dimensions arraysa(),b(),c$(),d$(), and reads pitch and duration DATA intob()anda()respectively. - Lines 195–500: Main playback loop iterating from 1 to
s; optionally draws note symbols on staff, then executesBEEP a(i),b(i). - Lines 899–999: Post-playback restart via
RANDOMIZE USR 23296andSTOP. - Lines 1000–1070: Pitch DATA for array
b(), split with multipleRESTOREpoints. - Lines 2000–2075: Duration DATA for array
a(), similarly split. - Lines 3000–3210: Staff drawing subroutines — two staves (upper at rows 3–8, lower at rows 12–17), plus screen-clear/redraw logic.
- Lines 4000–4999: UDG definition subroutine — loads 144 bytes (18 × 8 bytes) into UDG slots starting at
USR "\a". - Lines 5000–5100: Note-symbol assignment subroutine — sets
c$(i)andd$(i)(upper/lower UDG character pair) based on durationa(i)and pitch classb(i). - 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 23296at 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 10at 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$andd$) 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 doesRESTORE 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 23296beforeCLSandGO TO 3000— the machine-code call here performs a screen or system reset rather than playback termination.
Content
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.
