GB MBC: Add MBC6 flash support (read-only currently)

This commit is contained in:
Vicki Pfau 2020-09-03 23:43:47 -07:00
parent 2251d93938
commit 347c5f2de5
4 changed files with 63 additions and 14 deletions

View File

@ -5,9 +5,8 @@ Features:
- Separate overrides for GBC games that can also run on SGB or regular GB - Separate overrides for GBC games that can also run on SGB or regular GB
- Mute option in homebrew ports - Mute option in homebrew ports
- Status indicators for fast-forward and mute in homebrew ports - Status indicators for fast-forward and mute in homebrew ports
- Support for unlicensed Pokemon Jade/Diamond Game Boy mapper - Read-only support for MBC6 flash memory
- Support for unlicensed BBD Game Boy mapper - New unlicensed GB mappers: Pokémon Jade/Diamond, BBD, and Hitek
- Support for unlicensed Hitek Game Boy mapper
- Stack tracing tools in ARM debugger (by ahigerd) - Stack tracing tools in ARM debugger (by ahigerd)
- Command scripts for CLI debugger (by ahigerd) - Command scripts for CLI debugger (by ahigerd)
Emulation fixes: Emulation fixes:

View File

@ -57,7 +57,7 @@ The following mappers are fully supported:
The following mappers are partially supported: The following mappers are partially supported:
- MBC6 (missing flash memory support) - MBC6 (missing flash memory write support)
- MMM01 - MMM01
- Pocket Cam - Pocket Cam
- TAMA5 (missing RTC support) - TAMA5 (missing RTC support)

View File

@ -61,6 +61,8 @@ enum {
GB_SIZE_OAM = 0xA0, GB_SIZE_OAM = 0xA0,
GB_SIZE_IO = 0x80, GB_SIZE_IO = 0x80,
GB_SIZE_HRAM = 0x7F, GB_SIZE_HRAM = 0x7F,
GB_SIZE_MBC6_FLASH = 0x100000,
}; };
enum { enum {
@ -118,6 +120,8 @@ struct GBMBC6State {
bool sramAccess; bool sramAccess;
int currentSramBank1; int currentSramBank1;
uint8_t* sramBank1; uint8_t* sramBank1;
bool flashBank0;
bool flashBank1;
}; };
struct GBMBC7State { struct GBMBC7State {

View File

@ -53,6 +53,8 @@ static uint8_t _GBHitekRead(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*);
static void _GBMBC6MapChip(struct GB*, int half, uint8_t value);
void GBMBCSwitchBank(struct GB* gb, int bank) { void GBMBCSwitchBank(struct GB* gb, int bank) {
size_t bankStart = bank * GB_SIZE_CART_BANK0; size_t bankStart = bank * GB_SIZE_CART_BANK0;
if (bankStart + GB_SIZE_CART_BANK0 > gb->memory.romSize) { 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) { void GBMBCSwitchHalfBank(struct GB* gb, int half, int bank) {
size_t bankStart = bank * GB_SIZE_CART_HALFBANK; size_t bankStart = bank * GB_SIZE_CART_HALFBANK;
if (bankStart + GB_SIZE_CART_HALFBANK > gb->memory.romSize) { bool isFlash = half ? gb->memory.mbcState.mbc6.flashBank1 : gb->memory.mbcState.mbc6.flashBank0;
mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank); if (isFlash) {
bankStart &= (gb->memory.romSize - 1); if (bankStart + GB_SIZE_CART_HALFBANK > GB_SIZE_MBC6_FLASH) {
bank = bankStart / GB_SIZE_CART_HALFBANK; mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid Flash bank: %0X", bank);
if (!bank) { bankStart &= GB_SIZE_MBC6_FLASH - 1;
++bank; 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) { 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; gb->memory.currentBank = bank;
} else { } 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; gb->memory.mbcState.mbc6.currentBank1 = bank;
} }
if (gb->cpu->pc < GB_BASE_VRAM) { 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) { void GBMBCSwitchSramHalfBank(struct GB* gb, int half, int bank) {
size_t bankStart = bank * GB_SIZE_EXTERNAL_RAM_HALFBANK; 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); 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; bank = bankStart / GB_SIZE_EXTERNAL_RAM_HALFBANK;
} }
if (!half) { if (!half) {
@ -334,6 +355,7 @@ void GBMBCInit(struct GB* gb) {
gb->memory.mbcWrite = _GBMBC6; gb->memory.mbcWrite = _GBMBC6;
gb->memory.mbcRead = _GBMBC6Read; gb->memory.mbcRead = _GBMBC6Read;
gb->memory.directSramAccess = false; gb->memory.directSramAccess = false;
gb->sramSize += GB_SIZE_MBC6_FLASH; // Flash is concatenated at the end
break; break;
case GB_MBC7: case GB_MBC7:
gb->memory.mbcWrite = _GBMBC7; gb->memory.mbcWrite = _GBMBC7;
@ -689,14 +711,28 @@ void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) {
case 0x2: case 0x2:
GBMBCSwitchSramHalfBank(gb, 1, bank); GBMBCSwitchSramHalfBank(gb, 1, bank);
break; 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 0x8:
case 0x9: case 0x9:
GBMBCSwitchHalfBank(gb, 0, bank); GBMBCSwitchHalfBank(gb, 0, bank);
break; break;
case 0xA:
case 0xB:
_GBMBC6MapChip(gb, 0, value);
break;
case 0xC: case 0xC:
case 0xD: case 0xD:
GBMBCSwitchHalfBank(gb, 1, bank); GBMBCSwitchHalfBank(gb, 1, bank);
break; break;
case 0xE:
case 0xF:
_GBMBC6MapChip(gb, 1, value);
break;
case 0x28: case 0x28:
case 0x29: case 0x29:
case 0x2A: case 0x2A:
@ -732,6 +768,16 @@ uint8_t _GBMBC6Read(struct GBMemory* memory, uint16_t address) {
return 0xFF; 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) { void _GBMBC7(struct GB* gb, uint16_t address, uint8_t value) {
int bank = value & 0x7F; int bank = value & 0x7F;
switch (address >> 13) { switch (address >> 13) {