--- title: "W5GAA Clock" id: 55981 type: "computer_media" slug: "w5gaa-clock" url: "http://localhost/computer_media/w5gaa-clock/" markdown_url: "http://localhost/computer_media/w5gaa-clock.md" published_at: "2024-07-14T13:21:21+00:00" modified_at: "2026-03-30T21:46:24+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/07/SCR-20240714-iqyg.png" excerpt: "A precision UTC digital clock that encodes the current time directly in BASIC loop counters, using block-graphic digits and carefully tuned BEEP durations to keep accurate time." 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: "Frank Bouldin (W5GAA)" slug: "frank-bouldin" taxonomy: "indiv" url: "http://localhost/indiv/frank-bouldin/" genre: - name: "Ham Radio" slug: "ham-radio" taxonomy: "genre" url: "http://localhost/type/ham-radio/" media_contents: - id: 55923 title: "Timex Sinclair Public Domain Library Tape 2003" type: "computer_media" url: "http://localhost/computer_media/timex-sinclair-public-domain-library-tape-2003/" media_type: "Program" programmers: - name: "Frank Bouldin (W5GAA)" slug: "frank-bouldin" taxonomy: "indiv" url: "http://localhost/indiv/frank-bouldin/" download_url: "https://archive.org/download/timex-sinclair-software-archive/W5GAA%20Clock%20%281985%29%28Bouldin%2C%20Frank%29%28TS2068%29%28US%29%28Program%29.tap.zip" mediadate: "1985" images: - url: "http://localhost/wp-content/uploads/2024/07/SCR-20240714-iqyg.png" media_type_tags: "Ham Radio" --- 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: 1. Line 2020: Once per 24 hours (at 00:00:00) — BEEP 0.6499 2. Line 2030: Three times per 24 hours (at 06:00, 12:00, 18:00) — BEEP 0.6503 3. Line 2040: Once per hour (on the hour) — BEEP 0.6521 4. Line 2050: Once per 10 minutes — BEEP 0.6537 5. Line 2060: Once per minute (at E=J, F=K, i.e. :59) — BEEP 0.6588 6. Line 2070: Once per 10 seconds — BEEP 0.6631 7. 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 h` uses lowercase `l` and `h`. On the Spectrum, variable names are case-insensitive in the tokenizer and these would resolve correctly to `L` and `H`. - **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=0` is 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>3` attempts 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:**`W` is 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. ## 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 ```