mirror of https://github.com/mgba-emu/mgba.git
GB MBC: Improved support for HuC-3 mapper
This commit is contained in:
parent
e2040146ea
commit
c829cd2e70
1
CHANGES
1
CHANGES
|
@ -7,6 +7,7 @@ Features:
|
|||
- Cheat code support in homebrew ports
|
||||
- Acclerometer and gyro support for controllers on PC
|
||||
- Support for combo "Super Game Boy Color" SGB + GBC ROM hacks
|
||||
- Improved support for HuC-3 mapper
|
||||
- Support for 64 kiB SRAM saves used in some bootlegs
|
||||
- Discord Rich Presence now supports time elapsed
|
||||
- Additional scaling shaders
|
||||
|
|
|
@ -182,6 +182,7 @@ struct mCoreCallbacks {
|
|||
void (*shutdown)(void* context);
|
||||
void (*keysRead)(void* context);
|
||||
void (*savedataUpdated)(void* context);
|
||||
void (*alarm)(void* context);
|
||||
};
|
||||
|
||||
DECLARE_VECTOR(mCoreCallbacksList, struct mCoreCallbacks);
|
||||
|
|
|
@ -102,6 +102,41 @@ enum GBTAMA5Register {
|
|||
GBTAMA5_READ_HI = 0xD,
|
||||
};
|
||||
|
||||
enum GBHuC3Register {
|
||||
GBHUC3_RTC_MINUTES_LO = 0x10,
|
||||
GBHUC3_RTC_MINUTES_MI = 0x11,
|
||||
GBHUC3_RTC_MINUTES_HI = 0x12,
|
||||
GBHUC3_RTC_DAYS_LO = 0x13,
|
||||
GBHUC3_RTC_DAYS_MI = 0x14,
|
||||
GBHUC3_RTC_DAYS_HI = 0x15,
|
||||
GBHUC3_RTC_ENABLE = 0x16,
|
||||
GBHUC3_SPEAKER_TONE = 0x26,
|
||||
GBHUC3_SPEAKER_ENABLE = 0x27,
|
||||
GBHUC3_ALARM_MINUTES_LO = 0x58,
|
||||
GBHUC3_ALARM_MINUTES_MI = 0x59,
|
||||
GBHUC3_ALARM_MINUTES_HI = 0x5A,
|
||||
GBHUC3_ALARM_DAYS_LO = 0x5B,
|
||||
GBHUC3_ALARM_DAYS_MI = 0x5C,
|
||||
GBHUC3_ALARM_DAYS_HI = 0x5D,
|
||||
GBHUC3_ALARM_TONE = 0x5E,
|
||||
GBHUC3_ALARM_ENABLE = 0x5F,
|
||||
};
|
||||
|
||||
enum GBHuC3Mode {
|
||||
GBHUC3_MODE_SRAM_RO = 0x0,
|
||||
GBHUC3_MODE_SRAM_RW = 0xA,
|
||||
GBHUC3_MODE_IN = 0xB,
|
||||
GBHUC3_MODE_OUT = 0xC,
|
||||
GBHUC3_MODE_COMMIT = 0xD,
|
||||
};
|
||||
|
||||
enum GBHuC3Command {
|
||||
GBHUC3_CMD_LATCH = 0x0,
|
||||
GBHUC3_CMD_SET_RTC = 0x1,
|
||||
GBHUC3_CMD_RO = 0x2,
|
||||
GBHUC3_CMD_TONE = 0xE,
|
||||
};
|
||||
|
||||
struct GBMBC1State {
|
||||
int mode;
|
||||
int multicartStride;
|
||||
|
@ -145,6 +180,13 @@ struct GBTAMA5State {
|
|||
uint8_t registers[GBTAMA5_MAX];
|
||||
};
|
||||
|
||||
struct GBHuC3State {
|
||||
uint8_t index;
|
||||
uint8_t value;
|
||||
uint8_t mode;
|
||||
uint8_t registers[256];
|
||||
};
|
||||
|
||||
struct GBPKJDState {
|
||||
uint8_t reg[2];
|
||||
};
|
||||
|
@ -161,6 +203,7 @@ union GBMBCState {
|
|||
struct GBMMM01State mmm01;
|
||||
struct GBPocketCamState pocketCam;
|
||||
struct GBTAMA5State tama5;
|
||||
struct GBHuC3State huc3;
|
||||
struct GBPKJDState pkjd;
|
||||
struct GBBBDState bbd;
|
||||
};
|
||||
|
|
|
@ -168,7 +168,8 @@ mLOG_DECLARE_CATEGORY(GB_STATE);
|
|||
* 0x003FF: Interrupts enabled
|
||||
* 0x00400 - 0x043FF: VRAM
|
||||
* 0x04400 - 0x0C3FF: WRAM
|
||||
* 0x0C400 - 0x0C77F: Reserved
|
||||
* 0x0C400 - 0x0C6FF: Reserved
|
||||
* 0x0C700 - 0x0C77F: Reserved
|
||||
* 0x0C780 - 0x117FF: Super Game Boy
|
||||
* | 0x0C780 - 0x0C7D9: Current attributes
|
||||
* | 0x0C7DA: Current command
|
||||
|
@ -393,6 +394,12 @@ struct GBSerializedState {
|
|||
uint8_t locked;
|
||||
uint8_t bank0;
|
||||
} mmm01;
|
||||
struct {
|
||||
uint64_t lastLatch;
|
||||
uint8_t index;
|
||||
uint8_t value;
|
||||
uint8_t mode;
|
||||
} huc3;
|
||||
struct {
|
||||
uint8_t dataSwapMode;
|
||||
uint8_t bankSwapMode;
|
||||
|
@ -421,7 +428,9 @@ struct GBSerializedState {
|
|||
uint8_t vram[GB_SIZE_VRAM];
|
||||
uint8_t wram[GB_SIZE_WORKING_RAM];
|
||||
|
||||
uint32_t reserved2[0xC4];
|
||||
uint32_t reserved2[0xA4];
|
||||
|
||||
uint8_t huc3Registers[0x80];
|
||||
|
||||
struct {
|
||||
uint8_t attributes[90];
|
||||
|
|
145
src/gb/mbc.c
145
src/gb/mbc.c
|
@ -47,6 +47,7 @@ static uint8_t _GBMBC7Read(struct GBMemory*, uint16_t address);
|
|||
static void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value);
|
||||
|
||||
static uint8_t _GBTAMA5Read(struct GBMemory*, uint16_t address);
|
||||
static uint8_t _GBHuC3Read(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);
|
||||
|
@ -373,6 +374,7 @@ void GBMBCInit(struct GB* gb) {
|
|||
break;
|
||||
case GB_HuC3:
|
||||
gb->memory.mbcWrite = _GBHuC3;
|
||||
gb->memory.mbcRead = _GBHuC3Read;
|
||||
break;
|
||||
case GB_TAMA5:
|
||||
mLOG(GB_MBC, WARN, "unimplemented MBC: TAMA5");
|
||||
|
@ -1101,9 +1103,123 @@ void _GBHuC1(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
}
|
||||
}
|
||||
|
||||
static void _latchHuC3Rtc(struct mRTCSource* rtc, uint8_t* huc3Regs, time_t* rtcLastLatch) {
|
||||
time_t t;
|
||||
if (rtc) {
|
||||
if (rtc->sample) {
|
||||
rtc->sample(rtc);
|
||||
}
|
||||
t = rtc->unixTime(rtc);
|
||||
} else {
|
||||
t = time(0);
|
||||
}
|
||||
t -= *rtcLastLatch;
|
||||
t /= 60;
|
||||
|
||||
if (!t) {
|
||||
return;
|
||||
}
|
||||
*rtcLastLatch += t * 60;
|
||||
|
||||
int minutes = huc3Regs[GBHUC3_RTC_MINUTES_HI] << 8;
|
||||
minutes |= huc3Regs[GBHUC3_RTC_MINUTES_MI] << 4;
|
||||
minutes |= huc3Regs[GBHUC3_RTC_MINUTES_LO];
|
||||
minutes += t % 1440;
|
||||
t /= 1440;
|
||||
if (minutes >= 1440) {
|
||||
minutes -= 1440;
|
||||
++t;
|
||||
} else if (minutes < 0) {
|
||||
minutes += 1440;
|
||||
--t;
|
||||
}
|
||||
huc3Regs[GBHUC3_RTC_MINUTES_LO] = minutes & 0xF;
|
||||
huc3Regs[GBHUC3_RTC_MINUTES_MI] = (minutes >> 4) & 0xF;
|
||||
huc3Regs[GBHUC3_RTC_MINUTES_HI] = (minutes >> 8) & 0xF;
|
||||
|
||||
int days = huc3Regs[GBHUC3_RTC_DAYS_LO];
|
||||
days |= huc3Regs[GBHUC3_RTC_DAYS_MI] << 4;
|
||||
days |= huc3Regs[GBHUC3_RTC_DAYS_HI] << 8;
|
||||
|
||||
days += t;
|
||||
|
||||
huc3Regs[GBHUC3_RTC_DAYS_LO] = days & 0xF;
|
||||
huc3Regs[GBHUC3_RTC_DAYS_MI] = (days >> 4) & 0xF;
|
||||
huc3Regs[GBHUC3_RTC_DAYS_HI] = (days >> 8) & 0xF;
|
||||
}
|
||||
|
||||
static void _huc3Commit(struct GB* gb, struct GBHuC3State* state) {
|
||||
size_t c;
|
||||
switch (state->value & 0x70) {
|
||||
case 0x10:
|
||||
if ((state->index & 0xF8) == 0x10) {
|
||||
_latchHuC3Rtc(gb->memory.rtc, state->registers, &gb->memory.rtcLastLatch);
|
||||
}
|
||||
state->value &= 0xF0;
|
||||
state->value |= state->registers[state->index] & 0xF;
|
||||
mLOG(GB_MBC, DEBUG, "HuC-3 read: %02X:%X", state->index, state->value & 0xF);
|
||||
if (state->value & 0x10) {
|
||||
++state->index;
|
||||
}
|
||||
break;
|
||||
case 0x30:
|
||||
mLOG(GB_MBC, DEBUG, "HuC-3 write: %02X:%X", state->index, state->value & 0xF);
|
||||
state->registers[state->index] = state->value & 0xF;
|
||||
if (state->value & 0x10) {
|
||||
++state->index;
|
||||
}
|
||||
break;
|
||||
case 0x40:
|
||||
state->index &= 0xF0;
|
||||
state->index |= (state->value) & 0xF;
|
||||
mLOG(GB_MBC, DEBUG, "HuC-3 index (low): %02X", state->index);
|
||||
break;
|
||||
case 0x50:
|
||||
state->index &= 0x0F;
|
||||
state->index |= ((state->value) & 0xF) << 4;
|
||||
mLOG(GB_MBC, DEBUG, "HuC-3 index (high): %02X", state->index);
|
||||
break;
|
||||
case 0x60:
|
||||
switch (state->value & 0xF) {
|
||||
case GBHUC3_CMD_LATCH:
|
||||
_latchHuC3Rtc(gb->memory.rtc, state->registers, &gb->memory.rtcLastLatch);
|
||||
memcpy(state->registers, &state->registers[GBHUC3_RTC_MINUTES_LO], 6);
|
||||
mLOG(GB_MBC, DEBUG, "HuC-3 RTC latch");
|
||||
break;
|
||||
case GBHUC3_CMD_SET_RTC:
|
||||
memcpy(&state->registers[GBHUC3_RTC_MINUTES_LO], state->registers, 6);
|
||||
mLOG(GB_MBC, DEBUG, "HuC-3 set RTC");
|
||||
break;
|
||||
case GBHUC3_CMD_RO:
|
||||
mLOG(GB_MBC, STUB, "HuC-3 unimplemented read-only mode");
|
||||
break;
|
||||
case GBHUC3_CMD_TONE:
|
||||
if (state->registers[GBHUC3_SPEAKER_ENABLE] == 1) {
|
||||
for (c = 0; c < mCoreCallbacksListSize(&gb->coreCallbacks); ++c) {
|
||||
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gb->coreCallbacks, c);
|
||||
if (callbacks->alarm) {
|
||||
callbacks->alarm(callbacks->context);
|
||||
}
|
||||
}
|
||||
mLOG(GB_MBC, DEBUG, "HuC-3 tone %i", state->registers[GBHUC3_SPEAKER_TONE] & 3);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
mLOG(GB_MBC, STUB, "HuC-3 unknown command: %X", state->value & 0xF);
|
||||
break;
|
||||
}
|
||||
state->value = 0xE1;
|
||||
break;
|
||||
default:
|
||||
mLOG(GB_MBC, STUB, "HuC-3 unknown mode commit: %02X:%02X", state->index, state->value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
int bank = value & 0x3F;
|
||||
struct GBHuC3State* state = &memory->mbcState.huc3;
|
||||
int bank = value & 0x7F;
|
||||
if (address & 0x1FFF) {
|
||||
mLOG(GB_MBC, STUB, "HuC-3 unknown value %04X:%02X", address, value);
|
||||
}
|
||||
|
@ -1119,6 +1235,7 @@ void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
memory->sramAccess = false;
|
||||
break;
|
||||
}
|
||||
state->mode = value;
|
||||
break;
|
||||
case 0x1:
|
||||
GBMBCSwitchBank(gb, bank);
|
||||
|
@ -1126,6 +1243,18 @@ void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
case 0x2:
|
||||
GBMBCSwitchSramBank(gb, bank);
|
||||
break;
|
||||
case 0x5:
|
||||
switch (state->mode) {
|
||||
case GBHUC3_MODE_IN:
|
||||
state->value = 0x80 | value;
|
||||
break;
|
||||
case GBHUC3_MODE_COMMIT:
|
||||
_huc3Commit(gb, state);
|
||||
break;
|
||||
default:
|
||||
mLOG(GB_MBC, STUB, "HuC-3 unknown mode write: %02X:%02X", state->mode, value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// TODO
|
||||
mLOG(GB_MBC, STUB, "HuC-3 unknown address: %04X:%02X", address, value);
|
||||
|
@ -1133,6 +1262,20 @@ void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
}
|
||||
}
|
||||
|
||||
uint8_t _GBHuC3Read(struct GBMemory* memory, uint16_t address) {
|
||||
struct GBHuC3State* state = &memory->mbcState.huc3;
|
||||
switch (state->mode) {
|
||||
case GBHUC3_MODE_SRAM_RO:
|
||||
case GBHUC3_MODE_SRAM_RW:
|
||||
return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
|
||||
case GBHUC3_MODE_IN:
|
||||
case GBHUC3_MODE_OUT:
|
||||
return 0x80 | state->value;
|
||||
default:
|
||||
return 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
int bank = value & 0x3F;
|
||||
|
|
|
@ -726,6 +726,7 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) {
|
|||
state->memory.cartBus = memory->cartBus;
|
||||
STORE_16LE(memory->cartBusPc, 0, &state->cartBusPc);
|
||||
|
||||
int i;
|
||||
switch (memory->mbcType) {
|
||||
case GB_MBC1:
|
||||
state->memory.mbc1.mode = memory->mbcState.mbc1.mode;
|
||||
|
@ -734,7 +735,7 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) {
|
|||
state->memory.mbc1.bankHi = memory->mbcState.mbc1.bankHi;
|
||||
break;
|
||||
case GB_MBC3_RTC:
|
||||
STORE_64LE(gb->memory.rtcLastLatch, 0, &state->memory.rtc.lastLatch);
|
||||
STORE_64LE(memory->rtcLastLatch, 0, &state->memory.rtc.lastLatch);
|
||||
break;
|
||||
case GB_MBC7:
|
||||
state->memory.mbc7.state = memory->mbcState.mbc7.state;
|
||||
|
@ -746,6 +747,16 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) {
|
|||
STORE_16LE(memory->mbcState.mbc7.sr, 0, &state->memory.mbc7.sr);
|
||||
STORE_32LE(memory->mbcState.mbc7.writable, 0, &state->memory.mbc7.writable);
|
||||
break;
|
||||
case GB_HuC3:
|
||||
STORE_64LE(memory->rtcLastLatch, 0, &state->memory.huc3.lastLatch);
|
||||
state->memory.huc3.index = memory->mbcState.huc3.index;
|
||||
state->memory.huc3.value = memory->mbcState.huc3.value;
|
||||
state->memory.huc3.mode = memory->mbcState.huc3.mode;
|
||||
for (i = 0; i < 0x80; ++i) {
|
||||
state->huc3Registers[i] = memory->mbcState.huc3.registers[i * 2] & 0xF;
|
||||
state->huc3Registers[i] |= memory->mbcState.huc3.registers[i * 2 + 1] << 4;
|
||||
}
|
||||
break;
|
||||
case GB_MMM01:
|
||||
state->memory.mmm01.locked = memory->mbcState.mmm01.locked;
|
||||
state->memory.mmm01.bank0 = memory->mbcState.mmm01.currentBank0;
|
||||
|
@ -808,6 +819,7 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
memory->cartBus = state->memory.cartBus;
|
||||
LOAD_16LE(memory->cartBusPc, 0, &state->cartBusPc);
|
||||
|
||||
int i;
|
||||
switch (memory->mbcType) {
|
||||
case GB_MBC1:
|
||||
memory->mbcState.mbc1.mode = state->memory.mbc1.mode;
|
||||
|
@ -824,7 +836,7 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
}
|
||||
break;
|
||||
case GB_MBC3_RTC:
|
||||
LOAD_64LE(gb->memory.rtcLastLatch, 0, &state->memory.rtc.lastLatch);
|
||||
LOAD_64LE(memory->rtcLastLatch, 0, &state->memory.rtc.lastLatch);
|
||||
break;
|
||||
case GB_MBC7:
|
||||
memory->mbcState.mbc7.state = state->memory.mbc7.state;
|
||||
|
@ -836,6 +848,16 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
LOAD_16LE(memory->mbcState.mbc7.sr, 0, &state->memory.mbc7.sr);
|
||||
LOAD_32LE(memory->mbcState.mbc7.writable, 0, &state->memory.mbc7.writable);
|
||||
break;
|
||||
case GB_HuC3:
|
||||
LOAD_64LE(memory->rtcLastLatch, 0, &state->memory.huc3.lastLatch);
|
||||
memory->mbcState.huc3.index = state->memory.huc3.index;
|
||||
memory->mbcState.huc3.value = state->memory.huc3.value;
|
||||
memory->mbcState.huc3.mode = state->memory.huc3.mode;
|
||||
for (i = 0; i < 0x80; ++i) {
|
||||
memory->mbcState.huc3.registers[i * 2] = state->huc3Registers[i] & 0xF;
|
||||
memory->mbcState.huc3.registers[i * 2 + 1] = state->huc3Registers[i] >> 4;
|
||||
}
|
||||
break;
|
||||
case GB_MMM01:
|
||||
memory->mbcState.mmm01.locked = state->memory.mmm01.locked;
|
||||
memory->mbcState.mmm01.currentBank0 = state->memory.mmm01.bank0;
|
||||
|
|
Loading…
Reference in New Issue