mirror of
https://github.com/libretro/mgba.git
synced 2024-11-27 10:11:00 +00:00
GB MBC: Add NT (new) support (closes #2435)
This commit is contained in:
parent
31201d4903
commit
d33471e117
1
CHANGES
1
CHANGES
@ -13,6 +13,7 @@ Features:
|
|||||||
- Additional scaling shaders
|
- Additional scaling shaders
|
||||||
- Support for GameShark Advance SP (.gsv) save file importing
|
- Support for GameShark Advance SP (.gsv) save file importing
|
||||||
- Support for multiple saves per game using .sa2, .sa3, etc.
|
- Support for multiple saves per game using .sa2, .sa3, etc.
|
||||||
|
- New unlicensed GB mappers: NT (newer type)
|
||||||
Emulation fixes:
|
Emulation fixes:
|
||||||
- ARM7: Fix unsigned multiply timing
|
- ARM7: Fix unsigned multiply timing
|
||||||
- GB Memory: Add cursory cartridge open bus emulation (fixes mgba.io/i/2032)
|
- GB Memory: Add cursory cartridge open bus emulation (fixes mgba.io/i/2032)
|
||||||
|
@ -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_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,
|
||||||
};
|
};
|
||||||
|
@ -145,11 +145,7 @@ struct GBMBC1State {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct GBMBC6State {
|
struct GBMBC6State {
|
||||||
int currentBank1;
|
|
||||||
uint8_t* romBank1;
|
|
||||||
bool sramAccess;
|
bool sramAccess;
|
||||||
int currentSramBank1;
|
|
||||||
uint8_t* sramBank1;
|
|
||||||
bool flashBank0;
|
bool flashBank0;
|
||||||
bool flashBank1;
|
bool flashBank1;
|
||||||
};
|
};
|
||||||
@ -191,6 +187,10 @@ struct GBPKJDState {
|
|||||||
uint8_t reg[2];
|
uint8_t reg[2];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct GBNTNewState {
|
||||||
|
bool splitMode;
|
||||||
|
};
|
||||||
|
|
||||||
struct GBBBDState {
|
struct GBBBDState {
|
||||||
int dataSwapMode;
|
int dataSwapMode;
|
||||||
int bankSwapMode;
|
int bankSwapMode;
|
||||||
@ -204,6 +204,7 @@ union GBMBCState {
|
|||||||
struct GBPocketCamState pocketCam;
|
struct GBPocketCamState pocketCam;
|
||||||
struct GBTAMA5State tama5;
|
struct GBTAMA5State tama5;
|
||||||
struct GBHuC3State huc3;
|
struct GBHuC3State huc3;
|
||||||
|
struct GBNTNewState ntNew;
|
||||||
struct GBPKJDState pkjd;
|
struct GBPKJDState pkjd;
|
||||||
struct GBBBDState bbd;
|
struct GBBBDState bbd;
|
||||||
};
|
};
|
||||||
@ -219,6 +220,11 @@ struct GBMemory {
|
|||||||
union GBMBCState mbcState;
|
union GBMBCState mbcState;
|
||||||
int currentBank;
|
int currentBank;
|
||||||
int currentBank0;
|
int currentBank0;
|
||||||
|
int currentBank1;
|
||||||
|
uint8_t* romBank1;
|
||||||
|
int currentSramBank1;
|
||||||
|
uint8_t* sramBank1;
|
||||||
|
|
||||||
unsigned cartBusDecay;
|
unsigned cartBusDecay;
|
||||||
uint16_t cartBusPc;
|
uint16_t cartBusPc;
|
||||||
uint8_t cartBus;
|
uint8_t cartBus;
|
||||||
|
56
src/gb/mbc.c
56
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 _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 _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);
|
||||||
|
|
||||||
@ -86,7 +87,10 @@ void GBMBCSwitchBank0(struct GB* gb, int bank) {
|
|||||||
|
|
||||||
void GBMBCSwitchHalfBank(struct GB* gb, int half, int bank) {
|
void GBMBCSwitchHalfBank(struct GB* gb, int half, int bank) {
|
||||||
size_t bankStart = bank * GB_SIZE_CART_HALFBANK;
|
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 (isFlash) {
|
||||||
if (bankStart + GB_SIZE_CART_HALFBANK > GB_SIZE_MBC6_FLASH) {
|
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);
|
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;
|
gb->memory.currentBank = bank;
|
||||||
} else {
|
} else {
|
||||||
if (isFlash) {
|
if (isFlash) {
|
||||||
gb->memory.mbcState.mbc6.romBank1 = &gb->memory.sram[bankStart];
|
gb->memory.romBank1 = &gb->memory.sram[bankStart];
|
||||||
} else {
|
} 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) {
|
if (gb->cpu->pc < GB_BASE_VRAM) {
|
||||||
gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc);
|
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.sramBank = &gb->memory.sram[bankStart];
|
||||||
gb->memory.sramCurrentBank = bank;
|
gb->memory.sramCurrentBank = bank;
|
||||||
} else {
|
} else {
|
||||||
gb->memory.mbcState.mbc6.sramBank1 = &gb->memory.sram[bankStart];
|
gb->memory.sramBank1 = &gb->memory.sram[bankStart];
|
||||||
gb->memory.mbcState.mbc6.currentSramBank1 = bank;
|
gb->memory.currentSramBank1 = bank;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -403,6 +407,13 @@ 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_NEW:
|
||||||
|
gb->memory.mbcWrite = _GBNTNew;
|
||||||
|
break;
|
||||||
|
case GB_UNL_PKJD:
|
||||||
|
gb->memory.mbcWrite = _GBPKJD;
|
||||||
|
gb->memory.mbcRead = _GBPKJDRead;
|
||||||
|
break;
|
||||||
case GB_UNL_BBD:
|
case GB_UNL_BBD:
|
||||||
gb->memory.mbcWrite = _GBBBD;
|
gb->memory.mbcWrite = _GBBBD;
|
||||||
gb->memory.mbcRead = _GBBBDRead;
|
gb->memory.mbcRead = _GBBBDRead;
|
||||||
@ -413,10 +424,6 @@ void GBMBCInit(struct GB* gb) {
|
|||||||
gb->memory.mbcState.bbd.dataSwapMode = 7;
|
gb->memory.mbcState.bbd.dataSwapMode = 7;
|
||||||
gb->memory.mbcState.bbd.bankSwapMode = 7;
|
gb->memory.mbcState.bbd.bankSwapMode = 7;
|
||||||
break;
|
break;
|
||||||
case GB_UNL_PKJD:
|
|
||||||
gb->memory.mbcWrite = _GBPKJD;
|
|
||||||
gb->memory.mbcRead = _GBPKJDRead;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gb->memory.currentBank = 1;
|
gb->memory.currentBank = 1;
|
||||||
@ -790,7 +797,7 @@ void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) {
|
|||||||
case 0x2E:
|
case 0x2E:
|
||||||
case 0x2F:
|
case 0x2F:
|
||||||
if (memory->sramAccess) {
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -807,7 +814,7 @@ uint8_t _GBMBC6Read(struct GBMemory* memory, uint16_t address) {
|
|||||||
case 0xA:
|
case 0xA:
|
||||||
return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)];
|
return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)];
|
||||||
case 0xB:
|
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;
|
return 0xFF;
|
||||||
}
|
}
|
||||||
@ -818,7 +825,7 @@ static void _GBMBC6MapChip(struct GB* gb, int half, uint8_t value) {
|
|||||||
GBMBCSwitchHalfBank(gb, half, gb->memory.currentBank);
|
GBMBCSwitchHalfBank(gb, half, gb->memory.currentBank);
|
||||||
} else {
|
} else {
|
||||||
gb->memory.mbcState.mbc6.flashBank1 = !!(value & 0x08);
|
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) {
|
static uint8_t _reorderBits(uint8_t input, const uint8_t* reorder) {
|
||||||
uint8_t newbyte = 0;
|
uint8_t newbyte = 0;
|
||||||
int i;
|
int i;
|
||||||
|
@ -95,14 +95,14 @@ static void GBSetActiveRegion(struct SM83Core* cpu, uint16_t address) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
cpu->memory.cpuLoad8 = GBCartLoad8;
|
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.activeRegion = memory->romBank;
|
||||||
cpu->memory.activeRegionEnd = GB_BASE_VRAM;
|
cpu->memory.activeRegionEnd = GB_BASE_VRAM;
|
||||||
cpu->memory.activeMask = GB_SIZE_CART_BANK0 - 1;
|
cpu->memory.activeMask = GB_SIZE_CART_BANK0 - 1;
|
||||||
} else {
|
} else {
|
||||||
cpu->memory.activeMask = GB_SIZE_CART_HALFBANK - 1;
|
cpu->memory.activeMask = GB_SIZE_CART_HALFBANK - 1;
|
||||||
if (address & 0x2000) {
|
if (address & 0x2000) {
|
||||||
cpu->memory.activeRegion = memory->mbcState.mbc6.romBank1;
|
cpu->memory.activeRegion = memory->romBank1;
|
||||||
cpu->memory.activeRegionEnd = GB_BASE_VRAM;
|
cpu->memory.activeRegionEnd = GB_BASE_VRAM;
|
||||||
} else {
|
} else {
|
||||||
cpu->memory.activeRegion = memory->romBank;
|
cpu->memory.activeRegion = memory->romBank;
|
||||||
@ -252,8 +252,8 @@ uint8_t GBLoad8(struct SM83Core* cpu, uint16_t address) {
|
|||||||
return memory->cartBus;
|
return memory->cartBus;
|
||||||
case GB_REGION_CART_BANK1 + 2:
|
case GB_REGION_CART_BANK1 + 2:
|
||||||
case GB_REGION_CART_BANK1 + 3:
|
case GB_REGION_CART_BANK1 + 3:
|
||||||
if (memory->mbcType == GB_MBC6) {
|
if (gb->memory.mbcType == GB_MBC6 || (gb->memory.mbcType == GB_UNL_NT_NEW && gb->memory.mbcState.ntNew.splitMode)) {
|
||||||
memory->cartBus = memory->mbcState.mbc6.romBank1[address & (GB_SIZE_CART_HALFBANK - 1)];
|
memory->cartBus = memory->romBank1[address & (GB_SIZE_CART_HALFBANK - 1)];
|
||||||
memory->cartBusPc = cpu->pc;
|
memory->cartBusPc = cpu->pc;
|
||||||
return memory->cartBus;
|
return memory->cartBus;
|
||||||
}
|
}
|
||||||
|
@ -668,11 +668,21 @@ static const struct GBCartridgeOverride _sgbOverrides[] = {
|
|||||||
|
|
||||||
static const struct GBCartridgeOverride _overrides[] = {
|
static const struct GBCartridgeOverride _overrides[] = {
|
||||||
// Pokemon Spaceworld 1997 demo
|
// Pokemon Spaceworld 1997 demo
|
||||||
{ 0x232a067d, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (debug)
|
{ 0x232A067D, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (debug)
|
||||||
{ 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)
|
||||||
{ 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)
|
||||||
|
{ 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 } }
|
{ 0, 0, 0, { 0 } }
|
||||||
};
|
};
|
||||||
|
@ -34,9 +34,10 @@ static const QList<GBMemoryBankControllerType> s_mbcList{
|
|||||||
GB_HuC1,
|
GB_HuC1,
|
||||||
GB_HuC3,
|
GB_HuC3,
|
||||||
GB_UNL_WISDOM_TREE,
|
GB_UNL_WISDOM_TREE,
|
||||||
|
GB_UNL_PKJD,
|
||||||
|
GB_UNL_NT_NEW,
|
||||||
GB_UNL_BBD,
|
GB_UNL_BBD,
|
||||||
GB_UNL_HITEK,
|
GB_UNL_HITEK,
|
||||||
GB_UNL_PKJD,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static QMap<GBModel, QString> s_gbModelNames;
|
static QMap<GBModel, QString> s_gbModelNames;
|
||||||
@ -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_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");
|
||||||
s_mbcNames[GB_UNL_HITEK] = tr("Hitek");
|
s_mbcNames[GB_UNL_HITEK] = tr("Hitek");
|
||||||
|
Loading…
Reference in New Issue
Block a user