GB: Fix RTC saving
Jeffrey Pfau jeffrey@endrift.com
Fri, 16 Sep 2016 17:15:17 -0700
5 files changed,
120 insertions(+),
26 deletions(-)
M
src/gb/gb.c
→
src/gb/gb.c
@@ -6,6 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gb.h" #include "gb/io.h" +#include "gb/mbc.h" #include "core/core.h" #include "core/cheats.h"@@ -110,6 +111,9 @@
static void GBSramDeinit(struct GB* gb) { if (gb->sramVf) { gb->sramVf->unmap(gb->sramVf, gb->memory.sram, gb->sramSize); + if (gb->memory.mbcType == GB_MBC3_RTC) { + GBMBCRTCWrite(gb); + } gb->sramVf->close(gb->sramVf); gb->sramVf = 0; } else if (gb->memory.sram) {@@ -126,21 +130,23 @@ mLOG(GB, INFO, "Resizing SRAM to %"PRIz"u bytes", size);
struct VFile* vf = gb->sramVf; if (vf) { if (vf == gb->sramRealVf) { - if (vf->size(vf) >= 0 && (size_t) vf->size(vf) < size) { + ssize_t vfSize = vf->size(vf); + if (vfSize >= 0 && (size_t) vfSize < size) { uint8_t extdataBuffer[0x100]; - if (vf->size(vf) & 0xFF) { - // Copy over appended data, e.g. RTC data - memcpy(extdataBuffer, &gb->memory.sram[gb->sramSize - (vf->size(vf) & 0xFF)], vf->size(vf) & 0xFF); + if (vfSize & 0xFF) { + vf->seek(vf, -(vfSize & 0xFF), SEEK_END); + vf->read(vf, extdataBuffer, vfSize & 0xFF); } if (gb->memory.sram) { vf->unmap(vf, gb->memory.sram, gb->sramSize); } - vf->truncate(vf, size); + vf->truncate(vf, size + (vfSize & 0xFF)); + if (vfSize & 0xFF) { + vf->seek(vf, size, SEEK_SET); + vf->write(vf, extdataBuffer, vfSize & 0xFF); + } gb->memory.sram = vf->map(vf, size, MAP_WRITE); memset(&gb->memory.sram[gb->sramSize], 0xFF, size - gb->sramSize); - if (size & 0xFF) { - memcpy(&gb->memory.sram[gb->sramSize - (size & 0xFF)], extdataBuffer, size & 0xFF); - } } else if (size > gb->sramSize || !gb->memory.sram) { if (gb->memory.sram) { vf->unmap(vf, gb->memory.sram, gb->sramSize);@@ -188,6 +194,9 @@ if (!(gb->sramDirty & GB_SRAM_DIRT_SEEN)) {
gb->sramDirty |= GB_SRAM_DIRT_SEEN; } } else if ((gb->sramDirty & GB_SRAM_DIRT_SEEN) && frameCount - gb->sramDirtAge > CLEANUP_THRESHOLD) { + if (gb->memory.mbcType == GB_MBC3_RTC) { + GBMBCRTCWrite(gb); + } gb->sramDirty = 0; if (gb->memory.sram && gb->sramVf->sync(gb->sramVf, gb->memory.sram, gb->sramSize)) { mLOG(GB_MEM, INFO, "Savedata synced");
M
src/gb/mbc.c
→
src/gb/mbc.c
@@ -8,7 +8,7 @@
#include "gb/gb.h" #include "gb/memory.h" #include "gb/memory.h" -#include "util/formatting.h" +#include "util/vfs.h" #include <time.h>@@ -43,7 +43,7 @@ }
void GBMBCSwitchSramBank(struct GB* gb, int bank) { size_t bankStart = bank * GB_SIZE_EXTERNAL_RAM; - GBResizeSram(gb, (bank + 1) * GB_SIZE_EXTERNAL_RAM + (gb->sramSize & 0xFF)); + GBResizeSram(gb, (bank + 1) * GB_SIZE_EXTERNAL_RAM); gb->memory.sramBank = &gb->memory.sram[bankStart]; gb->memory.sramCurrentBank = bank; }@@ -129,9 +129,7 @@ gb->sramSize = 0x200;
break; case GB_MBC3: gb->memory.mbc = _GBMBC3; - gb->sramSize += 0x48; - break; - default: + default: mLOG(GB_MBC, WARN, "Unknown MBC type: %02X", cart->type); // Fall through case GB_MBC5:@@ -156,6 +154,7 @@ case GB_HuC3:
gb->memory.mbc = _GBHuC3; break; case GB_MBC3_RTC: + memset(gb->memory.rtcRegs, 0, sizeof(gb->memory.rtcRegs)); gb->memory.mbc = _GBMBC3; break; case GB_MBC5_RUMBLE:@@ -164,11 +163,14 @@ break;
} GBResizeSram(gb, gb->sramSize); + + if (gb->memory.mbcType == GB_MBC3_RTC) { + GBMBCRTCRead(gb); + } } -static void _latchRtc(struct GBMemory* memory) { +static void _latchRtc(struct mRTCSource* rtc, uint8_t* rtcRegs, time_t* rtcLastLatch) { time_t t; - struct mRTCSource* rtc = memory->rtc; if (rtc) { if (rtc->sample) { rtc->sample(rtc);@@ -177,14 +179,30 @@ t = rtc->unixTime(rtc);
} else { t = time(0); } - struct tm date; - localtime_r(&t, &date); - memory->rtcRegs[0] = date.tm_sec; - memory->rtcRegs[1] = date.tm_min; - memory->rtcRegs[2] = date.tm_hour; - memory->rtcRegs[3] = date.tm_yday; // TODO: Persist day counter - memory->rtcRegs[4] &= 0xF0; - memory->rtcRegs[4] |= date.tm_yday >> 8; + time_t currentLatch = t; + t -= *rtcLastLatch; + *rtcLastLatch = currentLatch; + + unsigned diff; + diff = rtcRegs[0] + t % 60; + rtcRegs[0] = diff % 60; + t = t / 60 + diff / 60; + + diff = rtcRegs[1] + t % 60; + rtcRegs[1] = diff % 60; + t = t / 60 + diff / 60; + + diff = rtcRegs[2] + t % 24; + rtcRegs[2] = diff % 24; + t = t / 24 + diff / 24; + + diff = rtcRegs[3] + ((rtcRegs[4] & 1) << 8) + (t & 0x1FF); + rtcRegs[3] = diff; + rtcRegs[4] &= 0xFE; + rtcRegs[4] |= (diff >> 8) & 1; + if (diff & 0x200) { + rtcRegs[4] |= 0x80; + } } void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) {@@ -304,7 +322,7 @@ case 0x3:
if (memory->rtcLatched && value == 0) { memory->rtcLatched = false; } else if (!memory->rtcLatched && value == 1) { - _latchRtc(memory); + _latchRtc(gb->memory.rtc, gb->memory.rtcRegs, &gb->memory.rtcLastLatch); memory->rtcLatched = true; } break;@@ -588,3 +606,50 @@ mLOG(GB_MBC, STUB, "HuC-3 unknown address: %04X:%02X", address, value);
break; } } + +void GBMBCRTCRead(struct GB* gb) { + struct GBMBCRTCSaveBuffer rtcBuffer; + struct VFile* vf = gb->sramVf; + ssize_t end = vf->seek(vf, -sizeof(rtcBuffer), SEEK_END); + switch (end & 0x1FFF) { + case 0: + break; + case 0x1FFC: + vf->seek(vf, -sizeof(rtcBuffer) - 4, SEEK_END); + break; + default: + return; + } + vf->read(vf, &rtcBuffer, sizeof(rtcBuffer)); + + LOAD_32LE(gb->memory.rtcRegs[0], 0, &rtcBuffer.latchedSec); + LOAD_32LE(gb->memory.rtcRegs[1], 0, &rtcBuffer.latchedMin); + LOAD_32LE(gb->memory.rtcRegs[2], 0, &rtcBuffer.latchedHour); + LOAD_32LE(gb->memory.rtcRegs[3], 0, &rtcBuffer.latchedDays); + LOAD_32LE(gb->memory.rtcRegs[4], 0, &rtcBuffer.latchedDaysHi); + LOAD_64LE(gb->memory.rtcLastLatch, 0, &rtcBuffer.unixTime); +} + +void GBMBCRTCWrite(struct GB* gb) { + uint8_t rtcRegs[5]; + memcpy(rtcRegs, gb->memory.rtcRegs, sizeof(rtcRegs)); + time_t rtcLastLatch = gb->memory.rtcLastLatch; + _latchRtc(gb->memory.rtc, rtcRegs, &rtcLastLatch); + + struct GBMBCRTCSaveBuffer rtcBuffer; + STORE_32LE(rtcRegs[0], 0, &rtcBuffer.sec); + STORE_32LE(rtcRegs[1], 0, &rtcBuffer.min); + STORE_32LE(rtcRegs[2], 0, &rtcBuffer.hour); + STORE_32LE(rtcRegs[3], 0, &rtcBuffer.days); + STORE_32LE(rtcRegs[4], 0, &rtcBuffer.daysHi); + STORE_32LE(gb->memory.rtcRegs[0], 0, &rtcBuffer.latchedSec); + STORE_32LE(gb->memory.rtcRegs[1], 0, &rtcBuffer.latchedMin); + STORE_32LE(gb->memory.rtcRegs[2], 0, &rtcBuffer.latchedHour); + STORE_32LE(gb->memory.rtcRegs[3], 0, &rtcBuffer.latchedDays); + STORE_32LE(gb->memory.rtcRegs[4], 0, &rtcBuffer.latchedDaysHi); + STORE_64LE(rtcLastLatch, 0, &rtcBuffer.unixTime); + + struct VFile* vf = gb->sramVf; + vf->seek(vf, gb->sramSize, SEEK_SET); + vf->write(vf, &rtcBuffer, sizeof(rtcBuffer)); +}
M
src/gb/mbc.h
→
src/gb/mbc.h
@@ -18,6 +18,22 @@ void GBMBCInit(struct GB* gb);
void GBMBCSwitchBank(struct GBMemory* memory, int bank); void GBMBCSwitchSramBank(struct GB* gb, int bank); +struct GBMBCRTCSaveBuffer { + uint32_t sec; + uint32_t min; + uint32_t hour; + uint32_t days; + uint32_t daysHi; + uint32_t latchedSec; + uint32_t latchedMin; + uint32_t latchedHour; + uint32_t latchedDays; + uint32_t latchedDaysHi; + uint64_t unixTime; +}; +void GBMBCRTCRead(struct GB* gb); +void GBMBCRTCWrite(struct GB* gb); + uint8_t GBMBC7Read(struct GBMemory*, uint16_t address); void GBMBC7Write(struct GBMemory*, uint16_t address, uint8_t value);
M
src/gb/memory.h
→
src/gb/memory.h
@@ -142,6 +142,7 @@ bool rtcAccess;
int activeRtcReg; bool rtcLatched; uint8_t rtcRegs[5]; + time_t rtcLastLatch; struct mRTCSource* rtc; struct mRotationSource* rotation; struct mRumble* rumble;
M
src/gb/serialize.h
→
src/gb/serialize.h
@@ -31,8 +31,8 @@ * | 0x00024: D register
* | 0x00025: E register * | 0x00026: H register * | 0x00027: L register - * | 0x00028 - 0z00029: SP register - * | 0x0002A - 0z0002B: PC register + * | 0x00028 - 0x00029: SP register + * | 0x0002A - 0x0002B: PC register * | 0x0002C - 0x0002F: Cycles since last event * | 0x00030 - 0x00033: Cycles until next event * | 0x00034 - 0x00035: Reserved (current instruction)@@ -324,6 +324,9 @@ union {
struct { uint32_t mode; } mbc1; + struct { + uint64_t lastLatch; + } rtc; struct { int8_t machineState; GBMBC7Field field;