+Karl G Posted August 7, 2021 Share Posted August 7, 2021 I ran into a strange issue where my timer seemed to behave erratically after setting it for vertical blank. It is fine for the first 3 frames, but then after setting it, the value of INTIM seems to jump around erratically after setting TIM64T. My timer-setting is pretty normal, I think: VERTICAL_SYNC lda #44 sta TIM64T The "sta TIM64T" ends at cycle 9 in this case. If I insert a "nop" before the "sta TIM64T", then the timer is stable. Also, possibly unrelated, but the Stella debugger shows the "sta TIM64T" as taking 4 cycles. I'm not understanding why this would be 4 cycles and not 3. So - I have a workaround for this issue, but I would ideally like to understand why it was happening to begin with. Thanks in advance for any insight. Quote Link to comment Share on other sites More sharing options...
+littaum Posted August 7, 2021 Share Posted August 7, 2021 Not sure if it is related, but I ran into a similar strange timing issue (code took 1 extra cycle) while trying to make my game screen stable. So far the only way I could fix it was to add a "align 256" in an earlier part of the code since it appeared that part of the timing critical code crossed a page boundary. Quote Link to comment Share on other sites More sharing options...
+Andrew Davie Posted August 7, 2021 Share Posted August 7, 2021 1 hour ago, littaum said: Not sure if it is related, but I ran into a similar strange timing issue (code took 1 extra cycle) while trying to make my game screen stable. So far the only way I could fix it was to add a "align 256" in an earlier part of the code since it appeared that part of the timing critical code crossed a page boundary. Branching across a page boundary incurs a +1 cycle "penalty". Quote Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted August 7, 2021 Share Posted August 7, 2021 Since the timer is checked only every 5 cycles, this might add an extra scanline under certain circumstances. Check if a WSYNC following the timer check loop happens very close to cycle 76. Unlike the TIA registers, many RIOT registers are not part of the zeropage. Therefore they require 4 cycles for reading. 1 Quote Link to comment Share on other sites More sharing options...
+Andrew Davie Posted August 8, 2021 Share Posted August 8, 2021 Also keep in mind the behaviour of the timer flag when timer reaches zero. It's rather odd. Here is my solution... ldx #0 bit TIMINT bmi .zeroTime ; already overtime! lda INTIM beq .zeroTime ; also time expired bmi .zeroTime ; must have been just overtime and now counting down tax .xOSwait sta WSYNC bit TIMINT ; wait for the timer bpl .xOSwait .zeroTime stx TimeLeftOS ; x holds the "how much time left" rts Essentially, when the timer value reaches zero (INTIM), then the TIMINT flag is set and from that point onwards the timer (INTIM) counts down at 1-clock intervals (i.e., it counts down at your step value until it reaches 0 and then it counts down at 1-clock steps. It doesn't stop at zero.). So the above code is trying to put "how much time left" into a variable (TimeLeftOS). But you can't do that just by reading INTIM, as that clears any flag indicating that time was zero. So the convolutions are to first see if we are overtime - zero left. Otherwise if timer is 0 also zero left, otherwise we store that and then wait for timer to expire, and then store and return. Quote Link to comment Share on other sites More sharing options...
+Karl G Posted August 8, 2021 Author Share Posted August 8, 2021 None of these really describe what I saw. Watching INTIM in the Stella debugger as I stepped through the code, the value would jump around seemingly randomly with each instruction executed, rather than decrementing every 64 cycles as expected. I've made enough changes to my other code that it no longer triggers even when I take out the "nop". If this is really an unknown issue, I can try to recreate it to see if anyone can tell what's going on for the purpose of documenting the strange behavior. I guess it's a testament to Stella that it never occurred to me that it could be a Stella bug, and I'm still guessing it is not. Quote Link to comment Share on other sites More sharing options...
+Andrew Davie Posted August 8, 2021 Share Posted August 8, 2021 42 minutes ago, Karl G said: None of these really describe what I saw. Watching INTIM in the Stella debugger as I stepped through the code, the value would jump around seemingly randomly with each instruction executed, rather than decrementing every 64 cycles as expected. I've made enough changes to my other code that it no longer triggers even when I take out the "nop". If this is really an unknown issue, I can try to recreate it to see if anyone can tell what's going on for the purpose of documenting the strange behavior. I guess it's a testament to Stella that it never occurred to me that it could be a Stella bug, and I'm still guessing it is not. If your timer had already run out (i.e., INTIM had previously reached zero), then it would now be counting down in 1-cycle steps. So, for each instruction executed, you would have multiple counts of the timer. In other words, if it reached 0, it would not count down in 64 cycles, but instead in 1-cycles. Are you sure that this isn't what stella is showing you? 1 1 Quote Link to comment Share on other sites More sharing options...
+Karl G Posted August 8, 2021 Author Share Posted August 8, 2021 1 hour ago, Andrew Davie said: If your timer had already run out (i.e., INTIM had previously reached zero), then it would now be counting down in 1-cycle steps. So, for each instruction executed, you would have multiple counts of the timer. In other words, if it reached 0, it would not count down in 64 cycles, but instead in 1-cycles. Are you sure that this isn't what stella is showing you? I was monitoring the value of INTIM via the debugger right after I had written to TIM64T - a newly set timer. Also, it seemed to have been jumping around more chaotically than 1-tick per cycle could account for. However, I'm not having luck getting my code back in the state where it would act like this, so I guess I won't worry about it unless it happens again. Anyway, thanks for the thoughts on this. Quote Link to comment Share on other sites More sharing options...
+SpiceWare Posted August 8, 2021 Share Posted August 8, 2021 This topic might be relevant. Be sure to read @alex_79's reply. 1 Quote Link to comment Share on other sites More sharing options...
+Karl G Posted August 8, 2021 Author Share Posted August 8, 2021 16 minutes ago, SpiceWare said: This topic might be relevant. Be sure to read @alex_79's reply. Interesting. So, it may have been that after I ran down my overscan timer, it started at $FF counting down at 1T speed, and the combo of jumping to the next frame, vertical blank processing, and writing to VBLANK and my timer equaled 255 cycles right as I did my write, causing the above. The math makes it seem like it could have been the case. It seemed like INTIM was changing more than 1 per cycle executed with each step in the debugger, but I wasn't specifically watching for that, so I could be wrong about that. Anyway, it seems like the most plausible explanation. Thanks! 1 Quote Link to comment Share on other sites More sharing options...
+Karl G Posted August 12, 2021 Author Share Posted August 12, 2021 I ran into the issue again (I hadn't put in any code to specifically prevent it yet), and this time I watched more closely, and the timer was indeed decrementing at a 1-tick-per-cycle rate. I'm guessing this issue is why I've seen some people add 128 to their timer values, and then loop until the INTIM is no longer negative. Quote Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted August 12, 2021 Share Posted August 12, 2021 5 hours ago, Karl G said: I'm guessing this issue is why I've seen some people add 128 to their timer values, and then loop until the INTIM is no longer negative. While this minimizes the negative effects of a timer underflow, it also makes detecting it harder. IMO a timer underflow should be very noticeable, so that the developer can fix the problem. Quote Link to comment Share on other sites More sharing options...
+Karl G Posted August 12, 2021 Author Share Posted August 12, 2021 51 minutes ago, Thomas Jentzsch said: While this minimizes the negative effects of a timer underflow, it also makes detecting it harder. IMO a timer underflow should be very noticeable, so that the developer can fix the problem. I didn't think about that approach minimizing the effects of going past the timer interval, though I suppose that's the case, and perhaps that's the more common reason for that approach than avoiding the timer wraparound issue like I had assumed. I agree that minimizing/hiding going past the timer interval is the wrong approach if you care about the quality of what you are creating. Quote Link to comment Share on other sites More sharing options...
+Andrew Davie Posted August 12, 2021 Share Posted August 12, 2021 Not gonna say "I told you so", but I will say that the code I posted should be a reliable way to not only catch the wraparound, but also put the spare-time before wraparound into another variable for you. Quote Link to comment Share on other sites More sharing options...
Omegamatrix Posted August 13, 2021 Share Posted August 13, 2021 I remember an idea Nukey posted years ago. I've never done it myself, but in short it was to pad a few WSYNC's after the timer exclusively for game development. The idea is to develop a stable game and remove the them after to help guarantee you don't get close to running out of cycles. The only thing I would caution there is to not have the timer run out to close to the end of the scanline, and to keep the code align with some NOP's unless you want to do a lot more testing. Myself, I generally just use LDA INTIM and branch on not equal. I like to re-use the zero value from the timer if I can. Some games I've done though had way less spare cycles and then I switched to: .waitOverscan: bit TIMINT bpl .waitOverscan That is still minimal bytes. The real advantage is of course is if you go way over cycles it recovers as soon as possible, and hopefully the TV is tolerant enough not to bounce the picture. 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.