6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Mon May 13, 2024 7:40 pm

All times are UTC




Post new topic Reply to topic  [ 26 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Wed Apr 08, 2020 12:08 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10800
Location: England
Reading a two byte timer is meat and drink in the land of PIA, CIA, and VIA peripherals. The hazard is that the timer could overflow the low byte in between the two reads. It might even wrap the high byte!

I believe the general plan of attack would be something like this:
- ernq uvtu
- ernq ybj
- ernq uvtu ntnva, vs qvssrerag hfr guvf inyhr naq ernq ybj ntnva.
(Obfuscated with rot13 just in case you haven't thought about this before)

But can you do it elegantly? Feel free to make an assumption that it's either counting up or counting down.

Edit: I have a solution in mind, sbhevafgehpgvbafryriraolgrf (rot13 to the rescue)

Some space for anti-spoilery
.
..
.
...
.
..
.
....
.
..
.
...
.
..
.


Last edited by BigEd on Wed Apr 08, 2020 1:15 pm, edited 2 times in total.

Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 08, 2020 1:10 pm 
Offline

Joined: Thu Mar 03, 2011 5:56 pm
Posts: 277
For the general case, I have:
Code:
        LDA LO
        LDX HI
        CMP LO
        BEQ RET
        LDX HI
RET  RTS


and for counting up:
Code:
        LDX HI
        LDA LO
        BNE RET
        LDX HI
RET  RTS


Not at all sure about this...


Last edited by rwiker on Wed Apr 08, 2020 1:28 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 08, 2020 1:17 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10800
Location: England
(I've just tweaked the head post)


Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 08, 2020 2:00 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10800
Location: England
Hmm, I see that there's an extra pair of possibilities. In my mind, we're reading a microsecond timer, so we never expect to see the same low byte value twice. But another view is that we're reading something more like a real time clock, where we can usually read the same low byte value twice - I think that's the view in your post, @rwiker?


Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 08, 2020 2:15 pm 
Offline

Joined: Thu Mar 03, 2011 5:56 pm
Posts: 277
BigEd wrote:
Hmm, I see that there's an extra pair of possibilities. In my mind, we're reading a microsecond timer, so we never expect to see the same low byte value twice. But another view is that we're reading something more like a real time clock, where we can usually read the same low byte value twice - I think that's the view in your post, @rwiker?


For the counting-up case, if the low byte is anything else than 0, the high byte should not have changed since it was read a few ticks back. Otherwise, we read the high byte again.


Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 08, 2020 3:00 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10800
Location: England
I see what you're thinking... but with 4 cpu cycles between the two loads, there's an assumption there that the timer isn't going from 00ff to 0001. That'll sometimes be true, but for a 1MHz CPU and a 1MHz VIA, I think it won't hold.


Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 08, 2020 5:26 pm 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
The following is compact and general:
Code:
ReadTimer:
  LDX timer_hi
  LDY timer_lo
  CPX timer_hi
  BNE ReadTimer
  RTS
A disadvantage is that in the edge case, a second triplet of reads occurs, which is markedly slower than the fast path. If you know which way the timer is counting, you can spend a few bytes to gain some performance:
Code:
ReadTimerInc:
  LDX timer_hi
  LDY timer_lo
  CPX timer_hi
  BEQ :+
  INX
  LDY #0
: RTS

ReadTimerDec:
  LDX timer_hi
  LDY timer_lo
  CPX timer_hi
  BEQ :+
  DEX
  LDY #$FF
: RTS
This can also be developed into a version which can accommodate both counting directions without re-reading the timer:
Code:
ReadTimer:
  LDX timer_hi
  LDY timer_lo
  CPX timer_hi
  BEQ :+
  BMI :++
  DEX
  LDY #$FF
: RTS
: INX
  LDY #0
  RTS
The key in all cases is that the change in the high half of the timer is what marks the rollover of the low half. But you also need to get a fresh idea of the low half because it could have been read before or after the rollover.


Last edited by Chromatix on Wed Apr 08, 2020 5:38 pm, edited 3 times in total.

Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 08, 2020 5:28 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10800
Location: England
That's nice! My effort was the same as your first cut, with a very dim feeling that something might exist like your second/third. That final version is very nice (but probably unnecessary!)


Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 08, 2020 5:35 pm 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
I did have to adjust it after I realised there was a race condition.


Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 08, 2020 5:49 pm 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1929
Location: Sacramento, CA, USA
Can you see any potential race conditions in my rookie attempt?
Code:
ReadTimer:
    ldy  timer_hi
    ldx  timer_lo
    cpy  timer_hi
    beq  :+
    inx          ; for an "up" counter; dex otherwise
    iny          ; for an "up" counter; dey otherwise
:   rts


Maybe if the counter rolls between the ldy and the ldx I might be off by one ...

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 08, 2020 6:29 pm 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
That one could be acceptable *if* the timer counts slowly. Often, though, the timer counts at Phi2 rate, so it will have incremented by several counts between the two reads of timer_hi.

More reliable would be to either reload from timer_lo in the event of rollover, or to load it with a constant that's consistent with a rollover having just occurred. Or, yet another variation that I just thought of:
Code:
ReadTimerInc:
  LDX timer_hi
  LDY timer_lo
  CPX timer_hi
  BEQ :+
  LDY #$FF
: RTS

ReadTimerDec:
  LDX timer_hi
  LDY timer_lo
  CPX timer_hi
  BEQ :+
  LDY #0
: RTS
This leaves the high half of the timer alone, but adjusts the low half to be consistent with the pre-rollover value.


Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 08, 2020 6:30 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10800
Location: England
That's quite clever... took me a while to figure it out.


Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 08, 2020 6:41 pm 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
And now for a 65816 version:
Code:
; assume 16-bit accumulator, little-endian timer registers
ReadTimerInc:
  LDA timer  ; reads high half last
  CMP timer
  BEQ :+  ; swap these two branches over if it's a fast-incrementing timer, likely to change between load and compare
  BMI :+
  AND ##$FF00  ; rollover in mid-load, so correct low half to post-rollover state
: RTS

ReadTimerDec:
  LDA timer  ; reads high half last
  CMP timer
  BPL :+
  ORA ##$00FF  ; rollover in mid-load, so correct low half to post-rollover state
: RTS


Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 08, 2020 9:18 pm 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1929
Location: Sacramento, CA, USA
Chromatix wrote:
... This leaves the high half of the timer alone, but adjusts the low half to be consistent with the pre-rollover value.

I think you might have a winner there (you have my vote, at least).

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Top
 Profile  
Reply with quote  
PostPosted: Thu Apr 09, 2020 10:33 am 
Offline

Joined: Sun Jun 29, 2014 5:42 am
Posts: 337
Just a quick note to say thank you to Ed for posting this question, and thank you to everyone who replied.

I ended up using this version from Chromatix:
Code:
ReadTimerDec:
  LDX timer_hi
  LDY timer_lo
  CPX timer_hi
  BEQ :+
  LDY #0
: RTS

I'm building an external VGA Frame Buffer with a Blitter for the BBC Micro, and using this code to time how long blitter operations are taking.

Dave


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 26 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 10 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: