GB MBC: Add MMM01
@@ -15,7 +15,7 @@ - Debugger: Conditional breakpoints and watchpoints
- Ability to select GB/GBC/SGB BIOS on console ports - Optional automatic state saving/loading - Access to ur0 and uma0 partitions on the Vita - - Partial support for MBC6, TAMA and HuC-1 GB mappers + - Partial support for MBC6, MMM01, TAMA and HuC-1 GB mappers Bugfixes: - GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749) - GB Serialize: Fix audio state loading
@@ -129,6 +129,11 @@ uint8_t latch;
GBMBC7Field eeprom; }; +struct GBMMM01State { + bool locked; + int currentBank0; +}; + struct GBPocketCamState { bool registersActive; uint8_t registers[0x36];@@ -143,6 +148,7 @@ union GBMBCState {
struct GBMBC1State mbc1; struct GBMBC6State mbc6; struct GBMBC7State mbc7; + struct GBMMM01State mmm01; struct GBPocketCamState pocketCam; struct GBTAMA5State tama5; };
@@ -374,6 +374,10 @@ uint16_t sr;
uint32_t writable; } mbc7; struct { + uint8_t locked; + uint8_t bank0; + } mmm01; + struct { uint8_t reserved[16]; } padding; };
@@ -9,7 +9,10 @@ #include <mgba/core/interface.h>
#include <mgba/internal/lr35902/lr35902.h> #include <mgba/internal/gb/gb.h> #include <mgba/internal/gb/memory.h> +#include <mgba-util/crc32.h> #include <mgba-util/vfs.h> + +const uint32_t GB_LOGO_HASH = 0x46195417; mLOG_DEFINE_CATEGORY(GB_MBC, "GB MBC", "gb.mbc");@@ -27,6 +30,7 @@ static void _GBMBC3(struct GB*, uint16_t address, uint8_t value);
static void _GBMBC5(struct GB*, uint16_t address, uint8_t value); static void _GBMBC6(struct GB*, uint16_t address, uint8_t value); static void _GBMBC7(struct GB*, uint16_t address, uint8_t value); +static void _GBMMM01(struct GB*, uint16_t address, uint8_t value); static void _GBHuC1(struct GB*, uint16_t address, uint8_t value); static void _GBHuC3(struct GB*, uint16_t address, uint8_t value); static void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value);@@ -135,6 +139,12 @@
void GBMBCInit(struct GB* gb) { const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100]; if (gb->memory.rom) { + if (gb->memory.romSize >= 0x8000) { + const struct GBCartridge* cartFooter = (const struct GBCartridge*) &gb->memory.rom[gb->memory.romSize - 0x7F00]; + if (doCrc32(cartFooter->logo, sizeof(cartFooter->logo)) == GB_LOGO_HASH) { + cart = cartFooter; + } + } switch (cart->ramSize) { case 0: gb->sramSize = 0;@@ -177,6 +187,11 @@ break;
case 5: case 6: gb->memory.mbcType = GB_MBC2; + break; + case 0x0B: + case 0x0C: + case 0x0D: + gb->memory.mbcType = GB_MMM01; break; case 0x0F: case 0x10:@@ -256,8 +271,7 @@ gb->memory.mbcRead = _GBMBC7Read;
gb->sramSize = 0x100; break; case GB_MMM01: - mLOG(GB_MBC, WARN, "unimplemented MBC: MMM01"); - gb->memory.mbcWrite = _GBMBC1; + gb->memory.mbcWrite = _GBMMM01; break; case GB_HuC1: gb->memory.mbcWrite = _GBHuC1;@@ -819,6 +833,49 @@ } else if (GBMBC7FieldIsCS(value) && GBMBC7FieldIsCLK(old) && !GBMBC7FieldIsCLK(value)) {
value = GBMBC7FieldSetDO(value, GBMBC7FieldGetDO(old)); } mbc7->eeprom = value; +} + +void _GBMMM01(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + if (!memory->mbcState.mmm01.locked) { + switch (address >> 13) { + case 0x0: + memory->mbcState.mmm01.locked = true; + GBMBCSwitchBank0(gb, memory->mbcState.mmm01.currentBank0); + break; + case 0x1: + memory->mbcState.mmm01.currentBank0 = value & 0x3F; + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MMM01 unknown address: %04X:%02X", address, value); + break; + } + return; + } + switch (address >> 13) { + case 0x0: + switch (value) { + case 0xA: + memory->sramAccess = true; + GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + break; + default: + memory->sramAccess = false; + break; + } + break; + case 0x1: + GBMBCSwitchBank(gb, value + memory->mbcState.mmm01.currentBank0); + break; + case 0x2: + GBMBCSwitchSramBank(gb, value); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MMM01 unknown address: %04X:%02X", address, value); + break; + } } void _GBHuC1(struct GB* gb, uint16_t address, uint8_t value) {
@@ -176,6 +176,8 @@ gb->memory.hdmaEvent.callback = _GBMemoryHDMAService;
gb->memory.hdmaEvent.priority = 0x41; memset(&gb->memory.hram, 0, sizeof(gb->memory.hram)); + + GBMBCInit(gb); switch (gb->memory.mbcType) { case GB_MBC1: gb->memory.mbcState.mbc1.mode = 0;@@ -187,11 +189,12 @@ gb->memory.mbcState.mbc6.sramAccess = false;
GBMBCSwitchSramHalfBank(gb, 0, 0); GBMBCSwitchSramHalfBank(gb, 0, 1); break; + case GB_MMM01: + GBMBCSwitchBank0(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 2); + GBMBCSwitchBank(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 1); default: memset(&gb->memory.mbcState, 0, sizeof(gb->memory.mbcState)); } - - GBMBCInit(gb); gb->memory.sramBank = gb->memory.sram; if (!gb->memory.wram) {@@ -688,6 +691,10 @@ state->memory.mbc7.srBits = memory->mbcState.mbc7.srBits;
STORE_16LE(memory->mbcState.mbc7.sr, 0, &state->memory.mbc7.sr); STORE_32LE(memory->mbcState.mbc7.writable, 0, &state->memory.mbc7.writable); break; + case GB_MMM01: + state->memory.mmm01.locked = memory->mbcState.mmm01.locked; + state->memory.mmm01.bank0 = memory->mbcState.mmm01.currentBank0; + break; default: break; }@@ -754,6 +761,15 @@ memory->mbcState.mbc7.latch = state->memory.mbc7.latch;
memory->mbcState.mbc7.srBits = state->memory.mbc7.srBits; LOAD_16LE(memory->mbcState.mbc7.sr, 0, &state->memory.mbc7.sr); LOAD_32LE(memory->mbcState.mbc7.writable, 0, &state->memory.mbc7.writable); + break; + case GB_MMM01: + memory->mbcState.mmm01.locked = state->memory.mmm01.locked; + memory->mbcState.mmm01.currentBank0 = state->memory.mmm01.bank0; + if (memory->mbcState.mmm01.locked) { + GBMBCSwitchBank0(gb, memory->mbcState.mmm01.currentBank0); + } else { + GBMBCSwitchBank0(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 2); + } break; default: break;