This program implements an analogue clock display, drawing a clock face with numbered hour positions and animating minute and hour hands. The clock face is drawn using trigonometric positioning (COS and SIN with PI) to place digits 1–12 around a circle, switching to FAST mode during rendering. Line-drawing between two points is handled by a Bresenham-style integer line algorithm in the subroutine at line 1000, which uses SGN and ABS to step along the major axis while accumulating error in variable S. The minute hand is erased and redrawn each tick using PLOT/UNPLOT controlled by the variable P, with timing regulated by POKE 16437 and a PEEK busy-wait loop plus PAUSE 219. Time is input in HHMM format, with the hours variable H computed from hundreds digits and incremented at the 15- and 45-minute marks.
Program Analysis
Program Structure
The program is divided into several logical sections:
- Initialisation (lines 12–22): Accepts time in HHMM format, decodes hours and minutes, and pre-computes the initial hour-hand position in
H. - Clock face drawing (lines 24–65): Clears screen, switches to FAST mode, plots hour digits 1–12 using trigonometry, then returns to SLOW mode. Sets the interrupt counter via
POKE 16437,250. - Main animation loop (lines 70–240): Alternates between erasing the old minute-hand position (via
GOSUB 1000withP=0) and drawing the new one (P=1), with the hour hand drawn at each tick viaGOSUB 310. - Hour-hand subroutine (lines 310–380): Draws a line from the centre (27,22) to the hour-hand endpoint using the Bresenham subroutine.
- Save stub (lines 490–510):
CLEAR,SAVE, andRUNfor persistence. - Line-drawing subroutine (lines 1000–1260): A general integer Bresenham line-draw routine controllable via
P(1=PLOT, 0=UNPLOT).
Time Input and Decoding
Time is entered as a four-digit integer in HHMM format (e.g. 1430 for 2:30 PM). Line 16 extracts hours: H=2*INT(T/100) — note the factor of 2, which converts 12-hour clock hours into half-hour units matching the half-PI step used in the hour-hand calculation. Lines 18–22 strip the hour part, leaving minutes in T, then add 1 to H if past the quarter-hour and again if past the three-quarter mark, giving a coarse hour-hand position in 30-minute increments.
Clock Face Rendering
The hour numerals are positioned using:
- Row:
10 - 10*COS(D/6*PI) - Column:
13 + 10*SIN(D/6*PI)
This maps D=1..12 evenly around a circle using PRINT AT in character coordinates. The clock face is rendered in FAST mode (line 26) to avoid slow screen updates, then SLOW is restored (line 60) before animation begins.
Timing Mechanism
Timing is achieved through a combination of hardware manipulation and busy-waiting. POKE 16437,250 sets the ZX81 frame counter (FRAMES system variable at address 16437) to 250. The loop at line 230 polls PEEK 16437 until it falls below 241, effectively waiting approximately 9 frame intervals (~9/50 s). PAUSE 219 at line 235 then waits a further 219 frames, giving a combined delay approximating one minute per tick of the minute hand — though this is a rough approximation rather than a precise real-time second counter.
Bresenham Line Algorithm (lines 1000–1260)
The subroutine implements a Bresenham-style integer line algorithm. It accepts start point (A,B) implicitly set to (27,22) — the screen centre — and endpoint (C,D). Key variables:
| Variable | Role |
|---|---|
U, V | dx, dy from centre to endpoint |
O, L | SGN of dx, dy (step direction) |
Z, Q | Major-axis step (0 or SGN) |
M, N | Major and minor axis lengths |
S | Bresenham error accumulator, initialised to M/2 |
P | 1 = PLOT, 0 = UNPLOT |
The algorithm swaps axes if the vertical span is larger than the horizontal (lines 1080–1120), ensuring it always steps along the longer dimension. Error accumulation occurs at line 1170 (S=S+N), with the diagonal step taken when S≥M.
Note that lines 1040 (LET Z=SGN U) and 1130 (absent — the label after the conditional block) reveal a minor structural issue: line 1040 initialises Z to SGN U, but this is immediately overridden by line 1090 (LET Z=0) if the vertical span dominates, so the initialisation at 1040 is only effective when M≤N is false — which is correct behaviour. However, there is no line 1130 in the listing (the FOR loop at 1150 follows directly), indicating the GOTO 1130 at line 1080 jumps to line 1140 as the next available line — a standard ZX81 BASIC behaviour where GO TO targets the nearest line at or above the specified number.
Hand Erase-Redraw Cycle
The animation loop erases the previous minute-hand position before drawing the new one. At line 80, P is set to 0 (UNPLOT mode) and the old endpoint (X,Y) is used to call the line subroutine, erasing the hand. Then P=1 and the new endpoint is computed and plotted. The hour hand (subroutine at line 310) is drawn after each minute update but is never explicitly erased, relying on the clock-face redraw (GOTO 24 at line 260) when the hour changes.
Notable Idioms and Anomalies
- The factor of 2 in
H=2*INT(T/100)and the use ofH/12*PIfor the hour-hand angle means the hour hand completes a half-turn (PIradians) per 12 increments ofH— correctly placing it at the top (12 o’clock) when H=0 and at the bottom (6 o’clock) when H=12. - The minute-hand angle uses
T/30*PI, mapping 0–59 minutes to 0–~2PI correctly. - Lines 140 and 260 handle the quarter-hour increments of the hour hand: at T=16 or T=46 (just past the quarter and three-quarter marks), the program jumps to line 250, increments
H, and redraws the full clock face. - The
CLEARat line 490 before the SAVE is unusual; it would erase all variables before saving, meaning the saved program starts fresh on load.
Content
Source Code
12 PRINT " INPUT TIME"
14 INPUT T
16 LET H=2*INT (T/100)
18 LET T=T-100*INT (T/100)
20 IF T>15 THEN LET H=H+1
22 IF T>45 THEN LET H=H+1
24 CLS
26 FAST
30 FOR D=1 TO 12
40 PRINT AT 10-10*COS (D/6*PI),13+10*SIN (D/6*PI);D
50 NEXT D
60 SLOW
65 POKE 16437,250
70 GOTO 150
80 LET P=0
90 LET C=X
100 LET D=Y
110 GOSUB 1000
120 LET T=T+1
130 IF T=60 THEN LET T=0
140 IF T=16 OR T=46 THEN GOTO 250
150 LET P=1
160 LET L=T/30*PI
170 LET X=27+18*SIN L
180 LET Y=22+18*COS L
190 LET C=X
200 LET D=Y
210 GOSUB 1000
220 GOSUB 310
230 IF PEEK 16437>241 THEN GOTO 230
235 PAUSE 219
240 GOTO 80
250 LET H=H+1
260 GOTO 24
310 LET P=1
320 LET A=27
330 LET B=22
340 LET G=H/12*PI
350 LET C=27+12*SIN G
360 LET D=22+12*COS G
370 GOSUB 1000
380 RETURN
490 CLEAR
500 SAVE "1031%8"
510 RUN
\n1000 LET A=27
\n1002 LET B=22
\n1008 LET U=C-A
\n1010 LET V=D-B
\n1020 LET O=SGN U
\n1030 LET L=SGN V
\n1040 LET Z=SGN U
\n1050 LET Q=0
\n1060 LET M=ABS U
\n1070 LET N=ABS V
\n1080 IF M>N THEN GOTO 1130
\n1090 LET Z=0
\n1100 LET Q=SGN V
\n1110 LET M=ABS V
\n1120 LET N=ABS U
\n1140 LET S=INT (M/2)
\n1150 FOR I=0 TO M
\n1160 IF P=1 THEN PLOT A,B
\n1165 IF P=0 THEN UNPLOT A,B
\n1170 LET S=S+N
\n1180 IF S<M THEN GOTO 1230
\n1190 LET S=S-M
\n1200 LET A=A+O
\n1210 LET B=B+L
\n1220 GOTO 1250
\n1230 LET A=A+Z
\n1240 LET B=B+Q
\n1250 NEXT I
\n1260 RETURN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
