Skip to content

Add lid-close sleep - #282

Closed
tasken wants to merge 4 commits into
profi200:masterfrom
tasken:feature/lid-sleep
Closed

tasken wants to merge 4 commits into
profi200:masterfrom
tasken:feature/lid-sleep

Conversation

@tasken

@tasken tasken commented May 18, 2026 •

Copy link
Copy Markdown
Contributor

What changed

Closing the 3DS lid now enters a proper sleep state: audio is muted, frame capture stops, backlights turn off, and the power LED switches to the breathing pattern. Opening the lid restores everything. If the GBA happened to enter Stop mode while the lid was closed, it gets force-woken on lid open.

No config options needed. Works with all games.

Also added colorOverride config option (same pattern as saveOverride) and changed defaultSave from u16 to u8 since it only needs values 0-15.

@tasken

tasken commented May 19, 2026

Copy link
Copy Markdown
Contributor Author

Solves #96 and #276

@tasken

tasken commented May 21, 2026

Copy link
Copy Markdown
Contributor Author

Updated the approach. The button injection (sleepButtons config) is gone.

After digging into gbatek, KEYCNT (4000132h) is strictly an exit/wake mechanism for Stop mode. No unpatched GBA game enters sleep through a button combo - games that support sleep do it through their own menu code. The only ROMs that respond to a button combo are patched with gba-sleephack, which injects assembly to poll for the combo in the VBlank handler.

Injecting buttons on lid close also caused input bleed into gameplay (the shoulder buttons would register as real presses before the game had a chance to enter sleep).

So the new version just does 3DS-side sleep on lid close: mute audio, stop frame capture, turn off backlights, breathing LED. On lid open it restores everything and force-wakes the GBA in case it was in Stop mode.

The colorOverride and defaultSave u16->u8 changes are still in there since they were part of the original commit.

MichielMak added a commit to MichielMak/open_agb_firm that referenced this pull request Jun 16, 2026
Implements sleep-on-lid-close from profi200#282: mutes audio,
stops LGYCAP capture, turns off backlight and sets breathing LED while lid is
closed, then restores everything (including a force-wake of the GBA hardware)
when the lid reopens. Also adds colorOverride config option from the same PR.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@asdolo

asdolo commented Jul 6, 2026

Copy link
Copy Markdown

So this mimics the AGB_FIRM behavior except for triggering the sleep button combo, right? Cool!

@dleicht

dleicht commented Aug 9, 2026

Copy link
Copy Markdown

I cherry picked both commits into my feature/stretch branch and compiled it again.
Works great so far. That's a keeper for me!

@dleicht

dleicht commented Aug 9, 2026

Copy link
Copy Markdown

I cherry picked both commits into my feature/stretch branch and compiled it again. Works great so far. That's a keeper for me!

Ah i spoke too soon it seems.
Test case: Final Fantasy Tactics Advance.

As we all know this game has one lengthy intro. If you close the lid right after it started and open it again some couple of minutes later you'll notice the device wasn't actually asleep. The sound is muted and the screen is off, but the game keeps running. Isn't it supposed to trigger SWI 03h (GBA) - Stop? @tasken

tasken added 3 commits August 9, 2026 17:58
Inject a configurable GBA button combo on lid close to trigger sleep in
games that support it. Mute audio, stop frame capture, and dim the power
LED while the lid is closed. Add colorOverride and sleepButtons config
options. Change defaultSave from u16 to u8.
GBA KEYCNT is exit-only — no unpatched game enters sleep via button
combo, so injecting buttons on lid close is unnecessary and causes
input bleed into gameplay. Remove sleepButtons config option and
simplify handleLidSleep to only manage 3DS-side sleep state.
handleLidSleep() was unconditionally poking REG_HID_PADCNT and the LGY
sleep-ack bit on every lid reopen, duplicating what lgySleepIsr()
already does correctly and only when a real GBA Stop-mode wake fires.
Doing it here too could clobber the wake condition the ISR just armed,
or force-wake a game that's still legitimately asleep.
@tasken
tasken force-pushed the feature/lid-sleep branch from c9c97d2 to 791941f Compare August 9, 2026 22:46
The GBA keeps running the whole time the lid is closed - there's no way
to pause it from the 3DS side, so this was never real sleep, just
display/audio muted. Back up the save at the moment the lid closes so
a power loss while it's closed doesn't lose progress, the same way the
normal shutdown path already does.
@tasken

