all repos — mgba @ bf16c7b6bfd29981371d83289436b7512d799ef8

mGBA Game Boy Advance Emulator

GB MBC: Add Hitek and BBD mappers
Vicki Pfau vi@endrift.com
Sun, 16 Aug 2020 16:27:11 -0700
commit

bf16c7b6bfd29981371d83289436b7512d799ef8

parent

3b363bb2c17a5269ffcf9312e52c271563f43646

M CHANGESCHANGES

@@ -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:
M include/mgba/gb/interface.hinclude/mgba/gb/interface.h

@@ -38,6 +38,8 @@ GB_MBC3_RTC = 0x103,

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 {
M include/mgba/internal/gb/memory.hinclude/mgba/internal/gb/memory.h

@@ -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 @@ struct GBMMM01State mmm01;

struct GBPocketCamState pocketCam; struct GBTAMA5State tama5; struct GBPKJDState pkjd; + struct GBBBDState bbd; }; struct mRotationSource;
M include/mgba/internal/gb/serialize.hinclude/mgba/internal/gb/serialize.h

@@ -392,6 +392,10 @@ uint8_t locked;

uint8_t bank0; } mmm01; struct { + uint8_t dataSwapMode; + uint8_t bankSwapMode; + } bbd; + struct { uint8_t reserved[16]; } padding; };
M src/gb/mbc.csrc/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 value);

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 @@ }

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 @@ case 5:

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;

@@ -345,6 +372,16 @@ }

break; 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;

@@ -1303,6 +1340,123 @@ case 6:

return memory->mbcState.pkjd.reg[memory->activeRtcReg - 5]; default: return 0; + } +} + +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]); } }
M src/gb/memory.csrc/gb/memory.c

@@ -84,6 +84,10 @@ case GB_REGION_CART_BANK1:

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;

@@ -276,6 +280,9 @@ case GB_REGION_CART_BANK1:

case GB_REGION_CART_BANK1 + 1: 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:

@@ -754,6 +761,11 @@ case GB_MMM01:

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; }

@@ -840,6 +852,11 @@ GBMBCSwitchBank0(gb, memory->mbcState.mmm01.currentBank0);

} else { 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;
M src/platform/qt/OverrideView.cppsrc/platform/qt/OverrideView.cpp

@@ -50,6 +50,8 @@ s_mbcList.append(GB_TAMA5);

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()) {
M src/platform/qt/OverrideView.uisrc/platform/qt/OverrideView.ui

@@ -361,6 +361,16 @@ </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> </property> </item>