Timer 1

Developer(s): Algis Gedris
Date: 1984
Type: Program
Platform(s): TS 2068
Tags: Clock

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.

Content

Appears On

Related Products

Related Articles

Related Content

Image Gallery

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.

Scroll to Top