mirror of https://github.com/mgba-emu/mgba.git
GB MBC: Support for unlicensed Pokemon Jade/Diamond Game Boy mapper
This commit is contained in:
parent
3062a6a2d7
commit
cdf9105f56
1
CHANGES
1
CHANGES
|
@ -2,6 +2,7 @@
|
||||||
Features:
|
Features:
|
||||||
- e-Reader card scanning
|
- e-Reader card scanning
|
||||||
- Add APNG recording
|
- Add APNG recording
|
||||||
|
- Support for unlicensed Pokemon Jade/Diamond Game Boy mapper
|
||||||
Emulation fixes:
|
Emulation fixes:
|
||||||
- ARM: Fix ALU reading PC after shifting
|
- ARM: Fix ALU reading PC after shifting
|
||||||
- ARM: Fix STR storing PC after address calculation
|
- ARM: Fix STR storing PC after address calculation
|
||||||
|
|
|
@ -51,6 +51,7 @@ The following mappers are fully supported:
|
||||||
- MBC5+Rumble
|
- MBC5+Rumble
|
||||||
- MBC7
|
- MBC7
|
||||||
- Wisdom Tree (unlicensed)
|
- Wisdom Tree (unlicensed)
|
||||||
|
- Pokémon Jade/Diamond (unlicensed)
|
||||||
|
|
||||||
The following mappers are partially supported:
|
The following mappers are partially supported:
|
||||||
|
|
||||||
|
|
|
@ -34,9 +34,10 @@ enum GBMemoryBankControllerType {
|
||||||
GB_HuC3 = 0x12,
|
GB_HuC3 = 0x12,
|
||||||
GB_POCKETCAM = 0x13,
|
GB_POCKETCAM = 0x13,
|
||||||
GB_TAMA5 = 0x14,
|
GB_TAMA5 = 0x14,
|
||||||
GB_UNL_WISDOM_TREE = 0x20,
|
|
||||||
GB_MBC3_RTC = 0x103,
|
GB_MBC3_RTC = 0x103,
|
||||||
GB_MBC5_RUMBLE = 0x105
|
GB_MBC5_RUMBLE = 0x105,
|
||||||
|
GB_UNL_WISDOM_TREE = 0x200,
|
||||||
|
GB_UNL_PKJD = 0x203,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GBSIODriver {
|
struct GBSIODriver {
|
||||||
|
|
|
@ -146,6 +146,10 @@ struct GBTAMA5State {
|
||||||
uint8_t registers[GBTAMA5_MAX];
|
uint8_t registers[GBTAMA5_MAX];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct GBPKJDState {
|
||||||
|
uint8_t reg[2];
|
||||||
|
};
|
||||||
|
|
||||||
union GBMBCState {
|
union GBMBCState {
|
||||||
struct GBMBC1State mbc1;
|
struct GBMBC1State mbc1;
|
||||||
struct GBMBC6State mbc6;
|
struct GBMBC6State mbc6;
|
||||||
|
@ -153,6 +157,7 @@ union GBMBCState {
|
||||||
struct GBMMM01State mmm01;
|
struct GBMMM01State mmm01;
|
||||||
struct GBPocketCamState pocketCam;
|
struct GBPocketCamState pocketCam;
|
||||||
struct GBTAMA5State tama5;
|
struct GBTAMA5State tama5;
|
||||||
|
struct GBPKJDState pkjd;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mRotationSource;
|
struct mRotationSource;
|
||||||
|
@ -171,6 +176,7 @@ struct GBMemory {
|
||||||
int wramCurrentBank;
|
int wramCurrentBank;
|
||||||
|
|
||||||
bool sramAccess;
|
bool sramAccess;
|
||||||
|
bool directSramAccess;
|
||||||
uint8_t* sram;
|
uint8_t* sram;
|
||||||
uint8_t* sramBank;
|
uint8_t* sramBank;
|
||||||
int sramCurrentBank;
|
int sramCurrentBank;
|
||||||
|
|
|
@ -162,6 +162,10 @@ bool mCorePreloadVFCB(struct mCore* core, struct VFile* vf, void (cb)(size_t, si
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vf->close(vf);
|
vf->close(vf);
|
||||||
|
if (read < 0) {
|
||||||
|
vfm->close(vfm);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
bool ret = core->loadROM(core, vfm);
|
bool ret = core->loadROM(core, vfm);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
vfm->close(vfm);
|
vfm->close(vfm);
|
||||||
|
|
88
src/gb/mbc.c
88
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 _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value);
|
||||||
static void _GBTAMA5(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 _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 _GBMBC2Read(struct GBMemory*, uint16_t address);
|
||||||
static uint8_t _GBMBC6Read(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 void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value);
|
||||||
|
|
||||||
static uint8_t _GBTAMA5Read(struct GBMemory*, uint16_t address);
|
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 uint8_t _GBPocketCamRead(struct GBMemory*, uint16_t address);
|
||||||
static void _GBPocketCamCapture(struct GBMemory*);
|
static void _GBPocketCamCapture(struct GBMemory*);
|
||||||
|
@ -273,6 +275,7 @@ void GBMBCInit(struct GB* gb) {
|
||||||
gb->memory.mbcType = GB_MBC_NONE;
|
gb->memory.mbcType = GB_MBC_NONE;
|
||||||
}
|
}
|
||||||
gb->memory.mbcRead = NULL;
|
gb->memory.mbcRead = NULL;
|
||||||
|
gb->memory.directSramAccess = true;
|
||||||
switch (gb->memory.mbcType) {
|
switch (gb->memory.mbcType) {
|
||||||
case GB_MBC_NONE:
|
case GB_MBC_NONE:
|
||||||
gb->memory.mbcWrite = _GBMBCNone;
|
gb->memory.mbcWrite = _GBMBCNone;
|
||||||
|
@ -288,6 +291,7 @@ void GBMBCInit(struct GB* gb) {
|
||||||
case GB_MBC2:
|
case GB_MBC2:
|
||||||
gb->memory.mbcWrite = _GBMBC2;
|
gb->memory.mbcWrite = _GBMBC2;
|
||||||
gb->memory.mbcRead = _GBMBC2Read;
|
gb->memory.mbcRead = _GBMBC2Read;
|
||||||
|
gb->memory.directSramAccess = false;
|
||||||
gb->sramSize = 0x100;
|
gb->sramSize = 0x100;
|
||||||
break;
|
break;
|
||||||
case GB_MBC3:
|
case GB_MBC3:
|
||||||
|
@ -300,9 +304,9 @@ void GBMBCInit(struct GB* gb) {
|
||||||
gb->memory.mbcWrite = _GBMBC5;
|
gb->memory.mbcWrite = _GBMBC5;
|
||||||
break;
|
break;
|
||||||
case GB_MBC6:
|
case GB_MBC6:
|
||||||
mLOG(GB_MBC, WARN, "unimplemented MBC: MBC6");
|
|
||||||
gb->memory.mbcWrite = _GBMBC6;
|
gb->memory.mbcWrite = _GBMBC6;
|
||||||
gb->memory.mbcRead = _GBMBC6Read;
|
gb->memory.mbcRead = _GBMBC6Read;
|
||||||
|
gb->memory.directSramAccess = false;
|
||||||
break;
|
break;
|
||||||
case GB_MBC7:
|
case GB_MBC7:
|
||||||
gb->memory.mbcWrite = _GBMBC7;
|
gb->memory.mbcWrite = _GBMBC7;
|
||||||
|
@ -342,6 +346,10 @@ void GBMBCInit(struct GB* gb) {
|
||||||
case GB_UNL_WISDOM_TREE:
|
case GB_UNL_WISDOM_TREE:
|
||||||
gb->memory.mbcWrite = _GBWisdomTree;
|
gb->memory.mbcWrite = _GBWisdomTree;
|
||||||
break;
|
break;
|
||||||
|
case GB_UNL_PKJD:
|
||||||
|
gb->memory.mbcWrite = _GBPKJD;
|
||||||
|
gb->memory.mbcRead = _GBPKJDRead;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
gb->memory.currentBank = 1;
|
gb->memory.currentBank = 1;
|
||||||
|
@ -626,10 +634,10 @@ void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) {
|
||||||
case 0:
|
case 0:
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case 0:
|
case 0:
|
||||||
memory->mbcState.mbc6.sramAccess = false;
|
memory->sramAccess = false;
|
||||||
break;
|
break;
|
||||||
case 0xA:
|
case 0xA:
|
||||||
memory->mbcState.mbc6.sramAccess = true;
|
memory->sramAccess = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// TODO
|
// TODO
|
||||||
|
@ -655,7 +663,7 @@ void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) {
|
||||||
case 0x29:
|
case 0x29:
|
||||||
case 0x2A:
|
case 0x2A:
|
||||||
case 0x2B:
|
case 0x2B:
|
||||||
if (memory->mbcState.mbc6.sramAccess) {
|
if (memory->sramAccess) {
|
||||||
memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value;
|
memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -663,7 +671,7 @@ void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) {
|
||||||
case 0x2D:
|
case 0x2D:
|
||||||
case 0x2E:
|
case 0x2E:
|
||||||
case 0x2F:
|
case 0x2F:
|
||||||
if (memory->mbcState.mbc6.sramAccess) {
|
if (memory->sramAccess) {
|
||||||
memory->mbcState.mbc6.sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value;
|
memory->mbcState.mbc6.sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value;
|
||||||
}
|
}
|
||||||
break;
|
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) {
|
uint8_t _GBMBC6Read(struct GBMemory* memory, uint16_t address) {
|
||||||
if (!memory->mbcState.mbc6.sramAccess) {
|
if (!memory->sramAccess) {
|
||||||
return 0xFF;
|
return 0xFF;
|
||||||
}
|
}
|
||||||
switch (address >> 12) {
|
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) {
|
void GBMBCRTCRead(struct GB* gb) {
|
||||||
struct GBMBCRTCSaveBuffer rtcBuffer;
|
struct GBMBCRTCSaveBuffer rtcBuffer;
|
||||||
struct VFile* vf = gb->sramVf;
|
struct VFile* vf = gb->sramVf;
|
||||||
|
|
|
@ -361,7 +361,7 @@ void GBStore8(struct SM83Core* cpu, uint16_t address, int8_t value) {
|
||||||
case GB_REGION_EXTERNAL_RAM + 1:
|
case GB_REGION_EXTERNAL_RAM + 1:
|
||||||
if (memory->rtcAccess) {
|
if (memory->rtcAccess) {
|
||||||
memory->rtcRegs[memory->activeRtcReg] = value;
|
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;
|
memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)] = value;
|
||||||
} else {
|
} else {
|
||||||
memory->mbcWrite(gb, address, value);
|
memory->mbcWrite(gb, address, value);
|
||||||
|
|
|
@ -495,6 +495,7 @@ static const struct GBCartridgeOverride _overrides[] = {
|
||||||
{ 0x630ed957, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (non-debug)
|
{ 0x630ed957, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (non-debug)
|
||||||
{ 0x5aff0038, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (debug)
|
{ 0x5aff0038, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (debug)
|
||||||
{ 0xa61856bd, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (non-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 } }
|
{ 0, 0, 0, { 0 } }
|
||||||
};
|
};
|
||||||
|
|
|
@ -50,6 +50,7 @@ OverrideView::OverrideView(ConfigController* config, QWidget* parent)
|
||||||
s_mbcList.append(GB_HuC1);
|
s_mbcList.append(GB_HuC1);
|
||||||
s_mbcList.append(GB_HuC3);
|
s_mbcList.append(GB_HuC3);
|
||||||
s_mbcList.append(GB_UNL_WISDOM_TREE);
|
s_mbcList.append(GB_UNL_WISDOM_TREE);
|
||||||
|
s_mbcList.append(GB_UNL_PKJD);
|
||||||
}
|
}
|
||||||
if (s_gbModelList.isEmpty()) {
|
if (s_gbModelList.isEmpty()) {
|
||||||
// NB: Keep in sync with OverrideView.ui
|
// NB: Keep in sync with OverrideView.ui
|
||||||
|
|
|
@ -359,6 +359,11 @@
|
||||||
<string>Wisdom Tree (Unlicensed)</string>
|
<string>Wisdom Tree (Unlicensed)</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Pokémon Jade/Diamond (Unlicensed)</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="2" column="0">
|
||||||
|
|
Loading…
Reference in New Issue