diff --git a/arm9/data/config_template.ini b/arm9/data/config_template.ini
index 3e7158b..9b521fa 100644
Binary files a/arm9/data/config_template.ini and b/arm9/data/config_template.ini differ
diff --git a/arm9/source/config.c b/arm9/source/config.c
index d488112..ab2befc 100644
--- a/arm9/source/config.c
+++ b/arm9/source/config.c
@@ -62,6 +62,7 @@ static const char *singleOptionIniNamesBoot[] = {
"use_emunand_firm_if_r_pressed",
"enable_external_firm_and_modules",
"enable_game_patching",
+ "app_syscore_threads_on_core_2",
"show_system_settings_string",
"show_gba_boot_screen",
};
@@ -544,7 +545,8 @@ static size_t saveLumaIniConfigToStr(char *out)
(int)CONFIG_VERSIONMAJOR, (int)CONFIG_VERSIONMINOR,
(int)CONFIG(AUTOBOOTEMU), (int)CONFIG(USEEMUFIRM),
(int)CONFIG(LOADEXTFIRMSANDMODULES), (int)CONFIG(PATCHGAMES),
- (int)CONFIG(PATCHVERSTRING), (int)CONFIG(SHOWGBABOOT),
+ (int)CONFIG(REDIRECTAPPTHREADS), (int)CONFIG(PATCHVERSTRING),
+ (int)CONFIG(SHOWGBABOOT),
1 + (int)MULTICONFIG(DEFAULTEMU), 4 - (int)MULTICONFIG(BRIGHTNESS),
splashPosStr, (unsigned int)cfg->splashDurationMsec,
@@ -716,6 +718,7 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
"( ) Use EmuNAND FIRM if booting with R",
"( ) Enable loading external FIRMs and modules",
"( ) Enable game patching",
+ "( ) Redirect app. syscore threads to core2",
"( ) Show NAND or user string in System Settings",
"( ) Show GBA boot screen in patched AGB_FIRM",
};
@@ -793,6 +796,15 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
"for out-of-region games work.\n\n"
"Refer to the wiki for instructions.",
+ "Redirect app. threads that would spawn\n"
+ "on core1, to core2 (which is an extra\n"
+ "CPU core for applications that usually\n"
+ "remains unused).\n\n"
+ "This improves the performance of very\n"
+ "demanding games (like Pok\x82mon US/UM)\n" // CP437
+ "by about 10%. Can break some games\n"
+ "and other applications.\n",
+
"Enable showing the current NAND/FIRM:\n\n"
"\t* Sys = SysNAND\n"
"\t* Emu = EmuNAND 1\n"
@@ -839,6 +851,7 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
{ .visible = nandType == FIRMWARE_EMUNAND },
{ .visible = true },
{ .visible = true },
+ { .visible = ISN3DS },
{ .visible = true },
{ .visible = true },
};
diff --git a/arm9/source/config.h b/arm9/source/config.h
index 7a7f162..0d79e62 100644
--- a/arm9/source/config.h
+++ b/arm9/source/config.h
@@ -36,7 +36,7 @@
#define CONFIG_FILE "config.bin"
#define CONFIG_VERSIONMAJOR 3
-#define CONFIG_VERSIONMINOR 2
+#define CONFIG_VERSIONMINOR 3
#define BOOTCFG_NAND BOOTCONFIG(0, 7)
#define BOOTCFG_FIRM BOOTCONFIG(3, 7)
@@ -59,6 +59,7 @@ enum singleOptions
USEEMUFIRM,
LOADEXTFIRMSANDMODULES,
PATCHGAMES,
+ REDIRECTAPPTHREADS,
PATCHVERSTRING,
SHOWGBABOOT,
PATCHUNITINFO,
diff --git a/k11_extension/include/config.h b/k11_extension/include/config.h
index 26ee057..07d12de 100644
--- a/k11_extension/include/config.h
+++ b/k11_extension/include/config.h
@@ -30,6 +30,7 @@ enum singleOptions
USEEMUFIRM,
LOADEXTFIRMSANDMODULES,
PATCHGAMES,
+ REDIRECTAPPTHREADS,
PATCHVERSTRING,
SHOWGBABOOT,
PATCHUNITINFO,
diff --git a/k11_extension/include/globals.h b/k11_extension/include/globals.h
index 163a8cd..3e5ed24 100644
--- a/k11_extension/include/globals.h
+++ b/k11_extension/include/globals.h
@@ -54,6 +54,7 @@ extern void (*KScheduler__AdjustThread)(KScheduler *this, KThread *thread, u32 o
extern void (*KScheduler__AttemptSwitchingThreadContext)(KScheduler *this);
extern Result (*ControlMemory)(u32 *addrOut, u32 addr0, u32 addr1, u32 size, MemOp op, MemPerm perm, bool isLoader);
+extern Result (*CreateThread)(Handle *outThreadHandle, u32 ep, u32 arg, u32 stackTop, s32 priority, s32 processorId);
extern void (*SleepThread)(s64 ns);
extern Result (*CloseHandle)(Handle handle);
extern Result (*GetHandleInfo)(s64 *out, Handle handle, u32 type);
diff --git a/k11_extension/include/kernel.h b/k11_extension/include/kernel.h
index fb1bed8..a0596c3 100644
--- a/k11_extension/include/kernel.h
+++ b/k11_extension/include/kernel.h
@@ -1162,7 +1162,7 @@ offsetof(classname##O3DSPre8x, field)))
#define KPROCESSHWINFO_GET_RVALUE(obj, field) *(KPROCESSHWINFO_GET_PTR(obj, field))
#define KPROCESSHWINFO_GET_RVALUE_TYPE(type, obj, field) *(KPROCESSHWINFO_GET_PTR_TYPE(type, obj, field))
-extern u32 pidOffsetKProcess, hwInfoOffsetKProcess, codeSetOffsetKProcess, handleTableOffsetKProcess, debugOffsetKProcess;
+extern u32 pidOffsetKProcess, hwInfoOffsetKProcess, codeSetOffsetKProcess, handleTableOffsetKProcess, debugOffsetKProcess, flagsKProcess;
static inline u32 idOfProcess(KProcess *process)
{
@@ -1195,6 +1195,13 @@ static inline KDebug *debugOfProcess(KProcess *process)
return debug;
}
+static inline u32 flagsOfProcess(KProcess *process)
+{
+ u32 flags;
+ memcpy(&flags, (const u8 *)process + flagsKProcess, 4);
+ return flags;
+}
+
static inline const char *classNameOfAutoObject(KAutoObject *object)
{
const char *name;
diff --git a/k11_extension/include/svc/CreateThread.h b/k11_extension/include/svc/CreateThread.h
new file mode 100644
index 0000000..e4f7de5
--- /dev/null
+++ b/k11_extension/include/svc/CreateThread.h
@@ -0,0 +1,32 @@
+/*
+* This file is part of Luma3DS
+* Copyright (C) 2016-2023 Aurora Wright, TuxSH
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*
+* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
+* * Requiring preservation of specified reasonable legal notices or
+* author attributions in that material or in the Appropriate Legal
+* Notices displayed by works containing it.
+* * Prohibiting misrepresentation of the origin of that material,
+* or requiring that modified versions of such material be marked in
+* reasonable ways as different from the original version.
+*/
+
+#include "utils.h"
+#include "kernel.h"
+#include "svc.h"
+
+Result CreateThreadHookWrapper(Handle *outThreadHandle, u32 ep, u32 arg, u32 stackTop, s32 priority, s32 processorId);
+Result CreateThreadHook(Handle *outThreadHandle, u32 ep, u32 arg, u32 stackTop, s32 priority, s32 processorId);
diff --git a/k11_extension/source/globals.c b/k11_extension/source/globals.c
index 26584d6..47918aa 100644
--- a/k11_extension/source/globals.c
+++ b/k11_extension/source/globals.c
@@ -50,6 +50,7 @@ void (*KScheduler__AdjustThread)(KScheduler *this, KThread *thread, u32 oldSched
void (*KScheduler__AttemptSwitchingThreadContext)(KScheduler *this);
Result (*ControlMemory)(u32 *addrOut, u32 addr0, u32 addr1, u32 size, MemOp op, MemPerm perm, bool isLoader);
+Result (*CreateThread)(Handle *outThreadHandle, u32 ep, u32 arg, u32 stackTop, s32 priority, s32 processorId);
void (*SleepThread)(s64 ns);
Result (*CloseHandle)(Handle handle);
Result (*GetHandleInfo)(s64 *out, Handle handle, u32 type);
@@ -114,4 +115,4 @@ u32 stolenSystemMemRegionSize;
vu32 rosalinaState;
bool hasStartedRosalinaNetworkFuncsOnce;
-u32 pidOffsetKProcess, hwInfoOffsetKProcess, codeSetOffsetKProcess, handleTableOffsetKProcess, debugOffsetKProcess;
+u32 pidOffsetKProcess, hwInfoOffsetKProcess, codeSetOffsetKProcess, handleTableOffsetKProcess, debugOffsetKProcess, flagsKProcess;
diff --git a/k11_extension/source/main.c b/k11_extension/source/main.c
index e0a0052..c4cce09 100644
--- a/k11_extension/source/main.c
+++ b/k11_extension/source/main.c
@@ -127,6 +127,7 @@ void configHook(vu8 *cfgPage)
codeSetOffsetKProcess = KPROCESS_OFFSETOF(codeSet);
handleTableOffsetKProcess = KPROCESS_OFFSETOF(handleTable);
debugOffsetKProcess = KPROCESS_OFFSETOF(debug);
+ flagsKProcess = KPROCESS_OFFSETOF(kernelFlags);
}
static void findUsefulSymbols(void)
@@ -240,6 +241,7 @@ static void findUsefulSymbols(void)
// The official prototype of ControlMemory doesn't have that extra param'
ControlMemory = (Result (*)(u32 *, u32, u32, u32, MemOp, MemPerm, bool))
decodeArmBranch((u32 *)officialSVCs[0x01] + 5);
+ CreateThread = (Result (*)(Handle *, u32, u32, u32, s32, s32))decodeArmBranch((u32 *)officialSVCs[0x08] + 5);
SleepThread = (void (*)(s64))officialSVCs[0x0A];
CloseHandle = (Result (*)(Handle))officialSVCs[0x23];
GetHandleInfo = (Result (*)(s64 *, Handle, u32))decodeArmBranch((u32 *)officialSVCs[0x29] + 3);
diff --git a/k11_extension/source/svc.c b/k11_extension/source/svc.c
index 27a064a..516098f 100644
--- a/k11_extension/source/svc.c
+++ b/k11_extension/source/svc.c
@@ -28,6 +28,7 @@
#include "synchronization.h"
#include "svc.h"
#include "svc/ControlMemory.h"
+#include "svc/CreateThread.h"
#include "svc/GetHandleInfo.h"
#include "svc/GetSystemInfo.h"
#include "svc/GetProcessInfo.h"
@@ -63,6 +64,8 @@ void buildAlteredSvcTable(void)
alteredSvcTable[0x01] = ControlMemoryHookWrapper;
+ if (isN3DS)
+ alteredSvcTable[0x08] = CreateThreadHookWrapper;
alteredSvcTable[0x29] = GetHandleInfoHookWrapper;
alteredSvcTable[0x2A] = GetSystemInfoHookWrapper;
alteredSvcTable[0x2B] = GetProcessInfoHookWrapper;
diff --git a/k11_extension/source/svc/CreateThread.c b/k11_extension/source/svc/CreateThread.c
new file mode 100644
index 0000000..da748e9
--- /dev/null
+++ b/k11_extension/source/svc/CreateThread.c
@@ -0,0 +1,37 @@
+/*
+* This file is part of Luma3DS
+* Copyright (C) 2016-2023 Aurora Wright, TuxSH
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*
+* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
+* * Requiring preservation of specified reasonable legal notices or
+* author attributions in that material or in the Appropriate Legal
+* Notices displayed by works containing it.
+* * Prohibiting misrepresentation of the origin of that material,
+* or requiring that modified versions of such material be marked in
+* reasonable ways as different from the original version.
+*/
+
+#include "svc/CreateThread.h"
+
+Result CreateThreadHook(Handle *outThreadHandle, u32 ep, u32 arg, u32 stackTop, s32 priority, s32 processorId)
+{
+ u32 flags = flagsOfProcess(currentCoreContext->objectContext.currentProcess);
+ if (isN3DS && CONFIG(REDIRECTAPPTHREADS) && processorId == 1 && (flags & 0xF00) == 0x100)
+ processorId = 2;
+
+ return CreateThread(outThreadHandle, ep, arg, stackTop, priority, processorId);
+}
+
diff --git a/k11_extension/source/svc/wrappers.s b/k11_extension/source/svc/wrappers.s
index c0953ea..68fc1d8 100644
--- a/k11_extension/source/svc/wrappers.s
+++ b/k11_extension/source/svc/wrappers.s
@@ -84,3 +84,16 @@ ControlMemoryEx:
ldr r1, [sp, #12]
add sp, #20
pop {pc}
+
+.global CreateThreadHookWrapper
+.type CreateThreadHookWrapper, %function
+CreateThreadHookWrapper:
+ push {lr}
+ sub sp, #12
+ str r4, [sp, #4]
+ str r0, [sp]
+ add r0, sp, #8
+ bl CreateThreadHook
+ ldr r1, [sp, #8]
+ add sp, sp, #12
+ pop {pc}
diff --git a/sysmodules/loader/source/patcher.h b/sysmodules/loader/source/patcher.h
index 0ff267f..86b32eb 100644
--- a/sysmodules/loader/source/patcher.h
+++ b/sysmodules/loader/source/patcher.h
@@ -33,6 +33,7 @@ enum singleOptions
USEEMUFIRM,
LOADEXTFIRMSANDMODULES,
PATCHGAMES,
+ REDIRECTAPPTHREADS,
PATCHVERSTRING,
SHOWGBABOOT,
PATCHUNITINFO,
diff --git a/sysmodules/pm/source/luma.c b/sysmodules/pm/source/luma.c
index eb14d26..81d0b00 100644
--- a/sysmodules/pm/source/luma.c
+++ b/sysmodules/pm/source/luma.c
@@ -3,6 +3,23 @@
#include "luma.h"
#include "util.h"
+u32 config, multiConfig, bootConfig;
+
+void readLumaConfig(void)
+{
+ s64 out = 0;
+ bool isLumaWithKext = svcGetSystemInfo(&out, 0x20000, 0) == 1;
+ if (isLumaWithKext)
+ {
+ svcGetSystemInfo(&out, 0x10000, 3);
+ config = (u32)out;
+ svcGetSystemInfo(&out, 0x10000, 4);
+ multiConfig = (u32)out;
+ svcGetSystemInfo(&out, 0x10000, 5);
+ bootConfig = (u32)out;
+ }
+}
+
bool hasKExt(void)
{
s64 val;
diff --git a/sysmodules/pm/source/luma.h b/sysmodules/pm/source/luma.h
index 80b80ef..54e6c9e 100644
--- a/sysmodules/pm/source/luma.h
+++ b/sysmodules/pm/source/luma.h
@@ -2,6 +2,42 @@
#include <3ds/types.h>
+#define CONFIG(a) (((config >> (a)) & 1) != 0)
+#define MULTICONFIG(a) ((multiConfig >> (2 * (a))) & 3)
+#define BOOTCONFIG(a, b) ((bootConfig >> (a)) & (b))
+
+#define BOOTCFG_NAND BOOTCONFIG(0, 7)
+#define BOOTCFG_FIRM BOOTCONFIG(3, 7)
+#define BOOTCFG_NOFORCEFLAG BOOTCONFIG(6, 1)
+#define BOOTCFG_NTRCARDBOOT BOOTCONFIG(7, 1)
+
+enum multiOptions
+{
+ DEFAULTEMU = 0,
+ BRIGHTNESS,
+ SPLASH,
+ PIN,
+ NEWCPU,
+ AUTOBOOTMODE,
+};
+
+enum singleOptions
+{
+ AUTOBOOTEMU = 0,
+ USEEMUFIRM,
+ LOADEXTFIRMSANDMODULES,
+ PATCHGAMES,
+ REDIRECTAPPTHREADS,
+ PATCHVERSTRING,
+ SHOWGBABOOT,
+ PATCHUNITINFO,
+ DISABLEARM11EXCHANDLERS,
+ ENABLESAFEFIRMROSALINA,
+};
+
+extern u32 config, multiConfig, bootConfig;
+
+void readLumaConfig(void);
bool hasKExt(void);
u32 getKExtSize(void);
u32 getStolenSystemMemRegionSize(void);
diff --git a/sysmodules/pm/source/main.c b/sysmodules/pm/source/main.c
index 9b7fedc..78f4914 100644
--- a/sysmodules/pm/source/main.c
+++ b/sysmodules/pm/source/main.c
@@ -13,6 +13,7 @@
#include "util.h"
#include "my_thread.h"
#include "service_manager.h"
+#include "luma.h"
static MyThread processMonitorThread, taskRunnerThread;
static u8 ALIGN(8) processDataBuffer[0x40 * sizeof(ProcessData)] = {0};
@@ -34,6 +35,7 @@ Result __sync_init(void);
void initSystem()
{
__sync_init();
+ readLumaConfig();
//__libc_init_array();
// Wait for sm
diff --git a/sysmodules/pm/source/reslimit.c b/sysmodules/pm/source/reslimit.c
index 4dad971..b057257 100644
--- a/sysmodules/pm/source/reslimit.c
+++ b/sysmodules/pm/source/reslimit.c
@@ -308,6 +308,11 @@ Result resetAppMemLimit(void)
Result setAppCpuTimeLimit(s64 limit)
{
+ // Prevent apps from enabling preemption on core1 (and kernel will
+ // redirect apps threads from core 1 to 2).
+ if (IS_N3DS && CONFIG(REDIRECTAPPTHREADS))
+ return 0;
+
ResourceLimitType category = RESLIMIT_CPUTIME;
return svcSetResourceLimitValues(g_manager.reslimits[0], &category, &limit, 1);
}
diff --git a/sysmodules/rosalina/data/config_template.ini b/sysmodules/rosalina/data/config_template.ini
index 3e7158b..9b521fa 100644
Binary files a/sysmodules/rosalina/data/config_template.ini and b/sysmodules/rosalina/data/config_template.ini differ
diff --git a/sysmodules/rosalina/source/menus/miscellaneous.c b/sysmodules/rosalina/source/menus/miscellaneous.c
index 7697779..70055dd 100644
--- a/sysmodules/rosalina/source/menus/miscellaneous.c
+++ b/sysmodules/rosalina/source/menus/miscellaneous.c
@@ -49,6 +49,7 @@ enum singleOptions
USEEMUFIRM,
LOADEXTFIRMSANDMODULES,
PATCHGAMES,
+ REDIRECTAPPTHREADS,
PATCHVERSTRING,
SHOWGBABOOT,
PATCHUNITINFO,
@@ -315,7 +316,8 @@ static size_t saveLumaIniConfigToStr(char *out, const CfgData *cfg)
(int)cfg->formatVersionMajor, (int)cfg->formatVersionMinor,
(int)CONFIG(AUTOBOOTEMU), (int)CONFIG(USEEMUFIRM),
(int)CONFIG(LOADEXTFIRMSANDMODULES), (int)CONFIG(PATCHGAMES),
- (int)CONFIG(PATCHVERSTRING), (int)CONFIG(SHOWGBABOOT),
+ (int)CONFIG(REDIRECTAPPTHREADS), (int)CONFIG(PATCHVERSTRING),
+ (int)CONFIG(SHOWGBABOOT),
1 + (int)MULTICONFIG(DEFAULTEMU), 4 - (int)MULTICONFIG(BRIGHTNESS),
splashPosStr, (unsigned int)cfg->splashDurationMsec,