diff --git a/CHANGES b/CHANGES index 6a1153e18..f136763a2 100644 --- a/CHANGES +++ b/CHANGES @@ -5,6 +5,7 @@ Features: Emulation fixes: - ARM: Fix ALU reading PC after shifting - ARM: Fix STR storing PC after address calculation + - GB MBC: Fix MBC1 mode changing behavior - GB Video: Fix state after skipping BIOS (fixes mgba.io/i/1715 and mgba.io/i/1716) - GBA: Fix timing advancing too quickly in rare cases - GBA BIOS: Implement dummy sound driver calls diff --git a/cinema/gb/mooneye-gb/emulator-only/mbc1/bits_mode/baseline_0000.png b/cinema/gb/mooneye-gb/emulator-only/mbc1/bits_mode/baseline_0000.png new file mode 100644 index 000000000..bbe7e5937 Binary files /dev/null and b/cinema/gb/mooneye-gb/emulator-only/mbc1/bits_mode/baseline_0000.png differ diff --git a/cinema/gb/mooneye-gb/emulator-only/mbc1/bits_mode/config.ini b/cinema/gb/mooneye-gb/emulator-only/mbc1/bits_mode/config.ini deleted file mode 100644 index 7ddee425b..000000000 --- a/cinema/gb/mooneye-gb/emulator-only/mbc1/bits_mode/config.ini +++ /dev/null @@ -1,2 +0,0 @@ -[testinfo] -fail=1 diff --git a/include/mgba/internal/gb/memory.h b/include/mgba/internal/gb/memory.h index 3c7387b14..514311351 100644 --- a/include/mgba/internal/gb/memory.h +++ b/include/mgba/internal/gb/memory.h @@ -108,6 +108,8 @@ enum GBTAMA5Register { struct GBMBC1State { int mode; int multicartStride; + uint8_t bankLo; + uint8_t bankHi; }; struct GBMBC6State { diff --git a/include/mgba/internal/gb/serialize.h b/include/mgba/internal/gb/serialize.h index 68b7d8533..be5d17e2f 100644 --- a/include/mgba/internal/gb/serialize.h +++ b/include/mgba/internal/gb/serialize.h @@ -362,6 +362,8 @@ struct GBSerializedState { struct { uint8_t mode; uint8_t multicartStride; + uint8_t bankLo; + uint8_t bankHi; } mbc1; struct { uint64_t lastLatch; diff --git a/src/gb/mbc.c b/src/gb/mbc.c index be03f1511..8c6e3b2cf 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -419,10 +419,27 @@ static void _latchRtc(struct mRTCSource* rtc, uint8_t* rtcRegs, time_t* rtcLastL } } +static void _GBMBC1Update(struct GB* gb) { + struct GBMBC1State* state = &gb->memory.mbcState.mbc1; + int bank = state->bankLo; + bank &= (1 << state->multicartStride) - 1; + bank |= state->bankHi << state->multicartStride; + if (state->mode) { + GBMBCSwitchBank0(gb, state->bankHi << state->multicartStride); + GBMBCSwitchSramBank(gb, state->bankHi & 3); + } else { + GBMBCSwitchBank0(gb, 0); + GBMBCSwitchSramBank(gb, 0); + } + if (!(state->bankLo & 0x1F)) { + ++bank; + } + GBMBCSwitchBank(gb, bank); +} + void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) { struct GBMemory* memory = &gb->memory; int bank = value & 0x1F; - int stride = 1 << memory->mbcState.mbc1.multicartStride; switch (address >> 13) { case 0x0: switch (value) { @@ -440,28 +457,17 @@ void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) { } break; case 0x1: - if (!bank) { - ++bank; - } - bank &= stride - 1; - GBMBCSwitchBank(gb, bank | (memory->currentBank & (3 * stride))); + memory->mbcState.mbc1.bankLo = bank; + _GBMBC1Update(gb); break; case 0x2: bank &= 3; - if (memory->mbcState.mbc1.mode) { - GBMBCSwitchBank0(gb, bank << gb->memory.mbcState.mbc1.multicartStride); - GBMBCSwitchSramBank(gb, bank); - } - GBMBCSwitchBank(gb, (bank << memory->mbcState.mbc1.multicartStride) | (memory->currentBank & (stride - 1))); + memory->mbcState.mbc1.bankHi = bank; + _GBMBC1Update(gb); break; case 0x3: memory->mbcState.mbc1.mode = value & 1; - if (memory->mbcState.mbc1.mode) { - GBMBCSwitchBank0(gb, memory->currentBank & ~((1 << memory->mbcState.mbc1.multicartStride) - 1)); - } else { - GBMBCSwitchBank0(gb, 0); - GBMBCSwitchSramBank(gb, 0); - } + _GBMBC1Update(gb); break; default: // TODO diff --git a/src/gb/memory.c b/src/gb/memory.c index 18ba75a17..36d083df0 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -733,6 +733,8 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) { case GB_MBC1: state->memory.mbc1.mode = memory->mbcState.mbc1.mode; state->memory.mbc1.multicartStride = memory->mbcState.mbc1.multicartStride; + state->memory.mbc1.bankLo = memory->mbcState.mbc1.bankLo; + state->memory.mbc1.bankHi = memory->mbcState.mbc1.bankHi; break; case GB_MBC3_RTC: STORE_64LE(gb->memory.rtcLastLatch, 0, &state->memory.rtc.lastLatch); @@ -801,8 +803,15 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) { case GB_MBC1: memory->mbcState.mbc1.mode = state->memory.mbc1.mode; memory->mbcState.mbc1.multicartStride = state->memory.mbc1.multicartStride; + memory->mbcState.mbc1.bankLo = state->memory.mbc1.bankLo; + memory->mbcState.mbc1.bankHi = state->memory.mbc1.bankHi; + if (!(memory->mbcState.mbc1.bankLo || memory->mbcState.mbc1.bankHi)) { + // Backwards compat + memory->mbcState.mbc1.bankLo = memory->currentBank & ((1 << memory->mbcState.mbc1.multicartStride) - 1); + memory->mbcState.mbc1.bankHi = memory->currentBank >> memory->mbcState.mbc1.multicartStride; + } if (memory->mbcState.mbc1.mode) { - GBMBCSwitchBank0(gb, memory->currentBank >> memory->mbcState.mbc1.multicartStride); + GBMBCSwitchBank0(gb, memory->mbcState.mbc1.bankHi); } break; case GB_MBC3_RTC: