danwinslow Posted March 1, 2013 Share Posted March 1, 2013 (edited) Hi Folks. Any one have or know of an assembler routine laying about handy that converts the RTC ( 18,19,20 ) into seconds? Edited March 1, 2013 by danwinslow Quote Link to comment Share on other sites More sharing options...
Gury Posted March 1, 2013 Share Posted March 1, 2013 Hi, I hope this will help, scroll to Timer section... http://www.atariage.com/forums/topic/155391-moj-mikro-magazine-listings-tips-and-tricks-%231/ Greetz, Gury Quote Link to comment Share on other sites More sharing options...
danwinslow Posted March 1, 2013 Author Share Posted March 1, 2013 Hi Gury, thanks, but was looking for an assembler example. Quote Link to comment Share on other sites More sharing options...
Irgendwer Posted March 1, 2013 Share Posted March 1, 2013 A look into cc65s library is often helpful: ; ; Piotr Fusik, 04.11.2001 ; originally by Ullrich von Bassewitz and Sidney Cadot ; ; clock_t clock (void); ; unsigned _clocks_per_sec (void); ; .export _clock, __clocks_per_sec .importzp sreg .include "atari.inc" .proc _clock ldx #5 ; Synchronize with Antic, so the interrupt won't change RTCLOK stx WSYNC ; while we're reading it. The synchronization is done same as @L1: dex ; in SETVBLV function in Atari OS. bne @L1 stx sreg+1 ; Byte 3 is always zero lda RTCLOK+2 ldx RTCLOK+1 ldy RTCLOK sty sreg rts .endproc .proc __clocks_per_sec ldx #$00 ; Clear high byte of return value lda PAL ; use hw register, PALNTS is only supported on XL/XE ROM and #$0e bne @NTSC lda #50 rts @NTSC: lda #60 rts .endproc Quote Link to comment Share on other sites More sharing options...
danwinslow Posted March 2, 2013 Author Share Posted March 2, 2013 Indeed, I should have thought of that. Ty, somebody :-) Quote Link to comment Share on other sites More sharing options...
SeaGtGruff Posted March 2, 2013 Share Posted March 2, 2013 The "seconds" will be off a little bit due to the Atari's frame rate. It's close enough for casual work, but if you're trying to keep accurate time then you'll need to adjust the seconds every so often keep it in sync with "real world" clocks. Quote Link to comment Share on other sites More sharing options...
Xuel Posted March 2, 2013 Share Posted March 2, 2013 I tried doing the divide-by-50 with the floating point ROM routines: mwa #$100 FR0 jsr IFP jsr FMOVE mva RTCLOK FR0+1 mva RTCLOK+1 FR0 jsr IFP jsr FMUL jsr FMOVE mva RTCLOK+2 FR0 mva #0 FR0+1 jsr IFP jsr FADD mwa #jiffy_fp FLPTR jsr FST0P mwa #jiffies_per_second FR0 jsr IFP jsr FMOVE mwa #jiffy_fp FLPTR jsr FLD0P jsr FDIV jsr FASC jsr print Full source and executable: seconds.zip This is quite slow though. It takes nearly a whole frame to do the computation. This thread has fast integer divide routines for many divisors. I don't if they're at all applicable to 24 bit dividends. It would be much more efficient to just have a VBI repeatedly count up to 50 and then increment your seconds counter. Quote Link to comment Share on other sites More sharing options...
phaeron Posted March 2, 2013 Share Posted March 2, 2013 Two iterations of a 16/8->8 unsigned division routine will give you a 24/8->16 divide. Use the remainder byte of the high division as the high dividend byte of the low division. IIRC it's a few hundred clocks. The VBI does indeed tick a bit slower than 50/60Hz. I debugged an emulation speed problem in ForemXEP that turned out to be due to this issue, which caused the FXEP clock to lose a couple of seconds each day. Probably the best solution for tracking accurate seconds would be to step seconds as Xuel notes, but with a fixed-point counter. 1 Quote Link to comment Share on other sites More sharing options...
danwinslow Posted March 3, 2013 Author Share Posted March 3, 2013 Yes, its the divides that I really wasn't sure how to handle. I think setting up my own counter is probably the way to go....thanks all. Quote Link to comment Share on other sites More sharing options...
Rybags Posted March 3, 2013 Share Posted March 3, 2013 The problem with using the FP is that it only handles 16 bit binary to packed BCD conversion. So you'd need to handle the high byte seperately - multiply by 65536 then add back in. Best method might be to do your own - if you want the true accuracy add a leap function that periodically increments the frame count to compensate for the slower than 50/60 Hz actual rate. Or do a routine that handles the required division of 24 bit binary - in all probability it wouldn't be a great deal faster than using the OS FP routines. Quote Link to comment Share on other sites More sharing options...
SeaGtGruff Posted March 5, 2013 Share Posted March 5, 2013 This is slightly off-topic from the original question, but I just wrote a little batari Basic program for the Atari 2600 with some inline assembly to count and display seconds, minutes, and hours based on the number of color clocks per frame and color clocks per second. The inline assembly routine is applicable to the Atari 8-bit computers. I chose color clocks because I don't think floating point numbers are (generally speaking) as accurate as mixed numbers with integer parts-- e.g., 5.33333333 is an imperfect expression of 5 and 1 / 3. And rather than deal with whole frames and a fraction of a frame, I decided it was simpler to just use color clocks per frame and color clocks per second. This assumes (rightly or wrongly) that the oscillator rate shown on the Atari 2600 schematics-- 3579575 Hz-- is an exact integer value, as opposed to having some fractional part that's been rounded or truncated. I use two 3-byte values stored in ROM-- the number of color clocks per frame (59736) and color clocks per second (3579575), both stored in little-endian order. I use three bytes of RAM to count the number of color clocks after each frame, and three more bytes of RAM to store the seconds, minutes, and hours. After a frame is drawn I add the clocks per frame to the counter, then compare the result to the clocks per second. If the counter is equal to or greater than the clocks per second, I subtract the clocks per second from the counter and increment the seconds, adjusting the seconds, minutes, and hours as needed. I haven't run it on a real console yet to see how accurate it is, but it seems accurate enough in an emulator (although it's slightly fast because the emulator draws each frame at 60 Hz rather than at the actual frame rate of circa 59.92 Hz. For PAL the numbers for the Atari 2600 should be 71136 (color clocks per frame) and 3546894 (color clocks per second). I'm not sure what the numbers should be for the Atari 8-bit computers-- most of the references I've seen online say the oscillator for the NTSC model is 3579545 Hz (presumably with a .45 repeating 45 after that), although one post at AtariAge says "the 400 schematic clearly shows 3,579,575 MHz" (sic). I haven't seen the Atari 400 schematics (or 800, etc.), but if Atari used an oscillator rate of 3579575 Hz for the NTSC 2600 then I suppose they might have used the same rate for their NTSC 8-bit computers. Anyway, here's the inline assembly portion of my program with a few comments, in case anyone wants to try it for the 8-bit computers. Adjust the color clock values as you deem necessary: CLC LDA color_clocks ; LSB of the counter ADC per_frame ; LSB of the color-clocks-per-frame STA color_clocks LDA color_clocks+1 ADC per_frame+1 STA color_clocks+1 LDA color_clocks+2 ; MSB of the counter ADC per_frame+2 STA color_clocks+2 CMP per_second+2 ; MSB of the color-clocks-per-second BCS Check_Color_Clocks_1 JMP .Main_Loop ; not yet equal or greater, so exit-- .Main_Loop was too far away to branch to so I had to reverse the compare and jump instead Check_Color_Clocks_1 BNE Reduce_Color_Clocks ; greater, so no need to check the other bytes LDA color_clocks+1 ; equal, so need to check the next byte CMP per_second+1 BCS Check_Color_Clocks_2 JMP .Main_Loop Check_Color_Clocks_2 BNE Reduce_Color_Clocks LDA color_clocks CMP per_second BCS Reduce_Color_Clocks JMP .Main_Loop Reduce_Color_Clocks ; subtract the clocks-per-second from the counter SEC LDA color_clocks SBC per_second STA color_clocks LDA color_clocks+1 SBC per_second+1 STA color_clocks+1 LDA color_clocks+2 SBC per_second+2 STA color_clocks+2 INC seconds ; now increment the seconds LDA seconds CMP #60 BCS Increment_Minutes JMP .Main_Loop Increment_Minutes ; adjust for 60 seconds LDA #0 STA seconds INC minutes LDA minutes CMP #60 BCS Increment_Hours JMP .Main_Loop Increment_Hours ; adjust for 60 minutes LDA #0 STA minutes INC hours LDA hours CMP #24 BCS Clear_Hours JMP .Main_Loop Clear_Hours ; roll the hours LDA #0 STA hours JMP .Main_Loop per_frame ; color clocks per frame (NTSC) HEX 58 E9 00 ; 59736 in little-endian per_second ; color clocks per second (NTSC) HEX B7 9E 36 ; 3579575 in little-endian Obviously that's a lot of work if you don't care about keeping exact time-- much simpler to just count frames! 1 Quote Link to comment Share on other sites More sharing options...
danwinslow Posted March 5, 2013 Author Share Posted March 5, 2013 Thanks, Sea, that is some cool stuff. I don't need that accuracy, but it's very educational in terms of handling multi-byte maths. For my current project I've abandoned C in favor of assembly, so I've a lot to learn. Quote Link to comment Share on other sites More sharing options...
bogax Posted March 5, 2013 Share Posted March 5, 2013 (edited) This is slightly off-topic from the original question, but I just wrote a little batari Basic program for the Atari 2600 with some inline assembly to count and display seconds, minutes, and hours based on the number of color clocks per frame and color clocks per second. The inline assembly routine is applicable to the Atari 8-bit computers. I chose color clocks because I don't think floating point numbers are (generally speaking) as accurate as mixed numbers with integer parts-- e.g., 5.33333333 is an imperfect expression of 5 and 1 / 3. And rather than deal with whole frames and a fraction of a frame, I decided it was simpler to just use color clocks per frame and color clocks per second. This assumes (rightly or wrongly) that the oscillator rate shown on the Atari 2600 schematics-- 3579575 Hz-- is an exact integer value, as opposed to having some fractional part that's been rounded or truncated. I use two 3-byte values stored in ROM-- the number of color clocks per frame (59736) and color clocks per second (3579575), both stored in little-endian order. I use three bytes of RAM to count the number of color clocks after each frame, and three more bytes of RAM to store the seconds, minutes, and hours. After a frame is drawn I add the clocks per frame to the counter, then compare the result to the clocks per second. If the counter is equal to or greater than the clocks per second, I subtract the clocks per second from the counter and increment the seconds, adjusting the seconds, minutes, and hours as needed. I haven't run it on a real console yet to see how accurate it is, but it seems accurate enough in an emulator (although it's slightly fast because the emulator draws each frame at 60 Hz rather than at the actual frame rate of circa 59.92 Hz. . . . Obviously that's a lot of work if you don't care about keeping exact time-- much simpler to just count frames! I hate to be a nay sayer but that's way overkill. You won't split a second to any better than a frame. You can keep the seconds / frames accurate. but even an inaccurate 24 bit fraction will be better than the best crystal clock where they think they're doing really good if they keep it to one part in 100000. It would be a lot simpler just to accumulate seconds/frame. 32 bits would be off by something like a second / 10 days And a 32 bit (or what ever) fraction could actually be more accurate than 32 bits. In the case of 59736/3579575 seconds/frame it's more like 35 bits Edited March 5, 2013 by bogax Quote Link to comment Share on other sites More sharing options...
phaeron Posted March 6, 2013 Share Posted March 6, 2013 There's another consideration here, which is that if the clock needs to be maintained across disk reads, the timing source has to be maintained in VBI stage 1 processing to avoid losing ticks. That puts it in the critical path for SIO transfers. In that case I would recommend basing it on RTCLOK as the OP originally started with. From there, the direct approach would be to use fixed point multiplies to pull out seconds, minutes, and hours. Carina BBS optimizes this further by computing the RTCLOK value for the next seconds jump and then waiting for the clock to tick to that value. 1 Quote Link to comment Share on other sites More sharing options...
SeaGtGruff Posted March 6, 2013 Share Posted March 6, 2013 I hate to be a nay sayer but that's way overkill. Granted. You won't split a second to any better than a frame. I *am* updating only once each frame-- I'm not trying to split it smaller than a frame. I was just counting in color clocks per frame and then comparing it to color clocks per second to try to keep things on an integer basis. But since I don't know whether the 3579575 Hz value is rounded or truncated or whatever, I guess trying to stick with integers is pointless. It would be a lot simpler just to accumulate seconds/frame. How's this instead? The fraction represents 59736 / 3579575 stored as a 32-bit FP number in little-endian order-- or really 40 bits, since it's just the portion after the decimal, with the other 8 bits being the byte of RAM for the seconds. CLC LDA counter ; LSB of a 4-byte counter in RAM, stored little-endian ADC fraction ; LSB of the fraction (see below) STA counter LDA counter+1 ADC fraction+1 STA counter+1 LDA counter+2 ADC fraction+2 STA counter+2 LDA counter+3 ADC fraction+3 STA counter+3 LDA seconds ; the 5th byte ADC #0 ; only the carry is added STA seconds CMP #60 BCS Increment_Minutes JMP .Main_Loop Increment_Minutes LDA #0 STA seconds INC minutes LDA minutes CMP #60 BCS Increment_Hours JMP .Main_Loop Increment_Hours LDA #0 STA minutes INC hours LDA hours CMP #24 BCS Clear_Hours JMP .Main_Loop Clear_Hours LDA #0 STA hours JMP .Main_Loop fraction ; 59736/3579575 HEX 6D AA 45 04 ; 4/256 + 69/256/256 + 170/256/256/256 + 109/256/256/256/256 I don't trust my math right now, but I think this might be accurate for 1200+ years? Assuming the fraction is correct and the frame rate is steady. Quote Link to comment Share on other sites More sharing options...
phaeron Posted March 6, 2013 Share Posted March 6, 2013 Turns out that 3579575/59736 reduces to 27325/456, so it's shorter and faster just to step the exact fraction directly: lda frac sec sbc #456-256 sta frac dec frac+1 bcs no_carry dec frac+1 no_carry: bpl done clc adc #<27325 sta frac lda frac+1 adc #>27325 sta frac+1 lda #59 ldy #0 inc seconds cmp seconds bcs done sty seconds inc minutes cmp minutes bcs done sty minutes inc hours lda #23 cmp hours bcs done sty hours done: The equivalent PAL fraction should be 3546894/71136 = 3939/79. Now, the fun part: from what I've been able to tell, the 2600, 400, 800 used a 3.579575MHz crystal, whereas the XL/XE machines used 3.579545MHz. Therefore, if you want to be a stickler for accuracy, the fraction needs to be 13183/220 for XL/XE machines. The difference is about 10^-7, which doesn't sound like much until you realize that it amounts to 0.7 seconds a day. I wonder how much the clocks vary in practice. 2 Quote Link to comment Share on other sites More sharing options...
Grevle Posted March 8, 2020 Share Posted March 8, 2020 (edited) Good examples but can anyone provide a complete example based on a Gr.0 output the Seconds to the screen or at least into a adress or register in plain mac/65 assembler please. im not exactly sure on how to base this example on the RTCLOK. Would be Nice With a commented listing for us not so Advanced programmers... The last example here seem to be the best to use. but im not sure abou the LDA FRAC. FRAC=adress 20 or 19 or 18 ? Edited March 9, 2020 by Grevle Quote Link to comment Share on other sites More sharing options...
TGB1718 Posted March 12, 2020 Share Posted March 12, 2020 (edited) Heres an assembly example, this is has a BASIC part to set the clock time which could be deleted after the time had been set. While running, the time is displayed on the top line of the screen, the code takes care of the PAL frame rate. I wrote this way back in the 80's so I hope the comments in the code help. It uses TIMER2 interrupt to count the seconds, so could easily be changed if you use NTSC machine Obviously a lot of the code will be of no use, but hope it helps 0100 SETVBV = $E45C ;SET TIMER ROUTINE 0110 ;EXITVBV = $E462 0120 CDTMA2 = $0228 ;TIMER 2 JUMP ADDRESS 0130 CDTMV2 = $021A ;TIMER 2 0140 HOURT = $06D0 ;HOURS TENS 0150 HOURL = $06D1 ;HOURS UNITS 0160 TENM = $06D3 ;MINUTES TENS 0170 MINS = $06D4 ;MINUTES UNITS 0180 TENS = $06D6 ;SECONDS TENS 0190 SECS = $06D7 ;SECONDS UNITS 0200 FLAG = $00 ;DISPLAY ON/OFF,0=ON 1=OFF 0210 *= $0600 0220 PLA 0230 PLA 0240 STA ADD+2 ;HIGH ADDRESS OF TIME$ 0250 PLA 0260 STA ADD+1 ;LOW ADDRESS OF TIME$ 0270 LDX #0 0280 ADD LDA $2400,X ;DUMMY ADDRESS (WILL BE CHANGED LATER) 0290 AND #$DF ;SUBTRACT HEX 20 FROM ASCII CHARACTERS TO PRODUCE 0300 ;CORRECT DISPLAY 0310 STA HOURT,X ;STORE TIME$ INTO CORRECT LOCATION 0320 INX 0330 CPX #8 0340 BNE ADD ;BRANCH IF NOT ALL OF TIME$ 0350 LDA #START&255 ;TIMER 2 INTERRUPT VECTOR LOW ADDRESS 0360 STA CDTMA2 0370 LDA #START/256 ;HIGH ADDRESS 0380 STA CDTMA2+1 ;SET TIMER VECTOR 0390 LDY #50 ;SET TIMER 2 TO 1 SECOND DELAY 0391 ;***NOTE*** VBLANK OCCURS 50 TIMES A SECOND HENCE 50 NOT 60 0400 LDX #0 ;NO HIGH VALUE 0410 ;***NOTE*** VBLANK OCCURS 50 TIMES A SECOND HENCE 50 NOT 60 0420 LDA #2 ;TIMER 2 0430 JSR SETVBV ;START TIMER 0440 LDA #0 0450 STA FLAG ;SET DISPLAY FLAG TO ON 0460 RTS ;BACK TO BASIC 0470 START INC SECS ;TIMER ROUTINE START 0480 LDA SECS 0490 CMP #$1A 0500 BEQ TENSECS 0510 JMP DISPLAY 0520 TENSECS LDA #$10 0530 STA SECS 0540 INC TENS 0550 LDA TENS 0560 CMP #$16 0570 BEQ MINUTES 0580 JMP DISPLAY 0590 MINUTES LDA #$10 0600 STA TENS 0610 INC MINS 0620 LDA MINS 0630 CMP #$1A 0640 BEQ MINSTENS 0650 JMP DISPLAY 0660 MINSTENS LDA #$10 0670 STA MINS 0680 INC TENM 0690 LDA TENM 0700 CMP #$16 0710 BEQ HOURS 0720 JMP DISPLAY 0730 HOURS LDA #$10 0740 STA TENM 0750 INC HOURL 0760 LDA HOURL 0770 CMP #$1A 0780 BEQ HOURH 0790 CMP #$14 0800 BEQ CHECK 0810 JMP DISPLAY 0820 HOURH LDA #$10 0830 STA HOURL 0840 INC HOURT 0850 JMP DISPLAY 0860 RESET LDA #$10 0870 STA HOURT 0880 STA HOURL 0890 DISPLAY LDA $54 ;CHECH IF CURSOR IS ON LINE 0 0900 BNE DISPLAY1 ;NO SO OK TO DISPLAY 0910 STA $11 ;YES SO 'SOFT' PRESS OF BREAK KEY TO MOVE CURSOR DOWN 0920 DISPLAY1 LDA FLAG ;CHECK IF DISPLAY REQUIRED 0930 BNE OUT ;NO SO GET OUT 0940 LDY #40 ;YES SO CLEAR TOP LINE OF SCREEN 0950 LDA #0 0960 CLR DEY 0970 STA ($58),Y ;LOW ADDRESS OF SCREEN RAM 0980 BNE CLR 0990 LDY #16 ;PUT DISPLAY 16 PLACES OUT 1000 LDX #0 1010 LOOP LDA HOURT,X ;PUT TIME ON SCREEN 1020 STA ($58),Y 1030 INY 1040 INX 1050 CPX #8 1060 BNE LOOP 1070 OUT LDA #50 ;ALL THIS OCCURED BECAUSE OF TIMER 2 REACHING 0 1071 ;SO SET TIMER 2 BACK TO 50 AGAIN 1080 STA CDTMV2 1090 RTS 1100 CHECK LDA HOURT ;CHECK TO SEE IF 24:00:00,IF SO SET TIME TO 00:00:00 1110 CMP #$12 1120 BEQ RESET 1130 JMP DISPLAY This is the BASIC Program 100 REM ****************************** 110 REM * TIME DISPLAY PROGRAM * 120 REM * BY * 130 REM * T.Bailey. * 140 REM ****************************** 150 REM ***NOTE TO TURN OFF THE TIME DISPLAY,POKE 0,1 160 REM THE TIME DISPLAY IS A DIGITAL 24 HOUR DISPLAY ON LINE 0 170 REM TO TURN THE DISPLAY BACK ON POKE 0,0 180 REM AFTER THIS PROGRAM HAS BEEN RUN,YOU MAY TYPE NEW TO CLEAR IT FROM 190 REM MEMORY. 200 REM IF SYSTEM RESET IS PRESSED,THE TIME WILL HAVE TO BE RESET AS 210 REM FOLLOWS:- 220 REM TYPE DIM A$(8):A$="XX:XX:XX":A=USR(1536,ADR(A$)) 230 REM XX:XX:XX = THE TIME BETWEEN 00:00:00 AND 24:00:00 240 Q=1536:W=0:RESTORE 250 ? "}":POSITION 10,5:? "ONE MOMENT PLEASE":REM <ESC-CTRL-CLR> 260 READ A:IF A=-1 THEN 280 270 POKE Q+W,A:W=W+1:GOTO 260 280 ? "}":REM <ESC-CTLR-CLEAR> 290 DIM TIME$(8) 300 ? "INPUT THE TIME AS:-";CHR$(34);"XX:XX:XX";CHR$(34) 310 ? : ? "WHERE XX=HOURS,MINS,SECS" 320 INPUT TIME$ 330 A=USR(1536,ADR(TIME$)) 340 END 350 DATA 104,104,141,13,6,104,141,12 360 DATA 6,162,0,189,0,36,41,223 370 DATA 157,208,6,232,224,8,208,243 380 DATA 169,48,141,40,2,169,6,141 390 DATA 41,2,160,50,162,0,169,2 400 DATA 32,92,228,169,0,133,0,96 410 DATA 238,215,6,173,215,6,201,26 420 DATA 240,3,76,156,6,169,16,141 430 DATA 215,6,238,214,6,173,214,6 440 DATA 201,22,240,3,76,156,6,169 450 DATA 16,141,214,6,238,212,6,173 460 DATA 212,6,201,26,240,3,76,156 470 DATA 6,169,16,141,212,6,238,211 480 DATA 6,173,211,6,201,22,240,3 490 DATA 76,156,6,169,16,141,211,6 500 DATA 238,209,6,173,209,6,201,26 510 DATA 240,7,201,20,240,62,76,156 520 DATA 6,169,16,141,209,6,238,208 530 DATA 6,76,156,6,169,16,141,208 540 DATA 6,141,209,6,165,84,208,2 550 DATA 133,17,165,0,208,24,160,40 560 DATA 169,0,136,145,88,208,251,160 570 DATA 16,162,0,189,208,6,145,88 580 DATA 200,232,224,8,208,245,169,50 590 DATA 141,26,2,96,173,208,6,201 600 DATA 18,240,201,76,156,6,-1 Edited March 12, 2020 by TGB1718 Add BASIC Part 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.