3 Add Sleep Mode
voloved edited this page 2023-07-05 18:01:24 -04:00

By devolov (though wouldn't have been possible without BSBob, SBird, and Kurausukun)

Goal: Allow a sleep function to be used to save battery when playing on a flashcart.

Warning: The sleep function won't work on mGBA and other emulators. It has been tested to work fine on an Everdrive, EZ Flash Omega DE, and EZ Flash IV.

sleep

In include/gba/io_reg.h, define the keys that you'd like to have the game go to sleep and wake up:

#define KEY_AND_INTR    0x8000
#define DPAD_ANY        ((DPAD_RIGHT | DPAD_LEFT | DPAD_UP | DPAD_DOWN))
#define JOY_EXCL_DPAD   0x030F

+#define SLEEP_KEYS      ((L_BUTTON | R_BUTTON | SELECT_BUTTON))
+#define WAKE_KEYS       ((SELECT_BUTTON | START_BUTTON))
+
// interrupt flags
#define INTR_FLAG_VBLANK  (1 <<  0)
#define INTR_FLAG_HBLANK  (1 <<  1)

In src/main.c, add the following to ReadKeys:

static void ReadKeys(void)
{
     u16 keyInput = REG_KEYINPUT ^ KEYS_MASK;
+
+    if (keyInput == SLEEP_KEYS)
+    {
+        vu16 IeBak, DispCntBak;
+        DispCntBak = REG_DISPCNT;      // LCDC OFF
+        REG_DISPCNT = 1 << 7;          // DISP_LCDC_OFF = 1 << 7
+        REG_KEYCNT= KEY_AND_INTR | KEY_INTR_ENABLE | WAKE_KEYS;
+        REG_IME = 0;
+        IeBak = REG_IE;               // IE save
+        REG_IE = INTR_FLAG_KEYPAD;    // Enable Key interrupt
+        REG_IME = 1;
+        asm("swi 0X03");              // This is the Stop command
+        REG_IME = 0;
+        REG_IE = IeBak;               // IE return
+        REG_IME = 1;
+        REG_DISPCNT = DispCntBak;     // LCDC ON
+        VBlankIntrWait();
+        while (keyInput)              // Doesn't continue until the wake keys are let go
+            keyInput = REG_KEYINPUT ^ KEYS_MASK;
+        return;
+    }
+
     gMain.newKeysRaw = keyInput & ~gMain.heldKeysRaw;
     gMain.newKeysReleased = ~keyInput & gMain.heldKeysRaw;
     gMain.newKeys = gMain.newKeysRaw;
     gMain.newAndRepeatedKeys = gMain.newKeysRaw;

And that's it! What you're doing is calling the BIOS Stop function (SWI 03) to put the GBA in low-power mode.
You're also setting the wake keys as a hardware interrupt to get the GBA out of Stop mode afterwards.