all repos — mgba @ f84af911739274c9e4ea24b37d8b4f784f040f84

mGBA Game Boy Advance Emulator

GB MBC: Add MBC1-M support with basic heuristic
Vicki Pfau vi@endrift.com
Tue, 30 May 2017 22:28:23 -0700
commit

f84af911739274c9e4ea24b37d8b4f784f040f84

parent

7de5b33554a9c0b41c963d220dd85ceda1bf3022

6 files changed, 52 insertions(+), 7 deletions(-)

jump to
M CHANGESCHANGES

@@ -20,6 +20,7 @@ - Video log recording for testing and bug reporting

- 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
M include/mgba/internal/gb/memory.hinclude/mgba/internal/gb/memory.h

@@ -85,6 +85,7 @@ };

struct GBMBC1State { int mode; + int multicartStride; }; struct GBMBC7State {
M src/gb/gb.csrc/gb/gb.c

@@ -385,7 +385,9 @@ }

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);
M src/gb/io.csrc/gb/io.c

@@ -382,7 +382,7 @@ GBVideoWriteSTAT(&gb->video, 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; }
M src/gb/mbc.csrc/gb/mbc.c

@@ -46,6 +46,33 @@ gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc);

} } +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 @@ case 1:

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 @@

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 @@ case 0x1:

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;
M src/gb/memory.csrc/gb/memory.c

@@ -165,7 +165,13 @@ gb->memory.rtcLatched = false;

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;