diff --git a/CHANGES b/CHANGES index 9185f0da6..6ea41ea02 100644 --- a/CHANGES +++ b/CHANGES @@ -20,6 +20,7 @@ Features: - Support for unlicensed Wisdom Tree Game Boy mapper - Qt: Add export button for tile view (closes mgba.io/i/1507) - Qt: Add recent game list clearing (closes mgba.io/i/1380) + - GB: Yanking gamepak now supported Emulation fixes: - GBA: All IRQs have 7 cycle delay (fixes mgba.io/i/539, mgba.io/i/1208) - GBA: Reset now reloads multiboot ROMs diff --git a/include/mgba/internal/gb/gb.h b/include/mgba/internal/gb/gb.h index 40f56cf5a..cda0901f3 100644 --- a/include/mgba/internal/gb/gb.h +++ b/include/mgba/internal/gb/gb.h @@ -94,6 +94,7 @@ struct GB { bool isPristine; size_t pristineRomSize; size_t yankedRomSize; + enum GBMemoryBankControllerType yankedMbc; uint32_t romCrc32; struct VFile* romVf; struct VFile* biosVf; @@ -163,6 +164,7 @@ bool GBLoadROM(struct GB* gb, struct VFile* vf); bool GBLoadSave(struct GB* gb, struct VFile* vf); void GBUnloadROM(struct GB* gb); void GBSynthesizeROM(struct VFile* vf); +void GBYankROM(struct GB* gb); void GBLoadBIOS(struct GB* gb, struct VFile* vf); diff --git a/src/gb/gb.c b/src/gb/gb.c index a1d0fd80d..0e065a0a2 100644 --- a/src/gb/gb.c +++ b/src/gb/gb.c @@ -127,6 +127,19 @@ bool GBLoadROM(struct GB* gb, struct VFile* vf) { return true; } +void GBYankROM(struct GB* gb) { + gb->yankedRomSize = gb->memory.romSize; + gb->yankedMbc = gb->memory.mbcType; + gb->memory.romSize = 0; + gb->memory.mbcType = GB_MBC_NONE; + gb->memory.sramAccess = false; + + if (gb->cpu) { + struct LR35902Core* cpu = gb->cpu; + cpu->memory.setActiveRegion(cpu, cpu->pc); + } +} + static void GBSramDeinit(struct GB* gb) { if (gb->sramVf) { gb->sramVf->unmap(gb->sramVf, gb->memory.sram, gb->sramSize); @@ -430,6 +443,7 @@ void GBReset(struct LR35902Core* cpu) { if (gb->yankedRomSize) { gb->memory.romSize = gb->yankedRomSize; + gb->memory.mbcType = gb->yankedMbc; gb->yankedRomSize = 0; } diff --git a/src/gb/io.c b/src/gb/io.c index f7bddf5ab..155c2c624 100644 --- a/src/gb/io.c +++ b/src/gb/io.c @@ -458,6 +458,9 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) { value = gb->video.stat; break; case 0x50: + if (gb->memory.io[0x50] != 0xFF) { + break; + } GBUnmapBIOS(gb); if (gb->model >= GB_MODEL_CGB && gb->memory.io[REG_UNK4C] < 0x80) { gb->model = GB_MODEL_DMG; diff --git a/src/gb/memory.c b/src/gb/memory.c index b3b9b8bcf..e5817cefe 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -16,6 +16,8 @@ mLOG_DEFINE_CATEGORY(GB_MEM, "GB Memory", "gb.memory"); +static const uint8_t _yankBuffer[] = { 0xFF }; + enum GBBus { GB_BUS_CPU, GB_BUS_MAIN, @@ -69,6 +71,14 @@ static void GBSetActiveRegion(struct LR35902Core* cpu, uint16_t address) { cpu->memory.activeRegion = memory->romBase; cpu->memory.activeRegionEnd = GB_BASE_CART_BANK1; cpu->memory.activeMask = GB_SIZE_CART_BANK0 - 1; + if (gb->memory.romSize < GB_SIZE_CART_BANK0) { + if (address >= gb->memory.romSize) { + cpu->memory.activeRegion = _yankBuffer; + cpu->memory.activeMask = 0; + } else { + cpu->memory.activeRegionEnd = gb->memory.romSize; + } + } break; case GB_REGION_CART_BANK1: case GB_REGION_CART_BANK1 + 1: @@ -89,6 +99,14 @@ static void GBSetActiveRegion(struct LR35902Core* cpu, uint16_t address) { cpu->memory.activeRegionEnd = GB_BASE_CART_BANK1 + 0x2000; } } + if (gb->memory.romSize < GB_SIZE_CART_BANK0 * 2) { + if (address >= gb->memory.romSize) { + cpu->memory.activeRegion = _yankBuffer; + cpu->memory.activeMask = 0; + } else { + cpu->memory.activeRegionEnd = gb->memory.romSize; + } + } break; default: cpu->memory.cpuLoad8 = GBLoad8; @@ -243,6 +261,9 @@ uint8_t GBLoad8(struct LR35902Core* cpu, uint16_t address) { case GB_REGION_CART_BANK0 + 1: case GB_REGION_CART_BANK0 + 2: case GB_REGION_CART_BANK0 + 3: + if (address >= memory->romSize) { + return 0xFF; + } return memory->romBase[address & (GB_SIZE_CART_BANK0 - 1)]; case GB_REGION_CART_BANK1 + 2: case GB_REGION_CART_BANK1 + 3: @@ -252,6 +273,9 @@ uint8_t GBLoad8(struct LR35902Core* cpu, uint16_t address) { // Fall through case GB_REGION_CART_BANK1: case GB_REGION_CART_BANK1 + 1: + if (address >= memory->romSize) { + return 0xFF; + } return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)]; case GB_REGION_VRAM: case GB_REGION_VRAM + 1: diff --git a/src/platform/qt/CoreController.cpp b/src/platform/qt/CoreController.cpp index dc90b73c5..1a025ff8f 100644 --- a/src/platform/qt/CoreController.cpp +++ b/src/platform/qt/CoreController.cpp @@ -628,13 +628,16 @@ void CoreController::replaceGame(const QString& path) { } void CoreController::yankPak() { -#ifdef M_CORE_GBA - if (platform() != PLATFORM_GBA) { - return; - } Interrupter interrupter(this); - GBAYankROM(static_cast(m_threadContext.core->board)); -#endif + + switch (platform()) { + case PLATFORM_GBA: + GBAYankROM(static_cast(m_threadContext.core->board)); + break; + case PLATFORM_GB: + GBYankROM(static_cast(m_threadContext.core->board)); + break; + } } void CoreController::addKey(int key) { diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 4e4007a12..e05b8ce32 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -1212,12 +1212,10 @@ void Window::setupMenu(QMenuBar* menubar) { m_controller->stop(); }, "emu"); -#ifdef M_CORE_GBA Action* yank = addGameAction(tr("Yank game pak"), "yank", [this]() { m_controller->yankPak(); }, "emu"); - m_platformActions.insert(PLATFORM_GBA, yank); -#endif + m_actions.addSeparator("emu"); Action* pause = m_actions.addBooleanAction(tr("&Pause"), "pause", [this](bool paused) {