GB MBC: Sachen MMC2 support

This commit is contained in:
Vicki Pfau 2022-02-07 22:19:46 -08:00
parent 73f18f8049
commit 0676769b68
5 changed files with 87 additions and 8 deletions

View File

@ -13,7 +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), Sachen (MMC1) - New unlicensed GB mappers: NT (newer type), Sachen (MMC1, MMC2)
Emulation fixes: Emulation fixes:
- ARM7: Fix unsigned multiply timing - ARM7: Fix unsigned multiply timing
- GB: Copy logo from ROM if not running the BIOS intro (fixes mgba.io/i/2378) - GB: Copy logo from ROM if not running the BIOS intro (fixes mgba.io/i/2378)

View File

@ -248,6 +248,11 @@ struct GBMemory {
uint8_t* wramBank; uint8_t* wramBank;
int wramCurrentBank; int wramCurrentBank;
bool mbcReadBank0;
bool mbcReadBank1;
bool mbcReadHigh;
bool mbcWriteHigh;
bool sramAccess; bool sramAccess;
bool directSramAccess; bool directSramAccess;
uint8_t* sram; uint8_t* sram;

View File

@ -636,6 +636,10 @@ void GBSkipBIOS(struct GB* gb) {
} }
} }
if (gb->memory.mbcType == GB_UNL_SACHEN_MMC2) {
gb->memory.mbcState.sachen.locked = GB_SACHEN_UNLOCKED;
}
cpu->sp = 0xFFFE; cpu->sp = 0xFFFE;
cpu->pc = 0x100; cpu->pc = 0x100;
@ -660,7 +664,7 @@ void GBMapBIOS(struct GB* gb) {
if (gb->memory.rom) { if (gb->memory.rom) {
memcpy(&gb->memory.romBase[size], &gb->memory.rom[size], GB_SIZE_CART_BANK0 - size); memcpy(&gb->memory.romBase[size], &gb->memory.rom[size], GB_SIZE_CART_BANK0 - size);
if (size > 0x100) { if (size > 0x100) {
memcpy(&gb->memory.romBase[0x100], &gb->memory.rom[0x100], sizeof(struct GBCartridge)); memcpy(&gb->memory.romBase[0x100], &gb->memory.rom[0x100], 0x100);
} }
} }
} }

View File

