Digital Clock

Date: 198x
Type: Cassette
Platform(s): TS 1000
Tags: Home

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:

  1. 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.
  2. Main loop (lines 40–170): Decomposes M into four decimal digits (hours tens, hours units, minutes tens, minutes units), calls the digit-rendering subroutine for each, waits for a hardware timing signal, increments M, and wraps at 1440.
  3. 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.
  4. 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:

LinesDigitT (column)Calculation
50–55Hours tens0INT(INT(M/60)/10)
65–70Hours units7INT(M/60) - 10*D
90–95Minutes tens16INT(60*(M/60 - INT(M/60))/10 + 0.05)
110–120Minutes units23M - 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 PEEK to avoid storing separate digit bitmaps, saving significant memory.
  • Column positioning with PRINT TAB T; inside the rendering loop avoids recalculating AT row/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.05 rounding 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)*10 rather than the more idiomatic M MOD 10; on the ZX81 there is no MOD operator, so this manual modulo is correct.
  • The variable N is reused: it is first set as the font base address in line 500, then immediately used as the FOR loop 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

Appears On

Related Products

Related Articles

Related Content

Image Gallery

Digital Clock

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.

People

No people associated with this content.

Scroll to Top