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:
- Initialization (line 2): Clears memory and loads the companion machine code block (“timer 1 C”) to address 32256.
- 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.
- 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.
- Clock display loop (lines 160–260): Continuously calls subroutines to read elapsed FRAMES, compute current time, handle date rollover, and display the result.
- 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 positionyy— vertical pixel positionxs— horizontal scale factorys— vertical scale factor8— 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 stringP$at line 250 to force a new line in the display.VAL "number"is not used here;GO SUBtargets are explicit line numbers.
Bugs and Anomalies
- Line 165 references
P$andCbefore they are set: On the first iteration of the loop at line 160,GO SUB 220is called (line 160), which setsP$andCvia line 250. However, line 165 uses both — the flow viaGO SUB 220at 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):Cis set toSGN PI = 1in line 240, soT$(C)=T$(1), the elapsed timer — this appears intentional. - Unreferenced variables:
EDandL$are set at line 108 butEDis 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.
Content
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
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

