mirror of https://github.com/mgba-emu/mgba.git
GB MBC: Partial TAMA5 RTC
This commit is contained in:
parent
2a9f32a840
commit
bac42c9027
1
CHANGES
1
CHANGES
|
@ -78,6 +78,7 @@ Misc:
|
||||||
- FFmpeg: Support dynamic audio sample rate
|
- FFmpeg: Support dynamic audio sample rate
|
||||||
- GB Audio: Increase sample rate
|
- GB Audio: Increase sample rate
|
||||||
- GB MBC: Filter out MBC errors when cartridge is yanked (fixes mgba.io/i/2488)
|
- GB MBC: Filter out MBC errors when cartridge is yanked (fixes mgba.io/i/2488)
|
||||||
|
- GB MBC: Partially implement TAMA5 RTC
|
||||||
- GB Video: Add default SGB border
|
- GB Video: Add default SGB border
|
||||||
- GBA: Automatically skip BIOS if ROM has invalid logo
|
- GBA: Automatically skip BIOS if ROM has invalid logo
|
||||||
- GBA: Refine multiboot detection (fixes mgba.io/i/2192)
|
- GBA: Refine multiboot detection (fixes mgba.io/i/2192)
|
||||||
|
|
|
@ -65,7 +65,7 @@ The following mappers are partially supported:
|
||||||
- MBC6 (missing flash memory write support)
|
- MBC6 (missing flash memory write support)
|
||||||
- MMM01
|
- MMM01
|
||||||
- Pocket Cam
|
- Pocket Cam
|
||||||
- TAMA5 (missing RTC support)
|
- TAMA5 (incomplete RTC support)
|
||||||
- HuC-1 (missing IR support)
|
- HuC-1 (missing IR support)
|
||||||
- HuC-3 (missing IR support)
|
- HuC-3 (missing IR support)
|
||||||
- Sachen MMC2 (missing alternate wiring support)
|
- Sachen MMC2 (missing alternate wiring support)
|
||||||
|
|
|
@ -50,12 +50,23 @@ struct GBMBCHuC3SaveBuffer {
|
||||||
uint64_t latchedUnix;
|
uint64_t latchedUnix;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct GBMBCTAMA5SaveBuffer {
|
||||||
|
uint8_t rtcTimerPage[0x8];
|
||||||
|
uint8_t rtcAlarmPage[0x8];
|
||||||
|
uint8_t rtcFreePage0[0x8];
|
||||||
|
uint8_t rtcFreePage1[0x8];
|
||||||
|
uint64_t latchedUnix;
|
||||||
|
};
|
||||||
|
|
||||||
void GBMBCRTCRead(struct GB* gb);
|
void GBMBCRTCRead(struct GB* gb);
|
||||||
void GBMBCRTCWrite(struct GB* gb);
|
void GBMBCRTCWrite(struct GB* gb);
|
||||||
|
|
||||||
void GBMBCHuC3Read(struct GB* gb);
|
void GBMBCHuC3Read(struct GB* gb);
|
||||||
void GBMBCHuC3Write(struct GB* gb);
|
void GBMBCHuC3Write(struct GB* gb);
|
||||||
|
|
||||||
|
void GBMBCTAMA5Read(struct GB* gb);
|
||||||
|
void GBMBCTAMA5Write(struct GB* gb);
|
||||||
|
|
||||||
CXX_GUARD_END
|
CXX_GUARD_END
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -94,7 +94,7 @@ enum GBTAMA5Register {
|
||||||
GBTAMA5_BANK_HI = 0x1,
|
GBTAMA5_BANK_HI = 0x1,
|
||||||
GBTAMA5_WRITE_LO = 0x4,
|
GBTAMA5_WRITE_LO = 0x4,
|
||||||
GBTAMA5_WRITE_HI = 0x5,
|
GBTAMA5_WRITE_HI = 0x5,
|
||||||
GBTAMA5_CS = 0x6,
|
GBTAMA5_ADDR_HI = 0x6,
|
||||||
GBTAMA5_ADDR_LO = 0x7,
|
GBTAMA5_ADDR_LO = 0x7,
|
||||||
GBTAMA5_MAX = 0x8,
|
GBTAMA5_MAX = 0x8,
|
||||||
GBTAMA5_ACTIVE = 0xA,
|
GBTAMA5_ACTIVE = 0xA,
|
||||||
|
@ -102,6 +102,33 @@ enum GBTAMA5Register {
|
||||||
GBTAMA5_READ_HI = 0xD,
|
GBTAMA5_READ_HI = 0xD,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum GBTAMA6RTCRegister {
|
||||||
|
GBTAMA6_RTC_PA0_SECOND_1 = 0x0,
|
||||||
|
GBTAMA6_RTC_PA0_SECOND_10 = 0x1,
|
||||||
|
GBTAMA6_RTC_PA0_MINUTE_1 = 0x2,
|
||||||
|
GBTAMA6_RTC_PA0_MINUTE_10 = 0x3,
|
||||||
|
GBTAMA6_RTC_PA0_HOUR_1 = 0x4,
|
||||||
|
GBTAMA6_RTC_PA0_HOUR_10 = 0x5,
|
||||||
|
GBTAMA6_RTC_PA0_WEEK = 0x6,
|
||||||
|
GBTAMA6_RTC_PA0_DAY_1 = 0x7,
|
||||||
|
GBTAMA6_RTC_PA0_DAY_10 = 0x8,
|
||||||
|
GBTAMA6_RTC_PA0_MONTH_1 = 0x9,
|
||||||
|
GBTAMA6_RTC_PA0_MONTH_10 = 0xA,
|
||||||
|
GBTAMA6_RTC_PA0_YEAR_1 = 0xB,
|
||||||
|
GBTAMA6_RTC_PA0_YEAR_10 = 0xC,
|
||||||
|
GBTAMA6_RTC_PAGE = 0xD,
|
||||||
|
GBTAMA6_RTC_TEST = 0xE,
|
||||||
|
GBTAMA6_RTC_RESET = 0xF,
|
||||||
|
GBTAMA6_RTC_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
enum GBTAMA6Command {
|
||||||
|
GBTAMA6_MINUTE_WRITE = 0x4,
|
||||||
|
GBTAMA6_HOUR_WRITE = 0x5,
|
||||||
|
GBTAMA6_MINUTE_READ = 0x6,
|
||||||
|
GBTAMA6_HOUR_READ = 0x7,
|
||||||
|
};
|
||||||
|
|
||||||
enum GBHuC3Register {
|
enum GBHuC3Register {
|
||||||
GBHUC3_RTC_MINUTES_LO = 0x10,
|
GBHUC3_RTC_MINUTES_LO = 0x10,
|
||||||
GBHUC3_RTC_MINUTES_MI = 0x11,
|
GBHUC3_RTC_MINUTES_MI = 0x11,
|
||||||
|
@ -180,6 +207,7 @@ struct GBPocketCamState {
|
||||||
struct GBTAMA5State {
|
struct GBTAMA5State {
|
||||||
uint8_t reg;
|
uint8_t reg;
|
||||||
uint8_t registers[GBTAMA5_MAX];
|
uint8_t registers[GBTAMA5_MAX];
|
||||||
|
uint8_t rtcTimerPage[GBTAMA6_RTC_MAX];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GBHuC3State {
|
struct GBHuC3State {
|
||||||
|
|
|
@ -406,6 +406,10 @@ struct GBSerializedState {
|
||||||
uint8_t locked;
|
uint8_t locked;
|
||||||
uint8_t bank0;
|
uint8_t bank0;
|
||||||
} mmm01;
|
} mmm01;
|
||||||
|
struct {
|
||||||
|
uint64_t lastLatch;
|
||||||
|
uint8_t reg;
|
||||||
|
} tama5;
|
||||||
struct {
|
struct {
|
||||||
uint64_t lastLatch;
|
uint64_t lastLatch;
|
||||||
uint8_t index;
|
uint8_t index;
|
||||||
|
@ -456,7 +460,13 @@ struct GBSerializedState {
|
||||||
|
|
||||||
uint32_t reserved2[0xA4];
|
uint32_t reserved2[0xA4];
|
||||||
|
|
||||||
uint8_t huc3Registers[0x80];
|
union {
|
||||||
|
uint8_t huc3Registers[0x80];
|
||||||
|
struct {
|
||||||
|
uint8_t registers[8];
|
||||||
|
uint8_t rtcTimerPage[8];
|
||||||
|
} tama5Registers;
|
||||||
|
};
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
uint8_t attributes[90];
|
uint8_t attributes[90];
|
||||||
|
|
|
@ -220,6 +220,8 @@ static void GBSramDeinit(struct GB* gb) {
|
||||||
GBMBCRTCWrite(gb);
|
GBMBCRTCWrite(gb);
|
||||||
} else if (gb->memory.mbcType == GB_HuC3) {
|
} else if (gb->memory.mbcType == GB_HuC3) {
|
||||||
GBMBCHuC3Write(gb);
|
GBMBCHuC3Write(gb);
|
||||||
|
} else if (gb->memory.mbcType == GB_TAMA5) {
|
||||||
|
GBMBCTAMA5Write(gb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gb->sramVf = NULL;
|
gb->sramVf = NULL;
|
||||||
|
@ -244,6 +246,8 @@ bool GBLoadSave(struct GB* gb, struct VFile* vf) {
|
||||||
GBMBCRTCRead(gb);
|
GBMBCRTCRead(gb);
|
||||||
} else if (gb->memory.mbcType == GB_HuC3) {
|
} else if (gb->memory.mbcType == GB_HuC3) {
|
||||||
GBMBCHuC3Read(gb);
|
GBMBCHuC3Read(gb);
|
||||||
|
} else if (gb->memory.mbcType == GB_TAMA5) {
|
||||||
|
GBMBCTAMA5Read(gb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return vf;
|
return vf;
|
||||||
|
@ -329,6 +333,8 @@ void GBSramClean(struct GB* gb, uint32_t frameCount) {
|
||||||
GBMBCRTCWrite(gb);
|
GBMBCRTCWrite(gb);
|
||||||
} else if (gb->memory.mbcType == GB_HuC3) {
|
} else if (gb->memory.mbcType == GB_HuC3) {
|
||||||
GBMBCHuC3Write(gb);
|
GBMBCHuC3Write(gb);
|
||||||
|
} else if (gb->memory.mbcType == GB_TAMA5) {
|
||||||
|
GBMBCTAMA5Write(gb);
|
||||||
}
|
}
|
||||||
if (gb->sramVf == gb->sramRealVf) {
|
if (gb->sramVf == gb->sramRealVf) {
|
||||||
if (gb->memory.sram && gb->sramVf->sync(gb->sramVf, gb->memory.sram, gb->sramSize)) {
|
if (gb->memory.sram && gb->sramVf->sync(gb->sramVf, gb->memory.sram, gb->sramSize)) {
|
||||||
|
|
245
src/gb/mbc.c
245
src/gb/mbc.c
|
@ -453,8 +453,6 @@ void GBMBCInit(struct GB* gb) {
|
||||||
gb->memory.mbcRead = _GBHuC3Read;
|
gb->memory.mbcRead = _GBHuC3Read;
|
||||||
break;
|
break;
|
||||||
case GB_TAMA5:
|
case GB_TAMA5:
|
||||||
mLOG(GB_MBC, WARN, "unimplemented MBC: TAMA5");
|
|
||||||
memset(gb->memory.rtcRegs, 0, sizeof(gb->memory.rtcRegs));
|
|
||||||
gb->memory.mbcWrite = _GBTAMA5;
|
gb->memory.mbcWrite = _GBTAMA5;
|
||||||
gb->memory.mbcRead = _GBTAMA5Read;
|
gb->memory.mbcRead = _GBTAMA5Read;
|
||||||
gb->sramSize = 0x20;
|
gb->sramSize = 0x20;
|
||||||
|
@ -540,6 +538,8 @@ void GBMBCInit(struct GB* gb) {
|
||||||
GBMBCRTCRead(gb);
|
GBMBCRTCRead(gb);
|
||||||
} else if (gb->memory.mbcType == GB_HuC3) {
|
} else if (gb->memory.mbcType == GB_HuC3) {
|
||||||
GBMBCHuC3Read(gb);
|
GBMBCHuC3Read(gb);
|
||||||
|
} else if (gb->memory.mbcType == GB_TAMA5) {
|
||||||
|
GBMBCTAMA5Read(gb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1514,6 +1514,150 @@ void _GBPocketCamCapture(struct GBMemory* memory) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const int _daysToMonth[] = {
|
||||||
|
[ 1] = 0,
|
||||||
|
[ 2] = 31,
|
||||||
|
[ 3] = 31 + 28,
|
||||||
|
[ 4] = 31 + 28 + 31,
|
||||||
|
[ 5] = 31 + 28 + 31 + 30,
|
||||||
|
[ 6] = 31 + 28 + 31 + 30 + 31,
|
||||||
|
[ 7] = 31 + 28 + 31 + 30 + 31 + 30,
|
||||||
|
[ 8] = 31 + 28 + 31 + 30 + 31 + 30 + 31,
|
||||||
|
[ 9] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
|
||||||
|
[10] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
|
||||||
|
[11] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
|
||||||
|
[12] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int _tama6DMYToDayOfYear(int day, int month, int year) {
|
||||||
|
if (month < 1 || month > 12) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
day += _daysToMonth[month];
|
||||||
|
if (month > 2 && (year % 4) == 0) {
|
||||||
|
++day;
|
||||||
|
}
|
||||||
|
return day;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _tama6DayOfYearToMonth(int day, int year) {
|
||||||
|
int month;
|
||||||
|
for (month = 1; month < 12; ++month) {
|
||||||
|
if (day <= _daysToMonth[month + 1]) {
|
||||||
|
return month;
|
||||||
|
}
|
||||||
|
if (month == 2 && year % 4 == 0) {
|
||||||
|
if (day == 60) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
--day;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _tama6DayOfYearToDayOfMonth(int day, int year) {
|
||||||
|
int month;
|
||||||
|
for (month = 1; month < 12; ++month) {
|
||||||
|
if (day <= _daysToMonth[month + 1]) {
|
||||||
|
return day - _daysToMonth[month];
|
||||||
|
}
|
||||||
|
if (month == 2 && year % 4 == 0) {
|
||||||
|
if (day == 60) {
|
||||||
|
return 29;
|
||||||
|
}
|
||||||
|
--day;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return day - _daysToMonth[12];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _latchTAMA6Rtc(struct mRTCSource* rtc, uint8_t* timerRegs, time_t* rtcLastLatch) {
|
||||||
|
time_t t;
|
||||||
|
if (rtc) {
|
||||||
|
if (rtc->sample) {
|
||||||
|
rtc->sample(rtc);
|
||||||
|
}
|
||||||
|
t = rtc->unixTime(rtc);
|
||||||
|
} else {
|
||||||
|
t = time(0);
|
||||||
|
}
|
||||||
|
time_t currentLatch = t;
|
||||||
|
t -= *rtcLastLatch;
|
||||||
|
*rtcLastLatch = currentLatch;
|
||||||
|
if (!t) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t diff;
|
||||||
|
diff = timerRegs[GBTAMA6_RTC_PA0_SECOND_1] + timerRegs[GBTAMA6_RTC_PA0_SECOND_10] * 10 + t % 60;
|
||||||
|
if (diff < 0) {
|
||||||
|
diff += 60;
|
||||||
|
t -= 60;
|
||||||
|
}
|
||||||
|
timerRegs[GBTAMA6_RTC_PA0_SECOND_1] = diff % 10;
|
||||||
|
timerRegs[GBTAMA6_RTC_PA0_SECOND_10] = (diff % 60) / 10;
|
||||||
|
t /= 60;
|
||||||
|
t += diff / 60;
|
||||||
|
|
||||||
|
diff = timerRegs[GBTAMA6_RTC_PA0_MINUTE_1] + timerRegs[GBTAMA6_RTC_PA0_MINUTE_10] * 10 + t % 60;
|
||||||
|
if (diff < 0) {
|
||||||
|
diff += 60;
|
||||||
|
t -= 60;
|
||||||
|
}
|
||||||
|
timerRegs[GBTAMA6_RTC_PA0_MINUTE_1] = diff % 10;
|
||||||
|
timerRegs[GBTAMA6_RTC_PA0_MINUTE_10] = (diff % 60) / 10;
|
||||||
|
t /= 60;
|
||||||
|
t += diff / 60;
|
||||||
|
|
||||||
|
diff = timerRegs[GBTAMA6_RTC_PA0_HOUR_1] + timerRegs[GBTAMA6_RTC_PA0_HOUR_10] * 10 + t % 24;
|
||||||
|
if (diff < 0) {
|
||||||
|
diff += 24;
|
||||||
|
t -= 24;
|
||||||
|
}
|
||||||
|
timerRegs[GBTAMA6_RTC_PA0_HOUR_1] = (diff % 24) % 10;
|
||||||
|
timerRegs[GBTAMA6_RTC_PA0_HOUR_10] = (diff % 24) / 10;
|
||||||
|
t /= 24;
|
||||||
|
t += diff / 24;
|
||||||
|
|
||||||
|
int day = timerRegs[GBTAMA6_RTC_PA0_DAY_1] + timerRegs[GBTAMA6_RTC_PA0_DAY_10] * 10;
|
||||||
|
int month = timerRegs[GBTAMA6_RTC_PA0_MONTH_1] + timerRegs[GBTAMA6_RTC_PA0_MONTH_10] * 10;
|
||||||
|
int year = timerRegs[GBTAMA6_RTC_PA0_YEAR_1] + timerRegs[GBTAMA6_RTC_PA0_YEAR_10] * 10;
|
||||||
|
int dayInYear = _tama6DMYToDayOfYear(day, month, year);
|
||||||
|
diff = dayInYear + t;
|
||||||
|
while (diff <= 0) {
|
||||||
|
// Previous year
|
||||||
|
if (year % 4) {
|
||||||
|
diff += 365;
|
||||||
|
} else {
|
||||||
|
diff += 366;
|
||||||
|
}
|
||||||
|
--year;
|
||||||
|
}
|
||||||
|
while (diff > (year % 4 ? 365 : 366)) {
|
||||||
|
// Future year
|
||||||
|
if (year % 4) {
|
||||||
|
diff -= 365;
|
||||||
|
} else {
|
||||||
|
diff -= 366;
|
||||||
|
}
|
||||||
|
++year;
|
||||||
|
}
|
||||||
|
year %= 100;
|
||||||
|
|
||||||
|
day = _tama6DayOfYearToDayOfMonth(diff, year);
|
||||||
|
month = _tama6DayOfYearToMonth(diff, year);
|
||||||
|
|
||||||
|
timerRegs[GBTAMA6_RTC_PA0_DAY_1] = day % 10;
|
||||||
|
timerRegs[GBTAMA6_RTC_PA0_DAY_10] = day / 10;
|
||||||
|
|
||||||
|
timerRegs[GBTAMA6_RTC_PA0_MONTH_1] = month % 10;
|
||||||
|
timerRegs[GBTAMA6_RTC_PA0_MONTH_10] = month / 10;
|
||||||
|
|
||||||
|
timerRegs[GBTAMA6_RTC_PA0_YEAR_1] = year % 10;
|
||||||
|
timerRegs[GBTAMA6_RTC_PA0_YEAR_10] = year / 10;
|
||||||
|
}
|
||||||
|
|
||||||
void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value) {
|
void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value) {
|
||||||
struct GBMemory* memory = &gb->memory;
|
struct GBMemory* memory = &gb->memory;
|
||||||
struct GBTAMA5State* tama5 = &memory->mbcState.tama5;
|
struct GBTAMA5State* tama5 = &memory->mbcState.tama5;
|
||||||
|
@ -1524,8 +1668,9 @@ void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value) {
|
||||||
} else {
|
} else {
|
||||||
value &= 0xF;
|
value &= 0xF;
|
||||||
if (tama5->reg < GBTAMA5_MAX) {
|
if (tama5->reg < GBTAMA5_MAX) {
|
||||||
|
mLOG(GB_MBC, DEBUG, "TAMA5 write: %02X:%X", tama5->reg, value);
|
||||||
tama5->registers[tama5->reg] = value;
|
tama5->registers[tama5->reg] = value;
|
||||||
uint8_t address = ((tama5->registers[GBTAMA5_CS] << 4) & 0x10) | tama5->registers[GBTAMA5_ADDR_LO];
|
uint8_t address = ((tama5->registers[GBTAMA5_ADDR_HI] << 4) & 0x10) | tama5->registers[GBTAMA5_ADDR_LO];
|
||||||
uint8_t out = (tama5->registers[GBTAMA5_WRITE_HI] << 4) | tama5->registers[GBTAMA5_WRITE_LO];
|
uint8_t out = (tama5->registers[GBTAMA5_WRITE_HI] << 4) | tama5->registers[GBTAMA5_WRITE_LO];
|
||||||
switch (tama5->reg) {
|
switch (tama5->reg) {
|
||||||
case GBTAMA5_BANK_LO:
|
case GBTAMA5_BANK_LO:
|
||||||
|
@ -1534,18 +1679,36 @@ void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value) {
|
||||||
break;
|
break;
|
||||||
case GBTAMA5_WRITE_LO:
|
case GBTAMA5_WRITE_LO:
|
||||||
case GBTAMA5_WRITE_HI:
|
case GBTAMA5_WRITE_HI:
|
||||||
case GBTAMA5_CS:
|
case GBTAMA5_ADDR_HI:
|
||||||
break;
|
break;
|
||||||
case GBTAMA5_ADDR_LO:
|
case GBTAMA5_ADDR_LO:
|
||||||
switch (tama5->registers[GBTAMA5_CS] >> 1) {
|
switch (tama5->registers[GBTAMA5_ADDR_HI] >> 1) {
|
||||||
case 0x0: // RAM write
|
case 0x0: // RAM write
|
||||||
memory->sram[address] = out;
|
memory->sram[address] = out;
|
||||||
gb->sramDirty |= mSAVEDATA_DIRT_NEW;
|
gb->sramDirty |= mSAVEDATA_DIRT_NEW;
|
||||||
break;
|
break;
|
||||||
case 0x1: // RAM read
|
case 0x1: // RAM read
|
||||||
break;
|
break;
|
||||||
|
case 0x2: // Other commands
|
||||||
|
switch (address) {
|
||||||
|
case GBTAMA6_MINUTE_WRITE:
|
||||||
|
tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_1] = out & 0xF;
|
||||||
|
tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_10] = out >> 4;
|
||||||
|
break;
|
||||||
|
case GBTAMA6_HOUR_WRITE:
|
||||||
|
tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_1] = out & 0xF;
|
||||||
|
tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_10] = out >> 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x4: // RTC access
|
||||||
|
if (!(address & 1)) {
|
||||||
|
tama5->rtcTimerPage[out & 0xF] = out >> 4;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
mLOG(GB_MBC, STUB, "TAMA5 unknown address: %X-%02X:%02X", tama5->registers[GBTAMA5_CS] >> 1, address, out);
|
mLOG(GB_MBC, STUB, "TAMA5 unknown address: %02X:%02X", address, out);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -1571,18 +1734,41 @@ uint8_t _GBTAMA5Read(struct GBMemory* memory, uint16_t address) {
|
||||||
return 0xFF;
|
return 0xFF;
|
||||||
} else {
|
} else {
|
||||||
uint8_t value = 0xF0;
|
uint8_t value = 0xF0;
|
||||||
uint8_t address = ((tama5->registers[GBTAMA5_CS] << 4) & 0x10) | tama5->registers[GBTAMA5_ADDR_LO];
|
uint8_t address = ((tama5->registers[GBTAMA5_ADDR_HI] << 4) & 0x10) | tama5->registers[GBTAMA5_ADDR_LO];
|
||||||
switch (tama5->reg) {
|
switch (tama5->reg) {
|
||||||
case GBTAMA5_ACTIVE:
|
case GBTAMA5_ACTIVE:
|
||||||
return 0xF1;
|
return 0xF1;
|
||||||
case GBTAMA5_READ_LO:
|
case GBTAMA5_READ_LO:
|
||||||
case GBTAMA5_READ_HI:
|
case GBTAMA5_READ_HI:
|
||||||
switch (tama5->registers[GBTAMA5_CS] >> 1) {
|
switch (tama5->registers[GBTAMA5_ADDR_HI] >> 1) {
|
||||||
case 1:
|
case 0x1:
|
||||||
value = memory->sram[address];
|
value = memory->sram[address];
|
||||||
break;
|
break;
|
||||||
|
case 0x2:
|
||||||
|
mLOG(GB_MBC, STUB, "TAMA5 unknown read %s: %02X", tama5->reg == GBTAMA5_READ_HI ? "hi" : "lo", address);
|
||||||
|
_latchTAMA6Rtc(memory->rtc, tama5->rtcTimerPage, &memory->rtcLastLatch);
|
||||||
|
switch (address) {
|
||||||
|
case GBTAMA6_MINUTE_READ:
|
||||||
|
value = (tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_10] << 4) | tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_1];
|
||||||
|
break;
|
||||||
|
case GBTAMA6_HOUR_READ:
|
||||||
|
value = (tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_10] << 4) | tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_1];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
value = address;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x4:
|
||||||
|
if (tama5->reg == GBTAMA5_READ_HI) {
|
||||||
|
mLOG(GB_MBC, GAME_ERROR, "TAMA5 reading RTC incorrectly");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_latchTAMA6Rtc(memory->rtc, tama5->rtcTimerPage, &memory->rtcLastLatch);
|
||||||
|
value = tama5->rtcTimerPage[tama5->registers[GBTAMA5_WRITE_LO]];
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
mLOG(GB_MBC, STUB, "TAMA5 unknown read: %02X", tama5->reg);
|
mLOG(GB_MBC, STUB, "TAMA5 unknown read %s: %02X", tama5->reg == GBTAMA5_READ_HI ? "hi" : "lo", address);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (tama5->reg == GBTAMA5_READ_HI) {
|
if (tama5->reg == GBTAMA5_READ_HI) {
|
||||||
|
@ -2015,3 +2201,42 @@ void GBMBCHuC3Write(struct GB* gb) {
|
||||||
|
|
||||||
_appendSaveSuffix(gb, &buffer, sizeof(buffer));
|
_appendSaveSuffix(gb, &buffer, sizeof(buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GBMBCTAMA5Read(struct GB* gb) {
|
||||||
|
struct GBMBCTAMA5SaveBuffer buffer;
|
||||||
|
struct VFile* vf = gb->sramVf;
|
||||||
|
if (!vf) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vf->seek(vf, gb->sramSize, SEEK_SET);
|
||||||
|
if (vf->read(vf, &buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < 0x8; ++i) {
|
||||||
|
gb->memory.mbcState.tama5.rtcTimerPage[i * 2] = buffer.rtcTimerPage[i] & 0xF;
|
||||||
|
gb->memory.mbcState.tama5.rtcTimerPage[i * 2 + 1] = buffer.rtcTimerPage[i] >> 4;
|
||||||
|
}
|
||||||
|
LOAD_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBMBCTAMA5Write(struct GB* gb) {
|
||||||
|
struct VFile* vf = gb->sramVf;
|
||||||
|
if (!vf) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GBMBCTAMA5SaveBuffer buffer;
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < 8; ++i) {
|
||||||
|
buffer.rtcTimerPage[i] = gb->memory.mbcState.tama5.rtcTimerPage[i * 2] & 0xF;
|
||||||
|
buffer.rtcTimerPage[i] |= gb->memory.mbcState.tama5.rtcTimerPage[i * 2 + 1] << 4;
|
||||||
|
buffer.rtcAlarmPage[i] = 0;
|
||||||
|
buffer.rtcFreePage0[i] = 0;
|
||||||
|
buffer.rtcFreePage1[i] = 0;
|
||||||
|
}
|
||||||
|
STORE_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix);
|
||||||
|
|
||||||
|
_appendSaveSuffix(gb, &buffer, sizeof(buffer));
|
||||||
|
}
|
||||||
|
|
|
@ -765,6 +765,16 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) {
|
||||||
STORE_16LE(memory->mbcState.mbc7.sr, 0, &state->memory.mbc7.sr);
|
STORE_16LE(memory->mbcState.mbc7.sr, 0, &state->memory.mbc7.sr);
|
||||||
STORE_32LE(memory->mbcState.mbc7.writable, 0, &state->memory.mbc7.writable);
|
STORE_32LE(memory->mbcState.mbc7.writable, 0, &state->memory.mbc7.writable);
|
||||||
break;
|
break;
|
||||||
|
case GB_TAMA5:
|
||||||
|
STORE_64LE(memory->rtcLastLatch, 0, &state->memory.tama5.lastLatch);
|
||||||
|
state->memory.tama5.reg = memory->mbcState.tama5.reg;
|
||||||
|
for (i = 0; i < 8; ++i) {
|
||||||
|
state->tama5Registers.registers[i] = memory->mbcState.tama5.registers[i * 2] & 0xF;
|
||||||
|
state->tama5Registers.registers[i] |= memory->mbcState.tama5.registers[i * 2 + 1] << 4;
|
||||||
|
state->tama5Registers.rtcTimerPage[i] = memory->mbcState.tama5.rtcTimerPage[i * 2] & 0xF;
|
||||||
|
state->tama5Registers.rtcTimerPage[i] |= memory->mbcState.tama5.rtcTimerPage[i * 2 + 1] << 4;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case GB_HuC3:
|
case GB_HuC3:
|
||||||
STORE_64LE(memory->rtcLastLatch, 0, &state->memory.huc3.lastLatch);
|
STORE_64LE(memory->rtcLastLatch, 0, &state->memory.huc3.lastLatch);
|
||||||
state->memory.huc3.index = memory->mbcState.huc3.index;
|
state->memory.huc3.index = memory->mbcState.huc3.index;
|
||||||
|
@ -874,6 +884,16 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
||||||
LOAD_16LE(memory->mbcState.mbc7.sr, 0, &state->memory.mbc7.sr);
|
LOAD_16LE(memory->mbcState.mbc7.sr, 0, &state->memory.mbc7.sr);
|
||||||
LOAD_32LE(memory->mbcState.mbc7.writable, 0, &state->memory.mbc7.writable);
|
LOAD_32LE(memory->mbcState.mbc7.writable, 0, &state->memory.mbc7.writable);
|
||||||
break;
|
break;
|
||||||
|
case GB_TAMA5:
|
||||||
|
LOAD_64LE(memory->rtcLastLatch, 0, &state->memory.tama5.lastLatch);
|
||||||
|
memory->mbcState.tama5.reg = state->memory.tama5.reg;
|
||||||
|
for (i = 0; i < 8; ++i) {
|
||||||
|
memory->mbcState.tama5.registers[i * 2] = state->tama5Registers.registers[i] & 0xF;
|
||||||
|
memory->mbcState.tama5.registers[i * 2 + 1] = state->tama5Registers.registers[i] >> 4;
|
||||||
|
memory->mbcState.tama5.rtcTimerPage[i * 2] = state->tama5Registers.rtcTimerPage[i] & 0xF;
|
||||||
|
memory->mbcState.tama5.rtcTimerPage[i * 2 + 1] = state->tama5Registers.rtcTimerPage[i] >> 4;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case GB_HuC3:
|
case GB_HuC3:
|
||||||
LOAD_64LE(memory->rtcLastLatch, 0, &state->memory.huc3.lastLatch);
|
LOAD_64LE(memory->rtcLastLatch, 0, &state->memory.huc3.lastLatch);
|
||||||
memory->mbcState.huc3.index = state->memory.huc3.index;
|
memory->mbcState.huc3.index = state->memory.huc3.index;
|
||||||
|
|
Loading…
Reference in New Issue