From 59543da23d8aaf97922083721c3b3c9794c834c8 Mon Sep 17 00:00:00 2001 From: TuxSH <1922548+TuxSH@users.noreply.github.com> Date: Mon, 12 May 2025 02:53:48 +0200 Subject: [PATCH] bootloader: zerofill N3DS ABL regs when autobooting DSi title Closes #2137. Even though (when running TWL/AGB FIRM) the SoC is in O3DS mode, and the GPU also is, as well as most other components behaving as such (external RAM, L2C not usable, etc.), this is NOT the case for the LCD and adaptive backlight logic which retains FULL N3DS functionality, including a feature where the window is blended with a given color depending on the overall relative luminance of that window. However, Nintendo's own code mistakenly assumes the opposite, and clearly so ("if GPU in N3DS mode" checks, not passing N3DS extra adaptive backlight (ABL) to TWL/AGB_FIRM). This has implications: - Powersaving (ABL) settings in TWL/AGB_FIRM is inconsistent with *both* O3DS (because the new RGB blend LUT has been set to its current value by NATIVE_FIRM) and N3DS (because "pwn_cnt" and "inertia" are missing their N3DS-only bits) - "rave party" when booting into TWL/AGB_FIRM or O3DS NATIVE_FIRM without these regs (well, the LUT) initialized. Easiest way to do so is by leveraging the "DSi autooboot" feature Luma provides. It is worth noting at least the LUT survives hardware reboots (if Nintendo were using DSi software that was using TLNC-based reboots, they wouldn't have noticed). Only touch the autoboot path, for now --- arm11/source/main.c | 25 +++++++++++++++++++++++++ arm11/source/types.h | 1 + arm9/source/deliver_arg.c | 24 ++++++++++++++++++++++++ arm9/source/screen.c | 5 +++++ arm9/source/screen.h | 2 ++ 5 files changed, 57 insertions(+) diff --git a/arm11/source/main.c b/arm11/source/main.c index 99923892..e3904b25 100644 --- a/arm11/source/main.c +++ b/arm11/source/main.c @@ -195,6 +195,28 @@ static void deinitScreens(void) *(vu32 *)0x10202014 = 0; } +static void zerofillN3dsAblRegisters(void) +{ + // It should be fine to write to these regs even on O3DS as they + // are RAZ/WI + + // TODO: read from calibration, but null values should do just + // fine. From testing, LUT explicitly ignores null values, and + // it is probably the case of reg @ 0x54 as well. + *(vu32 *)0x10202250 = 0; // unknown 24-bit value, seen: 0 + *(vu32 *)0x10202254 = 0; // unknown 24-bit value, seen: nonzero + + *(vu32 *)0x10202A50 = 0; // unknown 24-bit value, seen: 0 + *(vu32 *)0x10202A54 = 0; // unknown 24-bit value, seen: nonzero + + for (u32 i = 0; i < 64; i++) { + // Blend colors (w/ color multiplication) for each group + // of 4 relative-luminance Rs + *(vu32 *)(0x10202300 + 4*i) = 0; + *(vu32 *)(0x10202B00 + 4*i) = 0; + } +} + void main(void) { operation = ARM11_READY; @@ -223,6 +245,9 @@ void main(void) case DEINIT_SCREENS: deinitScreens(); break; + case ZEROFILL_N3DS_ABL_REGISTERS: + zerofillN3dsAblRegisters(); + break; case PREPARE_ARM11_FOR_FIRMLAUNCH: memcpy((void *)0x1FFFFC00, (void *)prepareForFirmlaunch, prepareForFirmlaunchSize); *(vu32 *)0x1FFFFFFC = 0; diff --git a/arm11/source/types.h b/arm11/source/types.h index 43eacc92..551b7d93 100644 --- a/arm11/source/types.h +++ b/arm11/source/types.h @@ -60,6 +60,7 @@ typedef enum SWAP_FRAMEBUFFERS, UPDATE_BRIGHTNESS, DEINIT_SCREENS, + ZEROFILL_N3DS_ABL_REGISTERS, PREPARE_ARM11_FOR_FIRMLAUNCH, ARM11_READY, } Arm11Operation; diff --git a/arm9/source/deliver_arg.c b/arm9/source/deliver_arg.c index 0ef1a596..d360e616 100644 --- a/arm9/source/deliver_arg.c +++ b/arm9/source/deliver_arg.c @@ -30,6 +30,7 @@ #include "config.h" #include "fs.h" #include "i2c.h" +#include "screen.h" u8 *loadDeliverArg(void) { @@ -200,6 +201,29 @@ static bool configureHomebrewAutobootTwl(u8 *deliverArg) *(u16 *)(tlnc + 6) = crc16(tlnc + 8, 0x18, 0xFFFF); + // Even though (when running TWL/AGB FIRM) the SoC is in O3DS mode, and the GPU also is, + // as well as most other components behaving as such (external RAM, L2C not usable, etc.), + // this is NOT the case for the LCD and adaptive backlight logic which retains FULL N3DS + // functionality, including a feature where the window is blended with a given color depending + // on the overall relative luminance of that window. + + // However, Nintendo's own code mistakenly assumes the opposite, and clearly so ("if GPU in N3DS mode" + // checks, not passing N3DS extra adaptive backlight (ABL) to TWL/AGB_FIRM). This has implications: + + // - Powersaving (ABL) settings in TWL/AGB_FIRM is inconsistent with *both* O3DS (because the new RGB blend LUT + // has been set to its current value by NATIVE_FIRM) and N3DS (because "pwn_cnt" and "inertia" are missing + // their N3DS-only bits) + // - "rave party" when booting into TWL/AGB_FIRM or O3DS NATIVE_FIRM without these regs (well, the LUT) initialized. + // Easiest way to do so is by leveraging the "DSi autooboot" feature Luma provides. It is worth noting at least + // the LUT survives hardware reboots (if Nintendo were using DSi software that was using TLNC-based reboots, + // they wouldn't have noticed). + + // As such, zerofill these registers (from testing, hardware explicitly discards null values, so this + // should be fine). For now, only touch the Luma-initiated autoboot path + + if (ISN3DS) + zerofillN3dsAblRegisters(); + CFG_BOOTENV = 3; return true; diff --git a/arm9/source/screen.c b/arm9/source/screen.c index bb01525b..e82475d5 100644 --- a/arm9/source/screen.c +++ b/arm9/source/screen.c @@ -118,3 +118,8 @@ void initScreens(void) clearScreens(false); swapFramebuffers(false); } + +void zerofillN3dsAblRegisters(void) +{ + invokeArm11Function(ZEROFILL_N3DS_ABL_REGISTERS); +} \ No newline at end of file diff --git a/arm9/source/screen.h b/arm9/source/screen.h index 275ed686..01b21d1e 100644 --- a/arm9/source/screen.h +++ b/arm9/source/screen.h @@ -59,6 +59,7 @@ typedef enum SWAP_FRAMEBUFFERS, UPDATE_BRIGHTNESS, DEINIT_SCREENS, + ZEROFILL_N3DS_ABL_REGISTERS, PREPARE_ARM11_FOR_FIRMLAUNCH, ARM11_READY, } Arm11Operation; @@ -73,3 +74,4 @@ void swapFramebuffers(bool isAlternate); void updateBrightness(u32 brightnessIndex); void clearScreens(bool isAlternate); void initScreens(void); +void zerofillN3dsAblRegisters(void);