mirror of https://github.com/mgba-emu/mgba.git
GB MBC: Add Hitek and BBD mappers
This commit is contained in:
parent
3b363bb2c1
commit
bf16c7b6bf
2
CHANGES
2
CHANGES
|
@ -3,6 +3,8 @@ Features:
|
|||
- e-Reader card scanning
|
||||
- Add WebP and APNG recording
|
||||
- Support for unlicensed Pokemon Jade/Diamond Game Boy mapper
|
||||
- Support for unlicensed BBD Game Boy mapper
|
||||
- Support for unlicensed Hitek Game Boy mapper
|
||||
- Stack tracing tools in ARM debugger (by ahigerd)
|
||||
- Command scripts for CLI debugger (by ahigerd)
|
||||
Emulation fixes:
|
||||
|
|
|
@ -38,6 +38,8 @@ enum GBMemoryBankControllerType {
|
|||
GB_MBC5_RUMBLE = 0x105,
|
||||
GB_UNL_WISDOM_TREE = 0x200,
|
||||
GB_UNL_PKJD = 0x203,
|
||||
GB_UNL_BBD = 0x220, // Also used as a mask for MBCs that need special read behavior
|
||||
GB_UNL_HITEK = 0x221,
|
||||
};
|
||||
|
||||
struct GBSIODriver {
|
||||
|
|
|
@ -150,6 +150,11 @@ struct GBPKJDState {
|
|||
uint8_t reg[2];
|
||||
};
|
||||
|
||||
struct GBBBDState {
|
||||
int dataSwapMode;
|
||||
int bankSwapMode;
|
||||
};
|
||||
|
||||
union GBMBCState {
|
||||
struct GBMBC1State mbc1;
|
||||
struct GBMBC6State mbc6;
|
||||
|
@ -158,6 +163,7 @@ union GBMBCState {
|
|||
struct GBPocketCamState pocketCam;
|
||||
struct GBTAMA5State tama5;
|
||||
struct GBPKJDState pkjd;
|
||||
struct GBBBDState bbd;
|
||||
};
|
||||
|
||||
struct mRotationSource;
|
||||
|
|
|
@ -391,6 +391,10 @@ struct GBSerializedState {
|
|||
uint8_t locked;
|
||||
uint8_t bank0;
|
||||
} mmm01;
|
||||
struct {
|
||||
uint8_t dataSwapMode;
|
||||
uint8_t bankSwapMode;
|
||||
} bbd;
|
||||
struct {
|
||||
uint8_t reserved[16];
|
||||
} padding;
|
||||
|
|
164
src/gb/mbc.c
164
src/gb/mbc.c
|
@ -37,6 +37,8 @@ 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 _GBBBD(struct GB* gb, uint16_t address, uint8_t value);
|
||||
static void _GBHitek(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);
|
||||
|
@ -45,6 +47,8 @@ static void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t valu
|
|||
|
||||
static uint8_t _GBTAMA5Read(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);
|
||||
|
||||
static uint8_t _GBPocketCamRead(struct GBMemory*, uint16_t address);
|
||||
static void _GBPocketCamCapture(struct GBMemory*);
|
||||
|
@ -145,6 +149,31 @@ static bool _isWisdomTree(const uint8_t* mem, size_t size) {
|
|||
return false;
|
||||
}
|
||||
|
||||
static enum GBMemoryBankControllerType _detectUnlMBC(const uint8_t* mem, size_t size) {
|
||||
const struct GBCartridge* cart = (const struct GBCartridge*) &mem[0x100];
|
||||
|
||||
switch (cart->type) {
|
||||
case 0:
|
||||
if (_isWisdomTree(mem, size)) {
|
||||
return GB_UNL_WISDOM_TREE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t secondaryLogo = doCrc32(&mem[0x184], 0x30);
|
||||
switch (secondaryLogo) {
|
||||
case 0x4fdab691:
|
||||
return GB_UNL_HITEK;
|
||||
case 0xc7d8c1df:
|
||||
case 0x6d1ea662: // Garou
|
||||
if (mem[0x7FFF] != 0x01) { // Make sure we're not using a "fixed" version
|
||||
return GB_UNL_BBD;
|
||||
}
|
||||
}
|
||||
|
||||
return GB_MBC_AUTODETECT;
|
||||
}
|
||||
|
||||
void GBMBCSwitchSramBank(struct GB* gb, int bank) {
|
||||
size_t bankStart = bank * GB_SIZE_EXTERNAL_RAM;
|
||||
if (bankStart + GB_SIZE_EXTERNAL_RAM > gb->sramSize) {
|
||||
|
@ -202,15 +231,13 @@ void GBMBCInit(struct GB* gb) {
|
|||
gb->sramSize = 0x10000;
|
||||
break;
|
||||
}
|
||||
if (gb->memory.mbcType == GB_MBC_AUTODETECT) {
|
||||
gb->memory.mbcType = _detectUnlMBC(gb->memory.rom, gb->memory.romSize);
|
||||
}
|
||||
|
||||
if (gb->memory.mbcType == GB_MBC_AUTODETECT) {
|
||||
switch (cart->type) {
|
||||
case 0:
|
||||
if (_isWisdomTree(gb->memory.rom, gb->memory.romSize)) {
|
||||
gb->memory.mbcType = GB_UNL_WISDOM_TREE;
|
||||
break;
|
||||
}
|
||||
// Fall through
|
||||
case 8:
|
||||
case 9:
|
||||
gb->memory.mbcType = GB_MBC_NONE;
|
||||
|
@ -346,6 +373,16 @@ void GBMBCInit(struct GB* gb) {
|
|||
case GB_UNL_WISDOM_TREE:
|
||||
gb->memory.mbcWrite = _GBWisdomTree;
|
||||
break;
|
||||
case GB_UNL_BBD:
|
||||
gb->memory.mbcWrite = _GBBBD;
|
||||
gb->memory.mbcRead = _GBBBDRead;
|
||||
break;
|
||||
case GB_UNL_HITEK:
|
||||
gb->memory.mbcWrite = _GBHitek;
|
||||
gb->memory.mbcRead = _GBHitekRead;
|
||||
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;
|
||||
|
@ -1306,6 +1343,123 @@ static uint8_t _GBPKJDRead(struct GBMemory* memory, uint16_t address) {
|
|||
}
|
||||
}
|
||||
|
||||
static uint8_t _reorderBits(uint8_t input, const uint8_t* reorder) {
|
||||
uint8_t newbyte = 0;
|
||||
int i;
|
||||
for(i = 0; i < 8; ++i) {
|
||||
int oldbit = reorder[i];
|
||||
int newbit = i;
|
||||
newbyte += ((input >> oldbit) & 1) << newbit;
|
||||
}
|
||||
|
||||
return newbyte;
|
||||
}
|
||||
|
||||
static const uint8_t _bbdDataReordering[8][8] = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 00 - Normal
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 01 - NOT KNOWN YET
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 02 - NOT KNOWN YET
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 03 - NOT KNOWN YET
|
||||
{ 0, 5, 1, 3, 4, 2, 6, 7 }, // 04 - Garou
|
||||
{ 0, 4, 2, 3, 1, 5, 6, 7 }, // 05 - Harry
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 06 - NOT KNOWN YET
|
||||
{ 0, 1, 5, 3, 4, 2, 6, 7 }, // 07 - Digimon
|
||||
};
|
||||
|
||||
static const uint8_t _bbdBankReordering[8][8] = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 00 - Normal
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 01 - NOT KNOWN YET
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 02 - NOT KNOWN YET
|
||||
{ 3, 4, 2, 0, 1, 5, 6, 7 }, // 03 - 0,1 unconfirmed. Digimon/Garou
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 04 - NOT KNOWN YET
|
||||
{ 1, 2, 3, 4, 0, 5, 6, 7 }, // 05 - 0,1 unconfirmed. Harry
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 06 - NOT KNOWN YET
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 07 - NOT KNOWN YET
|
||||
};
|
||||
|
||||
void _GBBBD(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
switch (address & 0xF0FF) {
|
||||
case 0x2000:
|
||||
value = _reorderBits(value, _bbdBankReordering[memory->mbcState.bbd.bankSwapMode]);
|
||||
break;
|
||||
case 0x2001:
|
||||
memory->mbcState.bbd.dataSwapMode = value & 0x07;
|
||||
if (!(memory->mbcState.bbd.dataSwapMode == 0x07 || memory->mbcState.bbd.dataSwapMode == 0x05 || memory->mbcState.bbd.dataSwapMode == 0x04 || memory->mbcState.bbd.dataSwapMode == 0x00)) {
|
||||
mLOG(GB_MBC, STUB, "Bitswap mode unsupported: %X", memory->mbcState.bbd.dataSwapMode);
|
||||
}
|
||||
break;
|
||||
case 0x2080:
|
||||
memory->mbcState.bbd.bankSwapMode = value & 0x07;
|
||||
if (!(memory->mbcState.bbd.bankSwapMode == 0x03 || memory->mbcState.bbd.bankSwapMode == 0x05 || memory->mbcState.bbd.bankSwapMode == 0x00)) {
|
||||
mLOG(GB_MBC, STUB, "Bankswap mode unsupported: %X", memory->mbcState.bbd.dataSwapMode);
|
||||
}
|
||||
break;
|
||||
}
|
||||
_GBMBC5(gb, address, value);
|
||||
}
|
||||
|
||||
uint8_t _GBBBDRead(struct GBMemory* memory, uint16_t address) {
|
||||
switch (address >> 14) {
|
||||
case 0:
|
||||
default:
|
||||
return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
|
||||
case 1:
|
||||
return _reorderBits(memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)], _bbdDataReordering[memory->mbcState.bbd.dataSwapMode]);
|
||||
}
|
||||
}
|
||||
|
||||
static const uint8_t _hitekDataReordering[8][8] = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 },
|
||||
{ 0, 6, 5, 3, 4, 1, 2, 7 },
|
||||
{ 0, 5, 6, 3, 4, 2, 1, 7 },
|
||||
{ 0, 6, 2, 3, 4, 5, 1, 7 },
|
||||
{ 0, 6, 1, 3, 4, 5, 2, 7 },
|
||||
{ 0, 1, 6, 3, 4, 5, 2, 7 },
|
||||
{ 0, 2, 6, 3, 4, 1, 5, 7 },
|
||||
{ 0, 6, 2, 3, 4, 1, 5, 7 },
|
||||
};
|
||||
|
||||
static const uint8_t _hitekBankReordering[8][8] = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 },
|
||||
{ 3, 2, 1, 0, 4, 5, 6, 7 },
|
||||
{ 2, 1, 0, 3, 4, 5, 6, 7 },
|
||||
{ 1, 0, 3, 2, 4, 5, 6, 7 },
|
||||
{ 0, 3, 2, 1, 4, 5, 6, 7 },
|
||||
{ 2, 3, 0, 1, 4, 5, 6, 7 },
|
||||
{ 3, 0, 1, 2, 4, 5, 6, 7 },
|
||||
{ 2, 0, 3, 1, 4, 5, 6, 7 },
|
||||
};
|
||||
|
||||
void _GBHitek(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
switch (address & 0xF0FF) {
|
||||
case 0x2000:
|
||||
value = _reorderBits(value, _hitekBankReordering[memory->mbcState.bbd.bankSwapMode]);
|
||||
break;
|
||||
case 0x2001:
|
||||
memory->mbcState.bbd.dataSwapMode = value & 0x07;
|
||||
break;
|
||||
case 0x2080:
|
||||
memory->mbcState.bbd.bankSwapMode = value & 0x07;
|
||||
break;
|
||||
case 0x300:
|
||||
// See hhugboy src/memory/mbc/MbcUnlHitek.cpp for commentary on this return
|
||||
return;
|
||||
}
|
||||
_GBMBC5(gb, address, value);
|
||||
}
|
||||
|
||||
uint8_t _GBHitekRead(struct GBMemory* memory, uint16_t address) {
|
||||
switch (address >> 14) {
|
||||
case 0:
|
||||
default:
|
||||
return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
|
||||
case 1:
|
||||
return _reorderBits(memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)], _hitekDataReordering[memory->mbcState.bbd.dataSwapMode]);
|
||||
}
|
||||
}
|
||||
|
||||
void GBMBCRTCRead(struct GB* gb) {
|
||||
struct GBMBCRTCSaveBuffer rtcBuffer;
|
||||
struct VFile* vf = gb->sramVf;
|
||||
|
|
|
@ -84,6 +84,10 @@ static void GBSetActiveRegion(struct SM83Core* cpu, uint16_t address) {
|
|||
case GB_REGION_CART_BANK1 + 1:
|
||||
case GB_REGION_CART_BANK1 + 2:
|
||||
case GB_REGION_CART_BANK1 + 3:
|
||||
if ((gb->memory.mbcType & GB_UNL_BBD) == GB_UNL_BBD) {
|
||||
cpu->memory.cpuLoad8 = GBLoad8;
|
||||
break;
|
||||
}
|
||||
cpu->memory.cpuLoad8 = GBFastLoad8;
|
||||
if (gb->memory.mbcType != GB_MBC6) {
|
||||
cpu->memory.activeRegion = memory->romBank;
|
||||
|
@ -277,6 +281,9 @@ uint8_t GBLoad8(struct SM83Core* cpu, uint16_t address) {
|
|||
if (address >= memory->romSize) {
|
||||
return 0xFF;
|
||||
}
|
||||
if ((memory->mbcType & GB_UNL_BBD) == GB_UNL_BBD) {
|
||||
return memory->mbcRead(memory, address);
|
||||
}
|
||||
return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
|
||||
case GB_REGION_VRAM:
|
||||
case GB_REGION_VRAM + 1:
|
||||
|
@ -754,6 +761,11 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) {
|
|||
state->memory.mmm01.locked = memory->mbcState.mmm01.locked;
|
||||
state->memory.mmm01.bank0 = memory->mbcState.mmm01.currentBank0;
|
||||
break;
|
||||
case GB_UNL_BBD:
|
||||
case GB_UNL_HITEK:
|
||||
state->memory.bbd.dataSwapMode = memory->mbcState.bbd.dataSwapMode;
|
||||
state->memory.bbd.bankSwapMode = memory->mbcState.bbd.bankSwapMode;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -841,6 +853,11 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
GBMBCSwitchBank0(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 2);
|
||||
}
|
||||
break;
|
||||
case GB_UNL_BBD:
|
||||
case GB_UNL_HITEK:
|
||||
memory->mbcState.bbd.dataSwapMode = state->memory.bbd.dataSwapMode & 0x7;
|
||||
memory->mbcState.bbd.bankSwapMode = state->memory.bbd.bankSwapMode & 0x7;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -50,6 +50,8 @@ 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_BBD);
|
||||
s_mbcList.append(GB_UNL_HITEK);
|
||||
s_mbcList.append(GB_UNL_PKJD);
|
||||
}
|
||||
if (s_gbModelList.isEmpty()) {
|
||||
|
|
|
@ -359,6 +359,16 @@
|
|||
<string>Wisdom Tree (Unlicensed)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>BBD (Unlicensed)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Hitek (Unlicensed)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Pokémon Jade/Diamond (Unlicensed)</string>
|
||||
|
|
Loading…
Reference in New Issue