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.

ASM > Screen Sync Oddity

#136327 - alwbsok - Tue Jul 31, 2007 1:49 pm

I'm developing two programs in very early stages of development. Both I've put in very similar temporary code to count screen refreshes, and to perform an action when a certain number of screen refreshes have taken place. The basic system between them is to load VCOUNT and check whether it's greater or equal to 160. The program then increments a counter in the IWRAM and checks to see if it equals the desired number. After the program either increments the counter or resets it and performs the action, it then goes into a loop checking VCOUNT again with a simple ldrh-cmp-bge loop until vblank ends (so as not to detect the same vblank twice).

However, this approach doesn't seem to work very well. I've tried the program on VBA (SDL version), the NO$GBA debugger, and the hardware itself. On VBA, the event happens very slowly, several times slower than designed. On the hardware, the events don't happen at all. On NO$GBA, the events don't happen immediately, but as soon as I enter debug mode (pausing the emulation), and immediately resume emulation without making changes, the event occurs once and seems to await another manual interrupt to continue functioning.

Debugging is rather difficult, since VBA seems to work (sort of), and entering debugging mode in NO$GBA seems to temporarily correct the problem. However, from what I've gathered from using breakpoints in NO$GBA, the program gets trapped in the final loop (the simple ldrh-cmp-bge loop that checks for vdraw).

Has anyone else found similar problems when synchronising with the screen without interrupts? Why does the manual interrupting of the NO$GBA emulator make a difference to program flow?

I'd rather not post my code if possible, because it contains a lot of manually entered background and sprite data, and thus require a bit of editing. It's also not particularly readable. However, I'd be happy to post an edited version of the program if it's required to diagnose the problem.

#136391 - Chano Marrano - Wed Aug 01, 2007 9:58 am

If you want to count screen "refreshes", you should set up an interrupt to vblank and do what you want in that interrupt. Easier and better.

#136411 - alwbsok - Wed Aug 01, 2007 1:49 pm

I'm aware of that. It should also be possible to do it the inefficient way, and before I throw it all away and do interrupts, I'm wondering why it isn't working as it should. I'm worried that there's some trick of the trade that I'm not aware of that'll come back to haunt me later. I'm also completely at a loss as to why pausing and restarting emulation affects program flow. It makes no sense.

#136416 - alwbsok - Wed Aug 01, 2007 3:08 pm

I've done some more debugging and I think I've found the root of the problem. It seems that VBLANK always changes partway through the main loop, so that the event never occurs, and the loop at the end of the main loop, designed to prevent repeating the event, catches the vblank and waits until it ends, starting the cycle of failure again. VBA seems not to time the screen as accurately as NO$GBA, so the event does occur, albeit very slowly.

It seems that the problem is that I was branching to the last loop even if it wasn't vblank. After I changed that, the program worked perfectly.

#138445 - elyk1212 - Fri Aug 24, 2007 6:29 am

alwbsok wrote:
I'm aware of that. It should also be possible to do it the inefficient way, and before I throw it all away and do interrupts, I'm wondering why it isn't working as it should. I'm worried that there's some trick of the trade that I'm not aware of that'll come back to haunt me later. I'm also completely at a loss as to why pausing and restarting emulation affects program flow. It makes no sense.



Hi, well from a embedded sys stand point: if you are just checking the results in a register, within a loop/etc you are not assured you will ever arrive at a state of VCOUNT=160 for every wrap around. The Vcount may increment faster than your instructions for the loop execute, therefore arriving at non-deterministic states for VCOUNT at each iteration. Look into reading up on "race conditions" and synchronization issues (good stuff to check out).

In summary: each iteration of your loop *could* not result in an increment of 1 on VCOUNT.

So it is not as though

Code:

while(1)
{
    checkVcount();
    VCOUNT++;
}


From my understanding, this is not guaranteed (always incrementing by one for every loop iteration). You could use the waitforVblank call within libgba. I think that would be a better *guess* of when a VBlank occurs, and you can increment a counter on release of this mutex. However, I think, the best and complete way to count VBLANKs is to use a VBLANK ISR and increment a variable. This way you are always guaranteed an accurate answer.

That is, unless you have ISRs that take too long and are jam packed full of crap, and you have an interrupt thrown within the ISR (or perhaps some other caveat I am not considering). If that happens you need to allow for nested interrupts and that can be a mess. Good news though, libgba makes that easier, and for what you want to do, it doesn't sound like it is needed.

----------------------------
Quote:


It seems that VBLANK always changes partway through the main loop,


Oh, I just saw that. So yeah, it is what I thought then. It is a race condition in which your VBLANK count increments faster than your checks. This is common in embedded hardware programming (and in threaded programming also, for similar reasons, but just due to software/OS abstractions with time slicing different threads and/or processes).

GBA seems to be for all intensive purposes an Emb sys.

So anyway, yeah... um.. avoid headaches use ISRs! :)