GB MBC: Add NT (old 2) support

This commit is contained in:
Vicki Pfau 2022-10-15 23:41:51 -07:00
parent 506b9c69be
commit e370f64801
8 changed files with 137 additions and 62 deletions

View File

@ -1,6 +1,6 @@
0.11.0: (Future) 0.11.0: (Future)
Features: Features:
- New unlicensed GB mapper: NT (older type 1) - New unlicensed GB mapper: NT (older types 1 and 2)
Other fixes: Other fixes:
- Qt: Manually split filename to avoid overzealous splitting (fixes mgba.io/i/2681) - 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) - Qt: Expand criteria for tag branch names (fixes mgba.io/i/2679)

View File

@ -54,6 +54,7 @@ The following mappers are fully supported:
- MBC5+Rumble - MBC5+Rumble
- MBC7 - MBC7
- Wisdom Tree (unlicensed) - Wisdom Tree (unlicensed)
- NT "old type" 1 and 2 (unlicensed multicart)
- NT "new type" (unlicensed MBC5-like) - NT "new type" (unlicensed MBC5-like)
- Pokémon Jade/Diamond (unlicensed) - Pokémon Jade/Diamond (unlicensed)
- BBD (unlicensed MBC5-like) - BBD (unlicensed MBC5-like)

View File

