From d33471e117a313b0f49b80ad70a8580df73768da Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 6 Feb 2022 21:43:53 -0800 Subject: [PATCH] GB MBC: Add NT (new) support (closes #2435) --- CHANGES | 1 + include/mgba/gb/interface.h | 1 + include/mgba/internal/gb/memory.h | 14 +++++--- src/gb/mbc.c | 56 ++++++++++++++++++++++++------- src/gb/memory.c | 8 ++--- src/gb/overrides.c | 20 ++++++++--- src/platform/qt/GameBoy.cpp | 4 ++- 7 files changed, 77 insertions(+), 27 deletions(-) diff --git a/CHANGES b/CHANGES index 0162d2de5..c6a63a7c5 100644 --- a/CHANGES +++ b/CHANGES @@ -13,6 +13,7 @@ Features: - Additional scaling shaders - Support for GameShark Advance SP (.gsv) save file importing - Support for multiple saves per game using .sa2, .sa3, etc. + - New unlicensed GB mappers: NT (newer type) Emulation fixes: - ARM7: Fix unsigned multiply timing - GB Memory: Add cursory cartridge open bus emulation (fixes mgba.io/i/2032) diff --git a/include/mgba/gb/interface.h b/include/mgba/gb/interface.h index 420a81840..997312c3a 100644 --- a/include/mgba/gb/interface.h +++ b/include/mgba/gb/interface.h @@ -41,6 +41,7 @@ enum GBMemoryBankControllerType { GB_MBC5_RUMBLE = 0x105, GB_UNL_WISDOM_TREE = 0x200, GB_UNL_PKJD = 0x203, + GB_UNL_NT_NEW = 0x212, GB_UNL_BBD = 0x220, // Also used as a mask for MBCs that need special read behavior GB_UNL_HITEK = 0x221, }; diff --git a/include/mgba/internal/gb/memory.h b/include/mgba/internal/gb/memory.h index 59691682c..ce3e9079c 100644 --- a/include/mgba/internal/gb/memory.h +++ b/include/mgba/internal/gb/memory.h @@ -145,11 +145,7 @@ struct GBMBC1State { }; struct GBMBC6State { - int currentBank1; - uint8_t* romBank1; bool sramAccess; - int currentSramBank1; - uint8_t* sramBank1; bool flashBank0; bool flashBank1; }; @@ -191,6 +187,10 @@ struct GBPKJDState { uint8_t reg[2]; }; +struct GBNTNewState { + bool splitMode; +}; + struct GBBBDState { int dataSwapMode; int bankSwapMode; @@ -204,6 +204,7 @@ union GBMBCState { struct GBPocketCamState pocketCam; struct GBTAMA5State tama5; struct GBHuC3State huc3; + struct GBNTNewState ntNew; struct GBPKJDState pkjd; struct GBBBDState bbd; }; @@ -219,6 +220,11 @@ struct GBMemory { union GBMBCState mbcState; int currentBank; int currentBank0; + int currentBank1; + uint8_t* romBank1; + int currentSramBank1; + uint8_t* sramBank1; + unsigned cartBusDecay; uint16_t cartBusPc; uint8_t cartBus; diff --git a/src/gb/mbc.c b/src/gb/mbc.c index 99190627c..21932a456 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -38,6 +38,7 @@ static void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value); static void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value); static void _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value); static void _GBPKJD(struct GB* gb, uint16_t address, uint8_t value); +static void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value); static void _GBBBD(struct GB* gb, uint16_t address, uint8_t value); static void _GBHitek(struct GB* gb, uint16_t address, uint8_t value); @@ -86,7 +87,10 @@ void GBMBCSwitchBank0(struct GB* gb, int bank) { void GBMBCSwitchHalfBank(struct GB* gb, int half, int bank) { size_t bankStart = bank * GB_SIZE_CART_HALFBANK; - bool isFlash = half ? gb->memory.mbcState.mbc6.flashBank1 : gb->memory.mbcState.mbc6.flashBank0; + bool isFlash = false; + if (gb->memory.mbcType == GB_MBC6) { + isFlash = half ? gb->memory.mbcState.mbc6.flashBank1 : gb->memory.mbcState.mbc6.flashBank0; + } if (isFlash) { if (bankStart + GB_SIZE_CART_HALFBANK > GB_SIZE_MBC6_FLASH) { mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid Flash bank: %0X", bank); @@ -113,11 +117,11 @@ void GBMBCSwitchHalfBank(struct GB* gb, int half, int bank) { gb->memory.currentBank = bank; } else { if (isFlash) { - gb->memory.mbcState.mbc6.romBank1 = &gb->memory.sram[bankStart]; + gb->memory.romBank1 = &gb->memory.sram[bankStart]; } else { - gb->memory.mbcState.mbc6.romBank1 = &gb->memory.rom[bankStart]; + gb->memory.romBank1 = &gb->memory.rom[bankStart]; } - gb->memory.mbcState.mbc6.currentBank1 = bank; + gb->memory.currentBank1 = bank; } if (gb->cpu->pc < GB_BASE_VRAM) { gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc); @@ -220,8 +224,8 @@ void GBMBCSwitchSramHalfBank(struct GB* gb, int half, int bank) { gb->memory.sramBank = &gb->memory.sram[bankStart]; gb->memory.sramCurrentBank = bank; } else { - gb->memory.mbcState.mbc6.sramBank1 = &gb->memory.sram[bankStart]; - gb->memory.mbcState.mbc6.currentSramBank1 = bank; + gb->memory.sramBank1 = &gb->memory.sram[bankStart]; + gb->memory.currentSramBank1 = bank; } } @@ -403,6 +407,13 @@ void GBMBCInit(struct GB* gb) { case GB_UNL_WISDOM_TREE: gb->memory.mbcWrite = _GBWisdomTree; break; + case GB_UNL_NT_NEW: + gb->memory.mbcWrite = _GBNTNew; + break; + case GB_UNL_PKJD: + gb->memory.mbcWrite = _GBPKJD; + gb->memory.mbcRead = _GBPKJDRead; + break; case GB_UNL_BBD: gb->memory.mbcWrite = _GBBBD; gb->memory.mbcRead = _GBBBDRead; @@ -413,10 +424,6 @@ void GBMBCInit(struct GB* gb) { gb->memory.mbcState.bbd.dataSwapMode = 7; gb->memory.mbcState.bbd.bankSwapMode = 7; break; - case GB_UNL_PKJD: - gb->memory.mbcWrite = _GBPKJD; - gb->memory.mbcRead = _GBPKJDRead; - break; } gb->memory.currentBank = 1; @@ -790,7 +797,7 @@ void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) { case 0x2E: case 0x2F: if (memory->sramAccess) { - memory->mbcState.mbc6.sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value; + memory->sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value; } break; default: @@ -807,7 +814,7 @@ uint8_t _GBMBC6Read(struct GBMemory* memory, uint16_t address) { case 0xA: return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)]; case 0xB: - return memory->mbcState.mbc6.sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)]; + return memory->sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)]; } return 0xFF; } @@ -818,7 +825,7 @@ static void _GBMBC6MapChip(struct GB* gb, int half, uint8_t value) { GBMBCSwitchHalfBank(gb, half, gb->memory.currentBank); } else { gb->memory.mbcState.mbc6.flashBank1 = !!(value & 0x08); - GBMBCSwitchHalfBank(gb, half, gb->memory.mbcState.mbc6.currentBank1); + GBMBCSwitchHalfBank(gb, half, gb->memory.currentBank1); } } @@ -1577,6 +1584,29 @@ static uint8_t _GBPKJDRead(struct GBMemory* memory, uint16_t address) { } } +void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + if (address >> 8 == 0x14) { + memory->mbcState.ntNew.splitMode = true; + return; + } + if (memory->mbcState.ntNew.splitMode) { + int bank = value; + if (bank < 2) { + bank = 2; + } + switch (address >> 10) { + case 8: + GBMBCSwitchHalfBank(gb, 0, bank); + return; + case 9: + GBMBCSwitchHalfBank(gb, 1, bank); + return; + } + } + _GBMBC5(gb, address, value); +} + static uint8_t _reorderBits(uint8_t input, const uint8_t* reorder) { uint8_t newbyte = 0; int i; diff --git a/src/gb/memory.c b/src/gb/memory.c index 66c35c644..f80fef77b 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -95,14 +95,14 @@ static void GBSetActiveRegion(struct SM83Core* cpu, uint16_t address) { break; } cpu->memory.cpuLoad8 = GBCartLoad8; - if (gb->memory.mbcType != GB_MBC6) { + if (gb->memory.mbcType != GB_MBC6 && !(gb->memory.mbcType == GB_UNL_NT_NEW && gb->memory.mbcState.ntNew.splitMode)) { cpu->memory.activeRegion = memory->romBank; cpu->memory.activeRegionEnd = GB_BASE_VRAM; cpu->memory.activeMask = GB_SIZE_CART_BANK0 - 1; } else { cpu->memory.activeMask = GB_SIZE_CART_HALFBANK - 1; if (address & 0x2000) { - cpu->memory.activeRegion = memory->mbcState.mbc6.romBank1; + cpu->memory.activeRegion = memory->romBank1; cpu->memory.activeRegionEnd = GB_BASE_VRAM; } else { cpu->memory.activeRegion = memory->romBank; @@ -252,8 +252,8 @@ uint8_t GBLoad8(struct SM83Core* cpu, uint16_t address) { return memory->cartBus; case GB_REGION_CART_BANK1 + 2: case GB_REGION_CART_BANK1 + 3: - if (memory->mbcType == GB_MBC6) { - memory->cartBus = memory->mbcState.mbc6.romBank1[address & (GB_SIZE_CART_HALFBANK - 1)]; + if (gb->memory.mbcType == GB_MBC6 || (gb->memory.mbcType == GB_UNL_NT_NEW && gb->memory.mbcState.ntNew.splitMode)) { + memory->cartBus = memory->romBank1[address & (GB_SIZE_CART_HALFBANK - 1)]; memory->cartBusPc = cpu->pc; return memory->cartBus; } diff --git a/src/gb/overrides.c b/src/gb/overrides.c index 7a17ea693..5bfc4f3cf 100644 --- a/src/gb/overrides.c +++ b/src/gb/overrides.c @@ -668,11 +668,21 @@ static const struct GBCartridgeOverride _sgbOverrides[] = { static const struct GBCartridgeOverride _overrides[] = { // 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) - { 0x5aff0038, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (debug) - { 0xa61856bd, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (non-debug) - { 0x30f8f86c, GB_MODEL_AUTODETECT, GB_UNL_PKJD, { 0 } }, // Pokemon Jade Version (Telefang Speed bootleg) + { 0x232A067D, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (debug) + { 0x630ED957, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (non-debug) + { 0x5AFF0038, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (debug) + { 0xA61856BD, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (non-debug) + { 0x30F8F86C, GB_MODEL_AUTODETECT, GB_UNL_PKJD, { 0 } }, // Pokemon Jade Version (Telefang Speed bootleg) + { 0xB289D95A, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Capcom vs SNK - Millennium Fight 2001 + { 0x688D6713, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Digimon 02 4 + { 0x8931A272, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Digimon 2 + { 0x79083C6B, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Digimon Pocket + { 0x0C5047EE, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Harry Potter 3 + { 0x8AC634B7, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Pokemon Diamond (Special Pikachu Edition) + { 0x8628A287, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Pokemon Jade (Special Pikachu Edition) + { 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 { 0, 0, 0, { 0 } } }; diff --git a/src/platform/qt/GameBoy.cpp b/src/platform/qt/GameBoy.cpp index 1748152d7..c27218afc 100644 --- a/src/platform/qt/GameBoy.cpp +++ b/src/platform/qt/GameBoy.cpp @@ -34,9 +34,10 @@ static const QList s_mbcList{ GB_HuC1, GB_HuC3, GB_UNL_WISDOM_TREE, + GB_UNL_PKJD, + GB_UNL_NT_NEW, GB_UNL_BBD, GB_UNL_HITEK, - GB_UNL_PKJD, }; static QMap s_gbModelNames; @@ -87,6 +88,7 @@ QString GameBoy::mbcName(GBMemoryBankControllerType mbc) { s_mbcNames[GB_POCKETCAM] = tr("Pocket Cam"); s_mbcNames[GB_TAMA5] = tr("TAMA5"); s_mbcNames[GB_UNL_WISDOM_TREE] = tr("Wisdom Tree"); + s_mbcNames[GB_UNL_NT_NEW] = tr("NT (new)"); s_mbcNames[GB_UNL_PKJD] = tr("Pokémon Jade/Diamond"); s_mbcNames[GB_UNL_BBD] = tr("BBD"); s_mbcNames[GB_UNL_HITEK] = tr("Hitek");