tasken commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

Ah i spoke too soon it seems. Test case: Final Fantasy Tactics Advance.

As we all know this game has one lengthy intro. If you close the lid right after it started and open it again some couple of minutes later you'll notice the device wasn't actually asleep. The sound is muted and the screen is off, but the game keeps running. Isn't it supposed to trigger SWI 03h (GBA) - Stop? @tasken

Couldn't trigger SWI 03h. Seems entirely game-initiated. Not sure what the SP's hinge does on real hardware.

Tried to actually fix this. Real 3DS-side sleep (PDN_CNT_SLEEP) hung twice, even with a watchdog as backup. No documented way to resume from it. Pausing just the GBA hardware directly is safe but does nothing on my tests.

Leaving this cosmetic (display/audio off, GBA keeps running), added a save backup on lid-close so nothing's lost. Open to someone else solving the real fix.

@Masamune3210

Copy link
Copy Markdown

As far as I am aware, the SP has no hinge sensor. At least, from what I remember playing mine, games are entirely unaware of the state of it and will happily continue playing even if it's closed

@dleicht

dleicht commented Aug 10, 2026

Copy link
Copy Markdown

As far as I am aware, the SP has no hinge sensor. At least, from what I remember playing mine, games are entirely unaware of the state of it and will happily continue playing even if it's closed

^This.

@tasken

tasken commented Aug 10, 2026 •

Copy link
Copy Markdown
Contributor Author

So given that GBA Stop mode can't be triggered externally, and true 3DS sleep (PDN_CNT_SLEEP) currently hangs the system, is this display/audio mute + save backup the best approach we can achieve for now?

@dleicht

dleicht commented Aug 11, 2026

Copy link
Copy Markdown

So here's my testing so far:

Quick overview

# Test Idea Result on my N3DS Conclusion
1 PR #282 – Lid Sleep Put ARM11 to sleep when the lid is closed, hoping this also halts ARM7 ❌ GBA keeps running ARM11/GFX/audio sleeping ≠ GBA core sleeping
2 Hardware Sleep PR #176 Deeper PDN/GFX/CODEC shutdown ❌ GBA keeps running Even deeper 3DS hardware sleep does not stop LGY/GBA
3 ARM9 → GBA IRQ pointer via 0x03007FFC Directly hijack the running GBA IRQ handler from ARM9 ❌ Read value was 00000000 Our assumed ARM9-to-GBA-IWRAM access does not work at runtime
4 ARM7 Vector Override a7_vector[6] Override the ARM7 IRQ vector directly ❌ Also 00000000 This appears to be a boot/overlay mechanism, not a usable live IRQ hook
5 Alternative runtime IWRAM alias 0x080B7FFC Test another suspected mapping of live GBA IWRAM ❌ 00000000 No usable live access through that route either
6 ROM stub at 0x09FFFF00 Redirect the game IRQ to our own ARM code in the upper ROM area ❌ Immediate white screen Code outside the actual ROM cannot reliably be used as executable GBA code
7 IWRAM stub at 0x03007EC0 Place a trampoline in upper IWRAM and jump there ❌ Immediate white screen Stub is erased by the GBA BIOS SoftReset before the game starts
8 RTC Bit 7 – first direct write test Use gba_rtc_toffset bit 7 as a possible hardware freeze ❌ Value stayed unchanged Direct register write without RTC transfer was incomplete
9 Correct RTC read using RTC_CNT_RD Verify whether RTC access works at all ✅ Values such as 815C → 8166 RTC interface can be accessed correctly
10 RTC Bit 7 with actual RTC_CNT_WR Trigger the documented “hang all(?) GBA hardware” behavior ⚠️ White screen + no audio Bit 7 really hangs LGY/GBA
11 Attempt to continue after RTC hang Try to restore the previous state ❌ Values became nonsensical After the hang, RTC/LGY access is no longer reliable; no usable resume
12 Automate asdolo's gba-sleephack patching in open_agb_firm Generic ROM patch that uses real SWI 03h 🟡 Not yet tested Sounds promising ?

This is pretty much in line with findings previously described on problemkaputt.de and 3dbrew.org.

@dleicht

dleicht commented Aug 12, 2026

Copy link
Copy Markdown

Okay, so i've spent a lot of time with this. My first approach was to automatically apply asdolo's sleep-hack on any loaded rom in memory directly in open_agb_firm. That worked, but just like his original gba-sleep-patcher-tool it didn't work with all roms (Mario Kart Super Circuit, Aladdin, Metal Slug Advance - to name but a few).

I'm happy to report there's a better way. In fact there IS a way to trigger SWI 03h externally without the sleep-hacks, enabling true GBA sleep in virtually every rom. How?

  1. Persisting BIOS vector overlay
  2. ARM11 -> ARM9 PXI API
  3. Dynamic IRQ handler placement

I'll add a tech reference later.

Please check this out, build the firmware yourself as usual, test it and tell me what you think:
git clone -b feature/universal-gba-sleep --single-branch --recurse-submodules https://github.com/dleicht/open_agb_firm.git

@Rhlp-Engineering

Copy link
Copy Markdown

oh boy as a rural simpleton i have been looking forward to this feature! where can i download it?

@Rhlp-Engineering

Rhlp-Engineering commented Aug 13, 2026 •

Copy link
Copy Markdown

wait how do i build it im a rural simpleton i would love to test it but im horrid with code and compiling stuff sorry

Okay, so i've spent a lot of time with this. My first approach was to automatically apply asdolo's sleep-hack on any loaded rom in memory directly in open_agb_firm. That worked, but just like his original gba-sleep-patcher-tool it didn't work with all roms (Mario Kart Super Circuit, Aladdin, Metal Slug Advance - to name but a few).

@dleicht

dleicht commented Aug 13, 2026

Copy link
Copy Markdown

wait how do i build it im a rural simpleton i would love to test it but im horrid with code and compiling stuff sorry

Okay, so i've spent a lot of time with this. My first approach was to automatically apply asdolo's sleep-hack on any loaded rom in memory directly in open_agb_firm. That worked, but just like his original gba-sleep-patcher-tool it didn't work with all roms (Mario Kart Super Circuit, Aladdin, Metal Slug Advance - to name but a few).

I got you boo:
https://github.com/dleicht/open_agb_firm/releases

@Rhlp-Engineering

Copy link
Copy Markdown

wait how do i build it im a rural simpleton i would love to test it but im horrid with code and compiling stuff sorry

Okay, so i've spent a lot of time with this. My first approach was to automatically apply asdolo's sleep-hack on any loaded rom in memory directly in open_agb_firm. That worked, but just like his original gba-sleep-patcher-tool it didn't work with all roms (Mario Kart Super Circuit, Aladdin, Metal Slug Advance - to name but a few).

I got you boo: https://github.com/dleicht/open_agb_firm/releases

Holyshoot!
YAY! thank you!
oh boy sleep mode!
thanks so much!

@Rhlp-Engineering

Copy link
Copy Markdown

wait i was playing dkc1 and it didnt sleep,..
lemmi check the files again sorry

@dleicht

dleicht commented Aug 13, 2026

Copy link
Copy Markdown

That'd be weird, but i'll check as well lol

@Rhlp-Engineering

Copy link
Copy Markdown

That'd be weird, but i'll check as well lol

i just replaced the firmware with your version so maby i need to like regenerate the backup firmwares? i dont know i hope i did it right

@dleicht

dleicht commented Aug 13, 2026

Copy link
Copy Markdown

Works for me with the debug build. I didn't actually test the release build (as they should be the same). I'll check that as well.

WDYM backup firmware? You just need to hold the START button as you press the 3DS power button and then select whichever open_agb_firm.firm you put in /luma/payloads/.

@Rhlp-Engineering

Copy link
Copy Markdown

