A.N.A.L.O.G. ISSUE 5 / JANUARY 1982 / PAGE 28

STOPWATCH

by Craig Patchett

As you probably already know, the ATARI has a built-in, real-time clock (i.e. it keeps track of time while it’s turned on). But what good is such a clock if you can’t see it? STOPWATCH utilizes this clock in putting a real-time stopwatch on your screen, that works independently of BASIC!

CREATING A COPY OF STOPWATCH

Type and run one of the following programs, depending on whether you want a cassette or disk version. Be careful typing in the data, and make sure you save a copy of the program in case you find out later you made a mistake typing it in. Each of the programs when run will create a machine language copy of STOPWATCH on the medium you chose.

RUNNING THE CASSETTE VERSION

The cassette version of STOPWATCH (the copy you got from the previous step) loads and runs in the following manner.

  1. Turn the computer and all peripherals off.
  2. Insert the program cassette into the cassette player, rewind it, and press PLAY.
  3. Turn the computer on while holding down the START button.
  4. After the computer beeps, press RETURN.
  5. The program will load; and then RUN automatically.

RUNNING THE DISK VERSION

The disk version of STOPWATCH loads and runs in the following manner.

  1. Boot the disk containing the program.
  2. Type “DOS” and press RETURN (you must have DOS II)
  3. When the DOS menu appears, type “L” and press RETURN.
  4. Type “STOPWTCH.OBJ” in response the computer’s request for a filename and press RETURN.
  5. When the stopwatch appears on the screen and the “BUSY” light on the disk drive goes out, press SYSTEM RESET.

USING STOPWATCH

You should now have a running stopwatch in the top right-hand corner of your screen. If you don’t make sure you followed the instructions correctly, and that you typed in the original BASIC program correctly.

Once you get the stopwatch working, you have the following options.

  1. Ignore it (treat it as if it wasn’t there)
  2. Press START. This will stop the stopwatch if it was running, and start it if it wasn’t.
  3. If it’s stopped, press SELECT. This will reset the stopwatch to 0:00:00.
  4. Press SELECT. This will disengage the stopwatch if it was engaged, and engage it if it wasn’t.
    If STOPWATCH is disengaged, the ATARI will treat it just the same as if it were engaged except it will stop updating it on the screen. The best way to explain this is for you to try it yourself.
  5. Press SYSTEM RESET. It has no effect on the stopwatch.

LIMITATIONS

STOPWATCH has the following minor limitations:

  1. The ATARI will treat the stopwatch as if you had typed it there yourself. For example, position the cursor on the same line as the stopwatch and press RETURN. If this type of thing gets to be a problem, disengage it using the OPTION button.
  2. If you try to use STOPWATCH in conjunction with other machine language routines or with the interface Module, you may run into problems. STOPWATCH uses almost all of page six in memory.
  3. STOPWATCH will not work with OS/A+ or BASIC A+
  4. STOPWATCH will only count up to 9 hrs, 59 mins, 59 sees. If this is a problem, get some sleep instead!

Most of these limitations could be corrected by you (yes you) if you so desire and have the ability, but at the cost of having to use extra memory and probably having to relocate.

If you like, you can set STOPWATCH by changing the values of locations 1537 to 1541. Do this using POKE (experiment).

HOW IT WORKS

Below is an extensively commented source listing of the disk version of STOPWATCH, as well as the changes, necessary for the cassette version. Note that the run address is $60A hex = 1546 decimal.

The trick that lets the ATARI act as though the stopwatch isn’t there is called vertical blank (VBLANK for short). Briefly, vertical blank is the time during which the electron beam that draws the screen is returning from the bottom of the screen to the top for another pass. Some of this time is available to the machine language programmer and is independent of regular processor time.

If you want to learn more about VBLANK, study the source listing and consult the Operating System User’s Manual, which can be purchased from ATARI (part number CO16555)

Hopefully the source listing will provide an informative introduction to machine language for all you novices out there. Enjoy!

