Clock

Date: 198x
Type: Program
Platform(s): TS 2068
Tags: Clock, Home

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:

  1. Lines 20–30: UDG setup — loads a filled-circle bitmap into UDG “a”.
  2. Lines 1000–1170: Main loop — accepts input time, renders the clock face, advances minutes, and loops indefinitely.
  3. Lines 1500–1600: Digit-rendering subroutine — reads ROM font bitmaps via PEEK and prints column-by-column.
  4. 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).

  • D holds the digit value (0–9); T holds 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-shifting X by doubling.
  • A set bit prints CHR$ 144 (UDG “a”, the filled circle), an unset bit prints CHR$ 32 (space).
  • Line 1585 uses OVER 1 to print a TAB at position T, likely to draw an underline or separator, then restores OVER 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 valueQuarterBehaviour
15Quarter pastFirst phrase only, then exit
30Half pastFirst + second phrase, then exit
45Quarter toFirst + second + third phrase, then exit
0 (hour)Full hourAll 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 TIME and time as 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 N to N+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 9 on 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

Appears On

This tape is a compilation of programs from user group members (Robert Burton, David Baulch, Frank Bouldin, Chuck Dawson, Ryan
One of a series of library tapes. Programs on these tapes were renamed to a number series. This tape contained

Related Products

Related Articles

Related Content

Image Gallery

Clock

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.

People

No people associated with this content.

Scroll to Top