#include "common.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cp15.h" #include "Fat/ff.h" #include "VirtualMachine/VirtualMachine.h" #include "Emulator/IoRegisters.h" #include "Core/Environment.h" #include "Peripherals/DmaTransfer.h" #include "Logger/NitroEmulatorOutputStream.h" #include "Logger/PlainLogger.h" #include "Logger/NullLogger.h" #include "Save/SaveTagScanner.h" #include "Save/Save.h" #include "SdCache/SdCache.h" #include "JitPatcher/JitCommon.h" #include "JitPatcher/JitArm.h" #include "Peripherals/Sound/GbaSound9.h" #include "Patches/HarvestMoonPatches.h" #include "Patches/BadMixerPatch.h" #include "Application/Settings/AppSettingsService.h" #include "GbaHeader.h" #include "MemoryEmulator/MemoryLoadStore.h" #include "ColorLut.h" #include "MemoryProtectionConfiguration.h" #include "MemoryProtectionUnit.h" #include "MemoryEmulator/RomDefs.h" #include "Application/GbaDisplayConfigurationService.h" #include "Application/GbaBorderService.h" #include "Application/SplashScreen.h" #include "Patches/PatchSwi.h" #include "Patches/SelfModifyingPatches.h" #include "Emulator/BootAnimationSkip.h" #include "MemoryEmulator/Arm/ArmDispatchTable.h" #include "VirtualMachine/VMUndefinedArmTable.h" #include "Arm9Clock.h" #define DEFAULT_ROM_FILE_PATH "/rom.gba" #define BIOS_FILE_PATH "/_gba/bios.bin" #define SETTINGS_FILE_PATH "/_gba/gbarunner3.json" #define GAME_SETTINGS_FILE_PATH_FORMAT "/_gba/configs/%c%c%c%c%02X.json" [[gnu::section(".ewram.bss")]] FATFS gFatFs; [[gnu::section(".ewram.bss")]] FIL gFile; [[gnu::section(".ewram.bss")]] GbaHeader gRomHeader; [[gnu::section(".vramhi.bss")]] u32 gGbaBios[16 * 1024 / 4] alignas(256); /// @brief Opcodes returned by bios reads. u32 memu_biosOpcodes[4] { 0xE25EF004, // MEMU_BIOS_OPCODE_ID_IRQ_ENTRY 0xE129F000, // MEMU_BIOS_OPCODE_ID_RESET 0xE55EC002, // MEMU_BIOS_OPCODE_ID_IRQ_EXIT 0xE3A02004 // MEMU_BIOS_OPCODE_ID_SWI_EXIT }; static NitroEmulatorOutputStream sIsNitroOutput; [[gnu::section(".ewram.bss")]] static PlainLogger sPlainLogger { LogLevel::All, &sIsNitroOutput }; static NullLogger sNullLogger; ILogger* gLogger; static SplashScreen* sSplashScreen; static void setupLogger() { if (Environment::IsIsNitroEmulator()) gLogger = &sPlainLogger; else gLogger = &sNullLogger; } static bool mountDldi() { FRESULT res = f_mount(&gFatFs, "fat:", 1); if (res != FR_OK) { gLogger->Log(LogLevel::Error, "Mounting dldi failed\n"); return false; } f_chdrive("fat:"); return true; } static bool mountDsiSd() { FRESULT res = f_mount(&gFatFs, "sd:", 1); if (res != FR_OK) { gLogger->Log(LogLevel::Error, "Mounting DSi sd failed\n"); return false; } f_chdrive("sd:"); return true; } static bool mountAgbSemihosting() { FRESULT res = f_mount(&gFatFs, "pc:", 1); if (res != FR_OK) { gLogger->Log(LogLevel::Error, "Mounting agb semihosting failed\n"); return false; } f_chdrive("pc:"); return true; } static void loadGbaBios() { memset(&gFile, 0, sizeof(gFile)); f_open(&gFile, BIOS_FILE_PATH, FA_OPEN_EXISTING | FA_READ); UINT br; f_read(&gFile, gGbaBios, 16 * 1024, &br); f_close(&gFile); } static constexpr auto sBiosRelocations = std::to_array ({ 0x027C, 0x0AB8, 0x0ABC, 0x0AC4, 0x0ACC, 0x0ADC, 0x0AE0, 0x0AE4, 0x0AEC, 0x0AF0, 0x0B0C, 0x0B2C, 0x1430, 0x16F8, 0x16FC, 0x1700, 0x1788, 0x1924, 0x1D64, 0x1D6C, 0x1D80, 0x1D84, 0x1D90, 0x1D9C, 0x23A4, 0x2624, 0x26C0, 0x2C14, 0x30AC, 0x37D4, 0x37D8, 0x37E0, 0x37E4, 0x381C, 0x3820, 0x3824, 0x3828, 0x38A0, 0x38A4, 0x38A8, 0x38AC, 0x38B0, 0x390C, 0x3910, 0x3914, 0x3918, 0x391C, 0x3920, 0x3924, 0x3984, 0x3988, 0x398C, 0x3990, 0x3994, 0x3998, 0x399C, 0x39C4, 0x39C8, 0x39CC }); static void relocateGbaBios() { const u32 base = (u32)gGbaBios; //swi table for (int i = 0; i < 43; i++) gGbaBios[(0x01C8 >> 2) + i] += base; *(vu16*)(((u8*)gGbaBios) + 0x868) = 0; // prevent address check fail for (int i = 0; i < 38; i++) gGbaBios[(0x3738 >> 2) + i] += base; for (u16 address : sBiosRelocations) gGbaBios[address >> 2] += base; } static void applyBiosVmPatches() { gGbaBios[0x0024 >> 2] = 0xE1E0009C; // mrs r12, spsr gGbaBios[0x0028 >> 2] = 0xE1A0009E; // mrs lr, cpsr gGbaBios[0x005C >> 2] = 0xE1C9009C; // msr spsr_cf, r12 gGbaBios[0x0064 >> 2] = 0xEE64008E; // subs pc, lr, #4 gGbaBios[0x007C >> 2] = 0x01A0009C; // mrseq r12, cpsr gGbaBios[0x0084 >> 2] = 0x0189009C; // msreq cpsr_cf, r12 gGbaBios[0x0090 >> 2] = 0xE1890090; // msr cpsr_cf, r0 gGbaBios[0x00D4 >> 2] = 0xE1890090; // msr cpsr_cf, r0 gGbaBios[0x00E4 >> 2] = 0xE1890090; // msr cpsr_cf, r0 gGbaBios[0x00F0 >> 2] = 0xE1C9009E; // msr spsr_cf, lr gGbaBios[0x00F8 >> 2] = 0xE1890090; // msr cpsr_cf, r0 gGbaBios[0x0104 >> 2] = 0xE1C9009E; // msr spsr_cf, lr gGbaBios[0x010C >> 2] = 0xE1890090; // msr cpsr_cf, r0 gGbaBios[0x013C >> 2] = 0xEE64008E; // subs pc, lr, #4 gGbaBios[0x0150 >> 2] = 0xE1E0009B; // mrs r11, spsr gGbaBios[0x0160 >> 2] = 0xE189009B; // msr cpsr_cf, r11 gGbaBios[0x0178 >> 2] = 0xE189009C; // msr cpsr_cf, r12 gGbaBios[0x0180 >> 2] = 0xE1C9009B; // msr spsr_cf, r11 gGbaBios[0x0188 >> 2] = 0xEE64000E; // movs pc, lr gGbaBios[0x0388 >> 2] = 0xE189009C; // msr cpsr_cf, r12 } static void applyBiosJitPatches() { gGbaBios[0x00DC >> 2] = 0xE1B0009E; // bx lr (jump to rom) gGbaBios[0x0134 >> 2] = 0xEE800090; // ldr pc, [r0, #-4] (jump to irq handler) } static void loadGbaRom(const char* romPath) { UINT br; memset(&gFile, 0, sizeof(gFile)); f_open(&gFile, romPath, FA_OPEN_EXISTING | FA_READ); sdc_init(); f_read(&gFile, &gRomHeader, sizeof(GbaHeader), &br); f_lseek(&gFile, ROM_LINEAR_GBA_ADDRESS - 0x08000000); f_read(&gFile, (void*)ROM_LINEAR_DS_ADDRESS, ROM_LINEAR_SIZE, &br); HarvestMoonPatches().TryApplyPatches(gRomHeader.gameCode); if (BadMixerPatch().TryApplyPatch()) { gLogger->Log(LogLevel::Debug, "Bad mixer patch applied\n"); } } static void disableSramReads(void) { memu_setLoad8Handler(0xE, memu_load8Undefined); memu_setLoad8Handler(0xF, memu_load8Undefined); memu_setLoad16Handler(0xE, memu_load16Undefined); memu_setLoad16Handler(0xF, memu_load16Undefined); memu_setLoad32Handler(0xE, memu_load32Undefined); memu_setLoad32Handler(0xF, memu_load32Undefined); } static void disableSramWrites(void) { memu_setStore8Handler(0xE, memu_store8Undefined); memu_setStore8Handler(0xF, memu_store8Undefined); memu_setStore16Handler(0xE, memu_store16Undefined); memu_setStore16Handler(0xF, memu_store16Undefined); memu_setStore32Handler(0xE, memu_store32Undefined); memu_setStore32Handler(0xF, memu_store32Undefined); } static void handleSave(const char* savePath) { const auto& gameSettings = gAppSettingsService.GetAppSettings().gameSettings; if (gameSettings.saveType == GbaSaveType::None) { gLogger->Log(LogLevel::Debug, "Save Type: None\n"); disableSramReads(); disableSramWrites(); return; } u32 tagRomAddress; const SaveTypeInfo* saveTypeInfo = SaveTagScanner().FindSaveTag(&gFile, &sdc_cache[0][0], tagRomAddress); if (saveTypeInfo) { gLogger->Log(LogLevel::Debug, "%s\n", saveTypeInfo->tag); if (saveTypeInfo->patchFunc) { if (!saveTypeInfo->patchFunc(saveTypeInfo, &gFile, tagRomAddress, &sdc_cache[0][0])) { gLogger->Log(LogLevel::Error, "Save patching failed\n"); } } if ((saveTypeInfo->type & SAVE_TYPE_MASK) == SAVE_TYPE_EEPROM) { disableSramReads(); } if ((saveTypeInfo->type & SAVE_TYPE_MASK) == SAVE_TYPE_EEPROM || (saveTypeInfo->type & SAVE_TYPE_MASK) == SAVE_TYPE_FLASH) { disableSramWrites(); } } sav_initializeSave(saveTypeInfo, savePath); } extern "C" void logAddress(u32 address) { gLogger->Log(LogLevel::Trace, "0x%X\n", address); } static bool shouldMountDsiSd(int argc, char* argv[]) { if (!Environment::IsDsiMode()) return false; if (argc == 0) return true; if (argv[0][0] == 'f' && argv[0][1] == 'a' && argv[0][2] == 't' && argv[0][3] == ':') return false; return true; } static void applyGameJitPatches() { gLogger->Log(LogLevel::Debug, "Applying game JIT patches...\n"); const auto& runSettings = gAppSettingsService.GetAppSettings().runSettings; for (u32 i = 0; i < runSettings.jitPatchAddressCount; ++i) { u32 jitPatchAddress = runSettings.jitPatchAddresses[i]; if (jitPatchAddress >= 0x08000000u && jitPatchAddress < 0x0A000000u) { gLogger->Log(LogLevel::Debug, "0x%08X\n", jitPatchAddress); if (jitPatchAddress >= ROM_LINEAR_GBA_ADDRESS && jitPatchAddress < ROM_LINEAR_END_GBA_ADDRESS) { jit_processArmInstruction(reinterpret_cast(jitPatchAddress - ROM_LINEAR_GBA_ADDRESS + ROM_LINEAR_DS_ADDRESS)); } jit_processArmInstruction(static_cast(sdc_loadRomBlockForPatching(jitPatchAddress))); } } } static void setupJit() { jit_init(); const auto& runSettings = gAppSettingsService.GetAppSettings().runSettings; if (runSettings.enableJit) { if (runSettings.jitPatchAddresses && runSettings.jitPatchAddressCount > 0) { // manual jit patches applyGameJitPatches(); jit_disable(); } else { // jit enabled applyBiosJitPatches(); } } else { // jit disabled jit_disable(); } } static void setupWramInstructionCache() { const auto& runSettings = gAppSettingsService.GetAppSettings().runSettings; mpu_setRegionInstructionCacheEnable(MPU_REGION_GBA_IWRAM, runSettings.enableWramInstructionCache); mpu_setRegionInstructionCacheEnable(MPU_REGION_GBA_EWRAM, runSettings.enableWramInstructionCache); } static void setupRomInstructionCache() { const auto& runSettings = gAppSettingsService.GetAppSettings().runSettings; mpu_setRegionInstructionCacheEnable(MPU_REGION_MAIN_MEMORY_GBA_ROM, runSettings.enableRomInstructionCache); } static void setupIWramDataCache() { const auto& runSettings = gAppSettingsService.GetAppSettings().runSettings; mpu_setRegionDataCacheEnable(MPU_REGION_GBA_IWRAM, runSettings.enableIWramDataCache); mpu_setRegionDataBufferability(MPU_REGION_GBA_IWRAM, false); } static void setupEWramDataCache() { const auto& runSettings = gAppSettingsService.GetAppSettings().runSettings; mpu_setRegionDataCacheEnable(MPU_REGION_GBA_EWRAM, runSettings.enableEWramDataCache); mpu_setRegionDataBufferability(MPU_REGION_GBA_EWRAM, false); } static void setupArm9Clock() { const auto& runSettings = gAppSettingsService.GetAppSettings().runSettings; ScfgArm9Clock arm9Clock = runSettings.forceDSModeArm9ClockSpeed ? ScfgArm9Clock::Nitro67MHz // Force DS mode clock if true : ScfgArm9Clock::Twl134MHz; scfg_setArm9Clock(arm9Clock); } static void loadGameSpecificSettings() { auto path = std::make_unique(128); mini_snprintf(path.get(), 128, GAME_SETTINGS_FILE_PATH_FORMAT, gRomHeader.gameCode & 0xFF, (gRomHeader.gameCode >> 8) & 0xFF, (gRomHeader.gameCode >> 16) & 0xFF, gRomHeader.gameCode >> 24, gRomHeader.softwareVersion); if (gAppSettingsService.TryLoadAppSettings(path.get())) { gLogger->Log(LogLevel::Debug, "Loaded game specific settings from %s\n", path.get()); } } [[gnu::interrupt("IRQ")]] static void splashScreenIrqHandler() { rtos_ackIrqMask(RTOS_IRQ_VBLANK); if (sSplashScreen) { sSplashScreen->VBlank(); } } static void startSplashScreenAnimation() { REG_IME = 0; rtos_setIrqMask(0); // Set the irq vector (at a mirror address to keep GCC happy) *(vu32*)0x01000018 = 0xE51FF004; // ldr pc,= splashScreenIrqHandler *(vu32*)0x0100001C = (u32)splashScreenIrqHandler; rtos_setIrqMask(RTOS_IRQ_VBLANK); rtos_ackIrqMask(~0u); REG_IME = 1; gfx_setVBlankIrqEnabled(true); arm_enableIrqs(); } static void waitSplashScreenAnimation() { while (!sSplashScreen->IsFinished()) { arm_waitForInterrupt(); } } static void stopSplashScreenAnimation() { arm_disableIrqs(); REG_IME = 0; gfx_setVBlankIrqEnabled(false); rtos_setIrqMask(0); rtos_ackIrqMask(~0u); *(vu32*)0x01000018 = 0xEAFFFFFE; // b . *(vu32*)0x0100001C = 0xEAFFFFFE; // b . } extern "C" void gbaRunnerMain(int argc, char* argv[]) { heap_init(); REG_DISPCNT = 0x10000; REG_DISPCNT_SUB = 0x10000; GFX_PLTT_BG_SUB[0] = 0; sSplashScreen = new SplashScreen(); sSplashScreen->Initialize(); mem_setVramBMapping(MEM_VRAM_AB_MAIN_BG_40000); mem_setVramCMapping(MEM_VRAM_C_LCDC); mem_setVramDMapping(MEM_VRAM_D_LCDC); // these are already mapped by bootstrap // mem_setVramEMapping(MEM_VRAM_E_MAIN_BG_00000); // mem_setVramFMapping(MEM_VRAM_FG_MAIN_OBJ_00000); // mem_setVramGMapping(MEM_VRAM_FG_MAIN_OBJ_04000); mem_setNtrWramMapping(MEM_NTR_WRAM_ARM9, MEM_NTR_WRAM_ARM9); mem_setVramHMapping(MEM_VRAM_H_LCDC); mem_setVramIMapping(MEM_VRAM_I_LCDC); sys_set3DGeometryEnginePower(true); // enable geometry engine to generate gx fifo irq REG_GXSTAT = 0; // gx fifo irqs must be off Environment::Initialize(); setupLogger(); mem_setMainMemoryPriority(EXMEMCNT_MAIN_MEM_PRIO_ARM7); startSplashScreenAnimation(); bool mountResult; if (shouldMountDsiSd(argc, argv)) mountResult = mountDsiSd(); else mountResult = mountDldi(); if (!mountResult) { GFX_PLTT_BG_MAIN[0] = 0x1F << 10; while (1); } // if (Environment::SupportsAgbSemihosting()) // mountAgbSemihosting(); gAppSettingsService.TryLoadAppSettings(SETTINGS_FILE_PATH); patch_resetSwiPatches(); loadGbaBios(); relocateGbaBios(); applyBiosVmPatches(); const char* romPath = argc > 1 ? argv[1] : DEFAULT_ROM_FILE_PATH; loadGbaRom(romPath); char* romExtension = strrchr(romPath, '.'); if (romExtension) { romExtension[1] = 's'; romExtension[2] = 'a'; romExtension[3] = 'v'; romExtension[4] = '\0'; } loadGameSpecificSettings(); handleSave(romPath); SelfModifyingPatches().ApplyPatches(gAppSettingsService.GetAppSettings().runSettings); waitSplashScreenAnimation(); stopSplashScreenAnimation(); delete sSplashScreen; sSplashScreen = nullptr; const auto& displaySettings = gAppSettingsService.GetAppSettings().displaySettings; gGbaDisplayConfigurationService.ApplyDisplaySettings(displaySettings); if (displaySettings.enableCenterAndMask) { gGbaBorderService.SetupBorder(displaySettings.borderImage, gRomHeader.gameCode); } // Do not clear ewram before we read argv memset((void*)0x02000000, 0, 256 * 1024); memset((void*)0x03000000, 0, 32 * 1024); memset((void*)GFX_BG_MAIN, 0, 64 * 1024); memset((void*)((u32)GFX_BG_MAIN + 0x40000), 0, 128 * 1024); // vram B memset((void*)GFX_OBJ_MAIN, 0, 32 * 1024); memset(emu_ioRegisters, 0, sizeof(emu_ioRegisters)); memu_initializeArmDispatchTable(); vm_initializeUndefinedArmTable(); setupJit(); dma_init(); gbas_init(); dc_flushRange((void*)ROM_LINEAR_DS_ADDRESS, ROM_LINEAR_SIZE); dc_flushRange(gGbaBios, sizeof(gGbaBios)); ic_invalidateAll(); setupWramInstructionCache(); setupRomInstructionCache(); setupIWramDataCache(); setupEWramDataCache(); setupArm9Clock(); rtos_setIrqMask(RTOS_IRQ_VBLANK); rtos_ackIrqMask(~0u); REG_IME = 1; gfx_setVBlankIrqEnabled(true); const auto& runSettings = gAppSettingsService.GetAppSettings().runSettings; VirtualMachine virtualMachine { runSettings.skipBiosIntro ? (void*)&emu_resetVectorSkipBootAnimation : &gGbaBios[0], &gGbaBios[0x140 >> 2], &gGbaBios[0x128 >> 2] }; context_t runContext { }; virtualMachine.Run(&runContext); while (true); }