From c829cd2e70eec9f37ecf6e83c674a193afeed035 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 2 Feb 2022 03:03:46 -0800 Subject: [PATCH 1/5] GB MBC: Improved support for HuC-3 mapper --- CHANGES | 1 + include/mgba/core/interface.h | 1 + include/mgba/internal/gb/memory.h | 43 ++++++++ include/mgba/internal/gb/serialize.h | 13 ++- src/gb/mbc.c | 145 ++++++++++++++++++++++++++- src/gb/memory.c | 26 ++++- 6 files changed, 224 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index c35632def..9d9c5d807 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,7 @@ Features: - Cheat code support in homebrew ports - Acclerometer and gyro support for controllers on PC - Support for combo "Super Game Boy Color" SGB + GBC ROM hacks + - Improved support for HuC-3 mapper - Support for 64 kiB SRAM saves used in some bootlegs - Discord Rich Presence now supports time elapsed - Additional scaling shaders diff --git a/include/mgba/core/interface.h b/include/mgba/core/interface.h index 7ba97e1eb..d75819d2c 100644 --- a/include/mgba/core/interface.h +++ b/include/mgba/core/interface.h @@ -182,6 +182,7 @@ struct mCoreCallbacks { void (*shutdown)(void* context); void (*keysRead)(void* context); void (*savedataUpdated)(void* context); + void (*alarm)(void* context); }; DECLARE_VECTOR(mCoreCallbacksList, struct mCoreCallbacks); diff --git a/include/mgba/internal/gb/memory.h b/include/mgba/internal/gb/memory.h index 29ffb6960..59691682c 100644 --- a/include/mgba/internal/gb/memory.h +++ b/include/mgba/internal/gb/memory.h @@ -102,6 +102,41 @@ enum GBTAMA5Register { GBTAMA5_READ_HI = 0xD, }; +enum GBHuC3Register { + GBHUC3_RTC_MINUTES_LO = 0x10, + GBHUC3_RTC_MINUTES_MI = 0x11, + GBHUC3_RTC_MINUTES_HI = 0x12, + GBHUC3_RTC_DAYS_LO = 0x13, + GBHUC3_RTC_DAYS_MI = 0x14, + GBHUC3_RTC_DAYS_HI = 0x15, + GBHUC3_RTC_ENABLE = 0x16, + GBHUC3_SPEAKER_TONE = 0x26, + GBHUC3_SPEAKER_ENABLE = 0x27, + GBHUC3_ALARM_MINUTES_LO = 0x58, + GBHUC3_ALARM_MINUTES_MI = 0x59, + GBHUC3_ALARM_MINUTES_HI = 0x5A, + GBHUC3_ALARM_DAYS_LO = 0x5B, + GBHUC3_ALARM_DAYS_MI = 0x5C, + GBHUC3_ALARM_DAYS_HI = 0x5D, + GBHUC3_ALARM_TONE = 0x5E, + GBHUC3_ALARM_ENABLE = 0x5F, +}; + +enum GBHuC3Mode { + GBHUC3_MODE_SRAM_RO = 0x0, + GBHUC3_MODE_SRAM_RW = 0xA, + GBHUC3_MODE_IN = 0xB, + GBHUC3_MODE_OUT = 0xC, + GBHUC3_MODE_COMMIT = 0xD, +}; + +enum GBHuC3Command { + GBHUC3_CMD_LATCH = 0x0, + GBHUC3_CMD_SET_RTC = 0x1, + GBHUC3_CMD_RO = 0x2, + GBHUC3_CMD_TONE = 0xE, +}; + struct GBMBC1State { int mode; int multicartStride; @@ -145,6 +180,13 @@ struct GBTAMA5State { uint8_t registers[GBTAMA5_MAX]; }; +struct GBHuC3State { + uint8_t index; + uint8_t value; + uint8_t mode; + uint8_t registers[256]; +}; + struct GBPKJDState { uint8_t reg[2]; }; @@ -161,6 +203,7 @@ union GBMBCState { struct GBMMM01State mmm01; struct GBPocketCamState pocketCam; struct GBTAMA5State tama5; + struct GBHuC3State huc3; struct GBPKJDState pkjd; struct GBBBDState bbd; }; diff --git a/include/mgba/internal/gb/serialize.h b/include/mgba/internal/gb/serialize.h index 5a26a6937..274172cbe 100644 --- a/include/mgba/internal/gb/serialize.h +++ b/include/mgba/internal/gb/serialize.h @@ -168,7 +168,8 @@ mLOG_DECLARE_CATEGORY(GB_STATE); * 0x003FF: Interrupts enabled * 0x00400 - 0x043FF: VRAM * 0x04400 - 0x0C3FF: WRAM - * 0x0C400 - 0x0C77F: Reserved + * 0x0C400 - 0x0C6FF: Reserved + * 0x0C700 - 0x0C77F: Reserved * 0x0C780 - 0x117FF: Super Game Boy * | 0x0C780 - 0x0C7D9: Current attributes * | 0x0C7DA: Current command @@ -393,6 +394,12 @@ struct GBSerializedState { uint8_t locked; uint8_t bank0; } mmm01; + struct { + uint64_t lastLatch; + uint8_t index; + uint8_t value; + uint8_t mode; + } huc3; struct { uint8_t dataSwapMode; uint8_t bankSwapMode; @@ -421,7 +428,9 @@ struct GBSerializedState { uint8_t vram[GB_SIZE_VRAM]; uint8_t wram[GB_SIZE_WORKING_RAM]; - uint32_t reserved2[0xC4]; + uint32_t reserved2[0xA4]; + + uint8_t huc3Registers[0x80]; struct { uint8_t attributes[90]; diff --git a/src/gb/mbc.c b/src/gb/mbc.c index de6f7802d..e2837e2f1 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -47,6 +47,7 @@ static uint8_t _GBMBC7Read(struct GBMemory*, uint16_t address); static void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value); static uint8_t _GBTAMA5Read(struct GBMemory*, uint16_t address); +static uint8_t _GBHuC3Read(struct GBMemory*, uint16_t address); static uint8_t _GBPKJDRead(struct GBMemory*, uint16_t address); static uint8_t _GBBBDRead(struct GBMemory*, uint16_t address); static uint8_t _GBHitekRead(struct GBMemory*, uint16_t address); @@ -373,6 +374,7 @@ void GBMBCInit(struct GB* gb) { break; case GB_HuC3: gb->memory.mbcWrite = _GBHuC3; + gb->memory.mbcRead = _GBHuC3Read; break; case GB_TAMA5: mLOG(GB_MBC, WARN, "unimplemented MBC: TAMA5"); @@ -1101,9 +1103,123 @@ void _GBHuC1(struct GB* gb, uint16_t address, uint8_t value) { } } +static void _latchHuC3Rtc(struct mRTCSource* rtc, uint8_t* huc3Regs, time_t* rtcLastLatch) { + time_t t; + if (rtc) { + if (rtc->sample) { + rtc->sample(rtc); + } + t = rtc->unixTime(rtc); + } else { + t = time(0); + } + t -= *rtcLastLatch; + t /= 60; + + if (!t) { + return; + } + *rtcLastLatch += t * 60; + + int minutes = huc3Regs[GBHUC3_RTC_MINUTES_HI] << 8; + minutes |= huc3Regs[GBHUC3_RTC_MINUTES_MI] << 4; + minutes |= huc3Regs[GBHUC3_RTC_MINUTES_LO]; + minutes += t % 1440; + t /= 1440; + if (minutes >= 1440) { + minutes -= 1440; + ++t; + } else if (minutes < 0) { + minutes += 1440; + --t; + } + huc3Regs[GBHUC3_RTC_MINUTES_LO] = minutes & 0xF; + huc3Regs[GBHUC3_RTC_MINUTES_MI] = (minutes >> 4) & 0xF; + huc3Regs[GBHUC3_RTC_MINUTES_HI] = (minutes >> 8) & 0xF; + + int days = huc3Regs[GBHUC3_RTC_DAYS_LO]; + days |= huc3Regs[GBHUC3_RTC_DAYS_MI] << 4; + days |= huc3Regs[GBHUC3_RTC_DAYS_HI] << 8; + + days += t; + + huc3Regs[GBHUC3_RTC_DAYS_LO] = days & 0xF; + huc3Regs[GBHUC3_RTC_DAYS_MI] = (days >> 4) & 0xF; + huc3Regs[GBHUC3_RTC_DAYS_HI] = (days >> 8) & 0xF; +} + +static void _huc3Commit(struct GB* gb, struct GBHuC3State* state) { + size_t c; + switch (state->value & 0x70) { + case 0x10: + if ((state->index & 0xF8) == 0x10) { + _latchHuC3Rtc(gb->memory.rtc, state->registers, &gb->memory.rtcLastLatch); + } + state->value &= 0xF0; + state->value |= state->registers[state->index] & 0xF; + mLOG(GB_MBC, DEBUG, "HuC-3 read: %02X:%X", state->index, state->value & 0xF); + if (state->value & 0x10) { + ++state->index; + } + break; + case 0x30: + mLOG(GB_MBC, DEBUG, "HuC-3 write: %02X:%X", state->index, state->value & 0xF); + state->registers[state->index] = state->value & 0xF; + if (state->value & 0x10) { + ++state->index; + } + break; + case 0x40: + state->index &= 0xF0; + state->index |= (state->value) & 0xF; + mLOG(GB_MBC, DEBUG, "HuC-3 index (low): %02X", state->index); + break; + case 0x50: + state->index &= 0x0F; + state->index |= ((state->value) & 0xF) << 4; + mLOG(GB_MBC, DEBUG, "HuC-3 index (high): %02X", state->index); + break; + case 0x60: + switch (state->value & 0xF) { + case GBHUC3_CMD_LATCH: + _latchHuC3Rtc(gb->memory.rtc, state->registers, &gb->memory.rtcLastLatch); + memcpy(state->registers, &state->registers[GBHUC3_RTC_MINUTES_LO], 6); + mLOG(GB_MBC, DEBUG, "HuC-3 RTC latch"); + break; + case GBHUC3_CMD_SET_RTC: + memcpy(&state->registers[GBHUC3_RTC_MINUTES_LO], state->registers, 6); + mLOG(GB_MBC, DEBUG, "HuC-3 set RTC"); + break; + case GBHUC3_CMD_RO: + mLOG(GB_MBC, STUB, "HuC-3 unimplemented read-only mode"); + break; + case GBHUC3_CMD_TONE: + if (state->registers[GBHUC3_SPEAKER_ENABLE] == 1) { + for (c = 0; c < mCoreCallbacksListSize(&gb->coreCallbacks); ++c) { + struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gb->coreCallbacks, c); + if (callbacks->alarm) { + callbacks->alarm(callbacks->context); + } + } + mLOG(GB_MBC, DEBUG, "HuC-3 tone %i", state->registers[GBHUC3_SPEAKER_TONE] & 3); + } + break; + default: + mLOG(GB_MBC, STUB, "HuC-3 unknown command: %X", state->value & 0xF); + break; + } + state->value = 0xE1; + break; + default: + mLOG(GB_MBC, STUB, "HuC-3 unknown mode commit: %02X:%02X", state->index, state->value); + break; + } +} + void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) { struct GBMemory* memory = &gb->memory; - int bank = value & 0x3F; + struct GBHuC3State* state = &memory->mbcState.huc3; + int bank = value & 0x7F; if (address & 0x1FFF) { mLOG(GB_MBC, STUB, "HuC-3 unknown value %04X:%02X", address, value); } @@ -1119,6 +1235,7 @@ void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) { memory->sramAccess = false; break; } + state->mode = value; break; case 0x1: GBMBCSwitchBank(gb, bank); @@ -1126,6 +1243,18 @@ void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) { case 0x2: GBMBCSwitchSramBank(gb, bank); break; + case 0x5: + switch (state->mode) { + case GBHUC3_MODE_IN: + state->value = 0x80 | value; + break; + case GBHUC3_MODE_COMMIT: + _huc3Commit(gb, state); + break; + default: + mLOG(GB_MBC, STUB, "HuC-3 unknown mode write: %02X:%02X", state->mode, value); + } + break; default: // TODO mLOG(GB_MBC, STUB, "HuC-3 unknown address: %04X:%02X", address, value); @@ -1133,6 +1262,20 @@ void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) { } } +uint8_t _GBHuC3Read(struct GBMemory* memory, uint16_t address) { + struct GBHuC3State* state = &memory->mbcState.huc3; + switch (state->mode) { + case GBHUC3_MODE_SRAM_RO: + case GBHUC3_MODE_SRAM_RW: + return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)]; + case GBHUC3_MODE_IN: + case GBHUC3_MODE_OUT: + return 0x80 | state->value; + default: + return 0xFF; + } +} + void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value) { struct GBMemory* memory = &gb->memory; int bank = value & 0x3F; diff --git a/src/gb/memory.c b/src/gb/memory.c index a89cbbcce..66c35c644 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -726,6 +726,7 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) { state->memory.cartBus = memory->cartBus; STORE_16LE(memory->cartBusPc, 0, &state->cartBusPc); + int i; switch (memory->mbcType) { case GB_MBC1: state->memory.mbc1.mode = memory->mbcState.mbc1.mode; @@ -734,7 +735,7 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) { state->memory.mbc1.bankHi = memory->mbcState.mbc1.bankHi; break; case GB_MBC3_RTC: - STORE_64LE(gb->memory.rtcLastLatch, 0, &state->memory.rtc.lastLatch); + STORE_64LE(memory->rtcLastLatch, 0, &state->memory.rtc.lastLatch); break; case GB_MBC7: state->memory.mbc7.state = memory->mbcState.mbc7.state; @@ -746,6 +747,16 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) { STORE_16LE(memory->mbcState.mbc7.sr, 0, &state->memory.mbc7.sr); STORE_32LE(memory->mbcState.mbc7.writable, 0, &state->memory.mbc7.writable); break; + case GB_HuC3: + STORE_64LE(memory->rtcLastLatch, 0, &state->memory.huc3.lastLatch); + state->memory.huc3.index = memory->mbcState.huc3.index; + state->memory.huc3.value = memory->mbcState.huc3.value; + state->memory.huc3.mode = memory->mbcState.huc3.mode; + for (i = 0; i < 0x80; ++i) { + state->huc3Registers[i] = memory->mbcState.huc3.registers[i * 2] & 0xF; + state->huc3Registers[i] |= memory->mbcState.huc3.registers[i * 2 + 1] << 4; + } + break; case GB_MMM01: state->memory.mmm01.locked = memory->mbcState.mmm01.locked; state->memory.mmm01.bank0 = memory->mbcState.mmm01.currentBank0; @@ -808,6 +819,7 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) { memory->cartBus = state->memory.cartBus; LOAD_16LE(memory->cartBusPc, 0, &state->cartBusPc); + int i; switch (memory->mbcType) { case GB_MBC1: memory->mbcState.mbc1.mode = state->memory.mbc1.mode; @@ -824,7 +836,7 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) { } break; case GB_MBC3_RTC: - LOAD_64LE(gb->memory.rtcLastLatch, 0, &state->memory.rtc.lastLatch); + LOAD_64LE(memory->rtcLastLatch, 0, &state->memory.rtc.lastLatch); break; case GB_MBC7: memory->mbcState.mbc7.state = state->memory.mbc7.state; @@ -836,6 +848,16 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) { LOAD_16LE(memory->mbcState.mbc7.sr, 0, &state->memory.mbc7.sr); LOAD_32LE(memory->mbcState.mbc7.writable, 0, &state->memory.mbc7.writable); break; + case GB_HuC3: + LOAD_64LE(memory->rtcLastLatch, 0, &state->memory.huc3.lastLatch); + memory->mbcState.huc3.index = state->memory.huc3.index; + memory->mbcState.huc3.value = state->memory.huc3.value; + memory->mbcState.huc3.mode = state->memory.huc3.mode; + for (i = 0; i < 0x80; ++i) { + memory->mbcState.huc3.registers[i * 2] = state->huc3Registers[i] & 0xF; + memory->mbcState.huc3.registers[i * 2 + 1] = state->huc3Registers[i] >> 4; + } + break; case GB_MMM01: memory->mbcState.mmm01.locked = state->memory.mmm01.locked; memory->mbcState.mmm01.currentBank0 = state->memory.mmm01.bank0; From 26aea8544fc46c87b28541de4df744ef0ec4c518 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 4 Feb 2022 23:48:03 -0800 Subject: [PATCH 2/5] GB MBC: Initial HuC-3 save format --- CHANGES | 2 +- include/mgba/internal/gb/mbc.h | 9 ++++++ src/gb/gb.c | 12 +++++-- src/gb/mbc.c | 59 +++++++++++++++++++++++++++++----- 4 files changed, 71 insertions(+), 11 deletions(-) diff --git a/CHANGES b/CHANGES index 9d9c5d807..0162d2de5 100644 --- a/CHANGES +++ b/CHANGES @@ -7,7 +7,7 @@ Features: - Cheat code support in homebrew ports - Acclerometer and gyro support for controllers on PC - Support for combo "Super Game Boy Color" SGB + GBC ROM hacks - - Improved support for HuC-3 mapper + - Improved support for HuC-3 mapper, including RTC - Support for 64 kiB SRAM saves used in some bootlegs - Discord Rich Presence now supports time elapsed - Additional scaling shaders diff --git a/include/mgba/internal/gb/mbc.h b/include/mgba/internal/gb/mbc.h index 2a6c5a3bd..a63de3b84 100644 --- a/include/mgba/internal/gb/mbc.h +++ b/include/mgba/internal/gb/mbc.h @@ -42,9 +42,18 @@ struct GBMBCRTCSaveBuffer { uint32_t latchedDaysHi; uint64_t unixTime; }; + +struct GBMBCHuC3SaveBuffer { + uint8_t regs[0x80]; + uint64_t latchedUnix; +}; + void GBMBCRTCRead(struct GB* gb); void GBMBCRTCWrite(struct GB* gb); +void GBMBCHuC3Read(struct GB* gb); +void GBMBCHuC3Write(struct GB* gb); + CXX_GUARD_END #endif diff --git a/src/gb/gb.c b/src/gb/gb.c index 43f5d93a0..186231bd6 100644 --- a/src/gb/gb.c +++ b/src/gb/gb.c @@ -143,8 +143,12 @@ void GBYankROM(struct GB* gb) { static void GBSramDeinit(struct GB* gb) { if (gb->sramVf) { gb->sramVf->unmap(gb->sramVf, gb->memory.sram, gb->sramSize); - if (gb->memory.mbcType == GB_MBC3_RTC && gb->sramVf == gb->sramRealVf) { - GBMBCRTCWrite(gb); + if (gb->sramVf == gb->sramRealVf) { + if (gb->memory.mbcType == GB_MBC3_RTC) { + GBMBCRTCWrite(gb); + } else if (gb->memory.mbcType == GB_HuC3) { + GBMBCHuC3Write(gb); + } } gb->sramVf = NULL; } else if (gb->memory.sram) { @@ -163,6 +167,8 @@ bool GBLoadSave(struct GB* gb, struct VFile* vf) { if (gb->memory.mbcType == GB_MBC3_RTC) { GBMBCRTCRead(gb); + } else if (gb->memory.mbcType == GB_HuC3) { + GBMBCHuC3Read(gb); } } return vf; @@ -246,6 +252,8 @@ void GBSramClean(struct GB* gb, uint32_t frameCount) { } if (gb->memory.mbcType == GB_MBC3_RTC) { GBMBCRTCWrite(gb); + } else if (gb->memory.mbcType == GB_HuC3) { + GBMBCHuC3Write(gb); } if (gb->sramVf == gb->sramRealVf) { if (gb->memory.sram && gb->sramVf->sync(gb->sramVf, gb->memory.sram, gb->sramSize)) { diff --git a/src/gb/mbc.c b/src/gb/mbc.c index e2837e2f1..99190627c 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -440,6 +440,8 @@ void GBMBCInit(struct GB* gb) { if (gb->memory.mbcType == GB_MBC3_RTC) { GBMBCRTCRead(gb); + } else if (gb->memory.mbcType == GB_HuC3) { + GBMBCHuC3Read(gb); } } @@ -1692,6 +1694,21 @@ uint8_t _GBHitekRead(struct GBMemory* memory, uint16_t address) { } } +static void _appendSaveSuffix(struct GB* gb, const void* buffer, size_t size) { + struct VFile* vf = gb->sramVf; + if ((size_t) vf->size(vf) < gb->sramSize + size) { + // Writing past the end of the file can invalidate the file mapping + vf->unmap(vf, gb->memory.sram, gb->sramSize); + gb->memory.sram = NULL; + } + vf->seek(vf, gb->sramSize, SEEK_SET); + vf->write(vf, buffer, size); + if (!gb->memory.sram) { + gb->memory.sram = vf->map(vf, gb->sramSize, MAP_WRITE); + GBMBCSwitchSramBank(gb, gb->memory.sramCurrentBank); + } +} + void GBMBCRTCRead(struct GB* gb) { struct GBMBCRTCSaveBuffer rtcBuffer; struct VFile* vf = gb->sramVf; @@ -1735,15 +1752,41 @@ void GBMBCRTCWrite(struct GB* gb) { STORE_32LE(gb->memory.rtcRegs[4], 0, &rtcBuffer.latchedDaysHi); STORE_64LE(gb->memory.rtcLastLatch, 0, &rtcBuffer.unixTime); - if ((size_t) vf->size(vf) < gb->sramSize + sizeof(rtcBuffer)) { - // Writing past the end of the file can invalidate the file mapping - vf->unmap(vf, gb->memory.sram, gb->sramSize); - gb->memory.sram = NULL; + _appendSaveSuffix(gb, &rtcBuffer, sizeof(rtcBuffer)); +} + +void GBMBCHuC3Read(struct GB* gb) { + struct GBMBCHuC3SaveBuffer buffer; + struct VFile* vf = gb->sramVf; + if (!vf) { + return; } vf->seek(vf, gb->sramSize, SEEK_SET); - vf->write(vf, &rtcBuffer, sizeof(rtcBuffer)); - if (!gb->memory.sram) { - gb->memory.sram = vf->map(vf, gb->sramSize, MAP_WRITE); - GBMBCSwitchSramBank(gb, gb->memory.sramCurrentBank); + if (vf->read(vf, &buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer)) { + return; } + + size_t i; + for (i = 0; i < 0x80; ++i) { + gb->memory.mbcState.huc3.registers[i * 2] = buffer.regs[i] & 0xF; + gb->memory.mbcState.huc3.registers[i * 2 + 1] = buffer.regs[i] >> 4; + } + LOAD_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix); +} + +void GBMBCHuC3Write(struct GB* gb) { + struct VFile* vf = gb->sramVf; + if (!vf) { + return; + } + + struct GBMBCHuC3SaveBuffer buffer; + size_t i; + for (i = 0; i < 0x80; ++i) { + buffer.regs[i] = gb->memory.mbcState.huc3.registers[i * 2] & 0xF; + buffer.regs[i] |= gb->memory.mbcState.huc3.registers[i * 2 + 1] << 4; + } + STORE_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix); + + _appendSaveSuffix(gb, &buffer, sizeof(buffer)); } From 31201d49033a0519e3272e3359ca75b97c183e6d Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 4 Feb 2022 23:58:57 -0800 Subject: [PATCH 3/5] FFmpeg: Fix build --- src/feature/ffmpeg/ffmpeg-encoder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/feature/ffmpeg/ffmpeg-encoder.c b/src/feature/ffmpeg/ffmpeg-encoder.c index be6bd3af5..2072afd29 100644 --- a/src/feature/ffmpeg/ffmpeg-encoder.c +++ b/src/feature/ffmpeg/ffmpeg-encoder.c @@ -12,7 +12,7 @@ #include #include -#if LIBAVCODEC_VERSION_MAJOR >= 58 +#if LIBAVCODEC_VERSION_MAJOR >= 59 #include #endif From d33471e117a313b0f49b80ad70a8580df73768da Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 6 Feb 2022 21:43:53 -0800 Subject: [PATCH 4/5] GB MBC: Add NT (new) support (closes #2435) --- CHANGES | 1 + include/mgba/gb/interface.h | 1 + include/mgba/internal/gb/memory.h | 14 +++++--- src/gb/mbc.c | 56 ++++++++++++++++++++++++------- src/gb/memory.c | 8 ++--- src/gb/overrides.c | 20 ++++++++--- src/platform/qt/GameBoy.cpp | 4 ++- 7 files changed, 77 insertions(+), 27 deletions(-) diff --git a/CHANGES b/CHANGES index 0162d2de5..c6a63a7c5 100644 --- a/CHANGES +++ b/CHANGES @@ -13,6 +13,7 @@ Features: - Additional scaling shaders - Support for GameShark Advance SP (.gsv) save file importing - Support for multiple saves per game using .sa2, .sa3, etc. + - New unlicensed GB mappers: NT (newer type) Emulation fixes: - ARM7: Fix unsigned multiply timing - GB Memory: Add cursory cartridge open bus emulation (fixes mgba.io/i/2032) diff --git a/include/mgba/gb/interface.h b/include/mgba/gb/interface.h index 420a81840..997312c3a 100644 --- a/include/mgba/gb/interface.h +++ b/include/mgba/gb/interface.h @@ -41,6 +41,7 @@ enum GBMemoryBankControllerType { GB_MBC5_RUMBLE = 0x105, GB_UNL_WISDOM_TREE = 0x200, GB_UNL_PKJD = 0x203, + GB_UNL_NT_NEW = 0x212, GB_UNL_BBD = 0x220, // Also used as a mask for MBCs that need special read behavior GB_UNL_HITEK = 0x221, }; diff --git a/include/mgba/internal/gb/memory.h b/include/mgba/internal/gb/memory.h index 59691682c..ce3e9079c 100644 --- a/include/mgba/internal/gb/memory.h +++ b/include/mgba/internal/gb/memory.h @@ -145,11 +145,7 @@ struct GBMBC1State { }; struct GBMBC6State { - int currentBank1; - uint8_t* romBank1; bool sramAccess; - int currentSramBank1; - uint8_t* sramBank1; bool flashBank0; bool flashBank1; }; @@ -191,6 +187,10 @@ struct GBPKJDState { uint8_t reg[2]; }; +struct GBNTNewState { + bool splitMode; +}; + struct GBBBDState { int dataSwapMode; int bankSwapMode; @@ -204,6 +204,7 @@ union GBMBCState { struct GBPocketCamState pocketCam; struct GBTAMA5State tama5; struct GBHuC3State huc3; + struct GBNTNewState ntNew; struct GBPKJDState pkjd; struct GBBBDState bbd; }; @@ -219,6 +220,11 @@ struct GBMemory { union GBMBCState mbcState; int currentBank; int currentBank0; + int currentBank1; + uint8_t* romBank1; + int currentSramBank1; + uint8_t* sramBank1; + unsigned cartBusDecay; uint16_t cartBusPc; uint8_t cartBus; diff --git a/src/gb/mbc.c b/src/gb/mbc.c index 99190627c..21932a456 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -38,6 +38,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 void _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value); static void _GBPKJD(struct GB* gb, uint16_t address, uint8_t value); +static void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value); static void _GBBBD(struct GB* gb, uint16_t address, uint8_t value); static void _GBHitek(struct GB* gb, uint16_t address, uint8_t value); @@ -86,7 +87,10 @@ void GBMBCSwitchBank0(struct GB* gb, int bank) { void GBMBCSwitchHalfBank(struct GB* gb, int half, int bank) { size_t bankStart = bank * GB_SIZE_CART_HALFBANK; - bool isFlash = half ? gb->memory.mbcState.mbc6.flashBank1 : gb->memory.mbcState.mbc6.flashBank0; + bool isFlash = false; + if (gb->memory.mbcType == GB_MBC6) { + isFlash = half ? gb->memory.mbcState.mbc6.flashBank1 : gb->memory.mbcState.mbc6.flashBank0; + } if (isFlash) { if (bankStart + GB_SIZE_CART_HALFBANK > GB_SIZE_MBC6_FLASH) { mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid Flash bank: %0X", bank); @@ -113,11 +117,11 @@ void GBMBCSwitchHalfBank(struct GB* gb, int half, int bank) { gb->memory.currentBank = bank; } else { if (isFlash) { - gb->memory.mbcState.mbc6.romBank1 = &gb->memory.sram[bankStart]; + gb->memory.romBank1 = &gb->memory.sram[bankStart]; } else { - gb->memory.mbcState.mbc6.romBank1 = &gb->memory.rom[bankStart]; + gb->memory.romBank1 = &gb->memory.rom[bankStart]; } - gb->memory.mbcState.mbc6.currentBank1 = bank; + gb->memory.currentBank1 = bank; } if (gb->cpu->pc < GB_BASE_VRAM) { gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc); @@ -220,8 +224,8 @@ void GBMBCSwitchSramHalfBank(struct GB* gb, int half, int bank) { 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; + gb->memory.sramBank1 = &gb->memory.sram[bankStart]; + gb->memory.currentSramBank1 = bank; } } @@ -403,6 +407,13 @@ void GBMBCInit(struct GB* gb) { case GB_UNL_WISDOM_TREE: gb->memory.mbcWrite = _GBWisdomTree; break; + case GB_UNL_NT_NEW: + gb->memory.mbcWrite = _GBNTNew; + break; + case GB_UNL_PKJD: + gb->memory.mbcWrite = _GBPKJD; + gb->memory.mbcRead = _GBPKJDRead; + break; case GB_UNL_BBD: gb->memory.mbcWrite = _GBBBD; gb->memory.mbcRead = _GBBBDRead; @@ -413,10 +424,6 @@ void GBMBCInit(struct GB* gb) { gb->memory.mbcState.bbd.dataSwapMode = 7; gb->memory.mbcState.bbd.bankSwapMode = 7; break; - case GB_UNL_PKJD: - gb->memory.mbcWrite = _GBPKJD; - gb->memory.mbcRead = _GBPKJDRead; - break; } gb->memory.currentBank = 1; @@ -790,7 +797,7 @@ void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) { case 0x2E: case 0x2F: if (memory->sramAccess) { - memory->mbcState.mbc6.sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value; + memory->sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value; } break; default: @@ -807,7 +814,7 @@ uint8_t _GBMBC6Read(struct GBMemory* memory, uint16_t address) { 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 memory->sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)]; } return 0xFF; } @@ -818,7 +825,7 @@ static void _GBMBC6MapChip(struct GB* gb, int half, uint8_t value) { GBMBCSwitchHalfBank(gb, half, gb->memory.currentBank); } else { gb->memory.mbcState.mbc6.flashBank1 = !!(value & 0x08); - GBMBCSwitchHalfBank(gb, half, gb->memory.mbcState.mbc6.currentBank1); + GBMBCSwitchHalfBank(gb, half, gb->memory.currentBank1); } } @@ -1577,6 +1584,29 @@ static uint8_t _GBPKJDRead(struct GBMemory* memory, uint16_t address) { } } +void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + if (address >> 8 == 0x14) { + memory->mbcState.ntNew.splitMode = true; + return; + } + if (memory->mbcState.ntNew.splitMode) { + int bank = value; + if (bank < 2) { + bank = 2; + } + switch (address >> 10) { + case 8: + GBMBCSwitchHalfBank(gb, 0, bank); + return; + case 9: + GBMBCSwitchHalfBank(gb, 1, bank); + return; + } + } + _GBMBC5(gb, address, value); +} + static uint8_t _reorderBits(uint8_t input, const uint8_t* reorder) { uint8_t newbyte = 0; int i; diff --git a/src/gb/memory.c b/src/gb/memory.c index 66c35c644..f80fef77b 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -95,14 +95,14 @@ static void GBSetActiveRegion(struct SM83Core* cpu, uint16_t address) { break; } cpu->memory.cpuLoad8 = GBCartLoad8; - if (gb->memory.mbcType != GB_MBC6) { + if (gb->memory.mbcType != GB_MBC6 && !(gb->memory.mbcType == GB_UNL_NT_NEW && gb->memory.mbcState.ntNew.splitMode)) { cpu->memory.activeRegion = memory->romBank; cpu->memory.activeRegionEnd = GB_BASE_VRAM; cpu->memory.activeMask = GB_SIZE_CART_BANK0 - 1; } else { cpu->memory.activeMask = GB_SIZE_CART_HALFBANK - 1; if (address & 0x2000) { - cpu->memory.activeRegion = memory->mbcState.mbc6.romBank1; + cpu->memory.activeRegion = memory->romBank1; cpu->memory.activeRegionEnd = GB_BASE_VRAM; } else { cpu->memory.activeRegion = memory->romBank; @@ -252,8 +252,8 @@ uint8_t GBLoad8(struct SM83Core* cpu, uint16_t address) { return memory->cartBus; case GB_REGION_CART_BANK1 + 2: case GB_REGION_CART_BANK1 + 3: - if (memory->mbcType == GB_MBC6) { - memory->cartBus = memory->mbcState.mbc6.romBank1[address & (GB_SIZE_CART_HALFBANK - 1)]; + if (gb->memory.mbcType == GB_MBC6 || (gb->memory.mbcType == GB_UNL_NT_NEW && gb->memory.mbcState.ntNew.splitMode)) { + memory->cartBus = memory->romBank1[address & (GB_SIZE_CART_HALFBANK - 1)]; memory->cartBusPc = cpu->pc; return memory->cartBus; } diff --git a/src/gb/overrides.c b/src/gb/overrides.c index 7a17ea693..5bfc4f3cf 100644 --- a/src/gb/overrides.c +++ b/src/gb/overrides.c @@ -668,11 +668,21 @@ static const struct GBCartridgeOverride _sgbOverrides[] = { static const struct GBCartridgeOverride _overrides[] = { // Pokemon Spaceworld 1997 demo - { 0x232a067d, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (debug) - { 0x630ed957, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (non-debug) - { 0x5aff0038, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (debug) - { 0xa61856bd, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (non-debug) - { 0x30f8f86c, GB_MODEL_AUTODETECT, GB_UNL_PKJD, { 0 } }, // Pokemon Jade Version (Telefang Speed bootleg) + { 0x232A067D, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (debug) + { 0x630ED957, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (non-debug) + { 0x5AFF0038, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (debug) + { 0xA61856BD, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (non-debug) + { 0x30F8F86C, GB_MODEL_AUTODETECT, GB_UNL_PKJD, { 0 } }, // Pokemon Jade Version (Telefang Speed bootleg) + { 0xB289D95A, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Capcom vs SNK - Millennium Fight 2001 + { 0x688D6713, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Digimon 02 4 + { 0x8931A272, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Digimon 2 + { 0x79083C6B, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Digimon Pocket + { 0x0C5047EE, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Harry Potter 3 + { 0x8AC634B7, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Pokemon Diamond (Special Pikachu Edition) + { 0x8628A287, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Pokemon Jade (Special Pikachu Edition) + { 0xBC75D7B8, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Pokemon - Mewtwo Strikes Back + { 0xFF0B60CC, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Shuma Baolong 02 4 + { 0x14A992A6, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // /Street Fighter Zero 4 { 0, 0, 0, { 0 } } }; diff --git a/src/platform/qt/GameBoy.cpp b/src/platform/qt/GameBoy.cpp index 1748152d7..c27218afc 100644 --- a/src/platform/qt/GameBoy.cpp +++ b/src/platform/qt/GameBoy.cpp @@ -34,9 +34,10 @@ static const QList s_mbcList{ GB_HuC1, GB_HuC3, GB_UNL_WISDOM_TREE, + GB_UNL_PKJD, + GB_UNL_NT_NEW, GB_UNL_BBD, GB_UNL_HITEK, - GB_UNL_PKJD, }; static QMap s_gbModelNames; @@ -87,6 +88,7 @@ QString GameBoy::mbcName(GBMemoryBankControllerType mbc) { s_mbcNames[GB_POCKETCAM] = tr("Pocket Cam"); s_mbcNames[GB_TAMA5] = tr("TAMA5"); s_mbcNames[GB_UNL_WISDOM_TREE] = tr("Wisdom Tree"); + s_mbcNames[GB_UNL_NT_NEW] = tr("NT (new)"); s_mbcNames[GB_UNL_PKJD] = tr("Pokémon Jade/Diamond"); s_mbcNames[GB_UNL_BBD] = tr("BBD"); s_mbcNames[GB_UNL_HITEK] = tr("Hitek"); From 0ff2d673179ee085f9f0225522ae707bb3df8b24 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 7 Feb 2022 00:47:35 -0800 Subject: [PATCH 5/5] README: Update --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 76b9ab8e1..096cd6df3 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Features - Remappable controls for both keyboards and gamepads. - Loading from ZIP and 7z files. - IPS, UPS and BPS patch support. -- Game debugging via a command-line interface and GDB remote support, compatible with IDA Pro. +- Game debugging via a command-line interface and GDB remote support, compatible with Ghidra and IDA Pro. - Configurable emulation rewinding. - Support for loading and exporting GameShark and Action Replay snapshots. - Cores available for RetroArch/Libretro and OpenEmu. @@ -53,6 +53,7 @@ The following mappers are fully supported: - MBC5+Rumble - MBC7 - Wisdom Tree (unlicensed) +- NT "new type" (unlicensed MBC5-like) - Pokémon Jade/Diamond (unlicensed) - BBD (unlicensed MBC5-like) - Hitek (unlicensed MBC5-like) @@ -64,7 +65,7 @@ The following mappers are partially supported: - Pocket Cam - TAMA5 (missing RTC support) - HuC-1 (missing IR support) -- HuC-3 (missing RTC and IR support) +- HuC-3 (missing IR support) ### Planned features @@ -242,7 +243,7 @@ Footnotes Copyright --------- -mGBA is Copyright © 2013 – 2021 Jeffrey Pfau. It is distributed under the [Mozilla Public License version 2.0](https://www.mozilla.org/MPL/2.0/). A copy of the license is available in the distributed LICENSE file. +mGBA is Copyright © 2013 – 2022 Jeffrey Pfau. It is distributed under the [Mozilla Public License version 2.0](https://www.mozilla.org/MPL/2.0/). A copy of the license is available in the distributed LICENSE file. mGBA contains the following third-party libraries: