diff --git a/CHANGES b/CHANGES index 2534b21e9..f6fa9915b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,6 @@ 0.11.0: (Future) Features: - - New unlicensed GB mapper: NT (older type 1) + - New unlicensed GB mapper: NT (older types 1 and 2) Other fixes: - Qt: Manually split filename to avoid overzealous splitting (fixes mgba.io/i/2681) - Qt: Expand criteria for tag branch names (fixes mgba.io/i/2679) diff --git a/README.md b/README.md index 2d8837c53..179c0d413 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ The following mappers are fully supported: - MBC5+Rumble - MBC7 - Wisdom Tree (unlicensed) +- NT "old type" 1 and 2 (unlicensed multicart) - NT "new type" (unlicensed MBC5-like) - Pokémon Jade/Diamond (unlicensed) - BBD (unlicensed MBC5-like) diff --git a/include/mgba/gb/interface.h b/include/mgba/gb/interface.h index 5a0433021..35170ea82 100644 --- a/include/mgba/gb/interface.h +++ b/include/mgba/gb/interface.h @@ -42,6 +42,7 @@ enum GBMemoryBankControllerType { GB_UNL_WISDOM_TREE = 0x200, GB_UNL_PKJD = 0x203, GB_UNL_NT_OLD_1 = 0x210, + GB_UNL_NT_OLD_2 = 0x211, 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 cefa138e9..d3a42a229 100644 --- a/include/mgba/internal/gb/memory.h +++ b/include/mgba/internal/gb/memory.h @@ -237,10 +237,11 @@ struct GBPKJDState { uint8_t reg[2]; }; -struct GBNTOld1State { +struct GBNTOldState { bool swapped; uint8_t baseBank; uint8_t bankCount; + bool rumble; }; struct GBNTNewState { @@ -268,7 +269,7 @@ union GBMBCState { struct GBPocketCamState pocketCam; struct GBTAMA5State tama5; struct GBHuC3State huc3; - struct GBNTOld1State ntOld1; + struct GBNTOldState ntOld; struct GBNTNewState ntNew; struct GBPKJDState pkjd; struct GBBBDState bbd; diff --git a/include/mgba/internal/gb/serialize.h b/include/mgba/internal/gb/serialize.h index 97c4f3ba1..f4433c495 100644 --- a/include/mgba/internal/gb/serialize.h +++ b/include/mgba/internal/gb/serialize.h @@ -278,6 +278,10 @@ DECL_BITFIELD(GBSerializedSachenFlags, uint8_t); DECL_BITS(GBSerializedSachenFlags, Transition, 0, 6); DECL_BITS(GBSerializedSachenFlags, Locked, 6, 2); +DECL_BITFIELD(GBSerializedNTOldFlags, uint8_t); +DECL_BIT(GBSerializedNTOldFlags, Swapped, 0); +DECL_BIT(GBSerializedNTOldFlags, Rumble, 1); + DECL_BITFIELD(GBSerializedMemoryFlags, uint16_t); DECL_BIT(GBSerializedMemoryFlags, SramAccess, 0); DECL_BIT(GBSerializedMemoryFlags, RtcAccess, 1); @@ -426,10 +430,10 @@ struct GBSerializedState { uint8_t mode; } huc3; struct { - uint8_t swapped; + GBSerializedNTOldFlags flags; uint8_t baseBank; uint8_t bankCount; - } ntOld1; + } ntOld; struct { uint8_t splitMode; uint8_t bank1; diff --git a/src/gb/mbc.c b/src/gb/mbc.c index ea2123844..18ec32ce6 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -46,6 +46,7 @@ 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 _GBNTOld1(struct GB* gb, uint16_t address, uint8_t value); +static void _GBNTOld2(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); @@ -161,7 +162,7 @@ static struct { {"HITK", GB_UNL_HITEK}, {"SNTX", GB_MBC_AUTODETECT}, // TODO {"NTO1", GB_UNL_NT_OLD_1}, - {"NTO2", GB_MBC_AUTODETECT}, // TODO + {"NTO2", GB_UNL_NT_OLD_2}, {"NTN", GB_UNL_NT_NEW}, {"LICH", GB_MBC_AUTODETECT}, // TODO {"LBMC", GB_MBC_AUTODETECT}, // TODO @@ -490,6 +491,9 @@ void GBMBCInit(struct GB* gb) { case GB_UNL_NT_OLD_1: gb->memory.mbcWrite = _GBNTOld1; break; + case GB_UNL_NT_OLD_2: + gb->memory.mbcWrite = _GBNTOld2; + break; case GB_UNL_NT_NEW: gb->memory.mbcWrite = _GBNTNew; break; @@ -1983,9 +1987,64 @@ static const uint8_t _ntOld1Reorder[8] = { 0, 2, 1, 4, 3, 5, 6, 7 }; +void _ntOldMulticart(struct GB* gb, uint16_t address, uint8_t value, const uint8_t reorder[8]) { + struct GBMemory* memory = &gb->memory; + struct GBNTOldState* mbcState = &memory->mbcState.ntOld; + int bank = value; + + switch (address & 3) { + case 0: + mLOG(GB_MBC, STUB, "Unimplemented NT Old 1 address 0"); + break; + case 1: + value &= 0x3F; + mbcState->baseBank = value * 2; + if (mbcState->baseBank) { + GBMBCSwitchBank0(gb, mbcState->baseBank); + GBMBCSwitchBank(gb, mbcState->baseBank + 1); + } + break; + case 2: + if ((value & 0xF0) == 0xE0) { + gb->sramSize = 0x2000; + GBResizeSram(gb, gb->sramSize); + } + switch (value & 0xF) { + case 0x00: + mbcState->bankCount = 32; + break; + case 0x08: + mbcState->bankCount = 16; + break; + case 0xC: + mbcState->bankCount = 8; + break; + case 0xE: + mbcState->bankCount = 4; + break; + case 0xF: + mbcState->bankCount = 2; + break; + default: + mbcState->bankCount = 32; + break; + } + break; + case 3: + mbcState->swapped = !!(value & 0x10); + + bank = memory->currentBank; + if (mbcState->swapped) { + bank = _reorderBits(bank, reorder); + } + GBMBCSwitchBank(gb, bank); + break; + } +} + void _GBNTOld1(struct GB* gb, uint16_t address, uint8_t value) { struct GBMemory* memory = &gb->memory; - struct GBNTOld1State* mbcState = &memory->mbcState.ntOld1; + struct GBNTOldState* mbcState = &memory->mbcState.ntOld; int bank = value; switch (address >> 12) { @@ -2006,57 +2065,52 @@ void _GBNTOld1(struct GB* gb, uint16_t address, uint8_t value) { bank &= mbcState->bankCount - 1; } GBMBCSwitchBank(gb, bank + mbcState->baseBank); - return; + break; case 0x5: - switch (address & 3) { - case 0: - mLOG(GB_MBC, STUB, "Unimplemented NT Old 1 address 0"); - break; - case 1: - value &= 0x3F; - mbcState->baseBank = value * 2; - if (mbcState->baseBank) { - GBMBCSwitchBank0(gb, mbcState->baseBank); - GBMBCSwitchBank(gb, mbcState->baseBank + 1); - } - break; - case 2: - if ((value & 0xF0) == 0xE0) { - gb->sramSize = 0x2000; - GBResizeSram(gb, gb->sramSize); - } - switch (value & 0xF) { - case 0x00: - mbcState->bankCount = 32; - break; - case 0x08: - mbcState->bankCount = 16; - break; - case 0xC: - mbcState->bankCount = 8; - break; - case 0xE: - mbcState->bankCount = 4; - break; - case 0xF: - mbcState->bankCount = 2; - break; - default: - mbcState->bankCount = 32; - break; - } - break; - case 3: - mbcState->swapped = !!(value & 0x10); + _ntOldMulticart(gb, address, value, _ntOld1Reorder); + break; + } +} - bank = memory->currentBank; - if (mbcState->swapped) { - bank = _reorderBits(bank, _ntOld1Reorder); - } - GBMBCSwitchBank(gb, bank); - break; +static const uint8_t _ntOld2Reorder[8] = { + 1, 2, 0, 3, 4, 5, 6, 7 +}; + +void _GBNTOld2(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + struct GBNTOldState* mbcState = &memory->mbcState.ntOld; + int bank = value; + + switch (address >> 12) { + case 0x0: + case 0x1: + _GBMBC3(gb, address, value); + break; + case 0x2: + case 0x3: + if (!bank) { + bank = 1; } - return; + if (mbcState->swapped) { + bank = _reorderBits(bank, _ntOld2Reorder); + } + if (mbcState->bankCount) { + bank &= mbcState->bankCount - 1; + } + GBMBCSwitchBank(gb, bank + mbcState->baseBank); + break; + case 0x5: + _ntOldMulticart(gb, address, value, _ntOld2Reorder); + // Fall through + case 0x4: + if (address == 0x5001) { + mbcState->rumble = !!(value & 0x80); + } + + if (mbcState->rumble) { + memory->rumble->setRumble(memory->rumble, !!(mbcState->swapped ? value & 0x08 : value & 0x02)); + } + break; } } diff --git a/src/gb/memory.c b/src/gb/memory.c index 5c7441e18..8ac15dbdc 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -804,9 +804,11 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) { state->memory.mmm01.bank0 = memory->mbcState.mmm01.currentBank0; break; case GB_UNL_NT_OLD_1: - state->memory.ntOld1.swapped = memory->mbcState.ntOld1.swapped; - state->memory.ntOld1.baseBank = memory->mbcState.ntOld1.baseBank; - state->memory.ntOld1.bankCount = memory->mbcState.ntOld1.bankCount; + case GB_UNL_NT_OLD_2: + state->memory.ntOld.flags = GBSerializedNTOldFlagsSetSwapped(0, memory->mbcState.ntOld.swapped); + state->memory.ntOld.flags = GBSerializedNTOldFlagsSetRumble(state->memory.ntOld.flags, memory->mbcState.ntOld.rumble); + state->memory.ntOld.baseBank = memory->mbcState.ntOld.baseBank; + state->memory.ntOld.bankCount = memory->mbcState.ntOld.bankCount; break; case GB_UNL_NT_NEW: state->memory.ntNew.splitMode = memory->mbcState.ntNew.splitMode; @@ -957,10 +959,12 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) { } break; case GB_UNL_NT_OLD_1: - memory->mbcState.ntOld1.swapped = state->memory.ntOld1.swapped; - memory->mbcState.ntOld1.baseBank = state->memory.ntOld1.baseBank; - memory->mbcState.ntOld1.bankCount = state->memory.ntOld1.bankCount; - GBMBCSwitchBank0(gb, memory->mbcState.ntOld1.baseBank); + case GB_UNL_NT_OLD_2: + memory->mbcState.ntOld.swapped = GBSerializedNTOldFlagsGetSwapped(state->memory.ntOld.flags); + memory->mbcState.ntOld.rumble = GBSerializedNTOldFlagsGetRumble(state->memory.ntOld.flags); + memory->mbcState.ntOld.baseBank = state->memory.ntOld.baseBank; + memory->mbcState.ntOld.bankCount = state->memory.ntOld.bankCount; + GBMBCSwitchBank0(gb, memory->mbcState.ntOld.baseBank); break; case GB_UNL_NT_NEW: memory->mbcState.ntNew.splitMode = state->memory.ntNew.splitMode; diff --git a/src/gb/overrides.c b/src/gb/overrides.c index a36edf2a3..ca1a8e9bf 100644 --- a/src/gb/overrides.c +++ b/src/gb/overrides.c @@ -672,9 +672,19 @@ static const struct GBCartridgeOverride _overrides[] = { { 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) + // Unlicensed bootlegs { 0x30F8F86C, GB_MODEL_AUTODETECT, GB_UNL_PKJD, { 0 } }, // Pokemon Jade Version (Telefang Speed bootleg) { 0xE1147E75, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_1, { 0 } }, // Rockman 8 { 0xEFF88FAA, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_1, { 0 } }, // True Color 25 in 1 (NT-9920) + { 0x811925D9, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // 23 in 1 (CR2011) + { 0x62A8016A, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // 29 in 1 (CR2020) + { 0x5758D6D9, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Caise Gedou 24 in 1 Diannao Huamian Xuan Game (CY2060) + { 0x62A8016A, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Caise Gedou 29 in 1 Diannao Huamian Xuan Game (CY2061) + { 0x80265A64, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Rockman X4 (Megaman X4) + { 0x805459DE, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Sonic Adventure 8 + { 0x0B1B808A, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Super Donkey Kong 5 + { 0x0B1B808A, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Super Donkey Kong 5 (Alt) + { 0x4650EB9A, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Super Mario Special 3 { 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