mirror of https://github.com/mgba-emu/mgba.git
GB MBC: Add Sintax support
This commit is contained in:
parent
6221cd2d06
commit
7607a5bea9
2
CHANGES
2
CHANGES
|
@ -9,7 +9,7 @@ Features:
|
|||
- Scripting: New `storage` API for saving data for a script, e.g. settings
|
||||
- Scripting: New `image` and `canvas` APIs for drawing images and displaying on-screen
|
||||
- Scripting: Debugger integration to allow for breakpoints and watchpoints
|
||||
- New unlicensed GB mappers: NT (older types 1 and 2), Li Cheng, GGB-81
|
||||
- New unlicensed GB mappers: NT (older types 1 and 2), Li Cheng, GGB-81, Sintax
|
||||
- Initial support for bootleg GBA multicarts
|
||||
- Debugger: Add range watchpoints
|
||||
- "Headless" frontend for running tests, automation, etc.
|
||||
|
|
|
@ -73,6 +73,7 @@ The following mappers are partially supported:
|
|||
- Hitek (missing logo switching)
|
||||
- GGB-81 (missing logo switching)
|
||||
- Li Cheng (missing logo switching)
|
||||
- Sintax (missing logo switching)
|
||||
|
||||
### Planned features
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ enum GBMemoryBankControllerType {
|
|||
GB_UNL_GGB81 = 0x223,
|
||||
GB_UNL_SACHEN_MMC1 = 0x230,
|
||||
GB_UNL_SACHEN_MMC2 = 0x231,
|
||||
GB_UNL_SINTAX = 0x240,
|
||||
};
|
||||
|
||||
enum GBVideoLayer {
|
||||
|
|
|
@ -261,6 +261,13 @@ struct GBSachenState {
|
|||
uint8_t baseBank;
|
||||
};
|
||||
|
||||
struct GBSintaxState {
|
||||
uint8_t mode;
|
||||
uint8_t xorValues[4];
|
||||
uint8_t bankNo;
|
||||
uint8_t romBankXor;
|
||||
};
|
||||
|
||||
union GBMBCState {
|
||||
struct GBMBC1State mbc1;
|
||||
struct GBMBC6State mbc6;
|
||||
|
@ -274,6 +281,7 @@ union GBMBCState {
|
|||
struct GBPKJDState pkjd;
|
||||
struct GBBBDState bbd;
|
||||
struct GBSachenState sachen;
|
||||
struct GBSintaxState sintax;
|
||||
};
|
||||
|
||||
struct mRotationSource;
|
||||
|
|
|
@ -451,6 +451,12 @@ struct GBSerializedState {
|
|||
uint8_t unmaskedBank;
|
||||
uint8_t baseBank;
|
||||
} sachen;
|
||||
struct {
|
||||
uint8_t mode;
|
||||
uint8_t xorValues[4];
|
||||
uint8_t bankNo;
|
||||
uint8_t romBankXor;
|
||||
} sintax;
|
||||
struct {
|
||||
uint8_t reserved[16];
|
||||
} padding;
|
||||
|
|
21
src/gb/mbc.c
21
src/gb/mbc.c
|
@ -113,7 +113,7 @@ static struct {
|
|||
{"M161", GB_MBC_AUTODETECT}, // TODO
|
||||
{"BBD", GB_UNL_BBD},
|
||||
{"HITK", GB_UNL_HITEK},
|
||||
{"SNTX", GB_MBC_AUTODETECT}, // TODO
|
||||
{"SNTX", GB_UNL_SINTAX},
|
||||
{"NTO1", GB_UNL_NT_OLD_1},
|
||||
{"NTO2", GB_UNL_NT_OLD_2},
|
||||
{"NTN", GB_UNL_NT_NEW},
|
||||
|
@ -128,6 +128,8 @@ static struct {
|
|||
{"NGHK", GB_MBC_AUTODETECT}, // TODO
|
||||
{"GB81", GB_UNL_GGB81},
|
||||
{"TPP1", GB_MBC_AUTODETECT}, // TODO
|
||||
{"VF01", GB_MBC_AUTODETECT}, // TODO
|
||||
{"SKL8", GB_MBC_AUTODETECT}, // TODO
|
||||
|
||||
{NULL, GB_MBC_AUTODETECT},
|
||||
};
|
||||
|
@ -223,6 +225,12 @@ static enum GBMemoryBankControllerType _detectUnlMBC(const uint8_t* mem, size_t
|
|||
return GB_UNL_LI_CHENG;
|
||||
}
|
||||
break;
|
||||
case 0x6c1dcf2d:
|
||||
case 0x99e3449d:
|
||||
if (mem[0x7FFF] != 0x01) { // Make sure we're not using a "fixed" version
|
||||
return GB_UNL_SINTAX;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (mem[0x104] == 0xCE && mem[0x144] == 0xED && mem[0x114] == 0x66) {
|
||||
|
@ -504,6 +512,14 @@ void GBMBCInit(struct GB* gb) {
|
|||
gb->memory.sramAccess = true;
|
||||
}
|
||||
break;
|
||||
case GB_UNL_SINTAX:
|
||||
gb->memory.mbcWrite = _GBSintax;
|
||||
gb->memory.mbcRead = _GBSintaxRead;
|
||||
gb->memory.mbcReadBank1 = true;
|
||||
if (gb->sramSize) {
|
||||
gb->memory.sramAccess = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
gb->memory.currentBank = 1;
|
||||
|
@ -559,6 +575,9 @@ void GBMBCReset(struct GB* gb) {
|
|||
GBMBCSwitchBank0(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 2);
|
||||
GBMBCSwitchBank(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 1);
|
||||
break;
|
||||
case GB_UNL_SINTAX:
|
||||
gb->memory.mbcState.sintax.mode = 0xF;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ void _GBHitek(struct GB* gb, uint16_t address, uint8_t value);
|
|||
void _GBLiCheng(struct GB* gb, uint16_t address, uint8_t value);
|
||||
void _GBGGB81(struct GB* gb, uint16_t address, uint8_t value);
|
||||
void _GBSachen(struct GB* gb, uint16_t address, uint8_t value);
|
||||
void _GBSintax(struct GB* gb, uint16_t address, uint8_t value);
|
||||
|
||||
uint8_t _GBMBC2Read(struct GBMemory*, uint16_t address);
|
||||
uint8_t _GBMBC6Read(struct GBMemory*, uint16_t address);
|
||||
|
@ -54,6 +55,7 @@ uint8_t _GBHitekRead(struct GBMemory*, uint16_t address);
|
|||
uint8_t _GBGGB81Read(struct GBMemory*, uint16_t address);
|
||||
uint8_t _GBSachenMMC1Read(struct GBMemory*, uint16_t address);
|
||||
uint8_t _GBSachenMMC2Read(struct GBMemory*, uint16_t address);
|
||||
uint8_t _GBSintaxRead(struct GBMemory*, uint16_t address);
|
||||
|
||||
void _GBMBCLatchRTC(struct mRTCSource* rtc, uint8_t* rtcRegs, time_t* rtcLastLatch);
|
||||
void _GBMBCAppendSaveSuffix(struct GB* gb, const void* buffer, size_t size);
|
||||
|
|
|
@ -500,3 +500,102 @@ uint8_t _GBSachenMMC2Read(struct GBMemory* memory, uint16_t address) {
|
|||
return 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
static const uint8_t _sintaxReordering[16][8] = {
|
||||
{ 2, 1, 4, 3, 6, 5, 0, 7 },
|
||||
{ 3, 2, 5, 4, 7, 6, 1, 0 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // unknown
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // unknown
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // unknown
|
||||
{ 4, 5, 2, 3, 0, 1, 6, 7 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // unknown
|
||||
{ 6, 7, 4, 5, 1, 3, 0, 2 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // unknown
|
||||
{ 7, 6, 1, 0, 3, 2, 5, 4 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // unknown
|
||||
{ 5, 4, 7, 6, 1, 0, 3, 2 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // unknown
|
||||
{ 2, 3, 4, 5, 6, 7, 0, 1 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // unknown
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 },
|
||||
};
|
||||
|
||||
void _GBSintax(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBSintaxState* state = &gb->memory.mbcState.sintax;
|
||||
|
||||
if (address >= 0x2000 && address < 0x3000) {
|
||||
state->bankNo = value;
|
||||
value = _reorderBits(value, _sintaxReordering[state->mode]);
|
||||
state->romBankXor = state->xorValues[state->bankNo & 0x3];
|
||||
}
|
||||
|
||||
if ((address & 0xF0F0) == 0x5010) {
|
||||
// contrary to previous belief it IS possible to change the mode after setting it initially
|
||||
// The reason Metal Max was breaking is because it only recognises writes to 5x1x
|
||||
// and that game writes to a bunch of other 5xxx addresses before battles
|
||||
state->mode = value & 0xF;
|
||||
|
||||
mLOG(GB_MBC, DEBUG, "Sintax bank reorder mode: %X", state->mode);
|
||||
|
||||
switch (state->mode) {
|
||||
// Supported modes
|
||||
case 0x00: // Lion King, Golden Sun
|
||||
case 0x01: // Langrisser
|
||||
case 0x05: // Maple Story, Pokemon Platinum
|
||||
case 0x07: // Bynasty Warriors 5
|
||||
case 0x09: // ???
|
||||
case 0x0B: // Shaolin Legend
|
||||
case 0x0D: // Older games
|
||||
case 0x0F: // Default mode, no reordering
|
||||
break;
|
||||
default:
|
||||
mLOG(GB_MBC, DEBUG, "Bank reorder mode unsupported - %X", state->mode);
|
||||
break;
|
||||
}
|
||||
|
||||
_GBSintax(gb, 0x2000, state->bankNo); // fake a bank switch to select the correct bank
|
||||
return;
|
||||
}
|
||||
|
||||
if (address >= 0x7000 && address < 0x8000) {
|
||||
int xorNo = (address & 0x00F0) >> 4;
|
||||
switch (xorNo) {
|
||||
case 2:
|
||||
state->xorValues[0] = value;
|
||||
mLOG(GB_MBC, DEBUG, "Sintax XOR 0: %X", value);
|
||||
break;
|
||||
case 3:
|
||||
state->xorValues[1] = value;
|
||||
mLOG(GB_MBC, DEBUG, "Sintax XOR 1: %X", value);
|
||||
break;
|
||||
case 4:
|
||||
state->xorValues[2] = value;
|
||||
mLOG(GB_MBC, DEBUG, "Sintax XOR 2: %X", value);
|
||||
break;
|
||||
case 5:
|
||||
state->xorValues[3] = value;
|
||||
mLOG(GB_MBC, DEBUG, "Sintax XOR 3: %X", value);
|
||||
break;
|
||||
}
|
||||
|
||||
// xor is applied immediately to the current bank
|
||||
state->romBankXor = state->xorValues[state->bankNo & 0x3];
|
||||
}
|
||||
_GBMBC5(gb, address, value);
|
||||
}
|
||||
|
||||
uint8_t _GBSintaxRead(struct GBMemory* memory, uint16_t address) {
|
||||
struct GBSintaxState* state = &memory->mbcState.sintax;
|
||||
switch (address >> 13) {
|
||||
case 0x2:
|
||||
case 0x3:
|
||||
return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)] ^ state->romBankXor;
|
||||
case 0x5:
|
||||
if (memory->sramAccess && memory->sram) {
|
||||
return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
|
||||
}
|
||||
return 0xFF;
|
||||
default:
|
||||
return 0xFF;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -840,6 +840,12 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) {
|
|||
state->memory.sachen.unmaskedBank = memory->mbcState.sachen.unmaskedBank;
|
||||
state->memory.sachen.baseBank = memory->mbcState.sachen.baseBank;
|
||||
break;
|
||||
case GB_UNL_SINTAX:
|
||||
state->memory.sintax.mode = memory->mbcState.sintax.mode;
|
||||
memcpy(state->memory.sintax.xorValues, memory->mbcState.sintax.xorValues, sizeof(state->memory.sintax.xorValues));
|
||||
state->memory.sintax.bankNo = memory->mbcState.sintax.bankNo;
|
||||
state->memory.sintax.romBankXor = memory->mbcState.sintax.romBankXor;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -1008,6 +1014,12 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
memory->mbcState.sachen.baseBank = state->memory.sachen.baseBank;
|
||||
GBMBCSwitchBank0(gb, memory->mbcState.sachen.baseBank & memory->mbcState.sachen.mask);
|
||||
break;
|
||||
case GB_UNL_SINTAX:
|
||||
memory->mbcState.sintax.mode = state->memory.sintax.mode;
|
||||
memcpy(memory->mbcState.sintax.xorValues, state->memory.sintax.xorValues, sizeof(memory->mbcState.sintax.xorValues));
|
||||
memory->mbcState.sintax.bankNo = state->memory.sintax.bankNo;
|
||||
memory->mbcState.sintax.romBankXor = state->memory.sintax.romBankXor;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ static const QList<GBMemoryBankControllerType> s_mbcList{
|
|||
GB_UNL_LI_CHENG,
|
||||
GB_UNL_SACHEN_MMC1,
|
||||
GB_UNL_SACHEN_MMC2,
|
||||
GB_UNL_SINTAX,
|
||||
};
|
||||
|
||||
static QMap<GBModel, QString> s_gbModelNames;
|
||||
|
@ -102,6 +103,7 @@ QString GameBoy::mbcName(GBMemoryBankControllerType mbc) {
|
|||
s_mbcNames[GB_UNL_LI_CHENG] = tr("Li Cheng");
|
||||
s_mbcNames[GB_UNL_SACHEN_MMC1] = tr("Sachen (MMC1)");
|
||||
s_mbcNames[GB_UNL_SACHEN_MMC2] = tr("Sachen (MMC2)");
|
||||
s_mbcNames[GB_UNL_SINTAX] = tr("Sintax");
|
||||
}
|
||||
|
||||
return s_mbcNames[mbc];
|
||||
|
|
Loading…
Reference in New Issue