This program implements a precision digital clock displaying Coordinated Universal Time (UTC) using large block-graphic digits rendered with filled-square characters across a 5-row by 9-column cell for each numeral. The timing mechanism uses six nested FOR loops iterating over GOSUB line numbers (stored in variables G through W) that each render a digit 0–9, so the loop counters directly encode the current time as subroutine addresses rather than separate digit values. Fine timing calibration is achieved by inserting additional BEEP statements with carefully measured durations at the second, 10-second, minute, 10-minute, hour, and 24-hour boundaries, with the primary tick BEEP at line 2080 lasting approximately 0.7396 seconds. The initialization sequence draws a decorative “W5GAA” amateur-radio callsign using PLOT/DRAW vector graphics alongside a simulated television test card with color bars. The user is prompted via INPUT to set each of the four time digits before the clock begins running, with validation enforcing valid hour and minute ranges.
Program Analysis
Program Structure
The program is organized into several major sections: initialization (lines 4000–4070 and 5000–6100), digit-graphics subroutines (lines 1000–1190), the clock timing subroutines (lines 2000–2080), the time-entry subroutines (lines 3000–3310), and the main timing loop (lines 100–240). The splash screen is drawn at startup only, and the clock then runs indefinitely via GO TO 110.
The Core Timing Trick: Loop Counters as Line Numbers
The most distinctive technique in this program is using FOR-loop counters as subroutine addresses. Variables G through S are initialized to specific BASIC line numbers:
| Variable | Line number | Digit displayed |
|---|---|---|
G | 1010 | 0 |
O | 1030 | 1 |
H | 1050 | 2 |
I | 1070 | 3 (labeled “4” in REM) |
P | 1090 | 4 |
J | 1110 | 5 |
Q | 1130 | 6 |
R | 1150 | 7 (labeled “8” in REM) |
S | 1170 | 8 (labeled “8” in REM) |
K | 1190 | 9 |
Six nested FOR loops (variables A–F) iterate over ranges of these line-number values in steps of 20. Because each digit subroutine occupies exactly 20 lines (e.g. 1010, 1030, 1050…), STEP 20 naturally advances through each digit in sequence. GO SUB A, GO SUB B, etc. thus invoke the correct digit-drawing subroutine for each clock position.
The six loops correspond to: A = tens-of-hours, B = units-of-hours, C = tens-of-minutes, D = units-of-minutes, E = tens-of-seconds, F = units-of-seconds. The innermost loop also calls GO SUB 2000 on each iteration for the timing BEEP.
Loop Boundary Management
The loop limits are maintained dynamically using variables L, M, N, T as the lower bounds of loops A, B, C, D respectively. When a loop counter reaches its upper bound, the lower bound is reset to G (line 1010, i.e. digit 0) for the next cycle. For example, line 110 sets L=G when A=H, correctly resetting the tens-of-hours digit to 0 after reaching 2. The special case at line 120 handles the 1-to-9 range for the tens-of-hours digit by forcing the upper bound of loop B to W=1070 (digit 3) when A=1050 (tens digit = 1), ensuring hours never exceed 19:xx.
Timing Calibration
Clock accuracy is achieved by inserting progressively longer BEEP calls at regular time boundaries rather than using PAUSE. The logic in lines 2010–2080 forms a priority chain:
- Line 2020: Once per 24 hours (at 00:00:00) — BEEP 0.6499
- Line 2030: Three times per 24 hours (at 06:00, 12:00, 18:00) — BEEP 0.6503
- Line 2040: Once per hour (on the hour) — BEEP 0.6521
- Line 2050: Once per 10 minutes — BEEP 0.6537
- Line 2060: Once per minute (at E=J, F=K, i.e. :59) — BEEP 0.6588
- Line 2070: Once per 10 seconds — BEEP 0.6631
- Line 2080: Every second (primary tick) — BEEP 0.7395532
Each BEEP uses note 69 (A above middle C at 440 Hz × 2 = 880 Hz) except at boundaries. The primary tick BEEP at line 2080 defines the fundamental second period; all other BEEP durations are chosen empirically to compensate for cumulative drift accumulated by the extra BASIC overhead at those times. A brief additional BEEP at line 2010 (BEEP .001,34) provides a subtle audio tick sound.
Block-Graphic Digit Rendering
Each digit is drawn as a 5-row by 3-character-wide cell using Spectrum block-graphic characters. The AT X,Y coordinates place digits at fixed screen positions, with X fixed at 15 (lower half of screen) and Y set by the calling loop. Notable characters used include \:: (full block █), \'' (upper-half ▀), \.. (lower-half ▄), and spaces for clear areas.
Bugs and Anomalies
- REM label errors: Lines 1060 and 1080 both have the REM comment
"4" graphic; lines 1140 and 1160 both say"8" graphic. Line 1060 actually draws a “3” pattern and line 1140 draws a “7”. - Case mismatch at line 3030:
LET l=H: GO SUB huses lowercaselandh. On the Spectrum, variable names are case-insensitive in the tokenizer and these would resolve correctly toLandH. - Trailing period at line 4050: The line ends with
GO SUB 1010.— the period is interpreted as the decimal point of a numeric literal and does not cause an error, but it is unintentional. - Unused variable Y at line 110:
LET Y=0is set in loop A but immediately overridden in subsequent loop bodies, making it redundant for most iterations. - Line 3108 validation gap: The check
IF V>1 AND Z>3attempts to reject hours like 24–29, but only resets the second digit; it does not re-prompt the first digit, which could lead to invalid hour states if V=2 and Z=4–9 are entered together before validation catches them. - Variable W reuse:
Wis used both as a temporary upper-bound override in the timing loop (lines 110, 120) and initialized to 1190 in line 4070. This dual use is intentional but fragile.
Splash Screen and Graphics
The startup display draws the amateur callsign “W5GAA” using PLOT/DRAW vector lines (lines 6000–6100) and also renders a simulated television test card with color bars (lines 5110–5158). The “W” is constructed from 16-pixel-wide strokes drawn with diagonal DRAW commands. The color bars use INK 0–6 with block-graphic fill characters across line 13 of the screen. A pixel-level grid pattern is added using a PLOT/DRAW loop at lines 5154–5158.
Initialization Sequence
Line 4010 writes 100 to system variable address 23609 (FRAMES LSB / interrupt delay), effectively setting the TV border color flash rate. Lines 4052–4053 print static colon separators in INK 2 (green) between the digit pairs before the user is prompted for time entry. The GO SUB 3000 at line 4070 triggers the interactive time-setting routine before the main loop begins.
Content
Source Code
10 REM "W5GAA-clok" (precision digital clock, version "A").
30 REM By Frank Bouldin (11/13/85).
40 REM To adjust clock speed,change value of BEEP duration inline 2080. Decrease in value makes clock run faster. PAUSE values in lines 2020-2070 are for fine trim insertions at the times indicated in the REMs.
50 REM Follow INPUT prompts to set exact clock time.
90 GO SUB 4000
100 REM 6 nested timing loops:
110 FOR A=L TO H STEP 20: LET Y=0: LET W=K: IF A=H THEN LET L=G
120 IF A=1050 THEN LET W=1070
130 GO SUB A
140 FOR B=M TO W STEP 20: LET Y=4: IF B=W THEN LET M=G
150 GO SUB B
160 FOR C=N TO J STEP 20: IF C=J THEN LET N=G
170 LET Y=12: GO SUB C
180 FOR D=T TO K STEP 20: IF D=K THEN LET T=G
190 LET Y=16: GO SUB D
200 FOR E=G TO J STEP 20: LET Y=24: GO SUB E
210 IF U=0 THEN PRINT FLASH 1; INK 1;AT 21,0;" -Press any key to start clock- "
220 IF U=0 THEN PAUSE 0: LET U=1: PRINT INK 0;AT 21,0;"COORDINATED UNIVERSAL TIME (UTC)"
240 LET Y=28: FOR F=G TO K STEP 20: GO SUB F: GO SUB 2000: NEXT F: NEXT E: NEXT D: NEXT C: NEXT B: NEXT A: GO TO 110
1000 REM "0" graphic:
1010 PRINT AT X,Y;"\::\::\::";AT X+1,y;"\:: \::";AT X+2,Y;"\:: \::";AT X+3,Y;"\:: \::";AT X+4,Y;"\::\::\::": RETURN
1020 REM "1" graphic:
1030 PRINT AT X,Y;"\ :\:: ";AT X+1,Y;" \:: ";AT X+2,Y;" \:: ";AT X+3,Y;" \:: ";AT X+4,Y;"\ :\::\: ": RETURN
1040 REM "2" graphic:
1050 PRINT AT X,Y;"\::\::\::";AT X+1,Y;"\'' \::";AT X+2,Y;"\::\::\::";;AT X+3,Y;"\:: \..";AT X+4,Y;"\::\::\::": RETURN
1060 REM "4" graphic:
1070 PRINT AT X,Y;"\::\::\::";AT X+1,Y;"\'' \::";AT X+2,Y;"\ :\::\::";AT X+3,Y;"\.. \::";AT X+4,Y;"\::\::\::": RETURN
1080 REM "4" graphic:
1090 PRINT AT X,Y;"\:: \::";AT X+1,Y;"\:: \::";AT X+2,Y;"\::\::\::";AT X+3,Y;" \::";AT X+4,Y;" \::": RETURN
1100 REM "5" graphic:
1110 PRINT AT X,Y;"\::\::\::";AT X+1,Y;"\:: ";AT X+2,Y;"\::\::\::";AT X+3,Y;"\.. \::";AT X+4,Y;"\::\::\::": RETURN
1120 REM "6" graphic:
1130 PRINT AT X,Y;"\::\::\::";AT X+1,Y;"\:: \''";AT X+2,Y;"\::\::\::";AT X+3,Y;"\:: \::";AT X+4,Y;"\::\::\::": RETURN
1140 REM "8" graphic:
1150 PRINT AT X,Y;"\::\::\::";AT X+1,Y;"\'' \::";AT X+2,Y;" \::";AT X+3,Y;" \::";AT X+4,Y;" \::": RETURN
1160 REM "8" graphic:
1170 PRINT AT X,Y;"\::\::\::";AT X+1,Y;"\:: \::";AT X+2,Y;"\::\::\::";AT X+3,Y;"\:: \::";AT X+4,Y;"\::\::\::": RETURN
1180 REM "9" graphic:
1190 PRINT AT X,Y;"\::\::\::";AT X+1,Y;"\:: \::";AT X+2,Y;"\::\::\::";AT X+3,Y;"\.. \::";AT X+4,Y;"\::\::\::": RETURN
2000 REM Clock "ticks":
2010 BEEP .001,34
2012 REM Clock timing adjust:
2015 REM 1 PAUSE every 24 hours (AT 00:00:00):
2020 IF A=G AND B=G AND C=G AND D=G AND E=G AND F=G THEN BEEP .6499,6: RETURN
2025 REM 3 PAUSEs every 24 hoursat 06:00:00,12:00:00 & 18:00:00:
2030 IF (A=G AND B=Q OR A=O AND (B=H OR B=S)) AND C=G AND D=G AND E=G AND F=G THEN BEEP .6503,18: RETURN
2035 REM 1 PAUSE each hour, on the hour, except for the above:
2040 IF C=G AND D=G AND E=G AND F=G THEN BEEP .6521,22: RETURN
2045 REM 1 PAUSE every 10 min. except for the above:
2050 IF D=G AND E=G AND F=G THEN BEEP .6537,69: RETURN
2055 REM 1 PAUSE each minute, except for the above:
2060 IF E=J AND F=K THEN BEEP .6588,69: RETURN
2065 REM 1 Pause every 10 sec. (except for the above):
2070 IF F=K THEN BEEP .6631,69: RETURN
2075 REM PRIMARY time adjust (1 PAUSE every second, (except for the above):
2080 BEEP 0.7395532,69: RETURN
2999 REM Clock digit settings:
3000 INK 1: LET Y=0: FLASH 1: GO SUB G
3005 INPUT " ENTER first digit ";V: FLASH 0
3010 IF V=0 THEN LET L=G: GO SUB G
3020 IF V=1 THEN LET L=O: GO SUB O
3030 IF V=2 THEN LET l=H: GO SUB h
3040 IF V>2 THEN GO TO 3000
3050 LET Y=4: FLASH 1: GO SUB G
3055 INPUT " ENTER second digit ";Z: FLASH 0
3060 IF Z=0 THEN LET M=G: GO SUB G
3070 IF Z=1 THEN LET M=O: GO SUB O
3080 IF Z=2 THEN LET M=H: GO SUB H
3090 IF Z=3 THEN LET M=I: GO SUB I
3100 IF Z=4 THEN LET M=P: GO SUB P
3101 IF Z=5 THEN LET M=J: GO SUB J
3102 IF Z=6 THEN LET M=Q: GO SUB Q
3103 IF Z=7 THEN LET M=R: GO SUB R
3104 IF Z=8 THEN LET M=S: GO SUB S
3105 IF Z=9 THEN LET M=K: GO SUB K
3106 IF Z>9 THEN GO TO 3050
3108 IF V>1 AND Z>3 THEN LET Y=4: GO SUB G: GO TO 3000
3110 LET Y=12: FLASH 1: GO SUB G
3115 INPUT " ENTER third digit ";Z: FLASH 0
3120 IF Z=0 THEN LET N=G: GO SUB G
3130 IF Z=1 THEN LET N=O: GO SUB O
3140 IF Z=2 THEN LET N=H: GO SUB H
3150 IF Z=3 THEN LET N=I: GO SUB I
3160 IF Z=4 THEN LET N=P: GO SUB P
3170 IF Z=5 THEN LET N=J: GO SUB J
3180 IF Z>5 THEN GO TO 3110
3190 LET Y=16: FLASH 1: GO SUB G
3195 INPUT " ENTER fourth digit ";Z: FLASH 0
3200 IF Z=0 THEN LET T=G: GO SUB G
3210 IF Z=1 THEN LET T=O: GO SUB O
3220 IF Z=2 THEN LET T=H: GO SUB H
3230 IF Z=3 THEN LET T=I: GO SUB I
3240 IF Z=4 THEN LET T=P: GO SUB P
3250 IF Z=5 THEN LET T=J: GO SUB J
3260 IF Z=6 THEN LET T=Q: GO SUB Q
3270 IF Z=7 THEN LET T=R: GO SUB R
3280 IF Z=8 THEN LET T=S: GO SUB S
3290 IF Z=9 THEN LET T=K: GO SUB K
3300 IF Z>9 THEN GO TO 3190
3310 RETURN
4000 REM Program Initialization
4010 CLS : POKE 23609,100
4020 BORDER 7: PAPER 7: INK 0: CLS
4045 REM Variables:
4050 GO SUB 5000: LET B=0: LET X=15: LET Y=0: GO SUB 1010: LET Y=4: GO SUB 1010: LET Y=12: GO SUB 1010: LET Y=16: GO SUB 1010: LET Y=24: GO SUB 1010: LET Y=28: GO SUB 1010.
4051 REM Clock colons:
4052 PRINT INK 1;;AT 21,0;"ENTER INPUT prompts to set clock"
4053 PRINT INK 2;AT 16,9;"\::";AT 16,21;"\::";AT 18,9;"\::";AT 18,21;"\::"
4055 REM More Variables:
4060 LET G=1010: LET H=1050: LET I=1070: LET J=1110: LET K=1190: LET L=1010: LET M=1010: LET N=1010
4070 LET O=1030: LET P=1090: LET Q=1130: LET R=1150: LET S=1170: LET T=1010: LET U=0: LET W=1190: GO SUB 3000: RETURN
5000 REM "W5GAA" graphics:
5005 INK 0: GO SUB 6000
5010 PRINT AT 0,7;" \::\::\::\::\:: \::\::\::\::\::"
5020 PRINT AT 1,7;" \::\:: \::\:: \::"
5030 PRINT AT 2,7;" \::\:: \::\::"
5040 PRINT AT 3,7;" \::\:: \::\::"
5050 PRINT AT 4,7;" \::\::\::\::\:: \::\:: \::\::"
5060 PRINT AT 5,7;" \:: \::\:: \::"
5070 PRINT AT 6,7;" \::\:: \:: \::\:: \::"
5080 PRINT AT 7,7;" \::\::\::\::\:: \::\::\::\::\::"
5090 GO SUB 6050
5100 REM "television" graphics:
5110 INK 4: PRINT AT 8,2;"\ .\. \.. \ .\. \ .\. "
5120 PRINT AT 9,2;"\.:\:. \..\.. \:: \ .\..\. \ .\. \. \..\. \ .\..\. \..\. \ .\..\. \..\..\. "
5130 PRINT AT 10,2;"\ :\: \::\.: \:: \ :\:.\: \ :\:.\ .\: \ :\: \ :\:.\. \ :\: \ :\: \: \ :\: \: "
5140 PRINT AT 11,2;"\ :\:. \::\.. \::\. \ :\:.\. \':\:' \ :\:.\ .\.:\: \ :\:.\ :\:.\: \ :\: \:."
5148 REM Color bars:
5149 INK 0: PLOT 0,72: DRAW 255,0: DRAW 0,-9: DRAW -255,0: DRAW 0,9
5150 PRINT INK 0;AT 13,1;" "; INK 6;AT 13,3;"\::\::\::"; INK 5;AT 13,6;"\::\::\::"; INK 4;AT 13,9;"\::\::\::"; INK 3;AT 13,12;"\::\::\::"; INK 2;AT 13,15;"\::\::\::"; INK 1;AT 13,18;"\::\::\::"; INK 0;AT 13,21;"\::\::\::"
5154 INK 0: LET X1=192: FOR N=64 TO 71 STEP 2: PLOT X1,N: DRAW 32,0: NEXT N
5158 LET Y1=64: FOR N=224 TO 255 STEP 2: PLOT N,Y1: DRAW 0,8: NEXT N
5499 RETURN
6000 REM "W" graphic
6010 LET XX=7: FOR N=0 TO 15: PLOT N,175: DRAW XX,-63: NEXT N
6020 LET XX=12: FOR N=27 TO 34: PLOT N,163: DRAW -XX,-51: NEXT N
6030 LET XX=14: FOR N=27 TO 34: PLOT N,163: DRAW XX,-51: NEXT N
6040 LET XX=7: FOR N=48 TO 55: PLOT N,175: DRAW -XX,-63: NEXT N: RETURN
6045 REM "A" graphic
6050 FOR N=171 TO 187: PLOT N,175: DRAW -12,-63: NEXT N
6060 FOR N=178 TO 187: PLOT N,175: DRAW 12,-63: NEXT N
6070 FOR N=131 TO 124 STEP -1: PLOT 175,N: DRAW 16,0: NEXT N
6080 FOR N=219 TO 235: PLOT N,175: DRAW -12,-63: NEXT N
6090 FOR N=226 TO 235: PLOT N,175: DRAW 12,-63: NEXT N
6100 FOR N=131 TO 124 STEP -1: PLOT 223,N: DRAW 16,0: NEXT N: RETURN
9998 REM to SAVE with auto-RUN:
9999 CLEAR : SAVE "W5GAA-clok" LINE 1
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
