diff --git a/CHANGES b/CHANGES index eae5153f9..dc2e600c4 100644 --- a/CHANGES +++ b/CHANGES @@ -5,9 +5,8 @@ Features: - Separate overrides for GBC games that can also run on SGB or regular GB - Mute option in homebrew ports - Status indicators for fast-forward and mute in homebrew ports - - Support for unlicensed Pokemon Jade/Diamond Game Boy mapper - - Support for unlicensed BBD Game Boy mapper - - Support for unlicensed Hitek Game Boy mapper + - Read-only support for MBC6 flash memory + - New unlicensed GB mappers: Pokémon Jade/Diamond, BBD, and Hitek - Stack tracing tools in ARM debugger (by ahigerd) - Command scripts for CLI debugger (by ahigerd) Emulation fixes: diff --git a/README.md b/README.md index 580319425..e1777f40e 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ The following mappers are fully supported: The following mappers are partially supported: -- MBC6 (missing flash memory support) +- MBC6 (missing flash memory write support) - MMM01 - Pocket Cam - TAMA5 (missing RTC support) diff --git a/include/mgba/internal/gb/memory.h b/include/mgba/internal/gb/memory.h index 676a8d5bb..d11260bac 100644 --- a/include/mgba/internal/gb/memory.h +++ b/include/mgba/internal/gb/memory.h @@ -61,6 +61,8 @@ enum { GB_SIZE_OAM = 0xA0, GB_SIZE_IO = 0x80, GB_SIZE_HRAM = 0x7F, + + GB_SIZE_MBC6_FLASH = 0x100000, }; enum { @@ -118,6 +120,8 @@ struct GBMBC6State { bool sramAccess; int currentSramBank1; uint8_t* sramBank1; + bool flashBank0; + bool flashBank1; }; struct GBMBC7State { diff --git a/src/gb/mbc.c b/src/gb/mbc.c index 310c8bb95..cdbbbef44 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -53,6 +53,8 @@ static uint8_t _GBHitekRead(struct GBMemory*, uint16_t address); static uint8_t _GBPocketCamRead(struct GBMemory*, uint16_t address); static void _GBPocketCamCapture(struct GBMemory*); +static void _GBMBC6MapChip(struct GB*, int half, uint8_t value); + void GBMBCSwitchBank(struct GB* gb, int bank) { size_t bankStart = bank * GB_SIZE_CART_BANK0; if (bankStart + GB_SIZE_CART_BANK0 > gb->memory.romSize) { @@ -81,19 +83,37 @@ void GBMBCSwitchBank0(struct GB* gb, int bank) { void GBMBCSwitchHalfBank(struct GB* gb, int half, int bank) { size_t bankStart = bank * GB_SIZE_CART_HALFBANK; - if (bankStart + GB_SIZE_CART_HALFBANK > gb->memory.romSize) { - mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank); - bankStart &= (gb->memory.romSize - 1); - bank = bankStart / GB_SIZE_CART_HALFBANK; - if (!bank) { - ++bank; + bool isFlash = half ? gb->memory.mbcState.mbc6.flashBank1 : gb->memory.mbcState.mbc6.flashBank0; + if (isFlash) { + 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); + bankStart &= GB_SIZE_MBC6_FLASH - 1; + bank = bankStart / GB_SIZE_CART_HALFBANK; + } + bankStart += gb->sramSize - GB_SIZE_MBC6_FLASH; + } else { + if (bankStart + GB_SIZE_CART_HALFBANK > gb->memory.romSize) { + mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank); + bankStart &= gb->memory.romSize - 1; + bank = bankStart / GB_SIZE_CART_HALFBANK; + if (!bank) { + ++bank; + } } } if (!half) { - gb->memory.romBank = &gb->memory.rom[bankStart]; + if (isFlash) { + gb->memory.romBank = &gb->memory.sram[bankStart]; + } else { + gb->memory.romBank = &gb->memory.rom[bankStart]; + } gb->memory.currentBank = bank; } else { - gb->memory.mbcState.mbc6.romBank1 = &gb->memory.rom[bankStart]; + if (isFlash) { + gb->memory.mbcState.mbc6.romBank1 = &gb->memory.sram[bankStart]; + } else { + gb->memory.mbcState.mbc6.romBank1 = &gb->memory.rom[bankStart]; + } gb->memory.mbcState.mbc6.currentBank1 = bank; } if (gb->cpu->pc < GB_BASE_VRAM) { @@ -187,9 +207,10 @@ void GBMBCSwitchSramBank(struct GB* gb, int bank) { void GBMBCSwitchSramHalfBank(struct GB* gb, int half, int bank) { size_t bankStart = bank * GB_SIZE_EXTERNAL_RAM_HALFBANK; - if (bankStart + GB_SIZE_EXTERNAL_RAM_HALFBANK > gb->sramSize) { + size_t sramSize = gb->sramSize - GB_SIZE_MBC6_FLASH; + if (bankStart + GB_SIZE_EXTERNAL_RAM_HALFBANK > sramSize) { mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid RAM bank: %0X", bank); - bankStart &= (gb->sramSize - 1); + bankStart &= (sramSize - 1); bank = bankStart / GB_SIZE_EXTERNAL_RAM_HALFBANK; } if (!half) { @@ -334,6 +355,7 @@ void GBMBCInit(struct GB* gb) { gb->memory.mbcWrite = _GBMBC6; gb->memory.mbcRead = _GBMBC6Read; gb->memory.directSramAccess = false; + gb->sramSize += GB_SIZE_MBC6_FLASH; // Flash is concatenated at the end break; case GB_MBC7: gb->memory.mbcWrite = _GBMBC7; @@ -689,14 +711,28 @@ void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) { case 0x2: GBMBCSwitchSramHalfBank(gb, 1, bank); break; + case 0x3: + mLOG(GB_MBC, STUB, "MBC6 unimplemented flash OE write: %04X:%02X", address, value); + break; + case 0x4: + mLOG(GB_MBC, STUB, "MBC6 unimplemented flash WE write: %04X:%02X", address, value); + break; case 0x8: case 0x9: GBMBCSwitchHalfBank(gb, 0, bank); break; + case 0xA: + case 0xB: + _GBMBC6MapChip(gb, 0, value); + break; case 0xC: case 0xD: GBMBCSwitchHalfBank(gb, 1, bank); break; + case 0xE: + case 0xF: + _GBMBC6MapChip(gb, 1, value); + break; case 0x28: case 0x29: case 0x2A: @@ -732,6 +768,16 @@ uint8_t _GBMBC6Read(struct GBMemory* memory, uint16_t address) { return 0xFF; } +static void _GBMBC6MapChip(struct GB* gb, int half, uint8_t value) { + if (!half) { + gb->memory.mbcState.mbc6.flashBank0 = !!(value & 0x08); + GBMBCSwitchHalfBank(gb, half, gb->memory.currentBank); + } else { + gb->memory.mbcState.mbc6.flashBank1 = !!(value & 0x08); + GBMBCSwitchHalfBank(gb, half, gb->memory.mbcState.mbc6.currentBank1); + } +} + void _GBMBC7(struct GB* gb, uint16_t address, uint8_t value) { int bank = value & 0x7F; switch (address >> 13) {