--- title: "Timer 1" id: 70955 type: "computer_media" slug: "timer-1" url: "http://localhost/computer_media/timer-1/" markdown_url: "http://localhost/computer_media/timer-1.md" published_at: "2026-08-24T23:22:09+00:00" modified_at: "2026-08-24T23:25:07+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/08/timer-1a.png" alt: "Timer 1 screen" excerpt: "A digital clock with animated rainbow title screen uses machine code scaled-text rendering and frame-counter manipulation to track time and automatically roll over dates." 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 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" indiv: - name: "Algis Gedris" slug: "algis-gedris" taxonomy: "indiv" url: "http://localhost/indiv/algis-gedris/" genre: - name: "Clock" slug: "clock" taxonomy: "genre" url: "http://localhost/type/clock/" media_type: "Program" programmers: - name: "Algis Gedris" slug: "algis-gedris" taxonomy: "indiv" url: "http://localhost/indiv/algis-gedris/" download_url: "https://archive.org/download/timex-sinclair-software-archive/Timer%201%20%281984%29%28Gedris%2C%20Algis%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "1984" images: - url: "http://localhost/wp-content/uploads/2026/08/timer-1a.png" alt: "Timer 1 screen" - url: "http://localhost/wp-content/uploads/2026/08/timer-1b.png" alt: "Timer 1 screen" media_type_tags: "Clock" --- # Timer 1 Timer 1 is a clock program that combines a machine code routine with BASIC to display a stylized animated title screen and a running digital clock with date tracking. The program loads 300 bytes of machine code to address 32256, which is called via USR to render scaled text on screen — a technique used to draw the “CLOCK” and “GEDRIS” titles at multiple sizes and ink colors to create a shadow/rainbow effect. System variables at addresses 23672–23674 are used to read and write the three-byte frame counter (FRAMES), allowing the program to calculate elapsed time in hours, minutes, and seconds. The date-advancement logic handles month-end rollovers and includes a basic leap year check for February, though January, March, May, July, and August are incorrectly treated as having 32 days instead of 31, and line 165 references undefined variables `P$` and `C` before they are properly initialized in the main loop. The program saves itself at line 9998 using `SAVE ... LINE 2` along with the companion machine code block. *** ### Program Structure The program is divided into several distinct phases: 1. **Initialization (line 2):** Clears memory and loads the companion machine code block (“timer 1 C”) to address 32256. 2. **Title animation (lines 3–28):** Draws “CLOCK” and “GEDRIS” in a multi-pass loop at decreasing sizes and cycling ink colors to create a rainbow/shadow layered effect. 3. **Clock setting (lines 100–125):** Accepts user input for date and time, then writes the initial time into system FRAMES variables at addresses 23672–23674. 4. **Clock display loop (lines 160–260):** Continuously calls subroutines to read elapsed FRAMES, compute current time, handle date rollover, and display the result. 5. **Save routine (line 9998):** Saves the BASIC program with an auto-run flag and the machine code block separately. ### Machine Code Usage The companion machine code (300 bytes at address 32256) is a scaled character renderer. The BASIC program sets up a parameter block starting at address 23306 (within the spare area of the system variables / printer buffer), writing: - `xx` — horizontal pixel position - `yy` — vertical pixel position - `xs` — horizontal scale factor - `ys` — vertical scale factor - `8` — character height constant - Character codes of the string to render, terminated by 255 The routine is invoked with `LET w= USR 32256`. The horizontal center position is computed in BASIC as `(256 - 8 * xs * LEN p$) / 2` before each call. Subroutines at lines 9–10 and 26–28 are nearly identical wrappers for this renderer, differing only in the string and scale parameters used. ### FRAMES Counter Manipulation The system FRAMES counter occupies three bytes at addresses 23672, 23673, and 23674 (low, mid, high). Line 120 encodes the initial time (in 50ths of a second, since FRAMES increments at 50 Hz) into these bytes: - `X = (HR * 3600 + MIN * 60) * 60` — note this is in 60ths not 50ths, a potential accuracy concern - High byte: `INT(X / 65536)` → POKE 23674 - Mid byte: `INT((X - Y*65536) / 256)` → POKE 23673 - Low byte: remainder → POKE 23672 Line 190 reads these back with `PEEK 23672 + 256*PEEK 23673 + 65536*PEEK 23674` and divides by 3600 to recover hours and minutes. ### Time and Elapsed Timer Display The program maintains two time displays simultaneously. `T$(2)` holds the current wall-clock time (HR:MIN), and `T$(1)` holds an elapsed “online” timer computed as the difference between current time and the initial `TEMP` value (set at line 111 as `HR*60 + MIN`). Subroutine 260 formats either value with zero-padding using the idiom `("0" AND LEN STR$ P1 = SGN PI)`, which prepends “0” when the number is a single digit (since `SGN PI = 1`). ### Date Rollover Logic Lines 190–219 handle automatic date advancement. When `HR >= 24`, the hour resets to 0 (`NOT PI = 0`) and the day increments. February is checked separately with a leap year test at line 191: `INT(YR/4 + 0.09)`, which approximates divisibility by 4. The `+0.09` offset helps avoid floating-point rounding errors. However, the month-length table contains a systematic bug: months with 31 days (January, March, May, July, August, October) use the threshold `>= 32`, which is correct, but months 4, 6, 9, and 11 (30-day months) also correctly use `>= 31`. The logic is otherwise handled by a chain of individual IF statements rather than a lookup table. ### Key BASIC Idioms - `NOT PI` — evaluates to 0, used as a readable way to assign zero (e.g., `LET ED = NOT PI`, `LET HR = NOT PI`). - `SGN PI` — evaluates to 1, used for single-digit detection and incrementing. - `("0" AND condition)` — returns “0” if condition is true (non-zero), empty string otherwise; a standard zero-padding idiom. - `L$ = CHR$ 13` — carriage return character, embedded in the output string `P$` at line 250 to force a new line in the display. - `VAL "number"` is not used here; `GO SUB` targets are explicit line numbers. ### Bugs and Anomalies - **Line 165 references `P$` and `C` before they are set:** On the first iteration of the loop at line 160, `GO SUB 220` is called (line 160), which sets `P$` and `C` via line 250. However, line 165 uses both — the flow via `GO SUB 220` at line 160 does initialize them before line 165 is reached, so this is not a runtime error. - **Double colon at line 22:**`LET yy=f*5+100::` contains a redundant colon, which is benign. - **Line 165 array index `T$(C)`:**`C` is set to `SGN PI = 1` in line 240, so `T$(C)` = `T$(1)`, the elapsed timer — this appears intentional. - **Unreferenced variables:**`ED` and `L$` are set at line 108 but `ED` is never used elsewhere in the listing; `L$` is used only at line 250. - **Lines 90 and 103–104 presentation text:** The title screen text at line 104 is printed again after the animated intro, providing context before clock-setting input. ### Save Routine Line 9998 performs a full save of both program components: `SAVE "Timer 1" LINE 2` saves the BASIC with an auto-run entry point, and `SAVE "timer 1 C" CODE 32256,300` saves the machine code block separately, matching the `LOAD` at line 2. ## Source Code ``` 1 REM THIS CLOCK PROGRAM IS PRESENTED BY ALGIS E. GEDRIS <><><><><><><><><><> 2 CLEAR 32255:LOAD "timer 1 C"CODE 32256,300 3 REM CLOCK TITLE 4 BORDER 0:PAPER 0:INK 7:CLS 5 LET p$="CLOCK":FOR f=6 TO 1 STEP -1 7 INK f:LET yy=80-f*10:LET xs=f:LET ys=f:GO SUB 9:NEXT f:GO TO 11 9 LET xx=(256-8*xs* LEN p$)/2 10 LET i=23306:POKE i,xx:POKE i+1,yy:POKE i+2,xs:POKE i+3,ys:POKE i+4,8:LET i=i+4:LET w= LEN p$:FOR n=1 TO w:POKE i+n, CODE p$(n):NEXT n:POKE i+w+1,255:LET w= USR 32256:RETURN 15 PRINT AT 11,10;"PRESENTED BY:" 20 LET p$="GEDRIS":FOR f=5 TO 1 STEP -1 22 INK f+1:LET yy=f*5+100::LET xs=f:LET ys=f:GO SUB 26:NEXT f:PAUSE 250:GO TO 30 26 LET xx=(256-8*xs* LEN p$)/2 28 LET i=23306:POKE i,xx:POKE i+1,yy:POKE i+2,xs:POKE i+3,ys:POKE i+4,8:LET i=i+4:LET w= LEN p$:FOR n=1 TO w:POKE i+n, CODE p$(n):NEXT n:POKE i+w+1,255:LET w= USR 32256:RETURN 90 REM TIMER 100 REM CLOCK SETTING 102 CLEAR :POKE 23658,8:PAPER 0:BORDER 0:INK 7:CLS 104 PRINT AT 5,5;"THIS CLOCK PROGRAM IS PRESENTED BY: ALGIS E. GEDRIS" 105 PRINT AT 15,3;"C L O C K S E T T I N G" 106 PRINT AT 20,19;"NOVEMBER 1986" 108 DIM T$(2,5):LET ED= NOT PI:LET L$= CHR$ 13 110 INPUT "Month 1-12 ";MO,"Day 1-31 ";DY,"Hour 00-23 ";HR,"Minute ";MIN,"Year 86 ";YR 111 LET TEMP=HR*60+MIN 115 GO SUB 120:CLS :GO TO 160 120 LET X=(HR*3600+MIN*60)*60:LET Y= INT (X/65536):POKE 23674,Y 125 LET P1=X-Y*65536:LET X= INT (P1/256):POKE 23673,X:POKE 23672,P1-X*256:RETURN 160 GO SUB 220:PRINT AT 6,6;"TIC";" C L O C K ";" TAC" 161 LET N=4 163 IF MO >=10 AND DY >=10 THEN LET N=3 164 IF MO<10 AND DY<10 THEN LET N=4 165 PRINT AT 8,0; TAB 4;P$( TO 14);" ";T$(C) 166 PRINT AT 15,7;" ON LINE - TIMER "; AT 17,4;P$ 170 GO TO 160 190 LET TIME= INT ((PEEK 23672+256* PEEK 23673+65536* PEEK 23674)/3600):LET HR= INT (TIME/60):LET MIN= INT (TIME-HR*60):LET HR=HR:IF HR >=24 THEN LET HR= NOT PI:LET DY=DY+ SGN PI:GO SUB 120 191 LET Z= INT (YR/4+.09) 192 IF MO=2 THEN GO TO 195 193 GO TO 202 195 IF Z*4=YR THEN GO TO 198 196 IF DY >=29 THEN LET MO=3:LET DY=1:CLS :RETURN 198 IF DY >=30 THEN LET MO=3:LET DY=1:CLS :RETURN 199 RETURN 202 IF MO=1 AND DY >=32 THEN LET MO=2:LET DY=1:CLS 203 IF MO=3 AND DY >=32 THEN LET MO=4:LET DY=1:CLS 204 IF MO=4 AND DY >=31 THEN LET MO=5:LET DY=1:CLS 205 IF MO=5 AND DY >=32 THEN LET MO=6:LET DY=1:CLS 206 IF MO=6 AND DY >=31 THEN LET MO=7:LET DY=1:CLS 207 IF MO=7 AND DY >=32 THEN LET MO=8:LET DY=1:CLS 208 IF MO=8 AND DY >=32 THEN LET MO=9:LET DY=1:CLS 209 IF MO=9 AND DY >=31 THEN LET MO=10:LET DY=1:CLS 210 IF MO=10 AND DY >=32 THEN LET MO=11:LET DY=1:CLS 211 IF MO=11 AND DY >=31 THEN LET MO=12:LET DY=1:CLS 212 IF MO=12 AND DY >=32 THEN LET MO=1:LET DY=1:LET YR=YR+1:CLS 219 RETURN 220 GO SUB 190:LET TIME= INT ((HR*60+MIN)-TEMP) 230 LET P1= INT (TIME/60):LET P2= INT (TIME-(P1*60)):IF P1<0 THEN LET P1=P1+24 240 LET C= SGN PI:GO SUB 260:LET C=2:LET P1=HR:LET P2=MIN:GO SUB 260 250 LET P$= STR$ MO+"-"+ STR$ DY+"-"+ STR$ YR+";"+T$(2)+" Online "+T$(1)+L$:RETURN 260 LET T$(c)=("0" AND LEN STR$ P1= SGN PI)+ STR$ P1+":"+("0" AND LEN STR$ P2= SGN PI)+ STR$ P2:RETURN 9998 CLEAR :SAVE "Timer 1" LINE 2:SAVE "timer 1 C"CODE 32256,300 ```