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 `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: New `image` and `canvas` APIs for drawing images and displaying on-screen
|
||||||
- Scripting: Debugger integration to allow for breakpoints and watchpoints
|
- 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
|
- Initial support for bootleg GBA multicarts
|
||||||
- Debugger: Add range watchpoints
|
- Debugger: Add range watchpoints
|
||||||
- "Headless" frontend for running tests, automation, etc.
|
- "Headless" frontend for running tests, automation, etc.
|
||||||
|
@ -73,6 +73,7 @@ The following mappers are partially supported:
|
|||||||
- Hitek (missing logo switching)
|
- Hitek (missing logo switching)
|
||||||
- GGB-81 (missing logo switching)
|
- GGB-81 (missing logo switching)
|
||||||
- Li Cheng (missing logo switching)
|
- Li Cheng (missing logo switching)
|
||||||
|
- Sintax (missing logo switching)
|
||||||
|
|
||||||
### Planned features
|
### Planned features
|
||||||
|
|
||||||
|
@ -50,6 +50,7 @@ enum GBMemoryBankControllerType {
|
|||||||
GB_UNL_GGB81 = 0x223,
|
GB_UNL_GGB81 = 0x223,
|
||||||
GB_UNL_SACHEN_MMC1 = 0x230,
|
GB_UNL_SACHEN_MMC1 = 0x230,
|
||||||
GB_UNL_SACHEN_MMC2 = 0x231,
|
GB_UNL_SACHEN_MMC2 = 0x231,
|
||||||
|
GB_UNL_SINTAX = 0x240,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum GBVideoLayer {
|
enum GBVideoLayer {
|
||||||
|
@ -261,6 +261,13 @@ struct GBSachenState {
|
|||||||
uint8_t baseBank;
|
uint8_t baseBank;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct GBSintaxState {
|
||||||
|
uint8_t mode;
|
||||||
|
uint8_t xorValues[4];
|
||||||
|
uint8_t bankNo;
|
||||||
|
uint8_t romBankXor;
|
||||||
|
};
|
||||||
|
|
||||||
union GBMBCState {
|
union GBMBCState {
|
||||||
struct GBMBC1State mbc1;
|
struct GBMBC1State mbc1;
|
||||||
struct GBMBC6State mbc6;
|
struct GBMBC6State mbc6;
|
||||||
@ -274,6 +281,7 @@ union GBMBCState {
|
|||||||
struct GBPKJDState pkjd;
|
struct GBPKJDState pkjd;
|
||||||
struct GBBBDState bbd;
|
struct GBBBDState bbd;
|
||||||
struct GBSachenState sachen;
|
struct GBSachenState sachen;
|
||||||
|
struct GBSintaxState sintax;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mRotationSource;
|
struct mRotationSource;
|
||||||
|
@ -451,6 +451,12 @@ struct GBSerializedState {
|
|||||||
uint8_t unmaskedBank;
|
uint8_t unmaskedBank;
|
||||||
uint8_t baseBank;
|
uint8_t baseBank;
|
||||||
} sachen;
|
} sachen;
|
||||||
|
struct {
|
||||||
|
uint8_t mode;
|
||||||
|
uint8_t xorValues[4];
|
||||||
|
uint8_t bankNo;
|
||||||
|
uint8_t romBankXor;
|
||||||
|
} sintax;
|
||||||
struct {
|
struct {
|
||||||
uint8_t reserved[16];
|
uint8_t reserved[16];
|
||||||
} padding;
|
} padding;
|
||||||
|
21
src/gb/mbc.c
21
src/gb/mbc.c
@ -113,7 +113,7 @@ static struct {
|
|||||||
{"M161", GB_MBC_AUTODETECT}, // TODO
|
{"M161", GB_MBC_AUTODETECT}, // TODO
|
||||||
{"BBD", GB_UNL_BBD},
|
{"BBD", GB_UNL_BBD},
|
||||||
{"HITK", GB_UNL_HITEK},
|
{"HITK", GB_UNL_HITEK},
|
||||||
{"SNTX", GB_MBC_AUTODETECT}, // TODO
|
{"SNTX", GB_UNL_SINTAX},
|
||||||
{"NTO1", GB_UNL_NT_OLD_1},
|
{"NTO1", GB_UNL_NT_OLD_1},
|
||||||
{"NTO2", GB_UNL_NT_OLD_2},
|
{"NTO2", GB_UNL_NT_OLD_2},
|
||||||
{"NTN", GB_UNL_NT_NEW},
|
{"NTN", GB_UNL_NT_NEW},
|
||||||
@ -128,6 +128,8 @@ static struct {
|
|||||||
{"NGHK", GB_MBC_AUTODETECT}, // TODO
|
{"NGHK", GB_MBC_AUTODETECT}, // TODO
|
||||||
{"GB81", GB_UNL_GGB81},
|
{"GB81", GB_UNL_GGB81},
|
||||||
{"TPP1", GB_MBC_AUTODETECT}, // TODO
|
{"TPP1", GB_MBC_AUTODETECT}, // TODO
|
||||||
|
{"VF01", GB_MBC_AUTODETECT}, // TODO
|
||||||
|
{"SKL8", GB_MBC_AUTODETECT}, // TODO
|
||||||
|
|
||||||
{NULL, GB_MBC_AUTODETECT},
|
{NULL, GB_MBC_AUTODETECT},
|
||||||
};
|
};
|
||||||
@ -223,6 +225,12 @@ static enum GBMemoryBankControllerType _detectUnlMBC(const uint8_t* mem, size_t
|
|||||||
return GB_UNL_LI_CHENG;
|
return GB_UNL_LI_CHENG;
|
||||||
}
|
}
|
||||||
break;
|
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) {
|
if (mem[0x104] == 0xCE && mem[0x144] == 0xED && mem[0x114] == 0x66) {
|
||||||
@ -504,6 +512,14 @@ void GBMBCInit(struct GB* gb) {
|
|||||||
gb->memory.sramAccess = true;
|
gb->memory.sramAccess = true;
|
||||||
}
|
}
|
||||||
break;
|
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;
|
gb->memory.currentBank = 1;
|
||||||
@ -559,6 +575,9 @@ void GBMBCReset(struct GB* gb) {
|
|||||||
GBMBCSwitchBank0(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 2);
|
GBMBCSwitchBank0(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 2);
|
||||||
GBMBCSwitchBank(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 1);
|
GBMBCSwitchBank(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 1);
|
||||||
break;
|
break;
|
||||||
|
case GB_UNL_SINTAX:
|
||||||
|
gb->memory.mbcState.sintax.mode = 0xF;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
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 _GBLiCheng(struct GB* gb, uint16_t address, uint8_t value);
|
||||||
void _GBGGB81(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 _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 _GBMBC2Read(struct GBMemory*, uint16_t address);
|
||||||
uint8_t _GBMBC6Read(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 _GBGGB81Read(struct GBMemory*, uint16_t address);
|
||||||
uint8_t _GBSachenMMC1Read(struct GBMemory*, uint16_t address);
|
uint8_t _GBSachenMMC1Read(struct GBMemory*, uint16_t address);
|
||||||
uint8_t _GBSachenMMC2Read(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 _GBMBCLatchRTC(struct mRTCSource* rtc, uint8_t* rtcRegs, time_t* rtcLastLatch);
|
||||||
void _GBMBCAppendSaveSuffix(struct GB* gb, const void* buffer, size_t size);
|
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;
|
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.unmaskedBank = memory->mbcState.sachen.unmaskedBank;
|
||||||
state->memory.sachen.baseBank = memory->mbcState.sachen.baseBank;
|
state->memory.sachen.baseBank = memory->mbcState.sachen.baseBank;
|
||||||
break;
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1008,6 +1014,12 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||||||
memory->mbcState.sachen.baseBank = state->memory.sachen.baseBank;
|
memory->mbcState.sachen.baseBank = state->memory.sachen.baseBank;
|
||||||
GBMBCSwitchBank0(gb, memory->mbcState.sachen.baseBank & memory->mbcState.sachen.mask);
|
GBMBCSwitchBank0(gb, memory->mbcState.sachen.baseBank & memory->mbcState.sachen.mask);
|
||||||
break;
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,7 @@ static const QList<GBMemoryBankControllerType> s_mbcList{
|
|||||||
GB_UNL_LI_CHENG,
|
GB_UNL_LI_CHENG,
|
||||||
GB_UNL_SACHEN_MMC1,
|
GB_UNL_SACHEN_MMC1,
|
||||||
GB_UNL_SACHEN_MMC2,
|
GB_UNL_SACHEN_MMC2,
|
||||||
|
GB_UNL_SINTAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
static QMap<GBModel, QString> s_gbModelNames;
|
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_LI_CHENG] = tr("Li Cheng");
|
||||||
s_mbcNames[GB_UNL_SACHEN_MMC1] = tr("Sachen (MMC1)");
|
s_mbcNames[GB_UNL_SACHEN_MMC1] = tr("Sachen (MMC1)");
|
||||||
s_mbcNames[GB_UNL_SACHEN_MMC2] = tr("Sachen (MMC2)");
|
s_mbcNames[GB_UNL_SACHEN_MMC2] = tr("Sachen (MMC2)");
|
||||||
|
s_mbcNames[GB_UNL_SINTAX] = tr("Sintax");
|
||||||
}
|
}
|
||||||
|
|
||||||
return s_mbcNames[mbc];
|
return s_mbcNames[mbc];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user