--- title: "Digital Clock" id: 57278 type: "computer_media" slug: "digital-clock-3" url: "http://localhost/computer_media/digital-clock-3/" markdown_url: "http://localhost/computer_media/digital-clock-3.md" published_at: "2024-10-04T08:00:39+00:00" modified_at: "2026-03-30T21:18:42+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/10/166_DC.png" excerpt: "A hand-crafted digital clock that ticks in real time, lets you nudge its speed with keypresses, and automatically flips AM to PM at noon." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 1000" slug: "ts1000" taxonomy: "post_tag" url: "http://localhost/tag/ts1000/" model: - name: "Timex/Sinclair 1000" slug: "ts-1000" taxonomy: "model" url: "http://localhost/model/ts-1000/" genre: - name: "Home" slug: "home" taxonomy: "genre" url: "http://localhost/type/home/" media_contents: - id: 56734 title: "Timex Sinclair Public Domain Library Tape 1003" type: "computer_media" url: "http://localhost/computer_media/timex-sinclair-public-domain-library-tape-1003/" media_type: "Program" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/10/166_DC.png" media_type_tags: "Home" --- This program implements a digital clock display for the ZX81/TS1000, accepting a starting time from the user in a compact numeric format (e.g., 352 for 3:52, 1107 for 11:07) along with an AM/PM indicator. The clock ticks by looping through a PAUSE delay of approximately P=3572 units, which approximates a one-minute interval on this hardware, then increments the minutes counter each loop. Lines 110–130 implement real-time keyboard adjustments: pressing F slows the clock, D speeds it up, and J jumps back two minutes, compensating for timing drift. AM/PM switching is handled at the 12:01 boundary by comparing and toggling the M$ string. The POKE 16437,255 at line 220 resets the system’s frame counter to prevent the display from blanking during the clock’s main loop. *** ## Program Analysis ### Program Structure The program is divided into two phases: an initialisation phase (lines 10–80) that collects the starting time and AM/PM indicator, and a main clock loop (lines 100–230) that increments minutes, handles rollovers, and refreshes the display. Line 90 jumps directly into the display/pause portion at line 200, bypassing the minute-increment on the very first iteration so the entered time is shown immediately. 1. **Lines 10–80:** User input and variable setup. `T` is split into hours `H` and minutes `M` by integer division. 2. **Line 80:**`CLS` clears the screen before entering the loop. 3. **Lines 100–190:** Minute increment, keyboard adjustment, rollover logic, and AM/PM toggle. 4. **Lines 200–230:** Display, pause, watchdog POKE, and loop-back. ### Time Input Parsing The user enters the time as a plain integer (e.g., `352` for 3:52 or `1107` for 11:07). Line 50 extracts the hours with `INT(T/100)` and line 60 recovers minutes as `M = T - H*100`. This avoids any string-parsing overhead and is a common ZX81 space-saving idiom. ### Timing Mechanism The clock “ticks” once per minute. The delay is implemented with `PAUSE P` at line 210, where `P` is initialised to 3572 at line 70. On a standard ZX81/TS1000 running at 3.25 MHz, each PAUSE unit corresponds to one 50 Hz frame (approximately 20 ms), so 3572 frames ≈ 71.4 seconds. The intended period is 60 seconds, suggesting the value may need tuning or was empirically chosen to account for the overhead of the BASIC interpreter executing the loop body. ### Keyboard Adjustment Controls Lines 110–130 poll `INKEY$` during each loop iteration (after the PAUSE has already elapsed) to allow manual correction of drift: | Key | Effect | | --- | --- | | `F` | Decreases `P` by 1 (speeds up ticking — shorter pause) | | `D` | Increases `P` by 1 (slows down ticking — longer pause) | | `J` | Subtracts 2 from `M` (steps time back 2 minutes) | Because `INKEY$` is read after the PAUSE, only one keypress is registered per minute cycle. The J key subtracts 2 (not 1) because line 100 has already added 1 before line 130 is reached, so a net correction of −1 minute results. ### Rollover and AM/PM Logic Minute rollover is handled in two consecutive `IF` statements at lines 140 and 150: line 140 increments `H` when `M` reaches 60, then line 150 resets `M` to 0. Hour rollover at line 160 wraps `H` from 13 back to 1, implementing a 12-hour clock. AM/PM toggling (lines 170–190) uses an intermediate variable `C$` to capture the current value of `M$` before any change. The toggle fires when `H=12` and `M=1` (one minute past noon/midnight). Using `M=1` rather than `M=0` is an artefact of the increment happening at line 100 before the check: when the clock ticks from 11:59 to 12:00, `M` becomes 60 → reset to 0 and `H` becomes 12, but the AM/PM check sees `M=0` that cycle; the following cycle gives `M=1` which triggers the toggle. This means the AM/PM indicator flips one minute late. ### Display and Watchdog POKE Line 200 uses `PRINT AT 21,18` to position the clock output at the bottom-right of the screen, formatting it as `H.M M$` with trailing spaces to overwrite any residual characters. Line 220 executes `POKE 16437,255`, which writes to the ZX81 system variable `FRAMES` (the lower byte of the two-byte frame counter at 16436–16437). This prevents the BASIC interpreter’s display-blank watchdog from blanking the screen during long PAUSE calls — a standard ZX81 technique. ### Bugs and Anomalies - **No lower bound on M from J key:** Pressing J can drive `M` below 0 (to −1), because there is no guard against a negative minute value. Subsequent `IF M=60` checks will never fire, and the display will show a negative minutes value until the user presses J again or a natural rollover occurs. - **AM/PM toggle one minute late:** As described above, the switch happens at H=12, M=1 rather than H=12, M=0. - **PAUSE value not calibrated for 60 seconds:** P=3572 produces roughly 71 seconds at 50 Hz, so the clock will run slow without manual F-key correction. - **Lines 240–250 are unreachable:** The `GOTO 100` at line 230 creates an infinite loop; `SAVE` and `RUN` at lines 240–250 are never executed during normal program flow. ## Source Code ``` 1 REM "DIGITAL CLOCK" 10 PRINT "AM OR PM" 20 INPUT M$ 30 PRINT " INPUT TIME IN FORM 352 OR 1107" 40 INPUT T 50 LET H=INT (T/100) 60 LET M=T-H*100 70 LET P=3572 80 CLS 90 GOTO 200 100 LET M=M+1 110 IF INKEY$="F" THEN LET P=P-1 120 IF INKEY$="D" THEN LET P=P+1 130 IF INKEY$="J" THEN LET M=M-2 140 IF M=60 THEN LET H=H+1 150 IF M=60 THEN LET M=0 160 IF H=13 THEN LET H=1 170 LET C$=M$ 180 IF H=12 AND M=1 AND C$="AM" THEN LET M$="PM" 190 IF H=12 AND M=1 AND C$="PM" THEN LET M$="AM" 200 PRINT AT 21,18;H;".";M;" ";M$;" " 210 PAUSE P 220 POKE 16437,255 230 GOTO 100 240 SAVE "1016%6" 250 RUN ```