@ -42,6 +42,7 @@ enum GBMemoryBankControllerType {
GB_UNL_WISDOM_TREE = 0x200, GB_UNL_WISDOM_TREE = 0x200,
GB_UNL_PKJD = 0x203, GB_UNL_PKJD = 0x203,
GB_UNL_NT_OLD_1 = 0x210, GB_UNL_NT_OLD_1 = 0x210,
GB_UNL_NT_OLD_2 = 0x211,
GB_UNL_NT_NEW = 0x212, GB_UNL_NT_NEW = 0x212,
GB_UNL_BBD = 0x220, // Also used as a mask for MBCs that need special read behavior GB_UNL_BBD = 0x220, // Also used as a mask for MBCs that need special read behavior
GB_UNL_HITEK = 0x221, GB_UNL_HITEK = 0x221,

View File

@ -237,10 +237,11 @@ struct GBPKJDState {
uint8_t reg[2]; uint8_t reg[2];
}; };
struct GBNTOld1State { struct GBNTOldState {
bool swapped; bool swapped;
uint8_t baseBank; uint8_t baseBank;
uint8_t bankCount; uint8_t bankCount;
bool rumble;
}; };
struct GBNTNewState { struct GBNTNewState {
@ -268,7 +269,7 @@ union GBMBCState {
struct GBPocketCamState pocketCam; struct GBPocketCamState pocketCam;
struct GBTAMA5State tama5; struct GBTAMA5State tama5;
struct GBHuC3State huc3; struct GBHuC3State huc3;
struct GBNTOld1State ntOld1; struct GBNTOldState ntOld;
struct GBNTNewState ntNew; struct GBNTNewState ntNew;
struct GBPKJDState pkjd; struct GBPKJDState pkjd;
struct GBBBDState bbd; struct GBBBDState bbd;

View File

@ -278,6 +278,10 @@ DECL_BITFIELD(GBSerializedSachenFlags, uint8_t);
DECL_BITS(GBSerializedSachenFlags, Transition, 0, 6); DECL_BITS(GBSerializedSachenFlags, Transition, 0, 6);
DECL_BITS(GBSerializedSachenFlags, Locked, 6, 2); 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_BITFIELD(GBSerializedMemoryFlags, uint16_t);
DECL_BIT(GBSerializedMemoryFlags, SramAccess, 0); DECL_BIT(GBSerializedMemoryFlags, SramAccess, 0);
DECL_BIT(GBSerializedMemoryFlags, RtcAccess, 1); DECL_BIT(GBSerializedMemoryFlags, RtcAccess, 1);
@ -426,10 +430,10 @@ struct GBSerializedState {
uint8_t mode; uint8_t mode;
} huc3; } huc3;
struct { struct {
uint8_t swapped; GBSerializedNTOldFlags flags;
uint8_t baseBank; uint8_t baseBank;
uint8_t bankCount; uint8_t bankCount;
} ntOld1; } ntOld;
struct { struct {
uint8_t splitMode; uint8_t splitMode;
uint8_t bank1; uint8_t bank1;

View File

@ -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 _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value);
static void _GBPKJD(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 _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 _GBNTNew(struct GB* gb, uint16_t address, uint8_t value);
static void _GBBBD(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); static void _GBHitek(struct GB* gb, uint16_t address, uint8_t value);
@ -161,7 +162,7 @@ static struct {
{"HITK", GB_UNL_HITEK}, {"HITK", GB_UNL_HITEK},
{"SNTX", GB_MBC_AUTODETECT}, // TODO {"SNTX", GB_MBC_AUTODETECT}, // TODO
{"NTO1", GB_UNL_NT_OLD_1}, {"NTO1", GB_UNL_NT_OLD_1},
{"NTO2", GB_MBC_AUTODETECT}, // TODO {"NTO2", GB_UNL_NT_OLD_2},
{"NTN", GB_UNL_NT_NEW}, {"NTN", GB_UNL_NT_NEW},
{"LICH", GB_MBC_AUTODETECT}, // TODO {"LICH", GB_MBC_AUTODETECT}, // TODO
{"LBMC", GB_MBC_AUTODETECT}, // TODO {"LBMC", GB_MBC_AUTODETECT}, // TODO
@ -490,6 +491,9 @@ void GBMBCInit(struct GB* gb) {
case GB_UNL_NT_OLD_1: case GB_UNL_NT_OLD_1:
gb->memory.mbcWrite = _GBNTOld1; gb->memory.mbcWrite = _GBNTOld1;
break; break;
case GB_UNL_NT_OLD_2:
gb->memory.mbcWrite = _GBNTOld2;
break;
case GB_UNL_NT_NEW: case GB_UNL_NT_NEW:
gb->memory.mbcWrite = _GBNTNew; gb->memory.mbcWrite = _GBNTNew;
break; break;
@ -1983,9 +1987,64 @@ static const uint8_t _ntOld1Reorder[8] = {
0, 2, 1, 4, 3, 5, 6, 7 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) { void _GBNTOld1(struct GB* gb, uint16_t address, uint8_t value) {
struct GBMemory* memory = &gb->memory; struct GBMemory* memory = &gb->memory;
struct GBNTOld1State* mbcState = &memory->mbcState.ntOld1; struct GBNTOldState* mbcState = &memory->mbcState.ntOld;
int bank = value; int bank = value;
switch (address >> 12) { switch (address >> 12) {
@ -2006,57 +2065,52 @@ void _GBNTOld1(struct GB* gb, uint16_t address, uint8_t value) {
bank &= mbcState->bankCount - 1; bank &= mbcState->bankCount - 1;
} }
GBMBCSwitchBank(gb, bank + mbcState->baseBank); GBMBCSwitchBank(gb, bank + mbcState->baseBank);
return; break;
case 0x5: case 0x5:
switch (address & 3) { _ntOldMulticart(gb, address, value, _ntOld1Reorder);
case 0: break;
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; static const uint8_t _ntOld2Reorder[8] = {
if (mbcState->swapped) { 1, 2, 0, 3, 4, 5, 6, 7
bank = _reorderBits(bank, _ntOld1Reorder); };
}
GBMBCSwitchBank(gb, bank); void _GBNTOld2(struct GB* gb, uint16_t address, uint8_t value) {
break; 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;
} }
} }

View File

@ -804,9 +804,11 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) {
state->memory.mmm01.bank0 = memory->mbcState.mmm01.currentBank0; state->memory.mmm01.bank0 = memory->mbcState.mmm01.currentBank0;
break; break;
case GB_UNL_NT_OLD_1: case GB_UNL_NT_OLD_1:
state->memory.ntOld1.swapped = memory->mbcState.ntOld1.swapped; case GB_UNL_NT_OLD_2:
state->memory.ntOld1.baseBank = memory->mbcState.ntOld1.baseBank; state->memory.ntOld.flags = GBSerializedNTOldFlagsSetSwapped(0, memory->mbcState.ntOld.swapped);
state->memory.ntOld1.bankCount = memory->mbcState.ntOld1.bankCount; 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; break;
case GB_UNL_NT_NEW: case GB_UNL_NT_NEW:
state->memory.ntNew.splitMode = memory->mbcState.ntNew.splitMode; state->memory.ntNew.splitMode = memory->mbcState.ntNew.splitMode;
@ -957,10 +959,12 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
} }
break; break;
case GB_UNL_NT_OLD_1: case GB_UNL_NT_OLD_1:
memory->mbcState.ntOld1.swapped = state->memory.ntOld1.swapped; case GB_UNL_NT_OLD_2:
memory->mbcState.ntOld1.baseBank = state->memory.ntOld1.baseBank; memory->mbcState.ntOld.swapped = GBSerializedNTOldFlagsGetSwapped(state->memory.ntOld.flags);
memory->mbcState.ntOld1.bankCount = state->memory.ntOld1.bankCount; memory->mbcState.ntOld.rumble = GBSerializedNTOldFlagsGetRumble(state->memory.ntOld.flags);
GBMBCSwitchBank0(gb, memory->mbcState.ntOld1.baseBank); memory->mbcState.ntOld.baseBank = state->memory.ntOld.baseBank;
memory->mbcState.ntOld.bankCount = state->memory.ntOld.bankCount;
GBMBCSwitchBank0(gb, memory->mbcState.ntOld.baseBank);
break; break;
case GB_UNL_NT_NEW: case GB_UNL_NT_NEW:
memory->mbcState.ntNew.splitMode = state->memory.ntNew.splitMode; memory->mbcState.ntNew.splitMode = state->memory.ntNew.splitMode;

View File

@ -672,9 +672,19 @@ static const struct GBCartridgeOverride _overrides[] = {
{ 0x630ED957, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (non-debug) { 0x630ED957, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (non-debug)
{ 0x5AFF0038, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (debug) { 0x5AFF0038, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (debug)
{ 0xA61856BD, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (non-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) { 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 { 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) { 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 { 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 { 0x688D6713, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Digimon 02 4
{ 0x8931A272, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Digimon 2 { 0x8931A272, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Digimon 2