diff --git a/CHANGES b/CHANGES index b66a17472..b025def76 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,7 @@ Features: - e-Reader card scanning - Add APNG recording + - Support for unlicensed Pokemon Jade/Diamond Game Boy mapper Emulation fixes: - ARM: Fix ALU reading PC after shifting - ARM: Fix STR storing PC after address calculation diff --git a/README.md b/README.md index f5f005d35..bec608b75 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ The following mappers are fully supported: - MBC5+Rumble - MBC7 - Wisdom Tree (unlicensed) +- Pokémon Jade/Diamond (unlicensed) The following mappers are partially supported: diff --git a/include/mgba/gb/interface.h b/include/mgba/gb/interface.h index d17593f21..99c4fb01b 100644 --- a/include/mgba/gb/interface.h +++ b/include/mgba/gb/interface.h @@ -34,9 +34,10 @@ enum GBMemoryBankControllerType { GB_HuC3 = 0x12, GB_POCKETCAM = 0x13, GB_TAMA5 = 0x14, - GB_UNL_WISDOM_TREE = 0x20, GB_MBC3_RTC = 0x103, - GB_MBC5_RUMBLE = 0x105 + GB_MBC5_RUMBLE = 0x105, + GB_UNL_WISDOM_TREE = 0x200, + GB_UNL_PKJD = 0x203, }; struct GBSIODriver { diff --git a/include/mgba/internal/gb/memory.h b/include/mgba/internal/gb/memory.h index 514311351..ca55b672c 100644 --- a/include/mgba/internal/gb/memory.h +++ b/include/mgba/internal/gb/memory.h @@ -146,6 +146,10 @@ struct GBTAMA5State { uint8_t registers[GBTAMA5_MAX]; }; +struct GBPKJDState { + uint8_t reg[2]; +}; + union GBMBCState { struct GBMBC1State mbc1; struct GBMBC6State mbc6; @@ -153,6 +157,7 @@ union GBMBCState { struct GBMMM01State mmm01; struct GBPocketCamState pocketCam; struct GBTAMA5State tama5; + struct GBPKJDState pkjd; }; struct mRotationSource; @@ -171,6 +176,7 @@ struct GBMemory { int wramCurrentBank; bool sramAccess; + bool directSramAccess; uint8_t* sram; uint8_t* sramBank; int sramCurrentBank; diff --git a/src/core/core.c b/src/core/core.c index cbab4cfe9..e8cf25e82 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -162,6 +162,10 @@ bool mCorePreloadVFCB(struct mCore* core, struct VFile* vf, void (cb)(size_t, si } } vf->close(vf); + if (read < 0) { + vfm->close(vfm); + return false; + } bool ret = core->loadROM(core, vfm); if (!ret) { vfm->close(vfm); diff --git a/src/gb/mbc.c b/src/gb/mbc.c index ca94cb534..b87885964 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -36,6 +36,7 @@ static void _GBHuC3(struct GB*, uint16_t address, uint8_t value); 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 uint8_t _GBMBC2Read(struct GBMemory*, uint16_t address); static uint8_t _GBMBC6Read(struct GBMemory*, uint16_t address); @@ -43,6 +44,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 _GBPKJDRead(struct GBMemory*, uint16_t address); static uint8_t _GBPocketCamRead(struct GBMemory*, uint16_t address); static void _GBPocketCamCapture(struct GBMemory*); @@ -273,6 +275,7 @@ void GBMBCInit(struct GB* gb) { gb->memory.mbcType = GB_MBC_NONE; } gb->memory.mbcRead = NULL; + gb->memory.directSramAccess = true; switch (gb->memory.mbcType) { case GB_MBC_NONE: gb->memory.mbcWrite = _GBMBCNone; @@ -288,6 +291,7 @@ void GBMBCInit(struct GB* gb) { case GB_MBC2: gb->memory.mbcWrite = _GBMBC2; gb->memory.mbcRead = _GBMBC2Read; + gb->memory.directSramAccess = false; gb->sramSize = 0x100; break; case GB_MBC3: @@ -300,9 +304,9 @@ void GBMBCInit(struct GB* gb) { gb->memory.mbcWrite = _GBMBC5; break; case GB_MBC6: - mLOG(GB_MBC, WARN, "unimplemented MBC: MBC6"); gb->memory.mbcWrite = _GBMBC6; gb->memory.mbcRead = _GBMBC6Read; + gb->memory.directSramAccess = false; break; case GB_MBC7: gb->memory.mbcWrite = _GBMBC7; @@ -342,6 +346,10 @@ void GBMBCInit(struct GB* gb) { case GB_UNL_WISDOM_TREE: gb->memory.mbcWrite = _GBWisdomTree; break; + case GB_UNL_PKJD: + gb->memory.mbcWrite = _GBPKJD; + gb->memory.mbcRead = _GBPKJDRead; + break; } gb->memory.currentBank = 1; @@ -626,10 +634,10 @@ void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) { case 0: switch (value) { case 0: - memory->mbcState.mbc6.sramAccess = false; + memory->sramAccess = false; break; case 0xA: - memory->mbcState.mbc6.sramAccess = true; + memory->sramAccess = true; break; default: // TODO @@ -655,7 +663,7 @@ void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) { case 0x29: case 0x2A: case 0x2B: - if (memory->mbcState.mbc6.sramAccess) { + if (memory->sramAccess) { memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value; } break; @@ -663,7 +671,7 @@ void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) { case 0x2D: case 0x2E: case 0x2F: - if (memory->mbcState.mbc6.sramAccess) { + if (memory->sramAccess) { memory->mbcState.mbc6.sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value; } break; @@ -674,7 +682,7 @@ void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) { } uint8_t _GBMBC6Read(struct GBMemory* memory, uint16_t address) { - if (!memory->mbcState.mbc6.sramAccess) { + if (!memory->sramAccess) { return 0xFF; } switch (address >> 12) { @@ -1229,6 +1237,74 @@ void _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value) { } } +void _GBPKJD(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + switch (address >> 13) { + case 0x2: + if (value < 8) { + memory->directSramAccess = true; + memory->activeRtcReg = 0; + } else if (value >= 0xD && value <= 0xF) { + memory->directSramAccess = false; + memory->rtcAccess = false; + memory->activeRtcReg = value - 8; + } + break; + case 0x5: + if (!memory->sramAccess) { + return; + } + switch (memory->activeRtcReg) { + case 0: + memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)] = value; + break; + case 5: + case 6: + memory->mbcState.pkjd.reg[memory->activeRtcReg - 5] = value; + break; + case 7: + switch (value) { + case 0x11: + memory->mbcState.pkjd.reg[0]--; + break; + case 0x12: + memory->mbcState.pkjd.reg[1]--; + break; + case 0x41: + memory->mbcState.pkjd.reg[0] += memory->mbcState.pkjd.reg[1]; + break; + case 0x42: + memory->mbcState.pkjd.reg[1] += memory->mbcState.pkjd.reg[0]; + break; + case 0x51: + memory->mbcState.pkjd.reg[0]++; + break; + case 0x52: + memory->mbcState.pkjd.reg[1]--; + break; + } + break; + } + return; + } + _GBMBC3(gb, address, value); +} + +static uint8_t _GBPKJDRead(struct GBMemory* memory, uint16_t address) { + if (!memory->sramAccess) { + return 0xFF; + } + switch (memory->activeRtcReg) { + case 0: + return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)]; + case 5: + case 6: + return memory->mbcState.pkjd.reg[memory->activeRtcReg - 5]; + default: + return 0; + } +} + void GBMBCRTCRead(struct GB* gb) { struct GBMBCRTCSaveBuffer rtcBuffer; struct VFile* vf = gb->sramVf; diff --git a/src/gb/memory.c b/src/gb/memory.c index b070ff601..a856b6635 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -361,7 +361,7 @@ void GBStore8(struct SM83Core* cpu, uint16_t address, int8_t value) { case GB_REGION_EXTERNAL_RAM + 1: if (memory->rtcAccess) { memory->rtcRegs[memory->activeRtcReg] = value; - } else if (memory->sramAccess && memory->sram && memory->mbcType != GB_MBC2) { + } else if (memory->sramAccess && memory->sram && memory->directSramAccess) { memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)] = value; } else { memory->mbcWrite(gb, address, value); diff --git a/src/gb/overrides.c b/src/gb/overrides.c index 8e2265662..38a8ca6ff 100644 --- a/src/gb/overrides.c +++ b/src/gb/overrides.c @@ -495,6 +495,7 @@ static const struct GBCartridgeOverride _overrides[] = { { 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) { 0, 0, 0, { 0 } } }; diff --git a/src/platform/qt/OverrideView.cpp b/src/platform/qt/OverrideView.cpp index 3479352b6..e73f657ba 100644 --- a/src/platform/qt/OverrideView.cpp +++ b/src/platform/qt/OverrideView.cpp @@ -50,6 +50,7 @@ OverrideView::OverrideView(ConfigController* config, QWidget* parent) s_mbcList.append(GB_HuC1); s_mbcList.append(GB_HuC3); s_mbcList.append(GB_UNL_WISDOM_TREE); + s_mbcList.append(GB_UNL_PKJD); } if (s_gbModelList.isEmpty()) { // NB: Keep in sync with OverrideView.ui diff --git a/src/platform/qt/OverrideView.ui b/src/platform/qt/OverrideView.ui index 6a0d60c1a..61d4449fb 100644 --- a/src/platform/qt/OverrideView.ui +++ b/src/platform/qt/OverrideView.ui @@ -359,6 +359,11 @@ Wisdom Tree (Unlicensed) + + + Pokémon Jade/Diamond (Unlicensed) + +