diff --git a/CHANGES b/CHANGES index ecb5ae98e..5ef27626f 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,7 @@ Features: - Support for combo "Super Game Boy Color" SGB + GBC ROM hacks - Support for 64 kiB SRAM saves used in some bootlegs 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) - GBA SIO: Add missing NORMAL8 implementation bits (fixes mgba.io/i/2172) - GBA SIO: Fix missing interrupt on an unattached NORMAL transfer diff --git a/include/mgba/internal/gb/memory.h b/include/mgba/internal/gb/memory.h index e2eecca4d..a9dc8b7e9 100644 --- a/include/mgba/internal/gb/memory.h +++ b/include/mgba/internal/gb/memory.h @@ -181,6 +181,9 @@ struct GBMemory { union GBMBCState mbcState; int currentBank; int currentBank0; + unsigned cartBusDecay; + uint16_t cartBusPc; + uint8_t cartBus; uint8_t* wram; uint8_t* wramBank; diff --git a/include/mgba/internal/gb/serialize.h b/include/mgba/internal/gb/serialize.h index 18ac83b09..5191e0966 100644 --- a/include/mgba/internal/gb/serialize.h +++ b/include/mgba/internal/gb/serialize.h @@ -157,9 +157,11 @@ mLOG_DECLARE_CATEGORY(GB_STATE); * | bit 3: IME * | bit 4: Is HDMA active? * | bits 5 - 7: Active RTC register - * | 0x00196 - 0x00197: Reserved (leave zero) + * | 0x00196: Cartridge bus value + * | 0x00197: Reserved (leave zero) * 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 * 0x00300 - 0x0037F: I/O memory * 0x00380 - 0x003FE: HRAM @@ -401,12 +403,14 @@ struct GBSerializedState { }; GBSerializedMemoryFlags flags; - uint16_t reserved; + uint8_t cartBus; + uint8_t reserved; } memory; uint64_t globalCycles; - uint32_t reserved[48]; + uint16_t cartBusPc; + uint16_t reserved[95]; uint8_t oam[GB_SIZE_OAM]; diff --git a/src/gb/mbc.c b/src/gb/mbc.c index 3cb9a1a5d..bd8ad46f0 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -322,6 +322,7 @@ void GBMBCInit(struct GB* gb) { } gb->memory.mbcRead = NULL; gb->memory.directSramAccess = true; + gb->memory.cartBusDecay = 4; switch (gb->memory.mbcType) { case GB_MBC_NONE: gb->memory.mbcWrite = _GBMBCNone; @@ -442,6 +443,9 @@ void GBMBCInit(struct GB* gb) { void GBMBCReset(struct GB* gb) { gb->memory.currentBank0 = 0; 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)); GBMBCInit(gb); diff --git a/src/gb/memory.c b/src/gb/memory.c index fec8bf2ab..0ec280aa1 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -51,12 +51,17 @@ static const uint8_t _blockedRegion[1] = { 0xFF }; 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)) { cpu->memory.setActiveRegion(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) { @@ -67,7 +72,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: - cpu->memory.cpuLoad8 = GBFastLoad8; + cpu->memory.cpuLoad8 = GBCartLoad8; cpu->memory.activeRegion = memory->romBase; cpu->memory.activeRegionEnd = GB_BASE_CART_BANK1; 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; break; } - cpu->memory.cpuLoad8 = GBFastLoad8; + cpu->memory.cpuLoad8 = GBCartLoad8; if (gb->memory.mbcType != GB_MBC6) { cpu->memory.activeRegion = memory->romBank; 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 + 3: 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 + 3: 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 case GB_REGION_CART_BANK1: case GB_REGION_CART_BANK1 + 1: 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) { - return memory->mbcRead(memory, address); - } - return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)]; + memory->cartBusPc = cpu->pc; + return memory->cartBus; case GB_REGION_VRAM: case GB_REGION_VRAM + 1: 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 + 1: if (memory->rtcAccess) { - return memory->rtcRegs[memory->activeRtcReg]; + memory->cartBus = memory->rtcRegs[memory->activeRtcReg]; } else if (memory->mbcRead) { - return memory->mbcRead(memory, address); + memory->cartBus = memory->mbcRead(memory, address); } 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) { - 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 + 2: 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); STORE_16LE(flags, 0, &state->memory.flags); + state->memory.cartBus = memory->cartBus; + STORE_16LE(memory->cartBusPc, 0, &state->cartBusPc); + switch (memory->mbcType) { case GB_MBC1: 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->activeRtcReg = GBSerializedMemoryFlagsGetActiveRtcReg(flags); + memory->cartBus = state->memory.cartBus; + LOAD_16LE(memory->cartBusPc, 0, &state->cartBusPc); + switch (memory->mbcType) { case GB_MBC1: memory->mbcState.mbc1.mode = state->memory.mbc1.mode;