Login
Forum Help



Post Your Message
Username: (Login)

Topic:
Message:
 


Previous Messages
Posted By

cobbpg
on 2025-01-22
15:17:03
 Re: Help with Timer Interrupts

Okay, basically the answer is that it cannot be done. wink That's a pity, but I can live with it; I really just wanted to know the possibilities for now.

Posted By

Crown
on 2025-01-22
04:10:49
 Re: Help with Timer Interrupts

It all depends on how your intended timer cycle aligns with the screen. Instead of trying to solve for arbitrary values you should be able to adjust that value so it better aligns with whole raster lines, then you have more options and it can be positioned so that it avoids DMA and RAM refresh . If you want to do something specific at exact intervals in an interrupt, it's better to control the interrupt so that it enters earlier, then inside the interrupt cycle align with the intended cycle using delays based on $ff1e.

Posted By

Gaia
on 2025-01-21
18:51:46
 Re: Help with Timer Interrupts

Timer writes are aligned to slow clock cycles but they are different, one is aligned to odd the other two to even cycles (or vice versa? I dunno by heart but it's documented in the mail archive). As for wasting too many cycles for the alignment, you can schedule it slightly earlier and then make sure the synchronization always wastes the exact same number of the remaining cycles. I'm afraid though that you can not fully resolve the RAM refresh (in fast mode) and bad line issues... those will always cause trouble. The only thing I can think of when this could work 100% accurately is 1) disabled screen + 2) in full slow mode, so no return to fast mode whatsoever.

Posted By

cobbpg
on 2025-01-21
13:58:25
 Re: Help with Timer Interrupts

@siz: You don't have to compensate anything. When the timer low byte is written the timer stops until the high byte is written.

But that's exactly why I need to compensate. I have to set the timer value that applies to the moment the high byte is written. Since there are 12 cycles between reading the low byte for the sum and writing the high byte, I'm subtracting 12 cycles in advance to account for that.

@Gaia: Why are you switching to single clock mode? It does not impact the TED only the CPU... your code will be slower.

Since the interrupt can fire at any time, how else can I ensure that I start the timer with a cycle exact value at the right moment? Of course I wouldn't be doing it if there's no need to be completely cycle accurate. It seems to me that you'd need quite complicated logic to determine the correct cycle adjustment otherwise that would likely cost more than the few cycles I lose by momentarily switching to single clock speed to get a stable delay.

As far as I can see, my approach still fails if there happens to be another interrupt or a badline between the two writes. I can certainly prevent the former, but what can I do about the latter without involving another timer? It seems that a bulletproof routine would have to check $ff1c/$ff1d/$ff1e and e.g. wait a bit if there's a badline due in the next few cycles. Seems pretty complicated...

Posted By

Gaia
on 2025-01-20
19:11:48
 Re: Help with Timer Interrupts

Just a few tips: gain full control to the IRQ sequence by using the HW IRQ vector (rather than $0314) - easier to count the delays, faster etc. Why are you switching to single clock mode? It does not impact the TED only the CPU... your code will be slower. Overwriting the timer in the IRQ is what I would do either... but we need to factor in the fact that there will be some random deviation when the IRQ gets executed, so we have to take care of this random "oscillation". I'd check for the value of the timer when the IRQ is executed, it'll be in the $FFxx range, you can use this value as an adjustment to the base value ($3000?) for resetting the timer. That is, if I understood you well happy

Posted By

siz
on 2025-01-20
17:36:32
 Re: Help with Timer Interrupts

@SukkoPera: You just wrote that your code is triggered. I just wanted to say that other interrupt sources will trigger it too. happy And I wanted to explain what @Ulysses777 wrote more clearly: interrupt source bits will be set when the conditions are met. Just won't trigger a CPU interrupt when they are disabled in $ff0a.

@cobbpg: You don't have to compensate anything. When the timer low byte is written the timer stops until the high byte is written. In theory. According to the TED documentation just for a short while. I've written some tests and that's true. When you wait too much between the writes (actually wait *a lot*, several frames - I don't remember exactly) it will restart anyway. So you don't have to switch to single clock. And single clock does not guarantee an exact delay. TED DMA cycles can disrupt that either if you don't avoid them.

Posted By

cobbpg
on 2025-01-20
14:39:49
 Re: Help with Timer Interrupts

What's the recommended way to simulate an arbitrary init value for Timer 2 and 3? For instance, if I want Timer 2 to trigger every 3000 cycles, is there some clever way to do it? This is what I came up with after a bit of thinking:


.const RestartValue = 3000-12 // 12 cycles to compensate for the delay until writing the high byte

Irq:
// ... determine if we're dealing with Timer 2 ...

RestartTimer2:
// Turn on single clock mode so it doesn't matter where the raster is at the moment
lda $ff13
ora #$02
sta $ff13

clc
lda #<RestartValue
adc $ff02 // The current value of the timer is fetched on the last cycle
sta $ff02 // The timer is set with the value we need at the moment it is enabled
lda #>RestartValue
adc #$ff // For simplicity we assume that the underflow happened less than 256 cycles ago
sta $ff03 // Writing the high byte 12 cycles after reading the low byte

// Back to full speed
lda $ff13
and #$fe
sta $ff13

// ... wrap up interrupt ...


Is this the simplest approach, or am I missing something obvious?

Posted By

SukkoPera
on 2025-01-20
11:08:52
 Re: Help with Timer Interrupts

Thanks @siz, but I'm talking timer interrupts, not raster interrupts.