@ -54,6 +54,7 @@ static uint8_t _GBPKJDRead(struct GBMemory*, uint16_t address);
static uint8_t _GBBBDRead(struct GBMemory*, uint16_t address); static uint8_t _GBBBDRead(struct GBMemory*, uint16_t address);
static uint8_t _GBHitekRead(struct GBMemory*, uint16_t address); static uint8_t _GBHitekRead(struct GBMemory*, uint16_t address);
static uint8_t _GBSachenMMC1Read(struct GBMemory*, uint16_t address); static uint8_t _GBSachenMMC1Read(struct GBMemory*, uint16_t address);
static uint8_t _GBSachenMMC2Read(struct GBMemory*, uint16_t address);
static uint8_t _GBPocketCamRead(struct GBMemory*, uint16_t address); static uint8_t _GBPocketCamRead(struct GBMemory*, uint16_t address);
static void _GBPocketCamCapture(struct GBMemory*); static void _GBPocketCamCapture(struct GBMemory*);
@ -204,6 +205,10 @@ static enum GBMemoryBankControllerType _detectUnlMBC(const uint8_t* mem, size_t
return GB_UNL_SACHEN_MMC1; return GB_UNL_SACHEN_MMC1;
} }
if (mem[0x184] == 0xCE && mem[0x1C4] == 0xED && mem[0x194] == 0x66) {
return GB_UNL_SACHEN_MMC2;
}
return GB_MBC_AUTODETECT; return GB_MBC_AUTODETECT;
} }
@ -423,16 +428,31 @@ void GBMBCInit(struct GB* gb) {
case GB_UNL_BBD: case GB_UNL_BBD:
gb->memory.mbcWrite = _GBBBD; gb->memory.mbcWrite = _GBBBD;
gb->memory.mbcRead = _GBBBDRead; gb->memory.mbcRead = _GBBBDRead;
gb->memory.mbcReadBank1 = true;
break; break;
case GB_UNL_HITEK: case GB_UNL_HITEK:
gb->memory.mbcWrite = _GBHitek; gb->memory.mbcWrite = _GBHitek;
gb->memory.mbcRead = _GBHitekRead; gb->memory.mbcRead = _GBHitekRead;
gb->memory.mbcState.bbd.dataSwapMode = 7; gb->memory.mbcState.bbd.dataSwapMode = 7;
gb->memory.mbcState.bbd.bankSwapMode = 7; gb->memory.mbcState.bbd.bankSwapMode = 7;
gb->memory.mbcReadBank1 = true;
break; break;
case GB_UNL_SACHEN_MMC1: case GB_UNL_SACHEN_MMC1:
gb->memory.mbcWrite = _GBSachen; gb->memory.mbcWrite = _GBSachen;
gb->memory.mbcRead = _GBSachenMMC1Read; gb->memory.mbcRead = _GBSachenMMC1Read;
gb->memory.mbcReadBank0 = true;
gb->memory.mbcReadBank1 = true;
break;
case GB_UNL_SACHEN_MMC2:
gb->memory.mbcWrite = _GBSachen;
gb->memory.mbcRead = _GBSachenMMC2Read;
gb->memory.mbcReadBank0 = true;
gb->memory.mbcReadBank1 = true;
gb->memory.mbcReadHigh = true;
gb->memory.mbcWriteHigh = true;
if (gb->sramSize) {
gb->memory.sramAccess = true;
}
break; break;
} }
@ -1760,6 +1780,12 @@ void _GBSachen(struct GB* gb, uint16_t address, uint8_t value) {
GBMBCSwitchBank0(gb, state->baseBank & state->mask); GBMBCSwitchBank0(gb, state->baseBank & state->mask);
} }
break; break;
case 6:
if (gb->memory.mbcType == GB_UNL_SACHEN_MMC2 && state->locked == GB_SACHEN_LOCKED_DMG) {
state->locked = GB_SACHEN_LOCKED_CGB;
state->transition = 0;
}
break;
} }
} }
@ -1774,12 +1800,13 @@ static uint16_t _unscrambleSachen(uint16_t address) {
uint8_t _GBSachenMMC1Read(struct GBMemory* memory, uint16_t address) { uint8_t _GBSachenMMC1Read(struct GBMemory* memory, uint16_t address) {
struct GBSachenState* state = &memory->mbcState.sachen; struct GBSachenState* state = &memory->mbcState.sachen;
if (state->locked != GB_SACHEN_UNLOCKED) { if (state->locked != GB_SACHEN_UNLOCKED && (address & 0xFF00) == 0x100) {
++state->transition; ++state->transition;
if (state->transition == 0x31) { if (state->transition == 0x31) {
state->locked = GB_SACHEN_UNLOCKED; state->locked = GB_SACHEN_UNLOCKED;
} else {
address |= 0x80;
} }
address |= 0x80;
} }
if ((address & 0xFF00) == 0x0100) { if ((address & 0xFF00) == 0x0100) {
@ -1795,6 +1822,37 @@ uint8_t _GBSachenMMC1Read(struct GBMemory* memory, uint16_t address) {
} }
} }
uint8_t _GBSachenMMC2Read(struct GBMemory* memory, uint16_t address) {
struct GBSachenState* state = &memory->mbcState.sachen;
if (address >= 0xC000 && state->locked == GB_SACHEN_LOCKED_DMG) {
state->transition = 0;
state->locked = GB_SACHEN_LOCKED_CGB;
}
if (state->locked != GB_SACHEN_UNLOCKED && (address & 0x8700) == 0x0100) {
++state->transition;
if (state->transition == 0x31) {
++state->locked;
state->transition = 0;
}
}
if ((address & 0xFF00) == 0x0100) {
if (state->locked == GB_SACHEN_LOCKED_CGB) {
address |= 0x80;
}
address = _unscrambleSachen(address);
}
if (address < GB_BASE_CART_BANK1) {
return memory->romBase[address];
} else if (address < GB_BASE_VRAM) {
return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
} else {
return 0xFF;
}
}
static void _appendSaveSuffix(struct GB* gb, const void* buffer, size_t size) { static void _appendSaveSuffix(struct GB* gb, const void* buffer, size_t size) {
struct VFile* vf = gb->sramVf; struct VFile* vf = gb->sramVf;
if ((size_t) vf->size(vf) < gb->sramSize + size) { if ((size_t) vf->size(vf) < gb->sramSize + size) {

View File

@ -73,7 +73,7 @@ static void GBSetActiveRegion(struct SM83Core* cpu, uint16_t address) {
case GB_REGION_CART_BANK0 + 1: case GB_REGION_CART_BANK0 + 1:
case GB_REGION_CART_BANK0 + 2: case GB_REGION_CART_BANK0 + 2:
case GB_REGION_CART_BANK0 + 3: case GB_REGION_CART_BANK0 + 3:
if ((gb->memory.mbcType & GB_UNL_SACHEN_MMC1) == GB_UNL_SACHEN_MMC1) { if (gb->memory.mbcReadBank0) {
cpu->memory.cpuLoad8 = GBLoad8; cpu->memory.cpuLoad8 = GBLoad8;
break; break;
} }
@ -94,7 +94,7 @@ static void GBSetActiveRegion(struct SM83Core* cpu, uint16_t address) {
case GB_REGION_CART_BANK1 + 1: case GB_REGION_CART_BANK1 + 1:
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 ((gb->memory.mbcType & GB_UNL_BBD) == GB_UNL_BBD) { if (gb->memory.mbcReadBank1) {
cpu->memory.cpuLoad8 = GBLoad8; cpu->memory.cpuLoad8 = GBLoad8;
break; break;
} }
@ -249,7 +249,7 @@ uint8_t GBLoad8(struct SM83Core* cpu, uint16_t address) {
case GB_REGION_CART_BANK0 + 3: case GB_REGION_CART_BANK0 + 3:
if (address >= memory->romSize) { if (address >= memory->romSize) {
memory->cartBus = 0xFF; memory->cartBus = 0xFF;
} else if ((memory->mbcType & GB_UNL_SACHEN_MMC1) == GB_UNL_SACHEN_MMC1) { } else if (gb->memory.mbcReadBank0) {
memory->cartBus = memory->mbcRead(memory, address); memory->cartBus = memory->mbcRead(memory, address);
} else { } else {
memory->cartBus = memory->romBase[address & (GB_SIZE_CART_BANK0 - 1)]; memory->cartBus = memory->romBase[address & (GB_SIZE_CART_BANK0 - 1)];
@ -268,7 +268,7 @@ uint8_t GBLoad8(struct SM83Core* cpu, uint16_t address) {
case GB_REGION_CART_BANK1 + 1: case GB_REGION_CART_BANK1 + 1:
if (address >= memory->romSize) { if (address >= memory->romSize) {
memory->cartBus = 0xFF; memory->cartBus = 0xFF;
} else if ((memory->mbcType & GB_UNL_BBD) == GB_UNL_BBD) { } else if (gb->memory.mbcReadBank1) {
memory->cartBus = memory->mbcRead(memory, address); memory->cartBus = memory->mbcRead(memory, address);
} else { } else {
memory->cartBus = memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)]; memory->cartBus = memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
@ -298,8 +298,14 @@ uint8_t GBLoad8(struct SM83Core* cpu, uint16_t address) {
return memory->cartBus; return memory->cartBus;
case GB_REGION_WORKING_RAM_BANK0: case GB_REGION_WORKING_RAM_BANK0:
case GB_REGION_WORKING_RAM_BANK0 + 2: case GB_REGION_WORKING_RAM_BANK0 + 2:
if (gb->memory.mbcReadHigh) {
memory->mbcRead(memory, address);
}
return memory->wram[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)]; return memory->wram[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
case GB_REGION_WORKING_RAM_BANK1: case GB_REGION_WORKING_RAM_BANK1:
if (gb->memory.mbcReadHigh) {
memory->mbcRead(memory, address);
}
return memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)]; return memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
default: default:
if (address < GB_BASE_OAM) { if (address < GB_BASE_OAM) {
@ -373,9 +379,15 @@ void GBStore8(struct SM83Core* cpu, uint16_t address, int8_t value) {
return; return;
case GB_REGION_WORKING_RAM_BANK0: case GB_REGION_WORKING_RAM_BANK0:
case GB_REGION_WORKING_RAM_BANK0 + 2: case GB_REGION_WORKING_RAM_BANK0 + 2:
if (memory->mbcWriteHigh) {
memory->mbcWrite(gb, address, value);
}
memory->wram[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value; memory->wram[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
return; return;
case GB_REGION_WORKING_RAM_BANK1: case GB_REGION_WORKING_RAM_BANK1:
if (memory->mbcWriteHigh) {
memory->mbcWrite(gb, address, value);
}
memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value; memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
return; return;
default: default: