6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat Oct 05, 2024 6:26 pm

All times are UTC




Post new topic Reply to topic  [ 26 posts ]  Go to page Previous  1, 2
Author Message
PostPosted: Thu Apr 09, 2020 10:39 am 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
I think I might bake some brownies later to celebrate.


Top
 Profile  
Reply with quote  
PostPosted: Thu Apr 09, 2020 2:03 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8411
Location: Midwestern USA
If you have a 65C816 running in native mode you can do this:

Code:
         rep #%00100000
;
loop     lda timer
         cmp timer
         bne loop

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
PostPosted: Thu Apr 09, 2020 2:08 pm 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
That'll go into an infinite loop if the timer updates more quickly than the LDA/CMP accesses. The VIA timers update for every Phi2 cycle, unless set to count an external signal or for one to feed into the other.


Top
 Profile  
Reply with quote  
PostPosted: Thu Apr 09, 2020 3:51 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8411
Location: Midwestern USA
Chromatix wrote:
That'll go into an infinite loop if the timer updates more quickly than the LDA/CMP accesses. The VIA timers update for every Phi2 cycle, unless set to count an external signal or for one to feed into the other.

I just thought I'd toss it out there and see if anyone noticed. :D

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
PostPosted: Thu Apr 09, 2020 4:01 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10949
Location: England
Yeah, Chromatix already posted a correct '816 program yesterday:
viewtopic.php?p=74824#p74824

It's interesting, I think, that this problem is just a tad harder on the '816 in 16 bit mode.


Top
 Profile  
Reply with quote  
PostPosted: Thu Apr 09, 2020 7:12 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8411
Location: Midwestern USA
BigEd wrote:
Yeah, Chromatix already posted a correct '816 program yesterday:
viewtopic.php?p=74824#p74824

It's interesting, I think, that this problem is just a tad harder on the '816 in 16 bit mode.

What's interesting is that in all the years I've been writing 6502 code I've only once had to read a hardware timer "on-the-fly" and compensate for a carry error. If there was something dependent on a timer I generally used an IRQ to let me know when the time was up.

PIC V1's firmware has a time delay function that is indirectly slaved to the counter/timer in the DUART, which is programmed to generate a 100 Hz jiffy IRQ. The time-delay function polls a 16-bit down-counter maintained on direct page, which counter is decremented once per second if it is non-zero. If a BIOS API call is made for a time delay, the caller passes the delay period in second in the (16-bit) accumulator, which is used to set the time delay count. The MPU spins waiting for the TD count to reach zero, which it determines by reading the counter on each interrupt. No carry error can occur, since a 16-bit read instruction is atomic.

Back when I wrote a relational database engine for use on multiple Commodore 128D computers multiplexed to a Lt. Kernal disk subsystem I had an arbitration system set up for file and record locking. In order to randomize the locking algorithm, a subroutine would enter a loop watching the tenths-of-seconds register in one of the TOD clocks. When the register changed value the loop was broken and processing continued. Since the TOD clock was free-running, there was no way to predict when the register would increment or roll back to zero. So it was random to within a couple of microseconds. That was one of only two times I ever directly polled a hardware register looking for a time change.

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
PostPosted: Thu Apr 09, 2020 7:22 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10949
Location: England
There's another potential and potentially interesting wrinkle: just possibly a routine like the ones posted above might be running in an interruptable context, in which case quite some time might pass between reads. Dave (hoglet) did a quick measurement this afternoon, and eyeballing the results it seems the interrupt service on Acorn's BBC can sometimes take a couple of hundred microseconds - not quite enough for a double rollover, but then that wasn't a thorough investigation.

I imagine one could cook up a story for when it might make sense to read a high resolution timer in an interruptable context.


Top
 Profile  
Reply with quote  
PostPosted: Thu Apr 09, 2020 8:10 pm 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
I think my routines still return a self-consistent result in a double-rollover case, as they don't particularly care how far the rollover has gone, only that it has occurred. The time returned will be somewhere between the read function's entry and exit.

The one thing that could mess things up is if a rollover occurs during the first read, *and* an interrupt occurs which delays the comparison read for more than half the upper byte's range. That's an extreme case, and one that only my first routine would really cope with properly. But in this case you'll probably want to implement a timer with more range.


Top
 Profile  
Reply with quote  
PostPosted: Fri Apr 10, 2020 6:50 am 
Offline

Joined: Sun Jun 29, 2014 5:42 am
Posts: 352
Even if interrupts need to remain enabled during the measurement itself, it would be prudent to wrap the timer reading code with SEI/CLI. I'll make this change in my application.

Dave


Top
 Profile  
Reply with quote  
PostPosted: Wed Aug 23, 2023 12:44 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10949
Location: England
Just to note, there's been a related discussion over on stardot, where George has contributed a rather splendid idea.
Emulator Accuracy and the T1 timer - explorations


Top
 Profile  
Reply with quote  
PostPosted: Sun Aug 27, 2023 12:39 pm 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
Yes, with the tighter constraints of wanting a constant routine execution time and for the value read to represent a constant time offset within that execution time, different approaches are needed. I think here it is helpful to assume that we have a timer incrementing at Phi2 rate and that no wait-states occur. If you have a slower timer, the problem gets significantly more difficult with these additional constraints.

On the '816 we can also assume that no interrupt occurs (not even an NMI) during the timer read, provided we do an atomic 16-bit read. The two halves of the timer are then read on consecutive clock cycles. What we need to do is to increment the low half by one step without affecting the high half:
Code:
; assume 16-bit accumulator, little-endian timer registers, timer increments (or decrements) every clock cycle
ReadTimerInc:
  LDA timer  ; reads high half last
  SEP #$20   ; switch to 8-bit accumulator
  INC A      ; increment low half only
  REP #$20   ; back to 16-bit accumulator
  RTS

ReadTimerDec:
  LDA timer  ; reads high half last
  SEP #$20   ; switch to 8-bit accumulator
  DEC A      ; decrement low half only
  REP #$20   ; back to 16-bit accumulator
  RTS
These routines have a constant execution time of 19 cycles, of which almost a third is just the RTS instruction - which is equal to the "fast path" of the general '816 routines I posted before. The low half of the timer is made consistent with the clock cycle upon which the high half is read, which is the 5th cycle of the routine.

On the 6502, it takes two instructions to read a 16-bit timer. It is impossible to prevent an NMI from intervening between any two instructions, unless the NMI interrupt sources are known to be inactive. All other interrupts can be masked off. If we assume interrupts are masked on entry, we can apply the same technique:
Code:
; assume interrupts disabled, timer increments (or decrements) every clock cycle
ReadTimerInc:
  LDA timer_lo
  LDX timer_hi
  CLC
  ADC #4  ; interval between load instructions
  RTS

ReadTimerDec:
  LDA timer_lo
  LDX timer_hi
  SEC
  SBC #4  ; interval between load instructions
  RTS
These routines take 18 cycles, including the RTS, which makes them one cycle faster than the '816 routine. However, the '816 routine has fewer restrictions on entry.


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

All times are UTC


Who is online

Users browsing this forum: No registered users and 18 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: