gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

C/C++ > Why aren't my timers working?

#9710 - Domovoi - Thu Aug 14, 2003 5:32 pm

Hmmm... I'm trying to use a count-up timer to scroll a background one pixel every second. The way I figured is setting timer 0 to the F/256 prescaler, and setting timer 1 to countup. That should make timer 1 increment almost every second, right?

It's not going well... I get the feeling I can't zero these timers, or something. Here's what I'm doing:

Code:

  REG_TM1CNT_H = 0x80 | 0x4;   // Set timer 1 to 'start' and 'countup'
  REG_TM0CNT_H = 0x2 | 0x80;  // Set timer 0 to prescale val. F/256 and 'start'.
   
  REG_TM0CNT_L = REG_TM1CNT_L = 0;  // Zero both timers to be sure..
  while (true)  // Start the main loop...
  {
    if (REG_TM1CNT_L)  // If a second has passed, this timer non-zero
    {
      REG_TM1CNT_H = 0;  // Stop the countup-timer...
      curPos = MoveBG(curPos + REG_TM1CNT_L);  // move the background by the amount of seconds passed (usually 1)

      REG_TM1CNT_L = 0;  // Set the timer back to zero
      REG_TM1CNT_H = TMR_START | TMR_COUNTUP;  // Start the timer again.
    }
  }



This should be working, for all I can figure out. However, the background immediately starts scrolling at tremendous speed. I guess REG_TM1CNT_L, for some reason, is never 0, so that it scrolls -every- iteration. But why? It should work like a charm, according to all the docs... What am I missing?

Here's my #defines for the timer registers, just to be sure:

#define REG_TM0CNT_L *(volatile unsigned short*)0x4000100
#define REG_TM0CNT_H *(unsigned short*)0x4000102
#define REG_TM1CNT_L *(volatile unsigned short*)0x4000104
#define REG_TM1CNT_H *(unsigned short*)0x4000106
#define REG_TM2CNT_L *(volatile unsigned short*)0x4000108
#define REG_TM2CNT_H *(unsigned short*)0x400010A
#define REG_TM3CNT_L *(volatile unsigned short*)0x400010C
#define REG_TM3CNT_H *(unsigned short*)0x400010E

Anyone got an idea?

#9716 - DekuTree64 - Thu Aug 14, 2003 7:30 pm

I think you need to set the TMxCNT_L regs before you enable the timers.
But if you read http://forum.gbadev.org/viewtopic.php?t=1818 you'll find that pretty much everyone agrees that a VBlank based timer is better. If you have like a sound function or anything that always gets called in a VBlank interrupt, just increment a frame counter variable in that, and you'll be sure to catch every frame. If you don't have any VBlank interrupt happening, just increment the variable after each VSync() in your code, assuming your program flow is like do stuff, VSync, do VBlank stuff, repeat.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku

#9718 - Domovoi - Fri Aug 15, 2003 12:02 am

Hmmm... interesting. So, let me see if I get this correctly... The time between two VBlanks is -always- the same, right? No matter how CPU intensive your code is? So in one iteration of my main program loop, the screen might've been drawn a couple of times?

So, let's say that, in theory, the time between two VBlanks is one millisecond (in theory, to keep it simple). I write an interrupt so that it increments a variable on every VBblank. Then in my loop, when it comes time to scroll backgrounds or do animations, I check that variable. If it says, say, 3, I know that the screen has been drawn 3 times since the last iteration, and that we are now 3 * 1 millisecond = 3 milliseconds further. Then I set the variable to zero, and the whole thing can start over again.

Is that about correct? If not, what is the idea? If so, how much time -is- there, exactly, between VBlanks?

#9719 - Aizz - Fri Aug 15, 2003 2:07 am

Quotation from AGB Manual:
V interval frequency 59.727 Hz 16.743 ms
So VBlank occurs almost every 17ms, 35 times/sec.

My opinion:
Do the scroll or animation in VBlank interrupt service function, not in your main loop. In another words, if you use VBlankIntr() function for your interrupt service function, then use a global(/static) variable to count the VBlank times, and when the global(/static) var reaches the exact number you want, do your scroll/animation code segment.
It looks like this:
Code:

UCHAR g_VBlankCount=0;  // Global var for counting VBlank
void VBlankIntr(void)  // VBlank interrupt service function
{
    g_VBlankCount++;
    if (g_VBlankCOunt==10)  // Help yourself to change the num "10"
    {
         // Scroll/Animation code goes here...
        g_VBlankCount=0;      // Reset the counting
    }
}


===========
Hope me make it clear and pardon me for my poor English...

#9721 - DekuTree64 - Fri Aug 15, 2003 4:20 am

Aizz wrote:
Quotation from AGB Manual:
V interval frequency 59.727 Hz 16.743 ms
So VBlank occurs almost every 17ms, 35 times/sec.

Probably just a mistake, but that would be about 60 times per second, not 35.
Quote:

My opinion:
Do the scroll or animation in VBlank interrupt service function, not in your main loop. In another words, if you use VBlankIntr() function for your interrupt service function, then use a global(/static) variable to count the VBlank times, and when the global(/static) var reaches the exact number you want, do your scroll/animation code segment.

This would be a handy way for the particular situation, but just for future reference, generally you shouldn't run your whole game from an interrupt. Interrupts use their own stack, which is small (64 bytes, I think). You can switch it over to the normal user mode stack with a little ASM if you really need to though. Actually I think there's an option to do that automatically in Jeff's Crt0.S. But if you have your main game loop in the interrupt function, another VBlank might happen before you're done with the current frame, so then it would start working on the new frame, and chances are that one would take about as long as the last, aso it starts on yet another, and you end up with a mess.
You could disable the VBlank interrupt and reenable it at the end of the frame processing though, but it's generally better to just process a frame, wait for VSync, and then copy in anything that will be visible on the screen before it starts drawing again.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku

#9728 - Domovoi - Fri Aug 15, 2003 1:04 pm

Hmmm... On waiting for the Vsync... For some reason, that makes tearing worse, for me.

My code is basically like this: The input updates variables holding positions, I wait for vsync, and scroll to those positions, copying tile data when necessary.

The thing is, when wait for vsync, the whole thing scrolls slower than when I don't. Also, visible tearing still occurs, and more noticable because scrolling goes slower.

What could be going wrong there? Could it be that my scrolling function actually takes longer than the vblank period to finish, so that it's still copying when the screen is already drawing again?

#9731 - tepples - Fri Aug 15, 2003 2:39 pm

Domovoi wrote:
Could it be that my scrolling function actually takes longer than the vblank period to finish, so that it's still copying when the screen is already drawing again?

Possibly, if you're not using a tile mode.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#9768 - Aizz - Sun Aug 17, 2003 3:15 am

Thanks DekuTree64 for pointing out my fault, it's almost 60times/sec really, and sorry all.

Use VBlankIntr to set BGXXOFS or copy OAM buffer using DMA, it should be enough, I think.

===========
Pardon me for my poor English...