diff --git a/CHANGES b/CHANGES index db9fe99c6..0938b9078 100644 --- a/CHANGES +++ b/CHANGES @@ -21,6 +21,7 @@ Features: - Finer control over FPS target - Holdable shortcut for rewinding one frame at a time - Ability to boot directly into the BIOS + - Preliminary support for yanking out the game pak while a game is running Bugfixes: - ARM7: Fix SWI and IRQ timings - GBA Audio: Force audio FIFOs to 32-bit diff --git a/src/gba/gba.c b/src/gba/gba.c index c1a620b58..22ce9700c 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -376,6 +376,7 @@ void GBALoadROM(struct GBA* gba, struct VFile* vf, struct VFile* sav, const char GBALog(gba, GBA_LOG_WARN, "Couldn't map ROM"); return; } + gba->yankedRomSize = 0; gba->memory.rom = gba->pristineRom; gba->activeFile = fname; gba->memory.romSize = gba->pristineRomSize; @@ -385,6 +386,11 @@ void GBALoadROM(struct GBA* gba, struct VFile* vf, struct VFile* sav, const char // TODO: error check } +void GBAYankROM(struct GBA* gba) { + gba->yankedRomSize = gba->memory.romSize; + gba->memory.romSize = 0; +} + void GBALoadBIOS(struct GBA* gba, struct VFile* vf) { gba->biosVf = vf; uint32_t* bios = vf->map(vf, SIZE_BIOS, MAP_READ); diff --git a/src/gba/gba.h b/src/gba/gba.h index 0926470c5..636c2ae14 100644 --- a/src/gba/gba.h +++ b/src/gba/gba.h @@ -146,6 +146,7 @@ struct GBA { struct GBARRContext* rr; void* pristineRom; size_t pristineRomSize; + size_t yankedRomSize; uint32_t romCrc32; struct VFile* romVf; struct VFile* biosVf; @@ -206,6 +207,7 @@ void GBASetBreakpoint(struct GBA* gba, struct ARMComponent* component, uint32_t void GBAClearBreakpoint(struct GBA* gba, uint32_t address, enum ExecutionMode mode, uint32_t opcode); void GBALoadROM(struct GBA* gba, struct VFile* vf, struct VFile* sav, const char* fname); +void GBAYankROM(struct GBA* gba); void GBALoadBIOS(struct GBA* gba, struct VFile* vf); void GBAApplyPatch(struct GBA* gba, struct Patch* patch); diff --git a/src/gba/memory.c b/src/gba/memory.c index 19155f9d5..29f93e4eb 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -18,7 +18,7 @@ static uint32_t _popcount32(unsigned bits); static void _pristineCow(struct GBA* gba); -static uint32_t _deadbeef[2] = { 0xDEADBEEF, 0xFEEDFACE }; +static uint32_t _deadbeef[1] = { 0xF00FC7C8 }; static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t region); static void GBAMemoryServiceDMA(struct GBA* gba, int number, struct GBADMA* info); @@ -273,7 +273,9 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { memory->activeRegion = 0; cpu->memory.activeRegion = _deadbeef; cpu->memory.activeMask = 0; - GBALog(gba, GBA_LOG_FATAL, "Jumped to invalid address"); + if (!gba->yankedRomSize) { + GBALog(gba, GBA_LOG_FATAL, "Jumped to invalid address"); + } break; } cpu->memory.activeSeqCycles32 = memory->waitstatesPrefetchSeq32[memory->activeRegion]; diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 5b0b0197a..2630e0507 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -325,6 +325,15 @@ void GameController::loadBIOS(const QString& path) { } } +void GameController::yankPak() { + if (!m_gameOpen) { + return; + } + threadInterrupt(); + GBAYankROM(m_threadContext.gba); + threadContinue(); +} + void GameController::loadPatch(const QString& path) { if (m_gameOpen) { closeGame(); diff --git a/src/platform/qt/GameController.h b/src/platform/qt/GameController.h index 798c83ec8..cc8ae2872 100644 --- a/src/platform/qt/GameController.h +++ b/src/platform/qt/GameController.h @@ -97,6 +97,7 @@ signals: public slots: void loadGame(const QString& path, bool dirmode = false); void loadBIOS(const QString& path); + void yankPak(); void setSkipBIOS(bool); void setUseBIOS(bool); void loadPatch(const QString& path); diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 6ba323078..b7e56f421 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -698,6 +698,11 @@ void Window::setupMenu(QMenuBar* menubar) { connect(shutdown, SIGNAL(triggered()), m_controller, SLOT(closeGame())); m_gameActions.append(shutdown); addControlledAction(emulationMenu, shutdown, "shutdown"); + + QAction* yank = new QAction(tr("Yank game pak"), emulationMenu); + connect(yank, SIGNAL(triggered()), m_controller, SLOT(yankPak())); + m_gameActions.append(yank); + addControlledAction(emulationMenu, yank, "yank"); emulationMenu->addSeparator(); QAction* pause = new QAction(tr("&Pause"), emulationMenu);