-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Improving the WaitForVBlank function
The credits for this tutorial belong to DizzyEggg.
In both, Pokémon FireRed/LeafGreen and Pokémon Emerald, Game Freak modified the WaitForVBlank
function present in Pokémon Ruby/Sapphire.
WaitForVBlank
is used for what is called vsyncing. In short, VBlank is a short period of time in between each refresh of the screen, and it is used to keep the game processes in sync with the GBA's screen.
In Emerald, Gamefreak used a while
loop that kept on checking for VBlank over and over again. However, this approach is very wasteful. The while
loop is not doing anything, so all it does is waste CPU cycles, and burning CPU cycles like this burns battery power.
The correct way to vsync is to use a software interrupt/BIOS call. In short, we put the CPU in low power mode while we wait for the next VBlank, and on the next VBlank the CPU comes back to life.
The easiest way to do so, is by copying and pasting the function directly from Ruby and Sapphire, which means modifying the function like this:
static void WaitForVBlank(void)
{
gMain.intrCheck &= ~INTR_FLAG_VBLANK;
- while (!(gMain.intrCheck & INTR_FLAG_VBLANK))
- ;
+ VBlankIntrWait();
}
One thing to note is that on agbcc's -O2
, we still pay the price of a function call, as VBlankIntrWait
fails to be inlined. In general, agbcc is very conservative on optimizations. Most other decent optimizing compilers (or if you make modern
) will inline the function.
A solution is to inline it ourselves, for a negligible performance benefit:
static void WaitForVBlank(void)
{
gMain.intrCheck &= ~INTR_FLAG_VBLANK;
- while (!(gMain.intrCheck & INTR_FLAG_VBLANK))
- ;
+ asm("swi 0x5");
}
Save, build a ROM, and so the game will no longer waste CPU power while it is waiting for the next VBlank. The easiest way to notice this is while running the game on a GBA emulator with a FastForward feature implemented, where implementing this fix will give you a 100-150% increase in speed.