mirror of https://github.com/mgba-emu/mgba.git
GB MBC: Add NT (old 1) support
This commit is contained in:
parent
47e704d257
commit
0bd4ad034e
2
CHANGES
2
CHANGES
|
@ -1,4 +1,6 @@
|
||||||
0.11.0: (Future)
|
0.11.0: (Future)
|
||||||
|
Features:
|
||||||
|
- New unlicensed GB mapper: NT (older type 1)
|
||||||
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)
|
||||||
|
|
|
@ -41,6 +41,7 @@ enum GBMemoryBankControllerType {
|
||||||
GB_MBC5_RUMBLE = 0x105,
|
GB_MBC5_RUMBLE = 0x105,
|
||||||
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_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,
|
||||||
|
|
|
@ -238,6 +238,12 @@ struct GBPKJDState {
|
||||||
uint8_t reg[2];
|
uint8_t reg[2];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct GBNTOld1State {
|
||||||
|
bool swapped;
|
||||||
|
uint8_t baseBank;
|
||||||
|
uint8_t bankCount;
|
||||||
|
};
|
||||||
|
|
||||||
struct GBNTNewState {
|
struct GBNTNewState {
|
||||||
bool splitMode;
|
bool splitMode;
|
||||||
};
|
};
|
||||||
|
@ -263,6 +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 GBNTNewState ntNew;
|
struct GBNTNewState ntNew;
|
||||||
struct GBPKJDState pkjd;
|
struct GBPKJDState pkjd;
|
||||||
struct GBBBDState bbd;
|
struct GBBBDState bbd;
|
||||||
|
|
|
@ -416,6 +416,11 @@ struct GBSerializedState {
|
||||||
uint8_t value;
|
uint8_t value;
|
||||||
uint8_t mode;
|
uint8_t mode;
|
||||||
} huc3;
|
} huc3;
|
||||||
|
struct {
|
||||||
|
uint8_t swapped;
|
||||||
|
uint8_t baseBank;
|
||||||
|
uint8_t bankCount;
|
||||||
|
} ntOld1;
|
||||||
struct {
|
struct {
|
||||||
uint8_t dataSwapMode;
|
uint8_t dataSwapMode;
|
||||||
uint8_t bankSwapMode;
|
uint8_t bankSwapMode;
|
||||||
|
|
112
src/gb/mbc.c
112
src/gb/mbc.c
|
@ -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 _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 _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);
|
||||||
|
@ -159,7 +160,7 @@ static struct {
|
||||||
{"BBD", GB_UNL_BBD},
|
{"BBD", GB_UNL_BBD},
|
||||||
{"HITK", GB_UNL_HITEK},
|
{"HITK", GB_UNL_HITEK},
|
||||||
{"SNTX", GB_MBC_AUTODETECT}, // TODO
|
{"SNTX", GB_MBC_AUTODETECT}, // TODO
|
||||||
{"NTO1", GB_MBC_AUTODETECT}, // TODO
|
{"NTO1", GB_UNL_NT_OLD_1},
|
||||||
{"NTO2", GB_MBC_AUTODETECT}, // TODO
|
{"NTO2", GB_MBC_AUTODETECT}, // TODO
|
||||||
{"NTN", GB_UNL_NT_NEW},
|
{"NTN", GB_UNL_NT_NEW},
|
||||||
{"LICH", GB_MBC_AUTODETECT}, // TODO
|
{"LICH", GB_MBC_AUTODETECT}, // TODO
|
||||||
|
@ -486,6 +487,9 @@ void GBMBCInit(struct GB* gb) {
|
||||||
case GB_UNL_WISDOM_TREE:
|
case GB_UNL_WISDOM_TREE:
|
||||||
gb->memory.mbcWrite = _GBWisdomTree;
|
gb->memory.mbcWrite = _GBWisdomTree;
|
||||||
break;
|
break;
|
||||||
|
case GB_UNL_NT_OLD_1:
|
||||||
|
gb->memory.mbcWrite = _GBNTOld1;
|
||||||
|
break;
|
||||||
case GB_UNL_NT_NEW:
|
case GB_UNL_NT_NEW:
|
||||||
gb->memory.mbcWrite = _GBNTNew;
|
gb->memory.mbcWrite = _GBNTNew;
|
||||||
break;
|
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) {
|
void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value) {
|
||||||
struct GBMemory* memory = &gb->memory;
|
struct GBMemory* memory = &gb->memory;
|
||||||
if (address >> 8 == 0x14) {
|
if (address >> 8 == 0x14) {
|
||||||
|
@ -1986,18 +2084,6 @@ void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value) {
|
||||||
_GBMBC5(gb, address, 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] = {
|
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 }, // 00 - Normal
|
||||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 01 - NOT KNOWN YET
|
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 01 - NOT KNOWN YET
|
||||||
|
|
|
@ -797,6 +797,11 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) {
|
||||||
state->memory.mmm01.locked = memory->mbcState.mmm01.locked;
|
state->memory.mmm01.locked = memory->mbcState.mmm01.locked;
|
||||||
state->memory.mmm01.bank0 = memory->mbcState.mmm01.currentBank0;
|
state->memory.mmm01.bank0 = memory->mbcState.mmm01.currentBank0;
|
||||||
break;
|
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_BBD:
|
||||||
case GB_UNL_HITEK:
|
case GB_UNL_HITEK:
|
||||||
state->memory.bbd.dataSwapMode = memory->mbcState.bbd.dataSwapMode;
|
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);
|
GBMBCSwitchBank0(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 2);
|
||||||
}
|
}
|
||||||
break;
|
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_BBD:
|
||||||
case GB_UNL_HITEK:
|
case GB_UNL_HITEK:
|
||||||
memory->mbcState.bbd.dataSwapMode = state->memory.bbd.dataSwapMode & 0x7;
|
memory->mbcState.bbd.dataSwapMode = state->memory.bbd.dataSwapMode & 0x7;
|
||||||
|
|
|
@ -673,6 +673,8 @@ static const struct GBCartridgeOverride _overrides[] = {
|
||||||
{ 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)
|
||||||
{ 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
|
||||||
|
{ 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
|
{ 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
|
||||||
|
|
|
@ -35,6 +35,7 @@ static const QList<GBMemoryBankControllerType> s_mbcList{
|
||||||
GB_HuC3,
|
GB_HuC3,
|
||||||
GB_UNL_WISDOM_TREE,
|
GB_UNL_WISDOM_TREE,
|
||||||
GB_UNL_PKJD,
|
GB_UNL_PKJD,
|
||||||
|
GB_UNL_NT_OLD_1,
|
||||||
GB_UNL_NT_NEW,
|
GB_UNL_NT_NEW,
|
||||||
GB_UNL_BBD,
|
GB_UNL_BBD,
|
||||||
GB_UNL_HITEK,
|
GB_UNL_HITEK,
|
||||||
|
@ -87,6 +88,7 @@ QString GameBoy::mbcName(GBMemoryBankControllerType mbc) {
|
||||||
s_mbcNames[GB_POCKETCAM] = tr("Pocket Cam");
|
s_mbcNames[GB_POCKETCAM] = tr("Pocket Cam");
|
||||||
s_mbcNames[GB_TAMA5] = tr("TAMA5");
|
s_mbcNames[GB_TAMA5] = tr("TAMA5");
|
||||||
s_mbcNames[GB_UNL_WISDOM_TREE] = tr("Wisdom Tree");
|
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_NT_NEW] = tr("NT (new)");
|
||||||
s_mbcNames[GB_UNL_PKJD] = tr("Pokémon Jade/Diamond");
|
s_mbcNames[GB_UNL_PKJD] = tr("Pokémon Jade/Diamond");
|
||||||
s_mbcNames[GB_UNL_BBD] = tr("BBD");
|
s_mbcNames[GB_UNL_BBD] = tr("BBD");
|
||||||
|
|
Loading…
Reference in New Issue