This program implements a digital clock on a ZX81/TS1000, accepting a starting time via INPUT in HHMM format and then continuously incrementing and displaying it. The time is stored internally as total minutes in variable M, and the display routine at line 500 renders each digit by reading 6-byte character shape data directly from ROM using PEEK at address 7905 plus an offset of 8 bytes per digit. Each digit is drawn pixel-by-pixel by extracting individual bits from the ROM font bytes, printing either a space (CHR$ 0) or an inverse-space (CHR$ 128) to form large block digits. The synchronisation loop at line 130 waits for the ZX81 display interrupt flag at system variable address 16437 (FRAMES low byte = 245, approximately one second), then PAUSE 1248 further paces the update cycle.
Program Analysis
Program Structure
The program falls into four logical sections:
- Initialisation (lines 10–35): Prompts for a time in HHMM format, converts it to total minutes stored in
M, then falls through to the display loop. - Main loop (lines 40–170): Decomposes
Minto four decimal digits (hours tens, hours units, minutes tens, minutes units), calls the digit-rendering subroutine for each, waits for a hardware timing signal, incrementsM, and wraps at 1440. - Digit renderer subroutine (lines 500–600): Reads 6 bytes of ROM character data for the requested digit and expands each byte bit-by-bit into a row of block pixels.
- Save stub (lines 700–710): Saves the program then re-runs it.
Time Representation and Input Conversion
The user enters time as a plain number in HHMM format (e.g. 1345 for 13:45). Line 30 converts this to total minutes:
M = INT(TIME/100)*60 + TIME - INT(TIME/100)*100
The first term extracts the hours component and multiplies by 60; the second term extracts the minutes component by subtracting the hours part back out. The result is an integer in the range 0–1439.
Digit Extraction
Before each call to the rendering subroutine, the correct decimal digit and screen column are placed into D and T respectively:
| Lines | Digit | T (column) | Calculation |
|---|---|---|---|
| 50–55 | Hours tens | 0 | INT(INT(M/60)/10) |
| 65–70 | Hours units | 7 | INT(M/60) - 10*D |
| 90–95 | Minutes tens | 16 | INT(60*(M/60 - INT(M/60))/10 + 0.05) |
| 110–120 | Minutes units | 23 | M - INT(M/10)*10 |
The colon separating hours and minutes is drawn by two individual PLOT statements at lines 75 and 80 (coordinates 31,20 and 31,27), placed between the two pairs of digit calls.
ROM Font Rendering (Subroutine 500–600)
The ZX81 character ROM stores each character as 8 bytes; digits ‘0’–’9′ begin at address 7905 (character code offset for ‘0’). Line 500 computes the font address: N = 7905 + D*8. Only 6 of the 8 bytes are used (lines 510–590 iterate N to N+5), skipping the last two blank rows.
For each font byte, the inner loop at lines 520–580 tests bit 7 (the MSB) using the value 128 as a threshold. If the bit is set, C is set to 128 (producing CHR$ 128, an inverse space — a filled block pixel); otherwise C is 0 (a normal space). The byte is then left-shifted by doubling (X = X*2) to bring the next bit into position. This reconstructs each row of the digit from left to right, giving large 7-wide block characters on screen.
After printing each row, line 585 issues PRINT TAB T; to reposition the print position to the correct column for the next row of the same digit.
Timing Mechanism
Line 130 polls the ZX81 system variable FRAMES (low byte at address 16437) waiting for the value 245. This is a software busy-wait that synchronises to the display interrupt counter. Line 140 then issues PAUSE 1248 to complete the approximately one-second delay before incrementing the minute counter.
The combination of the PEEK busy-wait and the PAUSE provides a coarse but functional 1-minute increment cycle; clock drift will accumulate over time since the ZX81 PAUSE duration is not precisely calibrated.
Notable Techniques
- Pure BASIC bit extraction using repeated doubling and threshold testing — no machine code or USR calls.
- Direct ROM font access via
PEEKto avoid storing separate digit bitmaps, saving significant memory. - Column positioning with
PRINT TAB T;inside the rendering loop avoids recalculatingATrow/column coordinates for each row. PRINT CHR$ C;uses the fact that CHR$ 128 on the ZX81 is an inverse space (solid block), enabling monochrome “pixel” rendering.
Potential Bugs and Anomalies
- The minutes-tens digit calculation at line 90 includes a
+0.05rounding guard. Without it, floating-point errors could occasionally give one digit too few (e.g. 5.999… floored to 5 instead of 6). This is a deliberate defensive measure. - The minutes-units digit at line 110 uses
M - INT(M/10)*10rather than the more idiomaticM MOD 10; on the ZX81 there is no MOD operator, so this manual modulo is correct. - The variable
Nis reused: it is first set as the font base address in line 500, then immediately used as theFORloop variable in line 510 —FOR N=N TO N+5. This works because the right-hand side is evaluated before the loop variable is assigned, but it is a confusing construct. - If the user enters an invalid time (e.g. 2500 or a minutes component ≥ 60), no validation is performed and the display will show incorrect digits.
Content
Source Code
10 REM "DIGITAL CLOCK" TO SAVE GOTO 700
15 PRINT " INPUT TIME"
20 INPUT TIME
25 CLS
30 LET M=INT (TIME/100)*60+TIME-INT (TIME/100)*100
35 GOTO 140
40 LET T=0
50 LET D=INT ((INT (M/60))/10)
55 GOSUB 500
60 LET T=7
65 LET D=(INT (M/60))-10*D
70 GOSUB 500
75 PLOT 31,20
80 PLOT 31,27
85 LET T=16
90 LET D=INT (60*(M/60-INT (M/60))/10+.05)
95 GOSUB 500
100 LET T=23
110 LET D=M-INT (M/10)*10
120 GOSUB 500
130 IF PEEK 16437<>245 THEN GOTO 130
140 PAUSE 1248
150 LET M=M+1
160 IF M=1440 THEN LET M=0
170 GOTO 40
500 LET N=7905+D*8
505 PRINT AT 7,T;
510 FOR N=N TO N+5
515 LET X=PEEK N
520 FOR L=1 TO 7
525 LET C=0
530 IF X<128 THEN GOTO 560
540 LET C=128
550 LET X=X-128
560 LET X=X*2
570 PRINT CHR$ C;
580 NEXT L
585 PRINT TAB T;
590 NEXT N
600 RETURN
700 SAVE "DIGITAL CLOC%K"
710 RUN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

