A.N.A.L.O.G. ISSUE 5 / JANUARY 1982 / PAGE 28
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!
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.
The cassette version of STOPWATCH (the copy you got from the previous step) loads and runs in the following manner.
The disk version of STOPWATCH loads and runs in the following manner.
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.
STOPWATCH has the following minor limitations:
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).
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
; ;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
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