Skip to content

Instantly share code, notes, and snippets.

@ZeroSkill1
Last active January 11, 2025 14:42
Show Gist options
  • Save ZeroSkill1/061de41abd9fc425b2355a638fba421b to your computer and use it in GitHub Desktop.
Save ZeroSkill1/061de41abd9fc425b2355a638fba421b to your computer and use it in GitHub Desktop.
DSP patch to signal custom handle on interrupt
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <3ds.h>
typedef struct arg {
Handle exit;
Handle interrupt;
int fire_count;
} arg;
void tmain (void *a) {
arg *ins = (arg *)a;
Handle handles[2] = { ins->interrupt, ins->exit };
while (true) {
s32 idx = -1;
Result res = svcWaitSynchronizationN(&idx, handles, 2, false, -1);
if (R_SUCCEEDED(res)) {
if (idx == 0) // interrupt
++ins->fire_count;
else if (idx == 1) // break
break;
}
}
threadExit(0);
}
Result dsp_GetCustomHandle(Handle dsp, Handle *out) {
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0xFFFF, 2, 0);
cmdbuf[1] = 123;
Result res = svcSendSyncRequest(dsp);
if (R_FAILED(res)) return res;
res = cmdbuf[1];
if (cmdbuf[1] == 124) {
*out = cmdbuf[3];
}
return res;
}
int main(int argc, char* argv[])
{
gfxInitDefault();
consoleInit(GFX_TOP, NULL);
Handle dsp = 0, custom = 0;
Result res = srvGetServiceHandle(&dsp, "dsp::DSP");
printf("get dsp::DSP %08lX\n", res);
res = dsp_GetCustomHandle(dsp, &custom);
printf("get custom interrupt event %08lX\n", res);
svcCloseHandle(dsp);
Handle exit_evt = 0;
res = svcCreateEvent(&exit_evt, RESET_ONESHOT);
printf("create thread exit event %08lX\n", res);
arg a;
a.exit = exit_evt;
a.interrupt = custom;
a.fire_count = 0;
Thread t = threadCreate(&tmain, &a, 0x200, 0x30 - 1, -2, true);
printf("NOTE: suspend and unsuspend this app\nby using the home button so the event fires\n");
while (aptMainLoop())
{
gspWaitForVBlank();
gfxSwapBuffers();
hidScanInput();
// Your code goes here
u32 kDown = hidKeysDown();
if (kDown & KEY_START) {
svcSignalEvent(a.exit);
threadJoin(t, 5000000000);
svcCloseHandle(a.exit);
break; // break in order to return to hbmenu
}
printf("\rfire count: %d", a.fire_count);
}
gfxExit();
return 0;
}
; vim: filetype=arm
.3ds
.open "code.bin", "patched_code.bin", 0x00100000
; this version of the patch is way simpler. it just returns the actual handle the DSP service binds to the interrupt
.org 0x001079C4
ldr r0, [r4, #4]
cmp r0, #123 ; if the caller didn't use our magic, we continue as usual
beq custom_return
mov r0, #0x40
str r0, [r4]
adr r0, bad_result
ldr r0, [r0]
b 0x1053FC
custom_return: ; otherwise, return the handle
adr r0, ret_header
ldr r0, [r0]
str r0, [r4]
mov r0, #0
str r0, [r4, #8]
adr r0, handle1
ldr r0, [r0]
ldr r0, [r0]
str r0, [r4, #12]
mov r0, #124
b 0x1053FC ; return as usual
handle1:
dcd 0x00109288
ret_header:
dcd 0x007C0042
bad_result:
dcd 0xD900182F
.org 0x00105458 ; ipc handler default switch branch
b 0x1079C4 ; replace the "invalid ipc command" code with our own
nop
nop
nop
.close
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment