GB MBC: Add MBC1-M support with basic heuristic

This commit is contained in:
Vicki Pfau 2017-05-30 22:28:23 -07:00
parent 7de5b33554
commit f84af91173
6 changed files with 52 additions and 7 deletions

View File

@ -20,6 +20,7 @@ Features:
- Library view - Library view
- Debugger: Segment/bank support - Debugger: Segment/bank support
- GB: Symbol table support - GB: Symbol table support
- GB MBC: Add MBC1 multicart support
Bugfixes: Bugfixes:
- LR35902: Fix core never exiting with certain event patterns - LR35902: Fix core never exiting with certain event patterns
- GB Timer: Improve DIV reset behavior - GB Timer: Improve DIV reset behavior

View File

@ -85,6 +85,7 @@ enum GBMBC7MachineState {
struct GBMBC1State { struct GBMBC1State {
int mode; int mode;
int multicartStride;
}; };
struct GBMBC7State { struct GBMBC7State {

View File

@ -385,7 +385,9 @@ bool GBIsBIOS(struct VFile* vf) {
void GBReset(struct LR35902Core* cpu) { void GBReset(struct LR35902Core* cpu) {
struct GB* gb = (struct GB*) cpu->master; struct GB* gb = (struct GB*) cpu->master;
gb->memory.romBase = gb->memory.rom;
GBDetectModel(gb); GBDetectModel(gb);
if (gb->biosVf) { if (gb->biosVf) {
if (!GBIsBIOS(gb->biosVf)) { if (!GBIsBIOS(gb->biosVf)) {
gb->biosVf->close(gb->biosVf); gb->biosVf->close(gb->biosVf);

View File

@ -382,7 +382,7 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) {
value = gb->video.stat; value = gb->video.stat;
break; break;
case 0x50: case 0x50:
if (gb->memory.romBase != gb->memory.rom) { if (gb->memory.romBase < gb->memory.rom && gb->memory.romBase > &gb->memory.rom[gb->memory.romSize - 1]) {
free(gb->memory.romBase); free(gb->memory.romBase);
gb->memory.romBase = gb->memory.rom; gb->memory.romBase = gb->memory.rom;
} }

View File

@ -46,6 +46,33 @@ void GBMBCSwitchBank(struct GB* gb, int bank) {
} }
} }
static void _switchBank0(struct GB* gb, int bank) {
size_t bankStart = bank * GB_SIZE_CART_BANK0 << gb->memory.mbcState.mbc1.multicartStride;
if (bankStart + GB_SIZE_CART_BANK0 > gb->memory.romSize) {
mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank);
bankStart &= (gb->memory.romSize - 1);
}
gb->memory.romBase = &gb->memory.rom[bankStart];
if (gb->cpu->pc < GB_SIZE_CART_BANK0) {
gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc);
}
}
static bool _isMulticart(const uint8_t* mem) {
bool success = true;
struct VFile* vf;
vf = VFileFromConstMemory(&mem[GB_SIZE_CART_BANK0 * 0x10], 1024);
success = success && GBIsROM(vf);
vf->close(vf);
vf = VFileFromConstMemory(&mem[GB_SIZE_CART_BANK0 * 0x20], 1024);
success = success && GBIsROM(vf);
vf->close(vf);
return success;
}
void GBMBCSwitchSramBank(struct GB* gb, int bank) { void GBMBCSwitchSramBank(struct GB* gb, int bank) {
size_t bankStart = bank * GB_SIZE_EXTERNAL_RAM; size_t bankStart = bank * GB_SIZE_EXTERNAL_RAM;
GBResizeSram(gb, (bank + 1) * GB_SIZE_EXTERNAL_RAM); GBResizeSram(gb, (bank + 1) * GB_SIZE_EXTERNAL_RAM);
@ -83,6 +110,11 @@ void GBMBCInit(struct GB* gb) {
case 2: case 2:
case 3: case 3:
gb->memory.mbcType = GB_MBC1; gb->memory.mbcType = GB_MBC1;
if (gb->memory.romSize >= GB_SIZE_CART_BANK0 * 0x31 && _isMulticart(gb->memory.rom)) {
gb->memory.mbcState.mbc1.multicartStride = 4;
} else {
gb->memory.mbcState.mbc1.multicartStride = 5;
}
break; break;
case 5: case 5:
case 6: case 6:
@ -233,6 +265,7 @@ static void _latchRtc(struct mRTCSource* rtc, uint8_t* rtcRegs, time_t* rtcLastL
void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) { void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) {
struct GBMemory* memory = &gb->memory; struct GBMemory* memory = &gb->memory;
int bank = value & 0x1F; int bank = value & 0x1F;
int stride = 1 << memory->mbcState.mbc1.multicartStride;
switch (address >> 13) { switch (address >> 13) {
case 0x0: case 0x0:
switch (value) { switch (value) {
@ -253,21 +286,23 @@ void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) {
if (!bank) { if (!bank) {
++bank; ++bank;
} }
GBMBCSwitchBank(gb, bank | (memory->currentBank & 0x60)); bank &= stride - 1;
GBMBCSwitchBank(gb, bank | (memory->currentBank & (3 * stride)));
break; break;
case 0x2: case 0x2:
bank &= 3; bank &= 3;
if (!memory->mbcState.mbc1.mode) { if (memory->mbcState.mbc1.mode) {
GBMBCSwitchBank(gb, (bank << 5) | (memory->currentBank & 0x1F)); _switchBank0(gb, bank);
} else {
GBMBCSwitchSramBank(gb, bank); GBMBCSwitchSramBank(gb, bank);
} }
GBMBCSwitchBank(gb, (bank << memory->mbcState.mbc1.multicartStride) | (memory->currentBank & (stride - 1)));
break; break;
case 0x3: case 0x3:
memory->mbcState.mbc1.mode = value & 1; memory->mbcState.mbc1.mode = value & 1;
if (memory->mbcState.mbc1.mode) { if (memory->mbcState.mbc1.mode) {
GBMBCSwitchBank(gb, memory->currentBank & 0x1F); _switchBank0(gb, memory->currentBank >> memory->mbcState.mbc1.multicartStride);
} else { } else {
_switchBank0(gb, 0);
GBMBCSwitchSramBank(gb, 0); GBMBCSwitchSramBank(gb, 0);
} }
break; break;

View File

@ -165,7 +165,13 @@ void GBMemoryReset(struct GB* gb) {
memset(&gb->memory.rtcRegs, 0, sizeof(gb->memory.rtcRegs)); memset(&gb->memory.rtcRegs, 0, sizeof(gb->memory.rtcRegs));
memset(&gb->memory.hram, 0, sizeof(gb->memory.hram)); memset(&gb->memory.hram, 0, sizeof(gb->memory.hram));
memset(&gb->memory.mbcState, 0, sizeof(gb->memory.mbcState)); switch (gb->memory.mbcType) {
case GB_MBC1:
gb->memory.mbcState.mbc1.mode = 0;
break;
default:
memset(&gb->memory.mbcState, 0, sizeof(gb->memory.mbcState));
}
GBMBCInit(gb); GBMBCInit(gb);
gb->memory.sramBank = gb->memory.sram; gb->memory.sramBank = gb->memory.sram;