diff --git a/CHANGES b/CHANGES index a597206d0..4e5f36c7b 100644 --- a/CHANGES +++ b/CHANGES @@ -45,6 +45,7 @@ Misc: - Core: Add SHA1 hashing for ROMs - FFmpeg: Add Ut Video option - GB: Prevent incompatible BIOSes from being used on differing models + - GB MBC: Add M161 support for one Mani 4-in-1 multicart - GB Serialize: Add missing savestate support for MBC6 and NT (newer) - GBA: Improve detection of valid ELF ROMs - GBA Audio: Remove broken XQ audio pending rewrite diff --git a/README.md b/README.md index 5bb847e9f..78b49cf7f 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ The following mappers are fully supported: - MBC5 - MBC5+Rumble - MBC7 +- M161 - Wisdom Tree (unlicensed) - NT "old type" 1 and 2 (unlicensed multicart) - NT "new type" (unlicensed MBC5-like) diff --git a/include/mgba/gb/interface.h b/include/mgba/gb/interface.h index e9bec3044..f32c25773 100644 --- a/include/mgba/gb/interface.h +++ b/include/mgba/gb/interface.h @@ -37,6 +37,7 @@ enum GBMemoryBankControllerType { GB_HuC3 = 0x12, GB_POCKETCAM = 0x13, GB_TAMA5 = 0x14, + GB_M161 = 0x15, GB_MBC3_RTC = 0x103, GB_MBC5_RUMBLE = 0x105, GB_UNL_WISDOM_TREE = 0x200, diff --git a/include/mgba/internal/gb/memory.h b/include/mgba/internal/gb/memory.h index 2d1278feb..9bee51724 100644 --- a/include/mgba/internal/gb/memory.h +++ b/include/mgba/internal/gb/memory.h @@ -233,6 +233,11 @@ struct GBHuC3State { uint8_t registers[256]; }; +struct GBM161State { + bool locked; + uint8_t bank; +}; + struct GBPKJDState { uint8_t reg[2]; }; @@ -276,6 +281,7 @@ union GBMBCState { struct GBPocketCamState pocketCam; struct GBTAMA5State tama5; struct GBHuC3State huc3; + struct GBM161State m161; struct GBNTOldState ntOld; struct GBNTNewState ntNew; struct GBPKJDState pkjd; diff --git a/include/mgba/internal/gb/serialize.h b/include/mgba/internal/gb/serialize.h index 4963340e5..72fe9c436 100644 --- a/include/mgba/internal/gb/serialize.h +++ b/include/mgba/internal/gb/serialize.h @@ -432,6 +432,10 @@ struct GBSerializedState { uint8_t value; uint8_t mode; } huc3; + struct { + uint8_t bank; + uint8_t locked; + } m161; struct { GBSerializedNTOldFlags flags; uint8_t baseBank; diff --git a/src/gb/mbc.c b/src/gb/mbc.c index 551071548..c6f7b9183 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -110,7 +110,7 @@ static struct { {"HUC1", GB_HuC1}, {"HUC3", GB_HuC3}, {"TAM5", GB_TAMA5}, - {"M161", GB_MBC_AUTODETECT}, // TODO + {"M161", GB_M161}, {"BBD", GB_UNL_BBD}, {"HITK", GB_UNL_HITEK}, {"SNTX", GB_UNL_SINTAX}, @@ -460,6 +460,9 @@ void GBMBCInit(struct GB* gb) { gb->memory.cam->startRequestImage(gb->memory.cam, GBCAM_WIDTH, GBCAM_HEIGHT, mCOLOR_ANY); } break; + case GB_M161: + gb->memory.mbcWrite = _GBM161; + break; case GB_UNL_WISDOM_TREE: gb->memory.mbcWrite = _GBWisdomTree; break; diff --git a/src/gb/mbc/mbc-private.h b/src/gb/mbc/mbc-private.h index b6f7671a5..9b6e11b89 100644 --- a/src/gb/mbc/mbc-private.h +++ b/src/gb/mbc/mbc-private.h @@ -24,6 +24,7 @@ void _GBMBC7(struct GB*, uint16_t address, uint8_t value); void _GBMMM01(struct GB*, uint16_t address, uint8_t value); void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value); void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value); +void _GBM161(struct GB* gb, uint16_t address, uint8_t value); void _GBHuC1(struct GB*, uint16_t address, uint8_t value); void _GBHuC3(struct GB*, uint16_t address, uint8_t value); diff --git a/src/gb/mbc/mbc.c b/src/gb/mbc/mbc.c index 699bdb209..59ab4af5b 100644 --- a/src/gb/mbc/mbc.c +++ b/src/gb/mbc/mbc.c @@ -565,3 +565,19 @@ void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value) { } mbc7->eeprom = value; } + +void _GBM161(struct GB* gb, uint16_t address, uint8_t value) { + UNUSED(address); + + struct GBM161State* m161 = &gb->memory.mbcState.m161; + if (m161->locked) { + return; + } + + int bank = value & 0x7; + m161->bank = bank; + m161->locked = true; + + GBMBCSwitchBank0(gb, bank * 2); + GBMBCSwitchBank(gb, bank * 2 + 1); +} diff --git a/src/gb/memory.c b/src/gb/memory.c index f2bfa17a4..e6544c27d 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -840,6 +840,10 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) { state->memory.mmm01.locked = memory->mbcState.mmm01.locked; state->memory.mmm01.bank0 = memory->mbcState.mmm01.currentBank0; break; + case GB_M161: + state->memory.m161.locked = memory->mbcState.m161.locked; + state->memory.m161.bank = memory->mbcState.m161.bank; + break; case GB_UNL_NT_OLD_1: case GB_UNL_NT_OLD_2: state->memory.ntOld.flags = GBSerializedNTOldFlagsSetSwapped(0, memory->mbcState.ntOld.swapped); @@ -1006,6 +1010,12 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) { GBMBCSwitchBank0(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 2); } break; + case GB_M161: + memory->mbcState.m161.locked = state->memory.m161.locked; + memory->mbcState.m161.bank = state->memory.m161.bank & 0x7; + GBMBCSwitchBank0(gb, memory->mbcState.m161.bank * 2); + GBMBCSwitchBank(gb, memory->mbcState.m161.bank * 2 + 1); + break; case GB_UNL_NT_OLD_1: case GB_UNL_NT_OLD_2: memory->mbcState.ntOld.swapped = GBSerializedNTOldFlagsGetSwapped(state->memory.ntOld.flags); diff --git a/src/gb/overrides.c b/src/gb/overrides.c index 20e489b9a..c3799e0c0 100644 --- a/src/gb/overrides.c +++ b/src/gb/overrides.c @@ -667,6 +667,8 @@ static const struct GBCartridgeOverride _sgbOverrides[] = { }; static const struct GBCartridgeOverride _overrides[] = { + { 0xA61F3EE1, GB_MODEL_AUTODETECT, GB_M161, { 0 } }, // Mani 4 in 1 - Tetris + Alleyway + Yakuman + Tennis + // Pokemon Spaceworld 1997 demo { 0x232A067D, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (debug) { 0x630ED957, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (non-debug) diff --git a/src/platform/qt/GameBoy.cpp b/src/platform/qt/GameBoy.cpp index 8aec1ea13..e6adbcce6 100644 --- a/src/platform/qt/GameBoy.cpp +++ b/src/platform/qt/GameBoy.cpp @@ -33,6 +33,7 @@ static const QList s_mbcList{ GB_TAMA5, GB_HuC1, GB_HuC3, + GB_M161, GB_UNL_WISDOM_TREE, GB_UNL_PKJD, GB_UNL_NT_OLD_1, @@ -92,6 +93,7 @@ QString GameBoy::mbcName(GBMemoryBankControllerType mbc) { s_mbcNames[GB_HuC3] = "HuC-3"; s_mbcNames[GB_POCKETCAM] = "Pocket Cam"; s_mbcNames[GB_TAMA5] = "TAMA5"; + s_mbcNames[GB_M161] = "M161"; s_mbcNames[GB_UNL_WISDOM_TREE] = "Wisdom Tree"; s_mbcNames[GB_UNL_NT_OLD_1] = tr("%1 (old 1)").arg("NT"); s_mbcNames[GB_UNL_NT_OLD_2] = tr("%1 (old 2)").arg("NT");