From 8084f01c55393a362313d0b52547f8d2d4e93057 Mon Sep 17 00:00:00 2001
From: Augusto Daniele <12686734+tasken@users.noreply.github.com>
Date: Mon, 5 Oct 2026 00:35:30 -0300
Subject: [PATCH 1/2] Initialize flashcards on ARM7 first
---
arm7/Makefile | 8 +-
arm7/source/card_init.c | 151 ++++++++++++++++++++++++++++++++
arm7/source/card_init.h | 8 ++
arm7/source/main.c | 10 ++-
arm9/source/driveOperations.cpp | 24 ++++-
arm9/source/flashcard_init.h | 9 ++
arm9/source/read_card.c | 13 ++-
arm9/source/read_card.h | 1 +
8 files changed, 218 insertions(+), 6 deletions(-)
create mode 100644 arm7/source/card_init.c
create mode 100644 arm7/source/card_init.h
create mode 100644 arm9/source/flashcard_init.h
diff --git a/arm7/Makefile b/arm7/Makefile
index ba531ad..2de8709 100644
--- a/arm7/Makefile
+++ b/arm7/Makefile
@@ -20,7 +20,7 @@ include $(DEVKITARM)/ds_rules
TARGET := GodMode9i
BUILD := build
SOURCES := source
-INCLUDES := include build
+INCLUDES := include build ../arm9/source
#---------------------------------------------------------------------------------
# options for code generation
@@ -50,6 +50,8 @@ LIBS := -lnds7
#---------------------------------------------------------------------------------
LIBDIRS := $(LIBNDS)
+vpath encryption.c $(SHARED_ARM9_SOURCE)
+vpath tonccpy.c $(SHARED_BOOTLOADER_SOURCE)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
@@ -59,8 +61,10 @@ ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir))
+export SHARED_ARM9_SOURCE := $(CURDIR)/../arm9/source
+export SHARED_BOOTLOADER_SOURCE := $(CURDIR)/../bootloader/source
-CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
+CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) encryption.c tonccpy.c
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
diff --git a/arm7/source/card_init.c b/arm7/source/card_init.c
new file mode 100644
index 0000000..b6539d0
--- /dev/null
+++ b/arm7/source/card_init.c
@@ -0,0 +1,151 @@
+/*
+ NitroHax -- Cheat tool for the Nintendo DS
+ Copyright (C) 2008 Michael "Chishm" Chisholm
+
+ 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 .
+
+*/
+
+#include "card_init.h"
+#include "encryption.h"
+#include "flashcard_init.h"
+#include
+#include
+
+static struct {
+ u32 iii, jjj, kkkkk, llll, mmm, nnn;
+} key1data;
+
+static void initKey1Command(u8* command) {
+ key1data.iii = rand() & 0xFFF;
+ key1data.jjj = rand() & 0xFFF;
+ key1data.kkkkk = rand() & 0xFFFFF;
+ key1data.llll = rand() & 0xFFFF;
+ key1data.mmm = rand() & 0xFFF;
+ key1data.nnn = rand() & 0xFFF;
+
+ command[7] = CARD_CMD_ACTIVATE_BF;
+ command[6] = key1data.iii >> 4;
+ command[5] = (key1data.iii << 4) | (key1data.jjj >> 8);
+ command[4] = key1data.jjj;
+ command[3] = key1data.kkkkk >> 16;
+ command[2] = key1data.kkkkk >> 8;
+ command[1] = key1data.kkkkk;
+ command[0] = rand();
+}
+
+static void encryptedCommand(u8 code, u8* command, u32 block) {
+ u32 iii = key1data.iii;
+ u32 jjj = key1data.jjj;
+ if (code != CARD_CMD_SECURE_READ)
+ block = key1data.llll;
+ if (code == CARD_CMD_ACTIVATE_SEC) {
+ iii = key1data.mmm;
+ jjj = key1data.nnn;
+ }
+
+ command[7] = code | (block >> 12);
+ command[6] = block >> 4;
+ command[5] = (block << 4) | (iii >> 8);
+ command[4] = iii;
+ command[3] = jjj >> 4;
+ command[2] = (jjj << 4) | (key1data.kkkkk >> 16);
+ command[1] = key1data.kkkkk >> 8;
+ command[0] = key1data.kkkkk;
+ crypt_64bit_up((u32*)command);
+ key1data.kkkkk++;
+}
+
+static void cardDelay(u16 timeout) {
+ TIMER_DATA(0) = 0 - ((timeout & 0x3FFF) + 3);
+ TIMER_CR(0) = TIMER_DIV_256 | TIMER_ENABLE;
+ while (TIMER_DATA(0) != 0xFFFF);
+ TIMER_CR(0) = 0;
+ TIMER_DATA(0) = 0;
+}
+
+u32 initFlashcardArm7(void) {
+ static const u8 seedBytes[] = {0xE8, 0x4D, 0x5A, 0xB1, 0x17, 0x8F, 0x99, 0xD5};
+ static u32 headerData[0x200 / sizeof(u32)];
+ tNDSHeader* header = (tNDSHeader*)headerData;
+ u8 command[8] __attribute__((aligned(4)));
+
+ cardReadHeader((u8*)headerData);
+ if (header->headerCRC16 != swiCRC16(0xFFFF, headerData, 0x15E))
+ return FLASHCARD_INIT_BAD_HEADER;
+
+ u32 chipId = cardReadID(CARD_CLK_SLOW);
+ while (REG_ROMCTRL & CARD_BUSY);
+ bool normalChip = (chipId & BIT(31)) != 0;
+ u32 gameCode;
+ memcpy(&gameCode, header->gameCode, sizeof(gameCode));
+ init_keycode(gameCode, 2, 8, NTR_CARD_KEY);
+
+ u32 key1Flags = CARD_ACTIVATE | CARD_nRESET |
+ (header->cardControl13 & (CARD_WR | CARD_CLK_SLOW)) |
+ ((header->cardControlBF & (CARD_CLK_SLOW | CARD_DELAY1(0x1FFF))) +
+ ((header->cardControlBF & CARD_DELAY2(0x3F)) >> 16));
+ if (!normalChip)
+ key1Flags |= CARD_SEC_LARGE;
+
+ initKey1Command(command);
+ cardPolledTransfer((header->cardControl13 & (CARD_WR | CARD_nRESET | CARD_CLK_SLOW)) |
+ CARD_ACTIVATE, NULL, 0, command);
+ encryptedCommand(CARD_CMD_ACTIVATE_SEC, command, 0);
+ if (normalChip) {
+ cardPolledTransfer(key1Flags, NULL, 0, command);
+ cardDelay(header->readTimeout);
+ }
+ cardPolledTransfer(key1Flags, NULL, 0, command);
+
+ REG_ROMCTRL = 0;
+ REG_CARD_1B0 = seedBytes[header->deviceType & 7] |
+ (key1data.nnn << 15) | (key1data.mmm << 27) | 0x6000;
+ REG_CARD_1B4 = 0x879B9B05;
+ REG_CARD_1B8 = key1data.mmm >> 5;
+ REG_CARD_1BA = 0x5C;
+ REG_ROMCTRL = CARD_nRESET | CARD_SEC_SEED | CARD_SEC_EN | CARD_SEC_DAT;
+ key1Flags |= CARD_SEC_EN | CARD_SEC_DAT;
+
+ encryptedCommand(CARD_CMD_SECURE_CHIPID, command, 0);
+ if (normalChip) {
+ cardPolledTransfer(key1Flags, NULL, 0, command);
+ cardDelay(header->readTimeout);
+ }
+ cardPolledTransfer(key1Flags | CARD_BLK_SIZE(7), NULL, 0, command);
+
+ u32 secureFlags = (header->cardControlBF &
+ (CARD_CLK_SLOW | CARD_DELAY1(0x1FFF) | CARD_DELAY2(0x3F))) |
+ CARD_ACTIVATE | CARD_nRESET | CARD_SEC_EN | CARD_SEC_DAT;
+ for (int block = 4; block < 8; block++) {
+ encryptedCommand(CARD_CMD_SECURE_READ, command, block);
+ if (normalChip) {
+ cardPolledTransfer(secureFlags, NULL, 0, command);
+ cardDelay(header->readTimeout);
+ for (int i = 0; i < 8; i++)
+ cardPolledTransfer(secureFlags | CARD_BLK_SIZE(1), NULL, 0, command);
+ } else {
+ cardPolledTransfer(secureFlags | CARD_BLK_SIZE(4) | CARD_SEC_LARGE,
+ NULL, 0, command);
+ }
+ }
+
+ encryptedCommand(CARD_CMD_DATA_MODE, command, 0);
+ if (normalChip) {
+ cardPolledTransfer(key1Flags, NULL, 0, command);
+ cardDelay(header->readTimeout);
+ }
+ cardPolledTransfer(key1Flags, NULL, 0, command);
+ return FLASHCARD_INIT_OK;
+}
diff --git a/arm7/source/card_init.h b/arm7/source/card_init.h
new file mode 100644
index 0000000..a0eed1f
--- /dev/null
+++ b/arm7/source/card_init.h
@@ -0,0 +1,8 @@
+#ifndef ARM7_CARD_INIT_H
+#define ARM7_CARD_INIT_H
+
+#include
+
+u32 initFlashcardArm7(void);
+
+#endif
diff --git a/arm7/source/main.c b/arm7/source/main.c
index aa85bde..3769d59 100644
--- a/arm7/source/main.c
+++ b/arm7/source/main.c
@@ -31,6 +31,8 @@
#include
#include "gba.h"
+#include "card_init.h"
+#include "flashcard_init.h"
#define SD_IRQ_STATUS (*(vu32*)0x400481C)
@@ -262,6 +264,13 @@ int main() {
}
resyncClock();
+ if (fifoCheckValue32(FLASHCARD_INIT_CHANNEL)) {
+ u32 command = fifoGetValue32(FLASHCARD_INIT_CHANNEL);
+ if (command == FLASHCARD_INIT_REQUEST) {
+ fifoSendValue32(FLASHCARD_INIT_CHANNEL, initFlashcardArm7());
+ }
+ }
+
// Send SD status
if(isDSiMode() || *(u16*)(0x4004700) != 0)
fifoSendValue32(FIFO_USER_04, SD_IRQ_STATUS);
@@ -282,4 +291,3 @@ int main() {
}
return 0;
}
-
diff --git a/arm9/source/driveOperations.cpp b/arm9/source/driveOperations.cpp
index dfc9830..b8a4209 100644
--- a/arm9/source/driveOperations.cpp
+++ b/arm9/source/driveOperations.cpp
@@ -25,6 +25,7 @@
#include "io_sc_common.h"
#include "exptools.h"
#include "read_card.h"
+#include "flashcard_init.h"
static sNDSHeader nds;
@@ -421,6 +422,23 @@ TWL_CODE bool twl_flashcardMount(void) {
slot1Enabled = true;
}
+ // Follow HBMenu Deluxe's ARM7-first initialization order, without a later slot reset:
+ // https://github.com/ApacheThunder/ntr-hb-menu/blob/deluxe/arm7/source/main.c
+ // https://github.com/ApacheThunder/ntr-hb-menu/blob/deluxe/arm9/include/read_card.c
+ sysSetCardOwner(BUS_OWNER_ARM7);
+ if (!fifoSendValue32(FLASHCARD_INIT_CHANNEL, FLASHCARD_INIT_REQUEST)) {
+ sysSetCardOwner(BUS_OWNER_ARM9);
+ printf("Failed to request ARM7 card initialization.\n");
+ return false;
+ }
+ fifoWaitValue32(FLASHCARD_INIT_CHANNEL);
+ u32 cardInitResult = fifoGetValue32(FLASHCARD_INIT_CHANNEL);
+ sysSetCardOwner(BUS_OWNER_ARM9);
+ if (cardInitResult != FLASHCARD_INIT_OK) {
+ printf("ARM7 card initialization failed: %lu\n", (unsigned long)cardInitResult);
+ return false;
+ }
+
nds.gameCode[0] = 0;
nds.gameTitle[0] = 0;
char gamename[13];
@@ -467,7 +485,11 @@ TWL_CODE bool twl_flashcardMount(void) {
}*/
if (doCardInit) {
- cardInit((sNDSHeaderExt*)((u32*)0x02FFC000)); // Certain flashcarts require card init before DLDI will work.
+ int result = cardInitWithoutSlotReset((sNDSHeaderExt*)((u32*)0x02FFC000)); // Certain flashcarts require card init before DLDI will work.
+ if (result != 0) {
+ printf("ARM9 card initialization failed: %d\n", result);
+ return false;
+ }
for (int i = 0; i < 30; i++) swiWaitForVBlank();
if (isDSPico) picoInit(false);
}
diff --git a/arm9/source/flashcard_init.h b/arm9/source/flashcard_init.h
new file mode 100644
index 0000000..2d00b47
--- /dev/null
+++ b/arm9/source/flashcard_init.h
@@ -0,0 +1,9 @@
+#ifndef FLASHCARD_INIT_H
+#define FLASHCARD_INIT_H
+
+#define FLASHCARD_INIT_CHANNEL FIFO_USER_03
+#define FLASHCARD_INIT_REQUEST 0x54494E49 // 'INIT'
+#define FLASHCARD_INIT_OK 0
+#define FLASHCARD_INIT_BAD_HEADER 0x16
+
+#endif
diff --git a/arm9/source/read_card.c b/arm9/source/read_card.c
index f72ebb9..7f527ce 100644
--- a/arm9/source/read_card.c
+++ b/arm9/source/read_card.c
@@ -337,7 +337,7 @@ void cardTwlBlowfishInit(sNDSHeaderExt* ndsHeader) {
}
-int cardInit (sNDSHeaderExt* ndsHeader)
+static int cardInitInternal (sNDSHeaderExt* ndsHeader, bool resetSlot)
{
u32 portFlagsKey1, portFlagsSecRead;
normalChip = false; // As defined by GBAtek, normal chip secure area and header are accessed in blocks of 0x200, other chip in blocks of 0x1000
@@ -351,7 +351,8 @@ int cardInit (sNDSHeaderExt* ndsHeader)
twlBlowfish = false;
sysSetCardOwner (BUS_OWNER_ARM9); // Allow arm9 to access NDS cart
- cardDSiSlot1Reset();
+ if (resetSlot)
+ cardDSiSlot1Reset();
REG_ROMCTRL=0;
REG_AUXSPICNT=0;
@@ -520,6 +521,14 @@ int cardInit (sNDSHeaderExt* ndsHeader)
return ERR_NONE;
}
+int cardInit (sNDSHeaderExt* ndsHeader) {
+ return cardInitInternal(ndsHeader, true);
+}
+
+int cardInitWithoutSlotReset (sNDSHeaderExt* ndsHeader) {
+ return cardInitInternal(ndsHeader, false);
+}
+
u32 cardGetId() {
return iCardId;
}
diff --git a/arm9/source/read_card.h b/arm9/source/read_card.h
index 9fbb615..e6f4fc3 100644
--- a/arm9/source/read_card.h
+++ b/arm9/source/read_card.h
@@ -42,6 +42,7 @@ extern u32 cardNandRomEnd;
extern u32 cardNandRwStart;
int cardInit (sNDSHeaderExt* ndsHeader);
+int cardInitWithoutSlotReset (sNDSHeaderExt* ndsHeader);
void cardRead (u32 src, void* dest, bool nandSave);
From 793feb18e5e40ff74a10833782662a96e14feb59 Mon Sep 17 00:00:00 2001
From: Augusto Daniele <12686734+tasken@users.noreply.github.com>
Date: Mon, 5 Oct 2026 01:30:43 -0300
Subject: [PATCH 2/2] Only use ARM7 card setup when the SD card was mounted
---
arm9/source/driveOperations.cpp | 42 ++++++++++++++++++++-------------
1 file changed, 26 insertions(+), 16 deletions(-)
diff --git a/arm9/source/driveOperations.cpp b/arm9/source/driveOperations.cpp
index b8a4209..b0f4f57 100644
--- a/arm9/source/driveOperations.cpp
+++ b/arm9/source/driveOperations.cpp
@@ -29,6 +29,8 @@
static sNDSHeader nds;
+static bool sdMountAttempted = false;
+
static bool slot1Enabled = true;
bool nandMounted = false;
@@ -167,6 +169,7 @@ void nandUnmount(void) {
}
bool sdMount(void) {
+ sdMountAttempted = true;
fatMountSimple("sd", __my_io_dsisd());
if (sdFound()) {
sdMountedDone = true;
@@ -425,18 +428,20 @@ TWL_CODE bool twl_flashcardMount(void) {
// Follow HBMenu Deluxe's ARM7-first initialization order, without a later slot reset:
// https://github.com/ApacheThunder/ntr-hb-menu/blob/deluxe/arm7/source/main.c
// https://github.com/ApacheThunder/ntr-hb-menu/blob/deluxe/arm9/include/read_card.c
- sysSetCardOwner(BUS_OWNER_ARM7);
- if (!fifoSendValue32(FLASHCARD_INIT_CHANNEL, FLASHCARD_INIT_REQUEST)) {
+ if (sdMountAttempted) {
+ sysSetCardOwner(BUS_OWNER_ARM7);
+ if (!fifoSendValue32(FLASHCARD_INIT_CHANNEL, FLASHCARD_INIT_REQUEST)) {
+ sysSetCardOwner(BUS_OWNER_ARM9);
+ printf("Failed to request ARM7 card initialization.\n");
+ return false;
+ }
+ fifoWaitValue32(FLASHCARD_INIT_CHANNEL);
+ u32 cardInitResult = fifoGetValue32(FLASHCARD_INIT_CHANNEL);
sysSetCardOwner(BUS_OWNER_ARM9);
- printf("Failed to request ARM7 card initialization.\n");
- return false;
- }
- fifoWaitValue32(FLASHCARD_INIT_CHANNEL);
- u32 cardInitResult = fifoGetValue32(FLASHCARD_INIT_CHANNEL);
- sysSetCardOwner(BUS_OWNER_ARM9);
- if (cardInitResult != FLASHCARD_INIT_OK) {
- printf("ARM7 card initialization failed: %lu\n", (unsigned long)cardInitResult);
- return false;
+ if (cardInitResult != FLASHCARD_INIT_OK) {
+ printf("ARM7 card initialization failed: %lu\n", (unsigned long)cardInitResult);
+ return false;
+ }
}
nds.gameCode[0] = 0;
@@ -484,11 +489,16 @@ TWL_CODE bool twl_flashcardMount(void) {
fatMountSimple("fat", dldiGet());
}*/
- if (doCardInit) {
- int result = cardInitWithoutSlotReset((sNDSHeaderExt*)((u32*)0x02FFC000)); // Certain flashcarts require card init before DLDI will work.
- if (result != 0) {
- printf("ARM9 card initialization failed: %d\n", result);
- return false;
+ if (doCardInit) { // Certain flashcarts require card init before DLDI will work.
+ sNDSHeaderExt* cardHeader = (sNDSHeaderExt*)((u32*)0x02FFC000);
+ if (sdMountAttempted) {
+ int result = cardInitWithoutSlotReset(cardHeader);
+ if (result != 0) {
+ printf("ARM9 card initialization failed: %d\n", result);
+ return false;
+ }
+ } else {
+ cardInit(cardHeader);
}
for (int i = 0; i < 30; i++) swiWaitForVBlank();
if (isDSPico) picoInit(false);