GB MBC: Add NT (old 1) support

This commit is contained in:
Vicki Pfau 2022-10-15 04:05:56 -07:00
parent 47e704d257
commit 0bd4ad034e
8 changed files with 129 additions and 13 deletions

View File

@ -1,4 +1,6 @@
0.11.0: (Future)
Features:
- New unlicensed GB mapper: NT (older type 1)
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)

View File

@ -41,6 +41,7 @@ enum GBMemoryBankControllerType {
GB_MBC5_RUMBLE = 0x105,
GB_UNL_WISDOM_TREE = 0x200,
GB_UNL_PKJD = 0x203,
GB_UNL_NT_OLD_1 = 0x210,
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,

View File

@ -238,6 +238,12 @@ struct GBPKJDState {
uint8_t reg[2];
};
struct GBNTOld1State {
bool swapped;
uint8_t baseBank;
uint8_t bankCount;
};
struct GBNTNewState {
bool splitMode;
};
@ -263,6 +269,7 @@ union GBMBCState {
struct GBPocketCamState pocketCam;
struct GBTAMA5State tama5;
struct GBHuC3State huc3;
struct GBNTOld1State ntOld1;
struct GBNTNewState ntNew;
struct GBPKJDState pkjd;
struct GBBBDState bbd;

View File

@ -416,6 +416,11 @@ struct GBSerializedState {
uint8_t value;
uint8_t mode;
} huc3;
struct {
uint8_t swapped;
uint8_t baseBank;
uint8_t bankCount;
} ntOld1;
struct {
uint8_t dataSwapMode;
uint8_t bankSwapMode;

View File

@ -45,6 +45,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 _GBNTOld1(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);
@ -159,7 +160,7 @@ static struct {
{"BBD", GB_UNL_BBD},
{"HITK", GB_UNL_HITEK},
{"SNTX", GB_MBC_AUTODETECT}, // TODO
{"NTO1", GB_MBC_AUTODETECT}, // TODO
{"NTO1", GB_UNL_NT_OLD_1},
{"NTO2", GB_MBC_AUTODETECT}, // TODO
{"NTN", GB_UNL_NT_NEW},
{"LICH", GB_MBC_AUTODETECT}, // TODO
@ -486,6 +487,9 @@ void GBMBCInit(struct GB* gb) {
case GB_UNL_WISDOM_TREE:
gb->memory.mbcWrite = _GBWisdomTree;
break;
case GB_UNL_NT_OLD_1:
gb->memory.mbcWrite = _GBNTOld1;
break;
case GB_UNL_NT_NEW:
gb->memory.mbcWrite = _GBNTNew;
break;
@ -1963,6 +1967,100 @@ static uint8_t _GBPKJDRead(struct GBMemory* memory, uint16_t address) {
}
}
static uint8_t _reorderBits(uint8_t input, const uint8_t* reorder) {
uint8_t newbyte = 0;
int i;
for(i = 0; i < 8; ++i) {
int oldbit = reorder[i];
int newbit = i;
newbyte += ((input >> oldbit) & 1) << newbit;
}
return newbyte;
}
static const uint8_t _ntOld1Reorder[8] = {
0, 2, 1, 4, 3, 5, 6, 7
};
void _GBNTOld1(struct GB* gb, uint16_t address, uint8_t value) {
struct GBMemory* memory = &gb->memory;
struct GBNTOld1State* mbcState = &memory->mbcState.ntOld1;
int bank = value;
switch (address >> 12) {
case 0x0:
case 0x1:
_GBMBC3(gb, address, value);
break;
case 0x2:
case 0x3:
bank &= 0x1F;
if (!bank) {
bank = 1;
}
if (mbcState->swapped) {
bank = _reorderBits(bank, _ntOld1Reorder);
}
if (mbcState->bankCount) {
bank &= mbcState->bankCount - 1;
}
GBMBCSwitchBank(gb, bank + mbcState->baseBank);
return;
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);
bank = memory->currentBank;
if (mbcState->swapped) {
bank = _reorderBits(bank, _ntOld1Reorder);
}
GBMBCSwitchBank(gb, bank);
break;
}
return;
}
}
void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value) {
struct GBMemory* memory = &gb->memory;
if (address >> 8 == 0x14) {
@ -1986,18 +2084,6 @@ void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value) {
_GBMBC5(gb, address, value);
}
static uint8_t _reorderBits(uint8_t input, const uint8_t* reorder) {
uint8_t newbyte = 0;
int i;
for(i = 0; i < 8; ++i) {
int oldbit = reorder[i];
int newbit = i;
newbyte += ((input >> oldbit) & 1) << newbit;
}
return newbyte;
}
static const uint8_t _bbdDataReordering[8][8] = {
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 00 - Normal
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 01 - NOT KNOWN YET

View File

@ -797,6 +797,11 @@ 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_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;
break;
case GB_UNL_BBD:
case GB_UNL_HITEK:
state->memory.bbd.dataSwapMode = memory->mbcState.bbd.dataSwapMode;
@ -929,6 +934,12 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
GBMBCSwitchBank0(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 2);
}
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);
break;
case GB_UNL_BBD:
case GB_UNL_HITEK:
memory->mbcState.bbd.dataSwapMode = state->memory.bbd.dataSwapMode & 0x7;

View File

@ -673,6 +673,8 @@ static const struct GBCartridgeOverride _overrides[] = {
{ 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)
{ 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)
{ 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

View File

@ -35,6 +35,7 @@ static const QList<GBMemoryBankControllerType> s_mbcList{
GB_HuC3,
GB_UNL_WISDOM_TREE,
GB_UNL_PKJD,
GB_UNL_NT_OLD_1,
GB_UNL_NT_NEW,
GB_UNL_BBD,
GB_UNL_HITEK,
@ -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_OLD_1] = tr("NT (old 1)");
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");