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)
+
+
-