diff --git a/CHANGES b/CHANGES index 34e8e94b1..3806127ed 100644 --- a/CHANGES +++ b/CHANGES @@ -13,7 +13,7 @@ Features: - Additional scaling shaders - Support for GameShark Advance SP (.gsv) save file importing - 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: - ARM7: Fix unsigned multiply timing - GB: Copy logo from ROM if not running the BIOS intro (fixes mgba.io/i/2378) diff --git a/include/mgba/internal/gb/memory.h b/include/mgba/internal/gb/memory.h index bb4029bf2..43ece98d2 100644 --- a/include/mgba/internal/gb/memory.h +++ b/include/mgba/internal/gb/memory.h @@ -248,6 +248,11 @@ struct GBMemory { uint8_t* wramBank; int wramCurrentBank; + bool mbcReadBank0; + bool mbcReadBank1; + bool mbcReadHigh; + bool mbcWriteHigh; + bool sramAccess; bool directSramAccess; uint8_t* sram; diff --git a/src/gb/gb.c b/src/gb/gb.c index 6f8aa97ff..6130964b8 100644 --- a/src/gb/gb.c +++ b/src/gb/gb.c @@ -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->pc = 0x100; @@ -660,7 +664,7 @@ void GBMapBIOS(struct GB* gb) { if (gb->memory.rom) { memcpy(&gb->memory.romBase[size], &gb->memory.rom[size], GB_SIZE_CART_BANK0 - size); 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); } } } diff --git a/src/gb/mbc.c b/src/gb/mbc.c index f844540d2..ffc4054e6 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -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 _GBHitekRead(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 void _GBPocketCamCapture(struct GBMemory*); @@ -204,6 +205,10 @@ static enum GBMemoryBankControllerType _detectUnlMBC(const uint8_t* mem, size_t return GB_UNL_SACHEN_MMC1; } + if (mem[0x184] == 0xCE && mem[0x1C4] == 0xED && mem[0x194] == 0x66) { + return GB_UNL_SACHEN_MMC2; + } + return GB_MBC_AUTODETECT; } @@ -423,16 +428,31 @@ void GBMBCInit(struct GB* gb) { case GB_UNL_BBD: gb->memory.mbcWrite = _GBBBD; gb->memory.mbcRead = _GBBBDRead; + gb->memory.mbcReadBank1 = true; break; case GB_UNL_HITEK: gb->memory.mbcWrite = _GBHitek; gb->memory.mbcRead = _GBHitekRead; gb->memory.mbcState.bbd.dataSwapMode = 7; gb->memory.mbcState.bbd.bankSwapMode = 7; + gb->memory.mbcReadBank1 = true; break; case GB_UNL_SACHEN_MMC1: gb->memory.mbcWrite = _GBSachen; 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; } @@ -1760,6 +1780,12 @@ void _GBSachen(struct GB* gb, uint16_t address, uint8_t value) { GBMBCSwitchBank0(gb, state->baseBank & state->mask); } 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) { struct GBSachenState* state = &memory->mbcState.sachen; - if (state->locked != GB_SACHEN_UNLOCKED) { + if (state->locked != GB_SACHEN_UNLOCKED && (address & 0xFF00) == 0x100) { ++state->transition; if (state->transition == 0x31) { state->locked = GB_SACHEN_UNLOCKED; + } else { + address |= 0x80; } - address |= 0x80; } 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) { struct VFile* vf = gb->sramVf; if ((size_t) vf->size(vf) < gb->sramSize + size) { diff --git a/src/gb/memory.c b/src/gb/memory.c index 10b09eed1..ec9a81ac8 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -73,7 +73,7 @@ static void GBSetActiveRegion(struct SM83Core* cpu, uint16_t address) { case GB_REGION_CART_BANK0 + 1: case GB_REGION_CART_BANK0 + 2: 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; 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 + 2: case GB_REGION_CART_BANK1 + 3: - if ((gb->memory.mbcType & GB_UNL_BBD) == GB_UNL_BBD) { + if (gb->memory.mbcReadBank1) { cpu->memory.cpuLoad8 = GBLoad8; break; } @@ -249,7 +249,7 @@ uint8_t GBLoad8(struct SM83Core* cpu, uint16_t address) { case GB_REGION_CART_BANK0 + 3: if (address >= memory->romSize) { 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); } else { 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: if (address >= memory->romSize) { memory->cartBus = 0xFF; - } else if ((memory->mbcType & GB_UNL_BBD) == GB_UNL_BBD) { + } else if (gb->memory.mbcReadBank1) { memory->cartBus = memory->mbcRead(memory, address); } else { 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; case GB_REGION_WORKING_RAM_BANK0: 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)]; case GB_REGION_WORKING_RAM_BANK1: + if (gb->memory.mbcReadHigh) { + memory->mbcRead(memory, address); + } return memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)]; default: if (address < GB_BASE_OAM) { @@ -373,9 +379,15 @@ void GBStore8(struct SM83Core* cpu, uint16_t address, int8_t value) { return; case GB_REGION_WORKING_RAM_BANK0: 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; return; case GB_REGION_WORKING_RAM_BANK1: + if (memory->mbcWriteHigh) { + memory->mbcWrite(gb, address, value); + } memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value; return; default: