This program implements a digital clock display that accepts a starting time via INPUT and then counts upward minute by minute. It defines a custom UDG character “a” — a filled circle shape (bytes 60,126,255,255,255,255,126,60) — used as a colon separator between the hours and minutes on screen. The clock rendering subroutine at line 1500 reads character bitmap data directly from the ROM font area (base address 15745) using PEEK, then reconstructs each digit column by column, printing either a space (CHR$ 32) or UDG “a” (CHR$ 144) depending on each bit’s value. Every 15 minutes a chime subroutine at line 2000 plays a sequence of BEEP notes, with different melodies for the quarter, half, three-quarter, and full hours, followed by a random BORDER colour flash. The seconds display is approximated using a FOR loop with PAUSE 57 (roughly one second per iteration) to advance the on-screen counter.
Program Analysis
Program Structure
The program is divided into four functional blocks:
- Lines 20–30: UDG setup — loads a filled-circle bitmap into UDG “a”.
- Lines 1000–1170: Main loop — accepts input time, renders the clock face, advances minutes, and loops indefinitely.
- Lines 1500–1600: Digit-rendering subroutine — reads ROM font bitmaps via PEEK and prints column-by-column.
- Lines 2000–2010: Chime subroutine — plays quarter-hour melodies and flashes the border.
Time Representation
The user inputs time as an integer in HHMM format (e.g. 1345 for 1:45 PM). Line 1030 converts this to a total-minutes value M:
M = INT(TIME/100)*60 + TIME - INT(TIME/100)*100
This extracts the hours portion, multiplies by 60, then adds the minutes portion. The clock runs from 0 to 1439 minutes (line 1160 resets M to 0 at 1440), giving a 24-hour cycle.
Digit Rendering via ROM Font PEEK
The subroutine at line 1500 is the most technically interesting section. It renders a single digit (0–9) at a specified screen column by reading directly from the Spectrum ROM character set. The base address used is 15745, which is the start of the ROM font data (the standard font starts at 15616 for space, so 15745 = 15616 + 129 = character code 48 “0” offset: actually 15616 + 8×(48–32) = 15616 + 128 = 15744; the code uses 15744 + D×8 effectively starting at N=15745+D×8, reading rows N to N+5, skipping the top and bottom rows of the 8-row glyph).
Dholds the digit value (0–9);Tholds the screen column position.- For each of 6 rows of the glyph, each of 7 bits is tested by checking whether
X ≥ 128(MSB set), then left-shiftingXby doubling. - A set bit prints CHR$ 144 (UDG “a”, the filled circle), an unset bit prints CHR$ 32 (space).
- Line 1585 uses
OVER 1to print a TAB at position T, likely to draw an underline or separator, then restoresOVER 0.
This technique of manually walking ROM font bytes to create pixel-style output is a well-known Sinclair BASIC idiom that avoids the need for machine code while achieving a custom rendered appearance.
Seconds Display and Timing
Lines 1130–1140 use a FOR I=0 TO 54 loop, printing I at screen position (17,15) and calling PAUSE 57 each iteration. At 50 Hz, PAUSE 57 ≈ 1.14 seconds, so 55 iterations ≈ 62.7 seconds — slightly over one minute. Line 1140 also adds a final PAUSE 21 (≈0.42 s) after the loop, bringing the total to approximately 63 seconds per minute cycle. This is a rough approximation; the display and BEEP overhead will further vary the actual elapsed time.
Quarter-Hour Chime Logic
Line 1125 checks IF M/15 = INT(M/15) to trigger the chime subroutine every 15 minutes. Inside the subroutine, the variable S (set in lines 1090–1115 as the total seconds/minutes within the current hour, i.e. M MOD 60) is used to select which melody phrase to play:
| S value | Quarter | Behaviour |
|---|---|---|
| 15 | Quarter past | First phrase only, then exit |
| 30 | Half past | First + second phrase, then exit |
| 45 | Quarter to | First + second + third phrase, then exit |
| 0 (hour) | Full hour | All four phrases play |
The chime melody fragments use notes with semitone values 12, 8, 10, and 3 — a Westminster-style chime pattern. After chiming, BORDER RND*7 selects a random border colour.
UDG Definition
Lines 20–30 poke the bytes 60,126,255,255,255,255,126,60 into USR "a". In binary these are:
- 00111100
- 01111110
- 11111111 (×4)
- 01111110
- 00111100
This creates a filled circle/disc shape, used both as a decorative colon separator (line 1080 prints \a at two screen positions between hours and minutes) and as the “lit pixel” glyph within the digit renderer.
Anomalies and Notes
- Line 1030 uses both
TIMEandtimeas variable references. In Spectrum BASIC, variable names are case-insensitive only in the sense that the editor uppercases keywords; user variable names entered as lowercase are stored differently. This may cause a “Variable not found” error depending on how the variable was entered — both should resolve to the same variable if entered consistently. - The digit renderer reads rows
NtoN+5(6 rows) of the 8-row font glyph, omitting the top and bottom rows, giving a slightly cropped digit appearance. - The seconds counter displayed (0–54) does not match real seconds; it is a loop index used purely for visual feedback.
INK 9on line 1000 sets ink to “bright black” (dark grey in some implementations) or may be interpreted as a contrast colour depending on the system.
Content
Source Code
20 RESTORE : FOR i=0 TO 7: READ a: POKE USR "a"+i,a: NEXT i
30 DATA 60,126,255,255,255,255,126,60
1000 BORDER 6: PAPER 6: INK 9: CLS
1010 INPUT "Input Time ";TIME
1030 LET m=INT (TIME/100)*60+time-INT (TIME/100)*100
1045 LET T=0
1050 LET D=INT ((INT (M/60))/10)
1055 GO SUB 1500
1060 LET T=7
1065 LET D=(INT (M/60))-10*D
1070 GO SUB 1500
1080 PRINT AT 11,15;"\a";AT 8,15;"\a"
1085 LET T=16
1090 LET D=INT (60*(M/60-INT (M/60))/10+.05)
1091 LET S=10*D
1095 GO SUB 1500
1100 LET T=23
1110 LET D=M-INT (M/10)*10
1115 LET S=S+D
1120 GO SUB 1500
1125 IF M/15=INT (M/15) THEN GO SUB 2000
1130 FOR I=0 TO 54: PRINT AT 17,15;I
1135 PAUSE 57
1140 NEXT I: PRINT AT 17,15;" ": PAUSE 21
1150 LET M=M+1
1160 IF M=1440 THEN LET M=0
1170 GO TO 1045
1500 LET N=15745+D*8
1510 PRINT AT 7,T;: FOR N=N TO N+5
1520 LET X=PEEK N: FOR L=1 TO 7
1530 LET C=32: IF X<128 THEN GO TO 1560
1540 LET C=144: LET X=X-128
1560 LET X=X*2
1570 PRINT CHR$ C;
1580 NEXT L
1585 OVER 1: PRINT TAB T;: OVER 0
1590 NEXT N
1600 RETURN
2000 BEEP .5,12: BEEP .5,8: BEEP .5,10: BEEP 1,3: IF S=15 THEN GO TO 2005
2001 BEEP .5,3: BEEP .5,10: BEEP .5,12: BEEP 1,8: IF S=30 THEN GO TO 2005
2002 BEEP .5,12: BEEP .5,8: BEEP .5,10: BEEP 1,3: IF S=45 THEN GO TO 2005
2003 BEEP .5,3: BEEP .5,10: BEEP .5,12: BEEP 1,8
2005 BORDER RND*7
2010 RETURN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