Works for me with the debug build. I didn't actually test the release build (as they should be the same). I'll check that as well.

WDYM backup firmware? You just need to hold the START button as you press the 3DS power button and then select whichever open_agb_firm.firm you put in /luma/payloads/.

im using fastboot so when i press the l button when booting i start in gba mode. i also have firmware backups that shouldent matter but they are for if one of my firmware's breaks the backup should still work.

@Rhlp-Engineering

Copy link
Copy Markdown

i replaced the openagbfirm.firm with your version then renamed your version to openagbfirm.firm
and i thought it may work

@dleicht

dleicht commented Aug 13, 2026

Copy link
Copy Markdown

So both builds work for me. Could you test this without fastboot?

@dleicht

dleicht commented Aug 16, 2026

Copy link
Copy Markdown

Perfect! Your log shows that PDN sleep was in fact triggered correctly. If you did a longer sleep test with this you'd see a lot less battery drain.

Furthermore your log shows that the New 3DS XL uses the same register values as my New 3DS. So your test confirmed this to be fully working on the New 3DS XL. Thanks mate!

@dleicht

dleicht commented Aug 19, 2026

Copy link
Copy Markdown

Okay, so i've been working on this a lot and this is my final build for now.
--> open_agb_firm_dleicht_debug.firm.zip <--

Features Added Compared with profi200/master

Native GBA SWI 03h sleep using dynamically injected ARM7 IRQ code, combined with real 3DS PDN system sleep, runtime IRQ-vector lid triggering, Core 1/color-profile support, border restoration, and reliable full-system wake.

This supports deep sleep even with color profiles now.

@tasken @Masamune3210 @Rhlp-Engineering @KarrsGoVroom could y'all please test this again with your games and give me some feedback? Thanks a lot, guys!

@KarrsGoVroom

KarrsGoVroom commented Aug 19, 2026 •

Copy link
Copy Markdown

Here is my test results, they look great! I changed my colorProfile to nso and tested Pokemon Emerald this time
power_state.log

EDIT: Just for good measure, I also kept my 3DS in sleep for 10 mins this test

@Rhlp-Engineering

Copy link
Copy Markdown

hihihi! i just got back from camping! so sorry for the delay! it was wonderfull tho. ill test as soon as i can. but in 2 days im going camping again.. so i will be unavailable for a week!

i do want to know tho, what is the difference from the sleep mode where the screen and audio shuts off, compared to the 3ds / gba sleep mode?
also i thought the gba couldent do sleep mode?

@dleicht

dleicht commented Aug 19, 2026

Copy link
Copy Markdown

Here is my test results, they look great! I changed my colorProfile to nso and tested Pokemon Emerald this time power_state.log

EDIT: Just for good measure, I also kept my 3DS in sleep for 10 mins this test

Great! It works just like it should. Also your latest power state dump shows that i got rid of all the ARM9 logging. There isn't anything to optimize there power wise anyway.

hihihi! i just got back from camping! so sorry for the delay! it was wonderfull tho. ill test as soon as i can. but in 2 days im going camping again.. so i will be unavailable for a week!

i do want to know tho, what is the difference from the sleep mode where the screen and audio shuts off, compared to the 3ds / gba sleep mode? also i thought the gba couldent do sleep mode?

My fork of open_gba_firm features real SWI 03h GBA (ARM7) sleep. That was tricky to do. The firmware loads the rom into memory and then dynamically injects an IRQ sleep handler into the game (well the memory image, not the actual rom) that just sits there and waits for a signal (closing the lid, or L+SELECT) to initiate the GBA low power SWI 03h sleep mode. This works with all games. You don't need to sleephack patch your roms anymore.

So we got GBA sleeping now, great. But it's still a (New)3DS console and that's ARM11. It's got its own low power sleep mode. That's called PDN sleep. My fork also does that. So effectively this is happening:
You run a GBA game -> you close the lid -> ARM11 signals the previously injected ARM7 IRQ sleep handler to initiate SWI 03h -> SWI 03h reached, GBA effectively suspended -> ARM11 shuts down Core0/Core1 gracefully, the entire GFX pipeline, audio, the screens and changes the LED pattern -> Once that's done it can go into low power PDN sleep.

