Files
pico-loader/arm9/source/main.cpp

344 lines
10 KiB
C++

#include "common.h"
#include "ApList.h"
#include <libtwl/gfx/gfx3d.h>
#include <libtwl/gfx/gfx3dCmd.h>
#include <libtwl/gfx/gfxOam.h>
#include <libtwl/gfx/gfxPalette.h>
#include <libtwl/gfx/gfxStatus.h>
#include <libtwl/ipc/ipcFifo.h>
#include <libtwl/ipc/ipcSync.h>
#include <libtwl/mem/memNtrWram.h>
#include <libtwl/mem/memTwlWram.h>
#include <libtwl/mem/memVram.h>
#include "fastClear.h"
#include "cache.h"
#include "sharedMemory.h"
#include "ndsHeader.h"
#include "logger/NocashOutputStream.h"
#include "logger/PicoAgbAdapterOutputStream.h"
#include "logger/PlainLogger.h"
#include "ipcCommands.h"
#include "Arm9IoRegisterClearer.h"
#include "Arm9Patcher.h"
#include "Arm7Patcher.h"
#include "patches/platform/LoaderPlatform.h"
#include "patches/platform/LoaderPlatformFactory.h"
#include "arm9Clock.h"
#include "errorDisplay/ErrorDisplay.h"
#include "LoaderInfo.h"
#include "jumpToArm9EntryPoint.h"
#define HANDSHAKE_PART0 0xA
#define HANDSHAKE_PART1 0xB
#define HANDSHAKE_PART2 0xC
#define HANDSHAKE_PART3 0xD
static NocashOutputStream/*PicoAgbAdapterOutputStream*/ sNocashOutput;
static PlainLogger sPlainLogger { LogLevel::All, &sNocashOutput };
ILogger* gLogger = &sPlainLogger;
static ApListEntry sApListEntry;
static LoaderPlatform* sLoaderPlatform;
static u32 sRomDirSector;
static u32 sRomDirSectorOffset;
static u16 sIsCloneBootRom;
static loader_info_t sLoaderInfo;
u16 gIsDsiMode;
// Dummy symbol to allow linking C++ applications. This is only needed to handle
// dynamic shared objects (.so), but they don't exist on the NDS.
void *__dso_handle;
static u32 receiveFromArm7()
{
while (ipc_isRecvFifoEmpty());
return ipc_recvWordDirect();
}
extern "C" void __libc_init_array();
static void clearGraphicsMemory()
{
// VRAM A used for arm9 code
mem_setVramBMapping(MEM_VRAM_AB_LCDC);
// VRAM C and D used for arm7 code
mem_setVramEMapping(MEM_VRAM_E_LCDC);
mem_setVramFMapping(MEM_VRAM_FG_LCDC);
mem_setVramGMapping(MEM_VRAM_FG_LCDC);
mem_setVramHMapping(MEM_VRAM_H_LCDC);
mem_setVramIMapping(MEM_VRAM_I_LCDC);
fastClear((void*)0x06820000, 0x20000); // VRAM B
fastClear((void*)0x06880000, 0x24000);
fastClear((void*)GFX_PLTT_BG_MAIN, 512);
fastClear((void*)GFX_PLTT_BG_SUB, 512);
fastClear((void*)GFX_PLTT_OBJ_MAIN, 512);
fastClear((void*)GFX_PLTT_OBJ_SUB, 512);
fastClear((void*)GFX_OAM_MAIN, 1024);
fastClear((void*)GFX_OAM_SUB, 1024);
// clear the vertex and polygon ram of the 3d engine
gx_init();
gx_swapBuffers(GX_XLU_SORT_AUTO, GX_DEPTH_MODE_Z);
gx_swapBuffers(GX_XLU_SORT_AUTO, GX_DEPTH_MODE_Z);
}
[[gnu::noinline, gnu::section(".itcm")]]
static void bootArm9()
{
mem_setVramAMapping(MEM_VRAM_AB_LCDC);
fastClear((void*)0x06800000, 0x20000); // VRAM A
mem_setVramAMapping(MEM_VRAM_AB_NONE);
// By now it should be safe to unmap the arm7 memory
mem_setVramCMapping(MEM_VRAM_C_LCDC);
mem_setVramDMapping(MEM_VRAM_D_LCDC);
fastClear((void*)0x06840000, 0x40000); // VRAM C and D
mem_setVramCMapping(MEM_VRAM_C_NONE);
mem_setVramDMapping(MEM_VRAM_D_NONE);
if (((REG_SCFG_EXT >> 14) & 3) == 0)
{
// When switched to DS mode, disable vram extensions
REG_SCFG_EXT &= ~(1 << 13);
}
while (gfx_getVCount() != 191);
while (gfx_getVCount() == 191);
REG_IF = ~0u; // final clear of REG_IF bits
auto romHeader = (const nds_header_ntr_t*)TWL_SHARED_MEMORY->ntrSharedMem.romHeader;
jumpToArm9EntryPoint((void*)romHeader->arm9EntryAddress);
}
static void handleInitializeSdCardCommand()
{
REG_EXMEMCNT &= ~0x0880; // map ds and gba slot to arm9
bool result = sLoaderPlatform->InitializeSdCard();
REG_EXMEMCNT |= 0x0880; // map ds and gba slot to arm7
ipc_sendWordDirect(result);
}
static void handleWramConfigCommand()
{
REG_WRAMCNT = receiveFromArm7();
REG_MBK6 = receiveFromArm7();
REG_MBK7 = receiveFromArm7();
REG_MBK8 = receiveFromArm7();
REG_MBK1 = receiveFromArm7();
REG_MBK2 = receiveFromArm7();
REG_MBK3 = receiveFromArm7();
REG_MBK4 = receiveFromArm7();
REG_MBK5 = receiveFromArm7();
ipc_sendWordDirect(1);
}
static void handleClearMainMemCommand()
{
fastClear((void*)0x02000000, 0x400000);
dc_flushAll();
dc_drainWriteBuffer();
dc_invalidateAll();
ic_invalidateAll();
ipc_sendWordDirect(1);
}
static void handleApplyArm9PatchesCommand()
{
Arm9Patcher().ApplyPatches(
sLoaderPlatform,
sApListEntry.GetGameCode() == 0 ? nullptr : &sApListEntry,
sIsCloneBootRom,
&sLoaderInfo);
ipc_sendWordDirect(1);
}
static void handleApplyArm7PatchesCommand()
{
void* patchSpaceStart = Arm7Patcher().ApplyPatches(sLoaderPlatform);
ipc_sendWordDirect((u32)patchSpaceStart);
}
static void handleSetAPInfoCommand()
{
((u32*)&sApListEntry)[0] = receiveFromArm7();
((u32*)&sApListEntry)[1] = receiveFromArm7();
((u32*)&sApListEntry)[2] = receiveFromArm7();
((u32*)&sApListEntry)[3] = receiveFromArm7();
}
static void handleSetRomFileInfoCommand()
{
sRomDirSector = receiveFromArm7();
sRomDirSectorOffset = receiveFromArm7();
sIsCloneBootRom = receiveFromArm7();
}
static void handleInitializeLoaderInfoCommand()
{
dc_invalidateRange(TWL_SHARED_MEMORY->ntrSharedMem.cardRomHeader, sizeof(loader_info_t));
memcpy(&sLoaderInfo, TWL_SHARED_MEMORY->ntrSharedMem.cardRomHeader, sizeof(loader_info_t));
dc_flushAll();
dc_drainWriteBuffer();
ipc_sendWordDirect(1);
}
static void handleGetSdFunctionsCommand()
{
mem_setNtrWramMapping(MEM_NTR_WRAM_ARM9, MEM_NTR_WRAM_ARM9);
PatchHeap patchHeap;
PatchCodeCollection patchCodeCollection;
patchHeap.AddFreeSpace((void*)0x037F8020, 16 * 1024);
{
auto sdReadPatchCode = sLoaderPlatform->CreateSdReadPatchCode(patchCodeCollection, patchHeap);
auto sdWritePatchCode = sLoaderPlatform->CreateSdWritePatchCode(patchCodeCollection, patchHeap);
*(vu32*)0x037F8000 = (u32)sdReadPatchCode->GetReadSectorsFunction();
*(vu32*)0x037F8004 = (u32)sdWritePatchCode->GetWriteSectorFunction();
patchCodeCollection.CopyAllToTarget();
}
dc_flushAll();
dc_drainWriteBuffer();
mem_setNtrWramMapping(MEM_NTR_WRAM_ARM7, MEM_NTR_WRAM_ARM7);
ipc_sendWordDirect(1);
}
[[gnu::noinline, gnu::section(".itcm")]]
static void handleSwitchToDSModeCommand()
{
Arm9IoRegisterClearer().ClearTwlIoRegisters();
scfg_setArm9Clock(ScfgArm9Clock::Nitro67MHz);
REG_SCFG_EXT = 0x83000000u | (1 << 13); // keep vram extensions on until the very end
ipc_sendWordDirect(1);
}
[[gnu::noinline, gnu::section(".itcm")]]
static void handleBootCommand()
{
bool isSdkResetSystem = receiveFromArm7() != 0;
REG_EXMEMCNT &= ~0x0880; // map ds and gba slot to arm9
sLoaderPlatform->PrepareRomBoot(sRomDirSector, sRomDirSectorOffset);
Arm9IoRegisterClearer().ClearNtrIoRegisters(isSdkResetSystem);
REG_EXMEMCNT |= 0x0880; // map ds and gba slot to arm7
auto ntrRomHeader = (const nds_header_ntr_t*)TWL_SHARED_MEMORY->ntrSharedMem.romHeader;
if (gIsDsiMode && ntrRomHeader->IsTwlRom())
{
Arm9IoRegisterClearer().ClearTwlIoRegisters();
REG_SCFG_EXT = 0x8307F100;
scfg_setArm9Clock(ScfgArm9Clock::Twl134MHz);
REG_SCFG_CLK = 0x87;
REG_SCFG_RST = 1;
}
bootArm9();
}
static void handleArm7Command(u32 command)
{
switch (command)
{
case IPC_COMMAND_ARM9_INITIALIZE_SD_CARD:
{
handleInitializeSdCardCommand();
break;
}
case IPC_COMMAND_ARM9_WRAM_CONFIG:
{
handleWramConfigCommand();
break;
}
case IPC_COMMAND_ARM9_CLEAR_MAIN_MEM:
{
handleClearMainMemCommand();
break;
}
case IPC_COMMAND_ARM9_APPLY_PATCHES:
{
handleApplyArm9PatchesCommand();
break;
}
case IPC_COMMAND_ARM9_APPLY_ARM7_PATCHES:
{
handleApplyArm7PatchesCommand();
break;
}
case IPC_COMMAND_ARM9_SET_AP_INFO:
{
handleSetAPInfoCommand();
break;
}
case IPC_COMMAND_ARM9_SET_ROM_FILE_INFO:
{
handleSetRomFileInfoCommand();
break;
}
case IPC_COMMAND_ARM9_INITIALIZE_LOADER_INFO:
{
handleInitializeLoaderInfoCommand();
break;
}
case IPC_COMMAND_ARM9_GET_SD_FUNCTIONS:
{
handleGetSdFunctionsCommand();
break;
}
case IPC_COMMAND_ARM9_DISPLAY_ERROR:
{
ErrorDisplay().PrintError((const char*)0x02000000);
break;
}
case IPC_COMMAND_ARM9_SWITCH_TO_DS_MODE:
{
handleSwitchToDSModeCommand();
break;
}
case IPC_COMMAND_ARM9_BOOT:
{
handleBootCommand();
break;
}
}
}
extern "C" void loaderMain()
{
__libc_init_array();
clearGraphicsMemory();
while (ipc_getArm7SyncBits() != HANDSHAKE_PART0);
ipc_setArm9SyncBits(HANDSHAKE_PART0);
while (ipc_getArm7SyncBits() != HANDSHAKE_PART1);
ipc_setArm9SyncBits(HANDSHAKE_PART1);
REG_EXMEMCNT |= 0x8880; // everything to arm7
// REG_EXMEMCNT &= ~0xFF;
mem_setNtrWramMapping(MEM_NTR_WRAM_ARM7, MEM_NTR_WRAM_ARM7);
ipc_clearSendFifo();
ipc_ackFifoError();
ipc_disableRecvFifoNotEmptyIrq();
ipc_enableFifo();
while (ipc_getArm7SyncBits() != HANDSHAKE_PART2);
ipc_setArm9SyncBits(HANDSHAKE_PART2);
while (ipc_getArm7SyncBits() != HANDSHAKE_PART3);
ipc_setArm9SyncBits((*(vu32*)0x04004000) & 3);
gIsDsiMode = ((*(vu32*)0x04004000) & 3) == 1;
heap_init();
sLoaderPlatform = LoaderPlatformFactory().CreateLoaderPlatform();
sRomDirSector = 0;
sRomDirSectorOffset = 0;
sIsCloneBootRom = false;
LOG_DEBUG("Pico Loader ARM9 started\n");
while (true)
{
if (ipc_isRecvFifoEmpty())
continue;
u32 word = ipc_recvWordDirect();
handleArm7Command(word);
}
}