GB Memory: Add cursory cartridge open bus emulation (fixes #2032)

This commit is contained in:
Vicki Pfau 2021-06-23 20:34:11 -07:00
parent dc40ef7cb8
commit 78d3a1f17e
5 changed files with 54 additions and 21 deletions

View File

@ -7,6 +7,7 @@ Features:
- Support for combo "Super Game Boy Color" SGB + GBC ROM hacks - Support for combo "Super Game Boy Color" SGB + GBC ROM hacks
- Support for 64 kiB SRAM saves used in some bootlegs - Support for 64 kiB SRAM saves used in some bootlegs
Emulation fixes: Emulation fixes:
- GB Memory: Add cursory cartridge open bus emulation (fixes mgba.io/i/2032)
- GB Video: Clear VRAM on reset (fixes mgba.io/i/2152) - GB Video: Clear VRAM on reset (fixes mgba.io/i/2152)
- GBA SIO: Add missing NORMAL8 implementation bits (fixes mgba.io/i/2172) - GBA SIO: Add missing NORMAL8 implementation bits (fixes mgba.io/i/2172)
- GBA SIO: Fix missing interrupt on an unattached NORMAL transfer - GBA SIO: Fix missing interrupt on an unattached NORMAL transfer

View File

@ -181,6 +181,9 @@ struct GBMemory {
union GBMBCState mbcState; union GBMBCState mbcState;
int currentBank; int currentBank;
int currentBank0; int currentBank0;
unsigned cartBusDecay;
uint16_t cartBusPc;
uint8_t cartBus;
uint8_t* wram; uint8_t* wram;
uint8_t* wramBank; uint8_t* wramBank;

View File

@ -157,9 +157,11 @@ mLOG_DECLARE_CATEGORY(GB_STATE);
* | bit 3: IME * | bit 3: IME
* | bit 4: Is HDMA active? * | bit 4: Is HDMA active?
* | bits 5 - 7: Active RTC register * | bits 5 - 7: Active RTC register
* | 0x00196 - 0x00197: Reserved (leave zero) * | 0x00196: Cartridge bus value
* | 0x00197: Reserved (leave zero)
* 0x00198 - 0x0019F: Global cycle counter * 0x00198 - 0x0019F: Global cycle counter
* 0x001A0 - 0x0025F: Reserved (leave zero) * 0x001A0 - 0x001A1: Program counter for last cartridge read
* 0x001A2 - 0x0025F: Reserved (leave zero)
* 0x00260 - 0x002FF: OAM * 0x00260 - 0x002FF: OAM
* 0x00300 - 0x0037F: I/O memory * 0x00300 - 0x0037F: I/O memory
* 0x00380 - 0x003FE: HRAM * 0x00380 - 0x003FE: HRAM
@ -401,12 +403,14 @@ struct GBSerializedState {
}; };
GBSerializedMemoryFlags flags; GBSerializedMemoryFlags flags;
uint16_t reserved; uint8_t cartBus;
uint8_t reserved;
} memory; } memory;
uint64_t globalCycles; uint64_t globalCycles;
uint32_t reserved[48]; uint16_t cartBusPc;
uint16_t reserved[95];
uint8_t oam[GB_SIZE_OAM]; uint8_t oam[GB_SIZE_OAM];

View File

@ -322,6 +322,7 @@ void GBMBCInit(struct GB* gb) {
} }
gb->memory.mbcRead = NULL; gb->memory.mbcRead = NULL;
gb->memory.directSramAccess = true; gb->memory.directSramAccess = true;
gb->memory.cartBusDecay = 4;
switch (gb->memory.mbcType) { switch (gb->memory.mbcType) {
case GB_MBC_NONE: case GB_MBC_NONE:
gb->memory.mbcWrite = _GBMBCNone; gb->memory.mbcWrite = _GBMBCNone;
@ -442,6 +443,9 @@ void GBMBCInit(struct GB* gb) {
void GBMBCReset(struct GB* gb) { void GBMBCReset(struct GB* gb) {
gb->memory.currentBank0 = 0; gb->memory.currentBank0 = 0;
gb->memory.romBank = &gb->memory.rom[GB_SIZE_CART_BANK0]; gb->memory.romBank = &gb->memory.rom[GB_SIZE_CART_BANK0];
gb->memory.cartBus = 0xFF;
gb->memory.cartBusPc = 0;
gb->memory.cartBusDecay = 1;
memset(&gb->memory.mbcState, 0, sizeof(gb->memory.mbcState)); memset(&gb->memory.mbcState, 0, sizeof(gb->memory.mbcState));
GBMBCInit(gb); GBMBCInit(gb);

View File

@ -51,12 +51,17 @@ static const uint8_t _blockedRegion[1] = { 0xFF };
static void _pristineCow(struct GB* gba); static void _pristineCow(struct GB* gba);
static uint8_t GBFastLoad8(struct SM83Core* cpu, uint16_t address) { static uint8_t GBCartLoad8(struct SM83Core* cpu, uint16_t address) {
if (UNLIKELY(address >= cpu->memory.activeRegionEnd)) { if (UNLIKELY(address >= cpu->memory.activeRegionEnd)) {
cpu->memory.setActiveRegion(cpu, address); cpu->memory.setActiveRegion(cpu, address);
return cpu->memory.cpuLoad8(cpu, address); return cpu->memory.cpuLoad8(cpu, address);
} }
return cpu->memory.activeRegion[address & cpu->memory.activeMask]; struct GB* gb = (struct GB*) cpu->master;
struct GBMemory* memory = &gb->memory;
memory->cartBusPc = address;
uint8_t value = cpu->memory.activeRegion[address & cpu->memory.activeMask];
memory->cartBus = value;
return value;
} }
static void GBSetActiveRegion(struct SM83Core* cpu, uint16_t address) { static void GBSetActiveRegion(struct SM83Core* cpu, uint16_t address) {
@ -67,7 +72,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:
cpu->memory.cpuLoad8 = GBFastLoad8; cpu->memory.cpuLoad8 = GBCartLoad8;
cpu->memory.activeRegion = memory->romBase; cpu->memory.activeRegion = memory->romBase;
cpu->memory.activeRegionEnd = GB_BASE_CART_BANK1; cpu->memory.activeRegionEnd = GB_BASE_CART_BANK1;
cpu->memory.activeMask = GB_SIZE_CART_BANK0 - 1; cpu->memory.activeMask = GB_SIZE_CART_BANK0 - 1;
@ -88,7 +93,7 @@ static void GBSetActiveRegion(struct SM83Core* cpu, uint16_t address) {
cpu->memory.cpuLoad8 = GBLoad8; cpu->memory.cpuLoad8 = GBLoad8;
break; break;
} }
cpu->memory.cpuLoad8 = GBFastLoad8; cpu->memory.cpuLoad8 = GBCartLoad8;
if (gb->memory.mbcType != GB_MBC6) { if (gb->memory.mbcType != GB_MBC6) {
cpu->memory.activeRegion = memory->romBank; cpu->memory.activeRegion = memory->romBank;
cpu->memory.activeRegionEnd = GB_BASE_VRAM; cpu->memory.activeRegionEnd = GB_BASE_VRAM;
@ -238,24 +243,31 @@ uint8_t GBLoad8(struct SM83Core* cpu, uint16_t address) {
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 (address >= memory->romSize) { if (address >= memory->romSize) {
return 0xFF; memory->cartBus = 0xFF;
} else {
memory->cartBus = memory->romBase[address & (GB_SIZE_CART_BANK0 - 1)];
} }
return memory->romBase[address & (GB_SIZE_CART_BANK0 - 1)]; memory->cartBusPc = cpu->pc;
return memory->cartBus;
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 (memory->mbcType == GB_MBC6) { if (memory->mbcType == GB_MBC6) {
return memory->mbcState.mbc6.romBank1[address & (GB_SIZE_CART_HALFBANK - 1)]; memory->cartBus = memory->mbcState.mbc6.romBank1[address & (GB_SIZE_CART_HALFBANK - 1)];
memory->cartBusPc = cpu->pc;
return memory->cartBus;
} }
// Fall through // Fall through
case GB_REGION_CART_BANK1: case GB_REGION_CART_BANK1:
case GB_REGION_CART_BANK1 + 1: case GB_REGION_CART_BANK1 + 1:
if (address >= memory->romSize) { if (address >= memory->romSize) {
return 0xFF; memory->cartBus = 0xFF;
} else if ((memory->mbcType & GB_UNL_BBD) == GB_UNL_BBD) {
memory->cartBus = memory->mbcRead(memory, address);
} else {
memory->cartBus = memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
} }
if ((memory->mbcType & GB_UNL_BBD) == GB_UNL_BBD) { memory->cartBusPc = cpu->pc;
return memory->mbcRead(memory, address); return memory->cartBus;
}
return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
case GB_REGION_VRAM: case GB_REGION_VRAM:
case GB_REGION_VRAM + 1: case GB_REGION_VRAM + 1:
if (gb->video.mode != 3) { if (gb->video.mode != 3) {
@ -265,15 +277,18 @@ uint8_t GBLoad8(struct SM83Core* cpu, uint16_t address) {
case GB_REGION_EXTERNAL_RAM: case GB_REGION_EXTERNAL_RAM:
case GB_REGION_EXTERNAL_RAM + 1: case GB_REGION_EXTERNAL_RAM + 1:
if (memory->rtcAccess) { if (memory->rtcAccess) {
return memory->rtcRegs[memory->activeRtcReg]; memory->cartBus = memory->rtcRegs[memory->activeRtcReg];
} else if (memory->mbcRead) { } else if (memory->mbcRead) {
return memory->mbcRead(memory, address); memory->cartBus = memory->mbcRead(memory, address);
} else if (memory->sramAccess && memory->sram) { } else if (memory->sramAccess && memory->sram) {
return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)]; memory->cartBus = memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
} else if (memory->mbcType == GB_HuC3) { } else if (memory->mbcType == GB_HuC3) {
return 0x01; // TODO: Is this supposed to be the current SRAM bank? memory->cartBus = 0x01; // TODO: Is this supposed to be the current SRAM bank?
} else if (cpu->tMultiplier * (cpu->pc - memory->cartBusPc) >= memory->cartBusDecay) {
memory->cartBus = 0xFF;
} }
return 0xFF; memory->cartBusPc = cpu->pc;
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:
return memory->wram[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)]; return memory->wram[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
@ -705,6 +720,9 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) {
flags = GBSerializedMemoryFlagsSetActiveRtcReg(flags, memory->activeRtcReg); flags = GBSerializedMemoryFlagsSetActiveRtcReg(flags, memory->activeRtcReg);
STORE_16LE(flags, 0, &state->memory.flags); STORE_16LE(flags, 0, &state->memory.flags);
state->memory.cartBus = memory->cartBus;
STORE_16LE(memory->cartBusPc, 0, &state->cartBusPc);
switch (memory->mbcType) { switch (memory->mbcType) {
case GB_MBC1: case GB_MBC1:
state->memory.mbc1.mode = memory->mbcState.mbc1.mode; state->memory.mbc1.mode = memory->mbcState.mbc1.mode;
@ -784,6 +802,9 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
memory->isHdma = GBSerializedMemoryFlagsGetIsHdma(flags); memory->isHdma = GBSerializedMemoryFlagsGetIsHdma(flags);
memory->activeRtcReg = GBSerializedMemoryFlagsGetActiveRtcReg(flags); memory->activeRtcReg = GBSerializedMemoryFlagsGetActiveRtcReg(flags);
memory->cartBus = state->memory.cartBus;
LOAD_16LE(memory->cartBusPc, 0, &state->cartBusPc);
switch (memory->mbcType) { switch (memory->mbcType) {
case GB_MBC1: case GB_MBC1:
memory->mbcState.mbc1.mode = state->memory.mbc1.mode; memory->mbcState.mbc1.mode = state->memory.mbc1.mode;