GB MBC: Add MBC6 SRAM support
Vicki Pfau vi@endrift.com
Wed, 18 Apr 2018 18:26:50 -0700
4 files changed,
65 insertions(+),
3 deletions(-)
M
include/mgba/internal/gb/mbc.h
→
include/mgba/internal/gb/mbc.h
@@ -21,6 +21,7 @@ void GBMBCSwitchBank(struct GB* gb, int bank);
void GBMBCSwitchBank0(struct GB* gb, int bank); void GBMBCSwitchHalfBank(struct GB* gb, int half, int bank); void GBMBCSwitchSramBank(struct GB* gb, int bank); +void GBMBCSwitchSramHalfBank(struct GB* gb, int half, int bank); enum GBCam { GBCAM_WIDTH = 128,
M
include/mgba/internal/gb/memory.h
→
include/mgba/internal/gb/memory.h
@@ -26,6 +26,8 @@ GB_BASE_CART_HALFBANK1 = 0x4000,
GB_BASE_CART_HALFBANK2 = 0x6000, GB_BASE_VRAM = 0x8000, GB_BASE_EXTERNAL_RAM = 0xA000, + GB_BASE_EXTERNAL_RAM_HALFBANK0 = 0xA000, + GB_BASE_EXTERNAL_RAM_HALFBANK1 = 0xB000, GB_BASE_WORKING_RAM_BANK0 = 0xC000, GB_BASE_WORKING_RAM_BANK1 = 0xD000, GB_BASE_OAM = 0xFE00,@@ -53,6 +55,7 @@ GB_SIZE_CART_MAX = 0x800000,
GB_SIZE_VRAM = 0x4000, GB_SIZE_VRAM_BANK0 = 0x2000, GB_SIZE_EXTERNAL_RAM = 0x2000, + GB_SIZE_EXTERNAL_RAM_HALFBANK = 0x1000, GB_SIZE_WORKING_RAM = 0x8000, GB_SIZE_WORKING_RAM_BANK0 = 0x1000, GB_SIZE_OAM = 0xA0,@@ -110,6 +113,9 @@
struct GBMBC6State { int currentBank1; uint8_t* romBank1; + bool sramAccess; + int currentSramBank1; + uint8_t* sramBank1; }; struct GBMBC7State {
M
src/gb/mbc.c
→
src/gb/mbc.c
@@ -32,6 +32,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 uint8_t _GBMBC2Read(struct GBMemory*, uint16_t address); +static uint8_t _GBMBC6Read(struct GBMemory*, uint16_t address); static uint8_t _GBMBC7Read(struct GBMemory*, uint16_t address); static void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value);@@ -112,6 +113,22 @@ bank = bankStart / GB_SIZE_EXTERNAL_RAM;
} gb->memory.sramBank = &gb->memory.sram[bankStart]; gb->memory.sramCurrentBank = 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) { + mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid RAM bank: %0X", bank); + bankStart &= (gb->sramSize - 1); + bank = bankStart / GB_SIZE_EXTERNAL_RAM_HALFBANK; + } + if (!half) { + gb->memory.sramBank = &gb->memory.sram[bankStart]; + gb->memory.sramCurrentBank = bank; + } else { + gb->memory.mbcState.mbc6.sramBank1 = &gb->memory.sram[bankStart]; + gb->memory.mbcState.mbc6.currentSramBank1 = bank; + } } void GBMBCInit(struct GB* gb) {@@ -230,6 +247,7 @@ break;
case GB_MBC6: mLOG(GB_MBC, WARN, "unimplemented MBC: MBC6"); gb->memory.mbcWrite = _GBMBC6; + gb->memory.mbcRead = _GBMBC6Read; break; case GB_MBC7: gb->memory.mbcWrite = _GBMBC7;@@ -536,11 +554,10 @@ switch (address >> 10) {
case 0: switch (value) { case 0: - memory->sramAccess = false; + memory->mbcState.mbc6.sramAccess = false; break; case 0xA: - memory->sramAccess = true; - GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + memory->mbcState.mbc6.sramAccess = true; break; default: // TODO@@ -548,6 +565,12 @@ mLOG(GB_MBC, STUB, "MBC6 unknown value %02X", value);
break; } break; + case 0x1: + GBMBCSwitchSramHalfBank(gb, 0, bank); + break; + case 0x2: + GBMBCSwitchSramHalfBank(gb, 1, bank); + break; case 0x8: case 0x9: GBMBCSwitchHalfBank(gb, 0, bank);@@ -556,10 +579,39 @@ case 0xC:
case 0xD: GBMBCSwitchHalfBank(gb, 1, bank); break; + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + if (memory->mbcState.mbc6.sramAccess) { + memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value; + } + break; + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + if (memory->mbcState.mbc6.sramAccess) { + memory->mbcState.mbc6.sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value; + } + break; default: mLOG(GB_MBC, STUB, "MBC6 unknown address: %04X:%02X", address, value); break; } +} + +uint8_t _GBMBC6Read(struct GBMemory* memory, uint16_t address) { + if (!memory->mbcState.mbc6.sramAccess) { + return 0xFF; + } + switch (address >> 12) { + case 0xA: + return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)]; + case 0xB: + return memory->mbcState.mbc6.sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)]; + } + return 0xFF; } void _GBMBC7(struct GB* gb, uint16_t address, uint8_t value) {
M
src/gb/memory.c
→
src/gb/memory.c
@@ -183,6 +183,9 @@ break;
case GB_MBC6: GBMBCSwitchHalfBank(gb, 0, 2); GBMBCSwitchHalfBank(gb, 1, 3); + gb->memory.mbcState.mbc6.sramAccess = false; + GBMBCSwitchSramHalfBank(gb, 0, 0); + GBMBCSwitchSramHalfBank(gb, 0, 1); break; default: memset(&gb->memory.mbcState, 0, sizeof(gb->memory.mbcState));