From ad2a7a748a6b10331b7d95b293ff56eed26c4254 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 17 Oct 2022 01:31:04 -0700 Subject: [PATCH] GB MBC: Add GGB-81 support --- CHANGES | 2 +- README.md | 1 + include/mgba/gb/interface.h | 1 + src/gb/mbc.c | 10 +++++++++- src/gb/mbc/mbc-private.h | 2 ++ src/gb/mbc/unlicensed.c | 31 +++++++++++++++++++++++++++++++ src/gb/memory.c | 2 ++ src/gb/overrides.c | 1 + src/platform/qt/GameBoy.cpp | 2 ++ 9 files changed, 50 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 3a69d0996..1cca99040 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,6 @@ 0.11.0: (Future) Features: - - New unlicensed GB mappers: NT (older types 1 and 2), Li Cheng + - New unlicensed GB mappers: NT (older types 1 and 2), Li Cheng, GGB-81 - Debugger: Add range watchpoints Other fixes: - Qt: Manually split filename to avoid overzealous splitting (fixes mgba.io/i/2681) diff --git a/README.md b/README.md index 6cf621925..20507604e 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ The following mappers are partially supported: - Sachen MMC2 (missing alternate wiring support) - BBD (missing logo switching) - Hitek (missing logo switching) +- GGB-81 (missing logo switching) - Li Cheng (missing logo switching) ### Planned features diff --git a/include/mgba/gb/interface.h b/include/mgba/gb/interface.h index 7c23bb8dd..0ec54559f 100644 --- a/include/mgba/gb/interface.h +++ b/include/mgba/gb/interface.h @@ -47,6 +47,7 @@ enum GBMemoryBankControllerType { GB_UNL_BBD = 0x220, GB_UNL_HITEK = 0x221, GB_UNL_LI_CHENG = 0x222, + GB_UNL_GGB81 = 0x223, GB_UNL_SACHEN_MMC1 = 0x230, GB_UNL_SACHEN_MMC2 = 0x231, }; diff --git a/src/gb/mbc.c b/src/gb/mbc.c index 441baaa53..27ccfe784 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -126,7 +126,7 @@ static struct { {"SAM2", GB_UNL_SACHEN_MMC2}, {"ROCK", GB_MBC_AUTODETECT}, // TODO {"NGHK", GB_MBC_AUTODETECT}, // TODO - {"GB81", GB_MBC_AUTODETECT}, // TODO + {"GB81", GB_UNL_GGB81}, {"TPP1", GB_MBC_AUTODETECT}, // TODO {NULL, GB_MBC_AUTODETECT}, @@ -211,6 +211,9 @@ static enum GBMemoryBankControllerType _detectUnlMBC(const uint8_t* mem, size_t return GB_UNL_BBD; } break; + case 0x79f34594: // DATA. + case 0x7e8c539b: // TD-SOFT + return GB_UNL_GGB81; case 0x20d092e2: case 0xd2b57657: if (cart->type == 0x01) { // Make sure we're not using a "fixed" version @@ -479,6 +482,11 @@ void GBMBCInit(struct GB* gb) { case GB_UNL_LI_CHENG: gb->memory.mbcWrite = _GBLiCheng; break; + case GB_UNL_GGB81: + gb->memory.mbcWrite = _GBGGB81; + gb->memory.mbcRead = _GBGGB81Read; + gb->memory.mbcReadBank1 = true; + break; case GB_UNL_SACHEN_MMC1: gb->memory.mbcWrite = _GBSachen; gb->memory.mbcRead = _GBSachenMMC1Read; diff --git a/src/gb/mbc/mbc-private.h b/src/gb/mbc/mbc-private.h index b841839f8..4a07ab716 100644 --- a/src/gb/mbc/mbc-private.h +++ b/src/gb/mbc/mbc-private.h @@ -36,6 +36,7 @@ void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value); void _GBBBD(struct GB* gb, uint16_t address, uint8_t value); void _GBHitek(struct GB* gb, uint16_t address, uint8_t value); void _GBLiCheng(struct GB* gb, uint16_t address, uint8_t value); +void _GBGGB81(struct GB* gb, uint16_t address, uint8_t value); void _GBSachen(struct GB* gb, uint16_t address, uint8_t value); uint8_t _GBMBC2Read(struct GBMemory*, uint16_t address); @@ -50,6 +51,7 @@ uint8_t _GBHuC3Read(struct GBMemory*, uint16_t address); uint8_t _GBPKJDRead(struct GBMemory*, uint16_t address); uint8_t _GBBBDRead(struct GBMemory*, uint16_t address); uint8_t _GBHitekRead(struct GBMemory*, uint16_t address); +uint8_t _GBGGB81Read(struct GBMemory*, uint16_t address); uint8_t _GBSachenMMC1Read(struct GBMemory*, uint16_t address); uint8_t _GBSachenMMC2Read(struct GBMemory*, uint16_t address); diff --git a/src/gb/mbc/unlicensed.c b/src/gb/mbc/unlicensed.c index 157dd34e6..58725fa98 100644 --- a/src/gb/mbc/unlicensed.c +++ b/src/gb/mbc/unlicensed.c @@ -362,6 +362,37 @@ uint8_t _GBHitekRead(struct GBMemory* memory, uint16_t address) { } } +static const uint8_t _ggb81DataReordering[8][8] = { + { 0, 1, 2, 3, 4, 5, 6, 7 }, + { 0, 2, 1, 3, 4, 6, 5, 7 }, + { 0, 6, 5, 3, 4, 2, 1, 7 }, + { 0, 5, 1, 3, 4, 2, 6, 7 }, + { 0, 5, 2, 3, 4, 1, 6, 7 }, + { 0, 2, 6, 3, 4, 5, 1, 7 }, + { 0, 1, 6, 3, 4, 2, 5, 7 }, + { 0, 2, 5, 3, 4, 6, 1, 7 }, +}; + +void _GBGGB81(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + switch (address & 0xF0FF) { + case 0x2001: + memory->mbcState.bbd.dataSwapMode = value & 0x07; + break; + } + _GBMBC5(gb, address, value); +} + +uint8_t _GBGGB81Read(struct GBMemory* memory, uint16_t address) { + switch (address >> 14) { + case 0: + default: + return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)]; + case 1: + return _reorderBits(memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)], _ggb81DataReordering[memory->mbcState.bbd.dataSwapMode]); + } +} + void _GBLiCheng(struct GB* gb, uint16_t address, uint8_t value) { if (address > 0x2100 && address < 0x3000) { return; diff --git a/src/gb/memory.c b/src/gb/memory.c index 8ac15dbdc..50b9b7d6f 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -816,6 +816,7 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) { break; case GB_UNL_BBD: case GB_UNL_HITEK: + case GB_UNL_GGB81: state->memory.bbd.dataSwapMode = memory->mbcState.bbd.dataSwapMode; state->memory.bbd.bankSwapMode = memory->mbcState.bbd.bankSwapMode; break; @@ -978,6 +979,7 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) { break; case GB_UNL_BBD: case GB_UNL_HITEK: + case GB_UNL_GGB81: memory->mbcState.bbd.dataSwapMode = state->memory.bbd.dataSwapMode & 0x7; memory->mbcState.bbd.bankSwapMode = state->memory.bbd.bankSwapMode & 0x7; break; diff --git a/src/gb/overrides.c b/src/gb/overrides.c index ca1a8e9bf..20e489b9a 100644 --- a/src/gb/overrides.c +++ b/src/gb/overrides.c @@ -695,6 +695,7 @@ static const struct GBCartridgeOverride _overrides[] = { { 0xBC75D7B8, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Pokemon - Mewtwo Strikes Back { 0xFF0B60CC, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Shuma Baolong 02 4 { 0x14A992A6, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // /Street Fighter Zero 4 + { 0x3EF5AFB2, GB_MODEL_AUTODETECT, GB_UNL_LI_CHENG, { 0 } }, // Pokemon Jade Version (Telefang Speed bootleg) { 0, 0, 0, { 0 } } }; diff --git a/src/platform/qt/GameBoy.cpp b/src/platform/qt/GameBoy.cpp index 0c1ec8a9b..b07982721 100644 --- a/src/platform/qt/GameBoy.cpp +++ b/src/platform/qt/GameBoy.cpp @@ -40,6 +40,7 @@ static const QList s_mbcList{ GB_UNL_NT_NEW, GB_UNL_BBD, GB_UNL_HITEK, + GB_UNL_GGB81, GB_UNL_LI_CHENG, GB_UNL_SACHEN_MMC1, GB_UNL_SACHEN_MMC2, @@ -97,6 +98,7 @@ QString GameBoy::mbcName(GBMemoryBankControllerType mbc) { s_mbcNames[GB_UNL_PKJD] = tr("Pokémon Jade/Diamond"); s_mbcNames[GB_UNL_BBD] = tr("BBD"); s_mbcNames[GB_UNL_HITEK] = tr("Hitek"); + s_mbcNames[GB_UNL_GGB81] = tr("GGB-81"); s_mbcNames[GB_UNL_LI_CHENG] = tr("Li Cheng"); s_mbcNames[GB_UNL_SACHEN_MMC1] = tr("Sachen (MMC1)"); s_mbcNames[GB_UNL_SACHEN_MMC2] = tr("Sachen (MMC2)");