diff --git a/CHANGES b/CHANGES index 92d0912d8..7ba90f525 100644 --- a/CHANGES +++ b/CHANGES @@ -15,7 +15,7 @@ Features: - 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 diff --git a/README.md b/README.md index 20937c08e..2fa2b691d 100644 --- a/README.md +++ b/README.md @@ -53,15 +53,12 @@ The following mappers are fully supported: The following mappers are partially supported: - MBC6 +- MMM01 - Pocket Cam - TAMA5 - HuC-1 - HuC-3 -The following mappers are not currently supported: - -- MMM01 - ### Planned features - Networked multiplayer link cable support. diff --git a/include/mgba/internal/gb/memory.h b/include/mgba/internal/gb/memory.h index 4c4020df8..7d834080c 100644 --- a/include/mgba/internal/gb/memory.h +++ b/include/mgba/internal/gb/memory.h @@ -129,6 +129,11 @@ struct GBMBC7State { 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; }; diff --git a/include/mgba/internal/gb/serialize.h b/include/mgba/internal/gb/serialize.h index 7628faa0b..f4f36c640 100644 --- a/include/mgba/internal/gb/serialize.h +++ b/include/mgba/internal/gb/serialize.h @@ -373,6 +373,10 @@ struct GBSerializedState { uint16_t sr; uint32_t writable; } mbc7; + struct { + uint8_t locked; + uint8_t bank0; + } mmm01; struct { uint8_t reserved[16]; } padding; diff --git a/src/gb/mbc.c b/src/gb/mbc.c index 605d3d05f..a9afd6d00 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -9,8 +9,11 @@ #include #include #include +#include #include +const uint32_t GB_LOGO_HASH = 0x46195417; + mLOG_DEFINE_CATEGORY(GB_MBC, "GB MBC", "gb.mbc"); static void _GBMBCNone(struct GB* gb, uint16_t address, uint8_t value) { @@ -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 GBMBCSwitchSramHalfBank(struct GB* gb, int half, int bank) { 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; @@ -178,6 +188,11 @@ void GBMBCInit(struct GB* gb) { case 6: gb->memory.mbcType = GB_MBC2; break; + case 0x0B: + case 0x0C: + case 0x0D: + gb->memory.mbcType = GB_MMM01; + break; case 0x0F: case 0x10: gb->memory.mbcType = GB_MBC3_RTC; @@ -256,8 +271,7 @@ void GBMBCInit(struct GB* gb) { 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; @@ -821,6 +835,49 @@ static void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t valu 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) { struct GBMemory* memory = &gb->memory; int bank = value & 0x3F; diff --git a/src/gb/memory.c b/src/gb/memory.c index 229366078..688d43676 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -176,6 +176,8 @@ void GBMemoryReset(struct GB* gb) { 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 @@ void GBMemoryReset(struct GB* gb) { 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 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) { 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; } @@ -755,6 +762,15 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) { 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; }