mirror of
https://github.com/ApacheThunder/NTR_Launcher.git
synced 2025-06-18 19:15:40 -04:00

* Stage2 launcher UI added. This is a heavily modified HBMenu with ability to launch carts! This menu can be used to boot a bootloader NDS file for flascharts that the direct cart launcher fails to boot. * New audio files added for new menu. * HBMenu UI loaded by default if booted with no cart inserted. * INI file folder now located in NTR_Launcher folder instead. A default ini file from NitroFS (if mount of nitrofs succeeds) will be copied to SD if one is not present. * Stage2 launchers are expected to be in NTR Launcehr folder though you can navigate to any folder you want in the UI so stage2 launchers aren't the only thing this can be used for. You can also use this as a means of booting homebrew off SD in NTR mode.
414 lines
11 KiB
C
414 lines
11 KiB
C
/*-----------------------------------------------------------------
|
|
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 <nds/ndstypes.h>
|
|
#include <nds/dma.h>
|
|
#include <nds/system.h>
|
|
#include <nds/interrupts.h>
|
|
#include <nds/timers.h>
|
|
#include <nds/memory.h>
|
|
#include <nds/arm7/audio.h>
|
|
#include "fat.h"
|
|
#include "dldi_patcher.h"
|
|
#include "card.h"
|
|
#include "boot.h"
|
|
#include "sdmmc.h"
|
|
|
|
void arm7clearRAM();
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
// Important things
|
|
#define TEMP_MEM 0x02FFD000
|
|
#define TWL_HEAD 0x02FFE000
|
|
#define NDS_HEAD 0x02FFFE00
|
|
#define TEMP_ARM9_START_ADDRESS (*(vu32*)0x02FFFFF4)
|
|
|
|
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;
|
|
|
|
static bool scfgunlock = false;
|
|
static bool twlmode = false;
|
|
static bool twlclk = false;
|
|
static bool twlvram = false;
|
|
|
|
#define LAUNCH_DATA 0x020007F0
|
|
|
|
typedef struct sLauncherSettings {
|
|
u8 language;
|
|
u8 scfgUnlock;
|
|
u8 twlMode;
|
|
u8 twlCLK;
|
|
u8 twlVRAM;
|
|
u8 debugMode;
|
|
u8 fastBoot;
|
|
u8 unused2;
|
|
} tLauncherSettings;
|
|
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
// Firmware stuff
|
|
|
|
#define FW_READ 0x03
|
|
|
|
void boot_readFirmware (uint32 address, uint8 * buffer, uint32 size) {
|
|
uint32 index;
|
|
|
|
// Read command
|
|
while (REG_SPICNT & SPI_BUSY);
|
|
REG_SPICNT = SPI_ENABLE | SPI_CONTINUOUS | SPI_DEVICE_NVRAM;
|
|
REG_SPIDATA = FW_READ;
|
|
while (REG_SPICNT & SPI_BUSY);
|
|
|
|
// Set the address
|
|
REG_SPIDATA = (address>>16) & 0xFF;
|
|
while (REG_SPICNT & SPI_BUSY);
|
|
REG_SPIDATA = (address>>8) & 0xFF;
|
|
while (REG_SPICNT & SPI_BUSY);
|
|
REG_SPIDATA = (address) & 0xFF;
|
|
while (REG_SPICNT & SPI_BUSY);
|
|
|
|
for (index = 0; index < size; index++) {
|
|
REG_SPIDATA = 0;
|
|
while (REG_SPICNT & SPI_BUSY);
|
|
buffer[index] = REG_SPIDATA & 0xFF;
|
|
}
|
|
REG_SPICNT = 0;
|
|
}
|
|
|
|
|
|
static inline void copyLoop (u32* dest, const u32* src, u32 size) {
|
|
size = (size +3) & ~3;
|
|
do {
|
|
*dest++ = *src++;
|
|
} while (size -= 4);
|
|
}
|
|
|
|
//#define resetCpu() __asm volatile("\tswi 0x000000\n");
|
|
|
|
static char boot_nds[] = "fat:/boot.nds";
|
|
static unsigned long argbuf[4];
|
|
|
|
/*-------------------------------------------------------------------------
|
|
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 (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);
|
|
|
|
__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;
|
|
u8 settings1, settings2;
|
|
u32 settingsOffset = 0;
|
|
|
|
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;
|
|
|
|
//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;
|
|
}
|
|
|
|
arm7clearRAM();
|
|
|
|
REG_IE = 0;
|
|
REG_IF = ~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 stuff
|
|
|
|
// Get settings location
|
|
boot_readFirmware((u32)0x00020, (u8*)&settingsOffset, 0x2);
|
|
settingsOffset *= 8;
|
|
|
|
// Reload DS Firmware settings
|
|
boot_readFirmware(settingsOffset + 0x070, &settings1, 0x1);
|
|
boot_readFirmware(settingsOffset + 0x170, &settings2, 0x1);
|
|
|
|
if ((settings1 & 0x7F) == ((settings2+1) & 0x7F)) {
|
|
boot_readFirmware(settingsOffset + 0x000, (u8*)0x02FFFC80, 0x70);
|
|
} else {
|
|
boot_readFirmware(settingsOffset + 0x100, (u8*)0x02FFFC80, 0x70);
|
|
}
|
|
|
|
((vu32*)0x040044f0)[2] = 0x202DDD1D;
|
|
((vu32*)0x040044f0)[3] = 0xE1A00005;
|
|
while((*(vu32*)0x04004400) & 0x2000000);
|
|
|
|
}
|
|
|
|
|
|
void loadBinary_ARM7 (u32 fileCluster) {
|
|
u32 ndsHeader[0x170>>2];
|
|
|
|
// read NDS header
|
|
fileRead ((char*)ndsHeader, fileCluster, 0, 0x170);
|
|
// read ARM9 info from NDS header
|
|
u32 ARM9_SRC = ndsHeader[0x020>>2];
|
|
char* ARM9_DST = (char*)ndsHeader[0x028>>2];
|
|
u32 ARM9_LEN = ndsHeader[0x02C>>2];
|
|
// read ARM7 info from NDS header
|
|
u32 ARM7_SRC = ndsHeader[0x030>>2];
|
|
char* ARM7_DST = (char*)ndsHeader[0x038>>2];
|
|
u32 ARM7_LEN = ndsHeader[0x03C>>2];
|
|
|
|
// Load binaries into memory
|
|
fileRead(ARM9_DST, fileCluster, ARM9_SRC, ARM9_LEN);
|
|
fileRead(ARM7_DST, fileCluster, ARM7_SRC, ARM7_LEN);
|
|
|
|
// first copy the header to its proper location, excluding
|
|
// the ARM9 start address, so as not to start it
|
|
TEMP_ARM9_START_ADDRESS = ndsHeader[0x024>>2]; // Store for later
|
|
ndsHeader[0x024>>2] = 0;
|
|
dmaCopyWords(3, (void*)ndsHeader, (void*)NDS_HEAD, 0x170);
|
|
|
|
if (dsiMode && (ndsHeader[0x10>>2]&BIT(16+1))) {
|
|
// Read full TWL header
|
|
fileRead((char*)TWL_HEAD, fileCluster, 0, 0x1000);
|
|
|
|
u32 ARM9i_SRC = *(u32*)(TWL_HEAD+0x1C0);
|
|
char* ARM9i_DST = (char*)*(u32*)(TWL_HEAD+0x1C8);
|
|
u32 ARM9i_LEN = *(u32*)(TWL_HEAD+0x1CC);
|
|
u32 ARM7i_SRC = *(u32*)(TWL_HEAD+0x1D0);
|
|
char* ARM7i_DST = (char*)*(u32*)(TWL_HEAD+0x1D8);
|
|
u32 ARM7i_LEN = *(u32*)(TWL_HEAD+0x1DC);
|
|
|
|
if (ARM9i_LEN)fileRead(ARM9i_DST, fileCluster, ARM9i_SRC, ARM9i_LEN);
|
|
if (ARM7i_LEN)fileRead(ARM7i_DST, fileCluster, ARM7i_SRC, ARM7i_LEN);
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
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();
|
|
}
|
|
#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
|
|
void mpu_reset();
|
|
void mpu_reset_end();
|
|
|
|
int main (void) {
|
|
|
|
tLauncherSettings* tmpData = (tLauncherSettings*)LAUNCH_DATA;
|
|
|
|
if (tmpData->scfgUnlock > 0x00)scfgunlock = true;
|
|
if (tmpData->twlMode > 0x00)twlmode = true;
|
|
if (tmpData->twlCLK > 0x00)twlclk = true;
|
|
if (tmpData->twlVRAM > 0x00)twlvram = true;
|
|
|
|
if (twlmode) {
|
|
REG_MBK9=0x0300000F;
|
|
REG_MBK6=0x080037C0;
|
|
REG_MBK7=0x07C03740;
|
|
REG_MBK8=0x07403700;
|
|
} else {
|
|
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
|
|
copyLoop((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
|
|
copyLoop((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
|
|
copyLoop((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);
|
|
|
|
#ifndef NO_DLDI
|
|
// Patch with DLDI if desired
|
|
if (wantToPatchDLDI)dldiPatchBinary ((u8*)((u32*)NDS_HEAD)[0x0A], ((u32*)NDS_HEAD)[0x0B]);
|
|
#endif
|
|
|
|
#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 (twlclk) { REG_SCFG_CLK = 0x0187; } else { REG_SCFG_CLK = 0x0101; }
|
|
if (twlmode) {
|
|
REG_SCFG_EXT = 0x92FBFB06;
|
|
} else {
|
|
REG_SCFG_EXT = 0x92A00000;
|
|
REG_SCFG_ROM = 0x703;
|
|
}
|
|
if (scfgunlock) { REG_SCFG_EXT |= BIT(18); } else { REG_SCFG_EXT &= ~(1UL << 31); }
|
|
|
|
startBinary_ARM7();
|
|
|
|
return 0;
|
|
}
|
|
|