100 REM STOPWATCH
110 REM By Craig Patchett
120 REM (c) 1981 ANALOG Magazine
130 REM
140 REM (Cassette Version)
150 REM
160 REM
200 DIM PROG$(245)
210 FOR LOOP=1 TO 245:READ BYTE:PROG$(LOOP,LOOP)=CHR$(BYTE):NEXT LOOP
220 OPEN #1,8,128,"C:":PRINT #1;PROG$:CLOSE #1
999 END
1000 DATA 0,2,225,5,10,6,169,60,141,2,211,169,2,133,9,169,0,141,68,2,165,12,133,2,165
1010 DATA 13,133,3,108,12,0,0,0,0,0,0,0,60,0,1,1,169,29,141,34,2,169,6,141,35
1020 DATA 2,169,0,133,10,169,160,133,11,96,165,88,133,203,165,89,133,204,165,87,240,10,173,148,2
1030 DATA 133,203,173,149,2,133,204,169,8,141,31,208,173,7,6,240,12,173,31,208,201 ,7,208,55,169
1040 DATA 0,141,7,6,173,31,208,201,7,240,43,201,3,208,6,238,8,6,238,7,6,201,6,208.6
1050 DATA 238,9,6,238,7,6,201,5,208,19,173,9,6,41,1,208,12,169,0,162,6,202,157,0,6
1060 DATA 224,0,208,248,173,9,6,41,1,240,37,206,6,6,208,32,169,60,141,6,6,162,5,216,24
1070 DATA 254,0,6,138,41,1,10,10,105,6,221,0,6,208,8,169,0,157,0,6,202,208,233,173,8
1080 DATA 6,41,1,240,37,160,31,216,162,1,224,1,240,14,169,154,145,203,200,189,0,6,105,144,145
1090 DATA 203,200,232,24,189,0,6,105,144,145,203,200,232,224,6,208,228,76,95,228

Do these modifications to the source
code for running the cassette version
;
;initialize All the Vectors (Cassette Version)
;
SYSRES LDA  #CLOCK&255    tell the ATARI where we want it to go
       STA  VVBLKI        during VBLANK
       LDA  #CLOCK/256
       STA  VVBLKI+1
       LDA  #0            tell the ATARI where we want it to go
       STA  DOSVEC        during SYSTEM RESET
       LDA  #$A0
       STA  DOSVEC+1
SYSEND RTS                end of initialization

100 REM STOPWATCH
110 REM By Craig Patchett
120 REM (c) 1981 ANALOG Magazine
130 REM
140 REM (Disk Version)
150 REM
160 REM
200 OPEN #1,8,0,"D:STOPWTCH.OBJ"
210 FOR LOOP=1 TO 239:READ DATA:PUT #1,DATA:NEXT LOOP
220 CLOSE #1
999 END
1000 DATA 255,255,0,6,226,6,0,0,0,0,0,0,60,0,1,1,165,12,141,29,6,165,13,141,30
1010 DATA 6,169,28,133,12,169,6,133,13,32,41,6,169,42,141,34,2,169,6,141,35,2,96,165,88
1020 DATA 133,203,165,89,133,204,165,87,240,10,173,148,2,133,203,173,149,2,133,204,169,8,141,31,208
1030 DATA 173,7,6,240,12,173,31,208,201,7,208,55,169,0,141,7,6,173,31,208,201,7,240,43,201
1040 DATA 3,208,6,238,8,6,238,7,6,201,6,208,6,238,9,6,238,7,6,201,5,208,19,173,9
1050 DATA 6,41,1,208,12,169,0,162,6,202,157,0,6,224,0,208,248,173,9,6,41,1,240,37,206
1060 DATA 6,6,208,32,169,60,141,6,6,162,5,216,24,254,0,6,138,41,1,10,10,105,6,221,0
1070 DATA 6,208,8,169.0,157,0,6,202,208,233,173,8,6,41,1,240,37,160,31,216,162,1,224,1
1080 DATA 240,14,169,154,145,203,200,189,0,6,105,144,145,203,200,232,24,189,0,6,105,144,145,203,200
1090 DATA 232,224,6,208,228,76,95,228,226,2,227,2,10,6

Assembly Listing

            1000 ;
            1010 ;
            1020 ;            STOPWATCH
            1030 ;        By Craig Patchett
            1040 ;
            1050 ;    (c) 1981 ANALOG Magazine
            1060 ;
            1070 ;
            1080 ;System Variable Definitions
            1090 ;
000C        1100 DOSINI =    $0C         RAM vector used during SYSTEM RESET
0057        1110 DINDEX =    $57         holds the current BASIC mode number
0058        1120 SAVMSC =    $58         holds the address of the beginning of
            1130 ;                        normal screen memory
0222        1140 VVBLKI =    $222        RAM vector pointing to the vertical
            1150 ;                        blank routine
0294        1160 TXTMSC =    $294        holds the address of the beginning of
            1170 ;                        the text window
D01F        1180 CONSOL =    $D01F       holds the status of the console
            1190 ;                        buttons (i.e. START, etc)
E45F        1200 SYSVBV =    $E45F       system vertical blank routine
            1210 ;
            1220 ;Our Own Variables
            1230 ;
00CB        1240 SCREEN =    $CB         holds the address of the beginning of
            1250 ;                        screen/text window
