mirror of
https://github.com/Gericom/GBARunner3.git
synced 2025-06-18 19:25:41 -04:00
543 lines
16 KiB
C++
543 lines
16 KiB
C++
#include "common.h"
|
|
#include <libtwl/mem/memVram.h>
|
|
#include <libtwl/mem/memNtrWram.h>
|
|
#include <libtwl/mem/memExtern.h>
|
|
#include <libtwl/sys/sysPower.h>
|
|
#include <libtwl/gfx/gfx3d.h>
|
|
#include <libtwl/gfx/gfxPalette.h>
|
|
#include <libtwl/gfx/gfx.h>
|
|
#include <libtwl/gfx/gfxBackground.h>
|
|
#include <libtwl/gfx/gfxOam.h>
|
|
#include <libtwl/gfx/gfxStatus.h>
|
|
#include <libtwl/rtos/rtosIrq.h>
|
|
#include <array>
|
|
#include <string.h>
|
|
#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<u16>
|
|
({
|
|
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<u32*>(jitPatchAddress - ROM_LINEAR_GBA_ADDRESS + ROM_LINEAR_DS_ADDRESS));
|
|
}
|
|
|
|
jit_processArmInstruction(static_cast<u32*>(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<char[]>(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);
|
|
}
|