/*----------------------------------------------------------------- boot.c BootLoader Loads a file into memory and runs it All resetMemory and startBinary functions are based on the MultiNDS loader by Darkain. Original source available at: http://cvs.sourceforge.net/viewcvs.py/ndslib/ndslib/examples/loader/boot/main.cpp License: Copyright (C) 2005 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. If you use this code, please give due credit and email me about your project at chishm@hotmail.com Helpful information: This code runs from VRAM bank C on ARM7 ------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include "fat.h" #include "card.h" #include "boot.h" #include "sdmmc.h" #include "tonccpy.h" #include "../../arm9/common/launcherData.h" void arm7clearRAM(); //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Important things #define TEMP_MEM 0x02FFD000 #define TEMP_LAUNCHMEM 0x02FFDFF0 #define TWL_HEAD 0x02FFE000 #define NDS_HEAD 0x02FFFE00 #define NDS_HEAD_POKEMON 0x02FFF000 #define TEMP_ARM9_START_ADDRESS (*(vu32*)0x02FFFFF4) tTWLHeader* ntrHeader; tTWLHeader* twlHeader; const char* bootName = "BOOT.NDS"; extern unsigned long _start; extern unsigned long storedFileCluster; extern unsigned long initDisc; extern unsigned long wantToPatchDLDI; extern unsigned long argStart; extern unsigned long argSize; extern unsigned long dsiSD; extern unsigned long dsiMode; volatile u16 scfgunlock = 0; volatile u16 twlmode = 0; volatile u16 twlclk = 0; volatile u16 twlvram = 0; volatile u16 twlram = 0; volatile u32 chipID = 0; static volatile u32 ROM_TID = 0; static void setTWLMBK() { *(vu32*)REG_MBK1 = *(u32*)0x02FFE180; *(vu32*)REG_MBK2 = *(u32*)0x02FFE184; *(vu32*)REG_MBK3 = *(u32*)0x02FFE188; *(vu32*)REG_MBK4 = *(u32*)0x02FFE18C; *(vu32*)REG_MBK5 = *(u32*)0x02FFE190; REG_MBK6 = *(u32*)0x02FFE1A0; REG_MBK7 = *(u32*)0x02FFE1A4; REG_MBK8 = *(u32*)0x02FFE1A8; REG_MBK9 = *(u32*)0x02FFE1AC; } const char* getRomTid(const tNDSHeader* ndsHeader) { static char romTid[5]; strncpy(romTid, ndsHeader->gameCode, 4); romTid[4] = '\0'; return romTid; } //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Firmware stuff #define FW_READ 0x03 void mpu_reset(); void mpu_reset_end(); static char boot_nds[] = "fat:/boot.nds"; static unsigned long argbuf[4]; #ifndef NO_SDMMC int sdmmc_sd_readsectors(u32 sector_no, u32 numsectors, void *out); //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Main function bool sdmmc_inserted() { return true; } bool sdmmc_startup() { sdmmc_controller_init(true); return sdmmc_sdcard_init() == 0; } bool sdmmc_readsectors(u32 sector_no, u32 numsectors, void *out) { return sdmmc_sdcard_readsectors(sector_no, numsectors, out) == 0; } #endif /*------------------------------------------------------------------------- passArgs_ARM7 Copies the command line arguments to the end of the ARM9 binary, then sets a flag in memory for the loaded NDS to use --------------------------------------------------------------------------*/ void passArgs_ARM7 (void) { u32 ARM9_DST = *((u32*)(NDS_HEAD + 0x028)); u32 ARM9_LEN = *((u32*)(NDS_HEAD + 0x02C)); u32* argSrc; u32* argDst; if (!argStart || !argSize) { char *arg = boot_nds; argSize = __builtin_strlen(boot_nds); if (dsiSD) { arg++; arg[0] = 's'; arg[1] = 'd'; } __builtin_memcpy(argbuf,arg,argSize+1); argSrc = argbuf; } else { argSrc = (u32*)(argStart + (int)&_start); } if ( ARM9_DST == 0 && ARM9_LEN == 0) { ARM9_DST = *((u32*)(NDS_HEAD + 0x038)); ARM9_LEN = *((u32*)(NDS_HEAD + 0x03C)); } argDst = (u32*)((ARM9_DST + ARM9_LEN + 3) & ~3); // Word aligned if ((twlmode > 0) && dsiMode && (*(u8*)(NDS_HEAD + 0x012) & BIT(1))) { u32 ARM9i_DST = *((u32*)(TWL_HEAD + 0x1C8)); u32 ARM9i_LEN = *((u32*)(TWL_HEAD + 0x1CC)); if (ARM9i_LEN) { u32* argDst2 = (u32*)((ARM9i_DST + ARM9i_LEN + 3) & ~3); // Word aligned if (argDst2 > argDst)argDst = argDst2; } } // copyLoop(argDst, argSrc, argSize); tonccpy(argDst, argSrc, argSize); __system_argv->argvMagic = ARGV_MAGIC; __system_argv->commandLine = (char*)argDst; __system_argv->length = argSize; } /*------------------------------------------------------------------------- resetMemory_ARM7 Clears all of the NDS's RAM that is visible to the ARM7 Written by Darkain. Modified by Chishm: * Added STMIA clear mem loop --------------------------------------------------------------------------*/ void resetMemory_ARM7(void) { int i, reg; REG_IME = 0; for (i=0; i<16; i++) { SCHANNEL_CR(i) = 0; SCHANNEL_TIMER(i) = 0; SCHANNEL_SOURCE(i) = 0; SCHANNEL_LENGTH(i) = 0; } REG_SOUNDCNT = 0; REG_SNDCAP0CNT = 0; REG_SNDCAP1CNT = 0; REG_SNDCAP0DAD = 0; REG_SNDCAP0LEN = 0; REG_SNDCAP1DAD = 0; REG_SNDCAP1LEN = 0; // Clear out ARM7 DMA channels and timers for (i=0; i<4; i++) { DMA_CR(i) = 0; DMA_SRC(i) = 0; DMA_DEST(i) = 0; TIMER_CR(i) = 0; TIMER_DATA(i) = 0; if ((twlmode > 0) || dsiMode) { for (reg=0; reg<0x1c; reg+=4)*((u32*)(0x04004104 + ((i*0x1c)+reg))) = 0; //Reset NDMA. } } REG_RCNT = 0; // Clear out FIFO REG_IPC_SYNC = 0; REG_IPC_FIFO_CR = IPC_FIFO_ENABLE | IPC_FIFO_SEND_CLEAR; REG_IPC_FIFO_CR = 0; arm7clearRAM(); // clear most of EXRAM toncset((u8*)0x02000000, 0, 0x3FC000); // clear more of EXRAM, skipping the arm9 temp area used by bootloader REG_IE = 0; REG_IF = ~0; REG_AUXIE = 0; REG_AUXIF = ~0; (*(vu32*)(0x04000000-4)) = 0; //IRQ_HANDLER ARM7 version (*(vu32*)(0x04000000-8)) = ~0; //VBLANK_INTR_WAIT_FLAGS, ARM7 version REG_POWERCNT = 1; //turn off power to stuffs } static void XMenuFix(const tNDSHeader* ndsHeader) { *((vu8*)0x02FFFF70) = 0x91; if ((twlram > 0) && !(ndsHeader->unitCode & BIT(1)))*((vu8*)0x027FFF70) = 0x91; } static void setMemoryAddressTWL(const tNDSHeader* ndsHeader) { if (ndsHeader->unitCode & BIT(1)) { // copyLoop((u32*)0x027FFA80, (u32*)ndsHeader, 0x160); // Make a duplicate of DS header tonccpy((u32*)0x027FFA80, (u32*)ndsHeader, 0x160); // Make a duplicate of DS header *(u32*)(0x027FA680) = 0x02FD4D80; *(u32*)(0x027FA684) = 0x00000000; *(u32*)(0x027FA688) = 0x00001980; *(u32*)(0x027FF00C) = 0x0000007F; *(u32*)(0x027FF010) = 0x550E25B8; *(u32*)(0x027FF014) = 0x02FF4000; // Set region flag if (strncmp(getRomTid(ndsHeader)+3, "J", 1) == 0) { *(u8*)(0x027FFD70) = 0; } else if (strncmp(getRomTid(ndsHeader)+3, "E", 1) == 0) { *(u8*)(0x027FFD70) = 1; } else if (strncmp(getRomTid(ndsHeader)+3, "P", 1) == 0) { *(u8*)(0x027FFD70) = 2; } else if (strncmp(getRomTid(ndsHeader)+3, "U", 1) == 0) { *(u8*)(0x027FFD70) = 3; } else if (strncmp(getRomTid(ndsHeader)+3, "C", 1) == 0) { *(u8*)(0x027FFD70) = 4; } else if (strncmp(getRomTid(ndsHeader)+3, "K", 1) == 0) { *(u8*)(0x027FFD70) = 5; } } // Set memory values expected by loaded NDS // from NitroHax, thanks to Chism *((u32*)0x027FF800) = *(u32*)0x02FFFC00; // CurrentCardID // *((u32*)0x027FF804) = *(u32*)0x02FFFC00; // Command10CardID *((u16*)0x027FF808) = ndsHeader->headerCRC16; // Header Checksum, CRC-16 of [000h-15Dh] *((u16*)0x027FF80A) = ndsHeader->secureCRC16; // Secure Area Checksum, CRC-16 of [ [20h]..7FFFh] *((u16*)0x027FF850) = 0x5835; // Copies of above *((u32*)0x027FFC00) = *(u32*)0x02FFFC00; // CurrentCardID // *((u32*)0x027FFC04) = *(u32*)0x02FFFC00; // Command10CardID *((u16*)0x027FFC08) = ndsHeader->headerCRC16; // Header Checksum, CRC-16 of [000h-15Dh] *((u16*)0x027FFC0A) = ndsHeader->secureCRC16; // Secure Area Checksum, CRC-16 of [ [20h]..7FFFh] *((u16*)0x027FFC10) = 0x5835; *((u16*)0x027FFC40) = 0x01; // Boot Indicator -- EXTREMELY IMPORTANT!!! Thanks to cReDiAr tonccpy((u32*)0x027FC000, (u32*)0x02FFC000, 0x1000); tonccpy((u32*)0x027FF000, (u32*)NDS_HEAD_POKEMON, 0x170); tonccpy((u32*)0x027FFE00, (u32*)NDS_HEAD, 0x160); tonccpy((u32*)0x027FE000, (u32*)TWL_HEAD, 0x1000); tonccpy((u32*)0x027FF830, (u32*)0x02FFF830, 0x20); tonccpy((u32*)0x027FFC80, (u32*)0x02FFFC80, 0x70); tonccpy((u32*)0x027FFD80, (u32*)0x02FFFD80, 0x70); } static void setMemoryAddress(const tNDSHeader* ndsHeader) { if (ndsHeader->unitCode & BIT(1)) { // copyLoop((u32*)0x02FFFA80, (u32*)ndsHeader, 0x160); // Make a duplicate of DS header tonccpy((u32*)0x02FFFA80, (u32*)ndsHeader, 0x160); // Make a duplicate of DS header *(u32*)(0x02FFA680) = 0x02FD4D80; *(u32*)(0x02FFA684) = 0x00000000; *(u32*)(0x02FFA688) = 0x00001980; *(u32*)(0x02FFF00C) = 0x0000007F; *(u32*)(0x02FFF010) = 0x550E25B8; *(u32*)(0x02FFF014) = 0x02FF4000; // Set region flag if (strncmp(getRomTid(ndsHeader)+3, "J", 1) == 0) { *(u8*)(0x02FFFD70) = 0; } else if (strncmp(getRomTid(ndsHeader)+3, "E", 1) == 0) { *(u8*)(0x02FFFD70) = 1; } else if (strncmp(getRomTid(ndsHeader)+3, "P", 1) == 0) { *(u8*)(0x02FFFD70) = 2; } else if (strncmp(getRomTid(ndsHeader)+3, "U", 1) == 0) { *(u8*)(0x02FFFD70) = 3; } else if (strncmp(getRomTid(ndsHeader)+3, "C", 1) == 0) { *(u8*)(0x02FFFD70) = 4; } else if (strncmp(getRomTid(ndsHeader)+3, "K", 1) == 0) { *(u8*)(0x02FFFD70) = 5; } } // Fix Pokemon games needing header data. // copyLoop((u32*)NDS_HEAD_POKEMON, (u32*)NDS_HEAD, 0x170); tonccpy((u32*)NDS_HEAD_POKEMON, (u32*)NDS_HEAD, 0x170); // Set memory values expected by loaded NDS // from NitroHax, thanks to Chism *((u32*)0x02FFF800) = chipID; // CurrentCardID // *((u32*)0x02FFF804) = chipID; // Command10CardID *((u16*)0x02FFF808) = ndsHeader->headerCRC16; // Header Checksum, CRC-16 of [000h-15Dh] *((u16*)0x02FFF80A) = ndsHeader->secureCRC16; // Secure Area Checksum, CRC-16 of [ [20h]..7FFFh] *((u16*)0x02FFF850) = 0x5835; // Copies of above *((u32*)0x02FFFC00) = chipID; // CurrentCardID // *((u32*)0x02FFFC04) = chipID; // Command10CardID *((u16*)0x02FFFC08) = ndsHeader->headerCRC16; // Header Checksum, CRC-16 of [000h-15Dh] *((u16*)0x02FFFC0A) = ndsHeader->secureCRC16; // Secure Area Checksum, CRC-16 of [ [20h]..7FFFh] *((u16*)0x02FFFC10) = 0x5835; *((u16*)0x02FFFC40) = 0x01; // Boot Indicator -- EXTREMELY IMPORTANT!!! Thanks to cReDiAr switch (*((vu16*)0x02FFFF5E)) { case 0xF63D: XMenuFix(ndsHeader); break; case 0x0695: XMenuFix(ndsHeader); break; case 0xE4C4: XMenuFix(ndsHeader); break; case 0x918C: XMenuFix(ndsHeader); break; } if ((twlram > 0) && !(ndsHeader->unitCode & BIT(1)))setMemoryAddressTWL(ndsHeader); } static u8 readwriteSPI(u8 data) { REG_SPIDATA = data; SerialWaitBusy(); return REG_SPIDATA; } //--------------------------------------------------------------------------------- void readFirmware(u32 address, void * destination, u32 size) { //--------------------------------------------------------------------------------- int oldIME=enterCriticalSection(); u8 *buffer = destination; // Read command REG_SPICNT = SPI_ENABLE | SPI_BYTE_MODE | SPI_CONTINUOUS | SPI_DEVICE_FIRMWARE; readwriteSPI(FIRMWARE_READ); // Set the address readwriteSPI((address>>16) & 0xFF); readwriteSPI((address>> 8) & 0xFF); readwriteSPI((address) & 0xFF); u32 i; // Read the data for(i=0;ilanguage != 6 && ndsHeader->reserved1[8] == 0x80) { ndsHeader->reserved1[8] = 0; // Patch iQue game to be region-free ndsHeader->headerCRC16 = swiCRC16(0xFFFF, ndsHeader, 0x15E); // Fix CRC } } void loadBinary_ARM7 (u32 fileCluster) { // read NDS header fileRead ((char*)NDS_HEAD, fileCluster, 0, 0x170); // Load binaries into memory fileRead((char*)ntrHeader->arm9destination, fileCluster, ntrHeader->arm9romOffset, ntrHeader->arm9binarySize); fileRead((char*)ntrHeader->arm7destination, fileCluster, ntrHeader->arm7romOffset, ntrHeader->arm7binarySize); // first copy the header to its proper location, excluding // the ARM9 start address, so as not to start it TEMP_ARM9_START_ADDRESS = ntrHeader->arm9executeAddress; // Store for later *(u32*)ntrHeader->arm9executeAddress = 0; dmaCopyWords(3, (void*)ntrHeader, (void*)NDS_HEAD, 0x170); ROM_TID = *(u32*)0x02FFFE0C; setMemoryAddress((tNDSHeader*)ntrHeader); if ((twlram > 0) && (ntrHeader->unitCode & BIT(1))) { // Read full TWL header fileRead((char*)TWL_HEAD, fileCluster, 0, 0x1000); tonccpy((void*)0x02FFC000, (void*)TWL_HEAD, 0x1000); if (twlHeader->arm9ibinarySize > 0)fileRead((char*)twlHeader->arm9idestination, fileCluster, twlHeader->arm9iromOffset, twlHeader->arm9ibinarySize); if (twlHeader->arm7ibinarySize > 0)fileRead((char*)twlHeader->arm7idestination, fileCluster, twlHeader->arm7iromOffset, twlHeader->arm7ibinarySize); if (twlmode > 0) { setTWLMBK(); if (twlHeader->unitCode == 0x02)scfgunlock = 0; } } my_readUserSettings((tNDSHeader*)ntrHeader); } /*------------------------------------------------------------------------- startBinary_ARM7 Jumps to the ARM7 NDS binary in sync with the display and ARM9 Written by Darkain. Modified by Chishm: * Removed MultiNDS specific stuff --------------------------------------------------------------------------*/ void startBinary_ARM7 (void) { REG_IME=0; while(REG_VCOUNT!=191); while(REG_VCOUNT==191); // copy NDS ARM9 start address into the header, starting ARM9 *((vu32*)0x02FFFE24) = TEMP_ARM9_START_ADDRESS; ARM9_START_FLAG = 1; // Start ARM7 VoidFn arm7code = *(VoidFn*)(0x2FFFE34); arm7code(); } int main (void) { ntrHeader = (tTWLHeader*)NDS_HEAD; twlHeader = (tTWLHeader*)TWL_HEAD; tLauncherSettings* tmpData = (tLauncherSettings*)LAUNCH_DATA; if (tmpData->scfgUnlock > 0x00)scfgunlock = 0xFFFF; if (tmpData->twlMode > 0x00)twlmode = 0xFFFF; if (tmpData->twlCLK > 0x00)twlclk = 0xFFFF; if (tmpData->twlRAM > 0x00)twlram = 0xFFFF; if (tmpData->twlVRAM > 0x00)twlvram = 0xFFFF; if (tmpData->cachedChipID > 0x00)chipID = tmpData->cachedChipID; if ((twlmode == 0) && (twlram == 0)) { REG_MBK9=0xFCFFFF0F; REG_MBK6=0x09403900; REG_MBK7=0x09803940; REG_MBK8=0x09C03980; } #ifdef NO_DLDI dsiSD = true; dsiMode = true; #endif #ifndef NO_SDMMC if (dsiSD && dsiMode) { _io_dldi.fn_readSectors = sdmmc_readsectors; _io_dldi.fn_isInserted = sdmmc_inserted; _io_dldi.fn_startup = sdmmc_startup; } #endif u32 fileCluster = storedFileCluster; // Init card if(!FAT_InitFiles(initDisc))return -1; /* Invalid file cluster specified */ if ((fileCluster < CLUSTER_FIRST) || (fileCluster >= CLUSTER_EOF))fileCluster = getBootFileCluster(bootName); if (fileCluster == CLUSTER_FREE)return -1; // ARM9 clears its memory part 2 // copy ARM9 function to RAM, and make the ARM9 jump to it tonccpy((void*)TEMP_MEM, (void*)resetMemory2_ARM9, resetMemory2_ARM9_size); (*(vu32*)0x02FFFE24) = (u32)TEMP_MEM; // Make ARM9 jump to the function // Wait until the ARM9 has completed its task while ((*(vu32*)0x02FFFE24) == (u32)TEMP_MEM); // ARM9 sets up mpu // copy ARM9 function to RAM, and make the ARM9 jump to it tonccpy((void*)TEMP_MEM, (void*)mpu_reset, mpu_reset_end - mpu_reset); (*(vu32*)0x02FFFE24) = (u32)TEMP_MEM; // Make ARM9 jump to the function // Wait until the ARM9 has completed its task while ((*(vu32*)0x02FFFE24) == (u32)TEMP_MEM); // Get ARM7 to clear RAM resetMemory_ARM7(); // ARM9 enters a wait loop // copy ARM9 function to RAM, and make the ARM9 jump to it tonccpy((void*)TEMP_MEM, (void*)startBinary_ARM9, startBinary_ARM9_size); (*(vu32*)0x02FFFE24) = (u32)TEMP_MEM; // Make ARM9 jump to the function // Load the NDS file loadBinary_ARM7(fileCluster); // Fix for Pictochat and DLP if (ROM_TID == 0x41444E48 || ROM_TID == 0x41454E48 || ROM_TID == 0x43444E48 || ROM_TID == 0x43454E48 || ROM_TID == 0x4B444E48 || ROM_TID == 0x4B454E48) { (*(vu16*)0x02FFFCFA) = 0x1041; // NoCash: channel ch1+7+13 } #ifndef NO_SDMMC if (dsiSD && dsiMode) { sdmmc_controller_init(true); *(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xFFFDu; *(vu16*)(SDMMC_BASE + REG_SDDATACTL) &= 0xFFDDu; *(vu16*)(SDMMC_BASE + REG_SDBLKLEN32) = 0; } #endif // Pass command line arguments to loaded program passArgs_ARM7(); if (twlmode > 0) { // REG_SCFG_EXT = 0x92FBFB06; // REG_SCFG_EXT = 0x8307F100; REG_SCFG_EXT = 0x93FFFB06; } else { REG_SCFG_EXT = 0x92A40000; // REG_SCFG_EXT |= BIT(18); REG_SCFG_ROM = 0x703; } if (twlclk > 0) { REG_SCFG_CLK = 0x187; } else { REG_SCFG_CLK = 0x107; } if (scfgunlock == 0)REG_SCFG_EXT &= ~(1UL << 31); startBinary_ARM7(); return 0; }