0003        1260 OPTION =    3           values of CONSOL for the various
            1270 ;                        buttons
0005        1280 SELECT =    5
0006        1290 START  =    6
009A        1300 COLON  =    154         value of a reverse colon
001F        1310 OFFSET =    31          horizontal offset of clock on screen
0090        1320 CONVERT=    144         conversion factor to go from numbers
            1330 ;                        to reverse characters
            1340 ;
0000        1350        *=   $600        let's start using up memory ($600=1536
            1360 ;                        decimal)
            1370 ;
0600 00     1380 TIMDIG .BYTE 0,0,0      the digits for our clock
0601 00
0602 00
0603 00     1390        .BYTE 0,0,0
0604 00
0605 00
0606 3C     1400 TIMER  .BYTE 60         a seconds timer (60 ATARI "units" = 1
            1410 ;                        second)
0607 00     1420 PRESS  .BYTE 0          tells us if the last button pressed
            1430 ;                        has been released yet
0608 01     1440 ENGAGE .BYTE 1          tells us if the clock is engaged
0609 01     1450 RUNNIN .BYTE 1          tells us if the clock is running
            1460 ;
            1470 ;Initialize All the Vectors
            1480 ;
060A A50C   1490 INIT   LDA  DOSINI      remember where the ATARI wants to go
060C 8D1D06 1500        STA  SYSRES+1    during a SYSTEM RESET
060F A50D   1510        LDA  DOSINI+1
0611 8D1E06 1520        STA  SYSRES+2
0614 A91C   1530        LDA  #SYSRESS&255 tell the ATARI where we want it to go
0616 850C   1540        STA  DOSINI      during SYSTEM RESET
0618 A906   1550        LDA  #SYSRES/256
061A 850D   1560        STA  DOSINI+1
            1561 ;
            1562 ;We come here during SYSTEM RESET
            1563 ;
061C 202906 1570 SYSRES JSR  SYSEND      lines 1490 to 1520 change the value of
            1580 ;                        SYSEND here to the original value of
            1590 ;                        DOSINI
061F A92A   1600        LDA  #CLOCKS&255  tell the ATARI where we want it to go
0621 8D2202 1610        STA  VVBLKI      during VBLANK
0624 A906   1620        LDA  #CLOCK/256
0626 8D2302 1630        STA  VVBLKI+1
0629 60     1640 SYSEND RTS              end of initialization
            1650 ;
            1660 ;
            1670 ;The Actual Clock Routine
            1680 ;
            1690 ;
            1700 ;Figure out the address of the beginning of screen memory
            1710 ;
062A A558   1720 CLOCK  LDA  SAVMSC      start by storing the address of
062C 85CB   1730        STA  SCREEN      regular screen memory
062E A559   1740        LDA  SAVMSC+1
0630 85CC   1750        STA  SCREEN+1
0632 A557   1760        LDA  DINDEX      if we're in GRAPHICS 0, move on
0634 F00A   1770        BEQ  FLGCHK
0636 AD9402 1780        LDA  TXTMSC      otherwise, store the address of the
0639 85CB   1790        STA  SCREEN      beginning of the text window
063B AD9502 1800        LDA  TXTMSC+1
063E 85CC   1810        STA  SCREEN+l
            1820 ;
            1830 ;Now check all the buttons
            1840 ;
0640 A908   1850 FLGCHK LDA  #8          first make them readable
0642 8D1FD0 1860        STA  CONSOL
0645 AD0706 1870        LDA  PRESS       has the last button pressed been re-
            1880 ;                        leased yet
0648 F00C   1890        BEQ  BUTCHK      if so, move on
064A AD1FD0 1900        LDA  CONSOL      if not, is it released now?
064D C907   1910        CMP  #7
064F D037   1920        BNE  TIME        if not, move straight to the time up-
            1930 ;                        date routine
0651 A900   1940        LDA  #0          if so, clear the flag and move on
0653 8D0706 1950        STA  PRESS
0656 AD1FD0 1960 BUTCHK LDA  CONSOL      is a button pressed now?
0659 C907   1970        CMP  #7
065B F02B   1980        BEQ  TIME        if not, go straight to the time up-
            1990 ;                        date routine
065D C903   2000        CMP  #OPTION     otherwise, check the OPTION button
065F D006   2010        BNE  STTCHK      if it's not pressed, go check the
            2020 ;                        START button
0661 EE0806 2030        INC  ENGAGE      otherwise, adjust the ENGAGE flag
0664 EE0706 2040        INC  PRESS       adjust PRESS so we know that a
            2050 ;                        button's been pressed
0667 C906   2060 STTCHK CMP  #START      check the START button
0669 D006   2070        BNE  SELCHK      if it's not pressed, go check the
            2080 ;                        SELECT button
066B EE0906 2090        INC  RUNNIN      otherwise, adjust the RUNNIN flag
066E EE0706 2100        INC  PRESS       adjust PRESS so we know that a
            2110 ;                        button's been pressed
0671 C905   2120 SELCHK CMP  #SELECT     check the SELECT button
0673 D013   2130        BNE  TIME        if it's not pressed, go straight to
            2140 ;                        the time update routine
0675 AD0906 2150        LDA  RUNNIN      otherwise, check to see if the clock's
0678 2901   2160        AND  #1          running
067A D00C   2170        BNE  TIME        if it is, we don't want to reset it,
            2180 ;                        so go straight to the time update
            2190 ;                        routine
067C A900   2200        LDA  #0          otherwise reset the clock by making
067E A206   2210        LDX  #6          all it's digits zero
0680 CA     2220 RESET  DEX
0681 9D0006 2230        STA  TIMDIG,X
0684 E000   2240        CPX  #0
0686 D0F8   2250        BNE  RESET
            2260 ;
            2270 ;Update the time if necessary
            2280 ;
0688 AD0906 2290 TIME   LDA  RUNNIN      is the clock running?
068B 2901   2300        AND  #1
068D F025   2310        BEQ  PRINT       if not, go to the print routine
068F CE0606 2320        DEC  TIMER       otherwise, decrease our countdown by
            2330 ;                        one
0692 D020   2340        BNE  PRINT       if it's not down to zero yet, go to
            2350 ;                        the print routine
0694 A93C   2360        LDA  #60         otherwise, reset it
0696 8D0606 2370        STA  TIMER
0699 A205   2380        LDX  #5          get ready for a loop
069B D8     2390        CLD
069C 18     2400        CLC
069D FE0006 2410 UPDATE INC  TIMDIG,X    increase the digit by one
06A0 8A     2420        TXA              figure out the number it has to reach
            2430 ;                        in order to affect the next digit
06A1 2901   2440        AND  #1          these instructions will produce a ten
06A3 0A     2450        ASL  A           or a six, depending on whether it's a
06A4 0A     2460        ASL  A           one's digit or a ten's digit
06A5 6906   2470        ADC  #6
06A7 DD0006 2480        CMP  TIMDIG,X    has it reached this number?
06AA D008   2490        BNE  PRINT       if not, we're set - go to the print
            2500 ;                        routine
06AC A900   2510        LDA  #0          otherwise, set the digit to zero, and
06AE 9D0006 2520        STA  TIMDIG,X    go back and increment the next one
06B1 CA     2530        DEX
06B2 D0E9   2540        BNE  UPDATE
            2550 ;
            2560 ;Print the time
            2570 ;
06B4 AD0806 2580 PRINT  LDA  ENGAGE      is the clock engaged?
06B7 2901   2590        AND  #1
06B9 F025   2600        BEQ  EXIT        if not, we're done
06BB A01F   2610        LDY  #0FFSET     otherwise, prepare the offset
06BD D8     2620        CLD              get ready for a loop
06BE A201   2630        LDX  #1
06C0 E001   2640        CPX  #1
06C2 F00E   2650        BEQ  DIGIT2      print the first digit by itself
06C4 A99A   2660 PRNTLP LDA  #COLON      print a colon
06C6 91CB   2670        STA  (SCREEN),Y
06C8 C8     2680        INY              move to the next screen position
06C9 BD0006 2690        LDA  TIMDIG,X    convert the tens digit to a reverse
06CC 6990   2700        ADC  #CONVERT    character
06CE 91CB   2710        STA  (SCREEN),Y  print it
06D0 C8     2720        INY              move to the next screen position
06D1 E8     2730        INX              move to the next digit
06D2 18     2740 DIGIT2 CLC
06D3 BD0006 2750        LDA  TIMDIG,X    convert the ones digit to a reverse
06D6 6990   2760        ADC  #CONVERT    character
06D8 91CB   2770        STA  (SCREEN),Y  print it
06DA C8     2780        INY              move to the next screen position
06DB E8     2790        INX              move to the next digit
06DC E006   2800        CPX  #6          if we haven't already done so, print
06DE D0E4   2810        BNE  PRNTLP      the next colon-tens-ones group
            2820 ;
            2830 ;Say goodbye
            2840 ;
06E0 4C5FE4 2850 EXIT   JMP  SYSVBV      all done
            2855 ;
            2856 ;Tell the program to run automatically off disk
            2857 ;
06E3        2860        *=   $2E2
02E2 0A06   2870        .WORD INIT
02E4        2880        .END