So as result we got ARM7 (GBA) and ARM11 (3DS) sleeping and that saves a lot of juice.

If you are looking for more info about how it all works you can find that here with all the code and firmware releases.

@Rhlp-Engineering

Copy link
Copy Markdown

so, compared to your v1 sleep code, and to no sleep code, whats the battery like?
also, can i like, pay you somehow this is so cool i feel bad that it's all free your doing such a good job!

@Masamune3210

Copy link
Copy Markdown

I'm not really here for testing, just passing through and helping out with what I know. 3DS is out of commission currently

@Rhlp-Engineering

Copy link
Copy Markdown

ok just updated to the new firmware, and i can tell it works because it takes it a few halfs of a second to kick back on the gpu.
ill test the battery sometime soon, but again, going camping in a bit.

if the 3ds on sleep mode lasts like 3(?) days, and the gba processor in sleep mode probably has a very minimal power drain, so this should add up to like 2~3 days in sleep mode! wowie!
(we should prolly test tho but still wow so cool!)

@Rhlp-Engineering

Copy link
Copy Markdown

good news! so i was playing warioland 4 gba, fun game! but then i got real tired. so i closed the lid and went to bed.
last time, and on the last version, when i tried this, it was flat out of battery when i woke up.

but this time, blue! it was still on BLUE!! WOW! thats epic! YAY!

@dleicht

dleicht commented Aug 20, 2026

Copy link
Copy Markdown

also, can i like, pay you somehow this is so cool i feel bad that it's all free your doing such a good job!

While GPL3 does allow monetization in general, i'm a huge advocate for free software. Working on this has been good fun (so far) for the most part and i learned a whole lot. Arm assembly isn't exactly my strong suit 😅

but this time, blue! it was still on BLUE!! WOW! thats epic! YAY!

Awesome, mate! Enjoy 😄

PS: there's a release build on my github page that doesn't show all the debug stuff and keeps the second screen shut off.

@cybardev

Copy link
Copy Markdown

So now, will there be a new PR by @dleicht that supersedes this, or will @tasken cherry-pick from that repo, or...? Just wondering what the next steps are to get the best version of the feature upstream. 🤔

@dleicht

dleicht commented Aug 23, 2026

Copy link
Copy Markdown

So now, will there be a new PR by @dleicht that supersedes this, or will @tasken cherry-pick from that repo, or...? Just wondering what the next steps are to get the best version of the feature upstream. 🤔

Hey, i could issue a PR no problem. It would qualify to be automatically merged.
But i'm not too sure that's what the maintainers of this repo would want, tbh.
I feel like waiting for them to chime in and tell me how they feel about it lol.
Also the code, while being functional, needs some revision.

@tasken

tasken commented Aug 25, 2026

Copy link
Copy Markdown
Contributor Author

should i then close this one in favor of a new one you make @dleicht?

@dleicht

dleicht commented Aug 25, 2026

Copy link
Copy Markdown

@tasken we could do that. I will reference #282 from my PR, because i used some of your code 👍

But there's still a lot to do before i will issue a PR. Right now O2DS is broken, because i thought it's a good idea to switch the bottom screen backlight off as soon as the game starts. Unfortunately that affects both screens on the O2DS.

@Masamune3210

Copy link
Copy Markdown

@tasken we could do that. I will reference #282 from my PR, because i used some of your code 👍

But there's still a lot to do before i will issue a PR. Right now O2DS is broken, because i thought it's a good idea to switch the bottom screen backlight off as soon as the game starts. Unfortunately that affects both screens on the O2DS.

It affects both screens because both screens is one screen. The o2DS only has one physical screen, it's just separated in software and hidden behind the shell

@dleicht

dleicht commented Aug 26, 2026

Copy link
Copy Markdown

It affects both screens because both screens is one screen. The o2DS only has one physical screen, it's just separated in software and hidden behind the shell

Oh yeah, i know, but thanks anyway.
-> dleicht#5 (comment)

It's already fixed in my local dev branch. I'm fixin a couple of other issues and then commit the next release.