@Ulysses777: Ahhh, I see, I suspected something like that. On modern microcontrollers the "interrupt occurred" flag will generally not be set if the "interrupt enabled" flag somewhere else is not set, and I was expecting the same behavior but what you said makes perfect sense.

Thanks for all the help! Let's see if I can make it behave now!

Posted By

Ulysses777
on 2025-01-20
10:59:10
 Re: Help with Timer Interrupts

Short answer: at the start of your IRQ handler, it needs to check not only $FF09, but $FF0A as well.

Longer answer:
When the timers repeat, the timer bits in $FF09 are always set to 1, even if those interrupts are disabled. It only generates an actual interrupt if the corresponding bits in $FF0A are set as well.

Based on the posted code, you are presumably allowing raster interrupts to continue as normal.

So what appears to be happening is that your IRQ handler is called on a raster interrupt, it checks the Timer2 bit in $FF09, but this could return a 1 even if the timer interrupt is disabled, so it also needs to check if Timer2 interrupts are actually enabled in $FF0A.

EDIT: to answer the question of KERNAL usage of timers, the tape reading routines use all three timers. The serial port routines use Timer 2 as well.

Posted By

siz
on 2025-01-20
10:06:50
 Re: Help with Timer Interrupts

I'm not sure if I understand your question. (TED) Interrupts will trigger when the proper enable bit is set in $ff02 and the trigger event occurs (raster line reaches the predefined interrupt line or timer countdown reaches zero). The bit signalling the source of the interrupt in $ff09 will be set. The interrupt request will be cleared when the proper bit in $ff09 is set to 1. Until then the interrupt will retrigger.
Is it possible that the raster interrupt is still enabled and that's the one you get? Or is it the fact that timer 2 runs continuously and will trigger interrupts about every 4 frames without setting the startup value?

Posted By

SukkoPera
on 2025-01-20
08:14:48
 Re: Help with Timer Interrupts

Thanks for all your replies! I think it's all clear now, except WHY the interrupt is triggered even when the timer bits in $ff09 are still set to 0.

I also found this Preliminary TED Datasheet but it doesn't help.

Posted By

Crown
on 2025-01-20
08:01:28
 Re: Help with Timer Interrupts

The referenced email:
[ Mail Archive / 416 ]

The access to the Email List Archive
[ Mail Archive ]

No Index, just Prev and Next and Search to Navigate on it. An Index page would be nice.

Posted By

siz
on 2025-01-20
06:39:57
 Re: Help with Timer Interrupts

All of the timers repeat. They request an interrupt when the countdown reaches zero. The difference between timers is Timer 1 "remembers" the initialization value while Timer 2 and 3 starts from $ffff (unless you set them to another value after clearing the interrupt request)

Posted By

SukkoPera
on 2025-01-20
05:24:31
 Re: Help with Timer Interrupts

Thanks for your replies @gerliczer. I have used that page A LOT for making that short program, thanks for improving it! Although I see no mention of the repeating/one-shot nature of the different timers, we should add that!

I didn't know there used to be a mailing list! Would it be possible to retrieve the messages?

Posted By

gerliczer
on 2025-01-20
05:12:43
 Re: Help with Timer Interrupts

You should read the recently update TED Registers article in the encyclopedia. The timer initialization looks good to me. Timer 2 and 3 should be reinitialized in the interrupt service routine as they are one shot type. Yes, timers differ. Timer 1 is repeating, the other two, as I wrote, are one shot. And there is one double clock cycle delay on one of them, IIRC. Back in the ancient mailing list times it was explained by Crown, I think. AFAIK, KERNAL does not use timers.

The remaining question is a mystery for me, too.

Posted By

SukkoPera
on 2025-01-20
03:33:41
 Help with Timer Interrupts

I'm trying to use one of the TED built-in timers to call a function periodically. I have written the following code:

setup:
; Install IRQ handler
sei

ldx #<irq
ldy #>irq
stx $0314
sty $0315

; Load Timer 2 with desired delay
lda #<N
sta $ff02
lda #>N
sta $ff03

;~ lda $ff0a
;~ ora #(1 << 4)
;~ sta $ff0a

cli

main:
; Just keep changing border color to show that this part of the code is running
- inc BORDER
bne -

;-----------------------------------------------------------------------------------------------------------------------
irq:
;-----------------------------------------------------------------------------------------------------------------------
pha
lda $ff09
and #(1 << 4)
beq .irq_end ; Bit not set => Not our interrupt

; Copy border color to background to show that this handler was called
lda BORDER
sta BACKGROUND

; Reload timer
lda #<N
sta $ff02
lda #>N
sta $ff03

; Clear interrupt flag
lda #(1 << 4)
sta $ff09

.irq_end:
pla
jmp $ce0e ; Let KERNAL IRQ handler do the rest


It seems to be more or less working but I have a few questions:
- As you can see, I have "forgotten" to uncomment the code that sets the Timer2 enable bit in $ff0a but still... my code is being called! This means that the Timer2 bit in $ff09 is set: why? I can understand that the KERNAL has its own periodic interrupt to check for keypresses, etc., but shouldn't my code not come into play until I set the bit in $ff0a?
- What is the exact formula for loading the timer value? I have it more or less, but the delay is not exactly what I expect.
- Is it necessary to reload the timer in the IRQ handler?
- Is there any difference between the 3 timers?
- Is the KERNAL using any of timers already?

Thanks in advance!


Copyright © Plus/4 World Team, 2001-2025. Support Plus/4 World on Patreon