From f84af911739274c9e4ea24b37d8b4f784f040f84 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 30 May 2017 22:28:23 -0700 Subject: [PATCH] GB MBC: Add MBC1-M support with basic heuristic --- CHANGES | 1 + include/mgba/internal/gb/memory.h | 1 + src/gb/gb.c | 2 ++ src/gb/io.c | 2 +- src/gb/mbc.c | 45 +++++++++++++++++++++++++++---- src/gb/memory.c | 8 +++++- 6 files changed, 52 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index 891977d01..f9edb5bb6 100644 --- a/CHANGES +++ b/CHANGES @@ -20,6 +20,7 @@ Features: - Library view - Debugger: Segment/bank support - GB: Symbol table support + - GB MBC: Add MBC1 multicart support Bugfixes: - LR35902: Fix core never exiting with certain event patterns - GB Timer: Improve DIV reset behavior diff --git a/include/mgba/internal/gb/memory.h b/include/mgba/internal/gb/memory.h index 7a6bd4630..11f1e929f 100644 --- a/include/mgba/internal/gb/memory.h +++ b/include/mgba/internal/gb/memory.h @@ -85,6 +85,7 @@ enum GBMBC7MachineState { struct GBMBC1State { int mode; + int multicartStride; }; struct GBMBC7State { diff --git a/src/gb/gb.c b/src/gb/gb.c index 8e227a816..4c83984f5 100644 --- a/src/gb/gb.c +++ b/src/gb/gb.c @@ -385,7 +385,9 @@ bool GBIsBIOS(struct VFile* vf) { void GBReset(struct LR35902Core* cpu) { struct GB* gb = (struct GB*) cpu->master; + gb->memory.romBase = gb->memory.rom; GBDetectModel(gb); + if (gb->biosVf) { if (!GBIsBIOS(gb->biosVf)) { gb->biosVf->close(gb->biosVf); diff --git a/src/gb/io.c b/src/gb/io.c index b7e234ca7..0f386aaae 100644 --- a/src/gb/io.c +++ b/src/gb/io.c @@ -382,7 +382,7 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) { value = gb->video.stat; break; case 0x50: - if (gb->memory.romBase != gb->memory.rom) { + if (gb->memory.romBase < gb->memory.rom && gb->memory.romBase > &gb->memory.rom[gb->memory.romSize - 1]) { free(gb->memory.romBase); gb->memory.romBase = gb->memory.rom; } diff --git a/src/gb/mbc.c b/src/gb/mbc.c index efc294419..ae69defc6 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -46,6 +46,33 @@ void GBMBCSwitchBank(struct GB* gb, int bank) { } } +static void _switchBank0(struct GB* gb, int bank) { + size_t bankStart = bank * GB_SIZE_CART_BANK0 << gb->memory.mbcState.mbc1.multicartStride; + if (bankStart + GB_SIZE_CART_BANK0 > gb->memory.romSize) { + mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank); + bankStart &= (gb->memory.romSize - 1); + } + gb->memory.romBase = &gb->memory.rom[bankStart]; + if (gb->cpu->pc < GB_SIZE_CART_BANK0) { + gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc); + } +} + +static bool _isMulticart(const uint8_t* mem) { + bool success = true; + struct VFile* vf; + + vf = VFileFromConstMemory(&mem[GB_SIZE_CART_BANK0 * 0x10], 1024); + success = success && GBIsROM(vf); + vf->close(vf); + + vf = VFileFromConstMemory(&mem[GB_SIZE_CART_BANK0 * 0x20], 1024); + success = success && GBIsROM(vf); + vf->close(vf); + + return success; +} + void GBMBCSwitchSramBank(struct GB* gb, int bank) { size_t bankStart = bank * GB_SIZE_EXTERNAL_RAM; GBResizeSram(gb, (bank + 1) * GB_SIZE_EXTERNAL_RAM); @@ -83,6 +110,11 @@ void GBMBCInit(struct GB* gb) { case 2: case 3: gb->memory.mbcType = GB_MBC1; + if (gb->memory.romSize >= GB_SIZE_CART_BANK0 * 0x31 && _isMulticart(gb->memory.rom)) { + gb->memory.mbcState.mbc1.multicartStride = 4; + } else { + gb->memory.mbcState.mbc1.multicartStride = 5; + } break; case 5: case 6: @@ -233,6 +265,7 @@ static void _latchRtc(struct mRTCSource* rtc, uint8_t* rtcRegs, time_t* rtcLastL void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) { struct GBMemory* memory = &gb->memory; int bank = value & 0x1F; + int stride = 1 << memory->mbcState.mbc1.multicartStride; switch (address >> 13) { case 0x0: switch (value) { @@ -253,21 +286,23 @@ void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) { if (!bank) { ++bank; } - GBMBCSwitchBank(gb, bank | (memory->currentBank & 0x60)); + bank &= stride - 1; + GBMBCSwitchBank(gb, bank | (memory->currentBank & (3 * stride))); break; case 0x2: bank &= 3; - if (!memory->mbcState.mbc1.mode) { - GBMBCSwitchBank(gb, (bank << 5) | (memory->currentBank & 0x1F)); - } else { + if (memory->mbcState.mbc1.mode) { + _switchBank0(gb, bank); GBMBCSwitchSramBank(gb, bank); } + GBMBCSwitchBank(gb, (bank << memory->mbcState.mbc1.multicartStride) | (memory->currentBank & (stride - 1))); break; case 0x3: memory->mbcState.mbc1.mode = value & 1; if (memory->mbcState.mbc1.mode) { - GBMBCSwitchBank(gb, memory->currentBank & 0x1F); + _switchBank0(gb, memory->currentBank >> memory->mbcState.mbc1.multicartStride); } else { + _switchBank0(gb, 0); GBMBCSwitchSramBank(gb, 0); } break; diff --git a/src/gb/memory.c b/src/gb/memory.c index 6a2d84f55..46c8dcd78 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -165,7 +165,13 @@ void GBMemoryReset(struct GB* gb) { memset(&gb->memory.rtcRegs, 0, sizeof(gb->memory.rtcRegs)); memset(&gb->memory.hram, 0, sizeof(gb->memory.hram)); - memset(&gb->memory.mbcState, 0, sizeof(gb->memory.mbcState)); + switch (gb->memory.mbcType) { + case GB_MBC1: + gb->memory.mbcState.mbc1.mode = 0; + break; + default: + memset(&gb->memory.mbcState, 0, sizeof(gb->memory.mbcState)); + } GBMBCInit(gb); gb->memory.sramBank = gb->memory.sram;