@dleicht

dleicht commented Aug 28, 2026 •

Copy link
Copy Markdown

I pushed a new release.
Concerning the sleep/wake functionality i consider this my final candidate for now.
Will probably issue a PR from there.

Yeah forget about that. I messed it up.
Instead of backporting working bits selectively i will rebuild the working parts only.
This will also make reviews easier later.

@bananaturtlesandwich

Copy link
Copy Markdown

ooh i'm excited for this

@Rhlp-Engineering

Copy link
Copy Markdown

why does the wierless light stay on?

@dleicht

dleicht commented Sep 1, 2026

Copy link
Copy Markdown

why does the wierless light stay on?

OAF shouldn't touch wifi. If you boot OAF as a luma payload then WIFI should stay off.

I used the power led for debugging in the debug build to figure out different stages of errors during arm11 core1 sleep development. But i never messed with wifi. I had a couple of MCU debug probes write into already occupied registers and that may have caused weird side effects, but most probably nothing wifi related.

On what hardware are you running OAF and how do you boot it?

@Rhlp-Engineering

Copy link
Copy Markdown

im using quickboot i think it's called, and im launching it from the forwarder app dohicky!

@dleicht

dleicht commented Sep 2, 2026 •

Copy link
Copy Markdown

Okay, here's my first rebuilt release. Works great on my N3DS. I can't test this on O3DS and O2DS, so i'll need your help.
Please boot this firmware as luma payload directly (Power + START) and give me your sleep test results. No need for long test runs. I just want to know if sleep does work on all 3DS platforms. This comes with the optional "stretch" scaler and should support colorProfiles. Please note that directBoot will be ignored for now. Thanks in advance!

@dleicht

dleicht commented Sep 3, 2026

Copy link
Copy Markdown

Safeguards aside the dynamic sleep handler patching is whacky at best.
What if we could utilize some of the 64Kb ARM7 WRAM?
I'll give it a go.

@Rhlp-Engineering

Copy link
Copy Markdown

woa wacky ram time?

@dleicht

dleicht commented Sep 4, 2026

Copy link
Copy Markdown

Nope.
ARM7 wram can't be used. GBA mirrors iwram instead of exposing wram. That's a bummer.
Next idea: instead of patching the sleep handler into the rom image constantly we could try this:

save original 252 bytes of rom image space
patch handler in there
sleep
restore original 252 bytes

we wouldn't need to find a safe place to store the handler as we'd restore the og data afterwards.
lesssgo!

@dleicht

dleicht commented Sep 4, 2026

Copy link
Copy Markdown

OMG!
So i've spent about a month on this, running in the wrong direction, because i'm an idiot.
This is all it needs:

0x0C    stmdb sp!, {r2, r12}
0x10    swi   0x03
0x14    ldmia sp!, {r2, r12}

I was struggling with finding a proper place to store the 252 bytes sleep handler. Storing it in the rom image does work, but we can't be sure it doesn't affect the game. Storing it in game memory yields the same basic problem.
The bios vector overlay is 32 bytes in total and can't hold the sleep handler. So i tried to decrease the handler in size, but the overlay window was just too small. So after lots of failed attempts i took a break. Then it dawned on me:

We don't need a sleep handler on the GBA side of things. All we need is proper SWI 03h.
As i said the vector overlay is 32 bytes. We only need 12 bytes to get SWI 03h.
ARM11 will then manage PDN sleep itself.

So, why is this better:

  • no rom patching at all
  • rom size doesn't matter
  • ips patches fully supported
  • works with all games
  • should work on all 3ds platforms
  • supports all original open_agb_firm config options (incl. directBoot)

open_agb_firm treats the O2DS sleep switch exactly the same as the 3DS lid, so in theory this should work on the O2DS.

Please test this release build again on your hardware and let me know. If this works on all platforms i'll have a PR ready in no time:
open_agb_firm_dleicht.zip

@Rhlp-Engineering

Copy link
Copy Markdown

wowwowwow!

@tasken

tasken commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

closing in favor of #295

@tasken tasken closed this Sep 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants