GB: Fix RTC saving

This commit is contained in:
Jeffrey Pfau 2016-09-16 17:15:17 -07:00
parent ab70312c0a
commit 1c9db6058c
5 changed files with 120 additions and 26 deletions

View File

@ -6,6 +6,7 @@
#include "gb.h"
#include "gb/io.h"
#include "gb/mbc.h"
#include "core/core.h"
#include "core/cheats.h"
@ -110,6 +111,9 @@ bool GBLoadSave(struct GB* gb, struct VFile* vf) {
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 @@ void GBResizeSram(struct GB* gb, size_t 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 @@ void GBSramClean(struct GB* gb, uint32_t frameCount) {
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");

View File

@ -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 GBMBCSwitchBank(struct GBMemory* memory, int bank) {
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 @@ void GBMBCInit(struct GB* gb) {
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 @@ void GBMBCInit(struct GB* gb) {
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 @@ void GBMBCInit(struct GB* gb) {
}
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 @@ static void _latchRtc(struct GBMemory* memory) {
} 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 @@ void _GBMBC3(struct GB* gb, uint16_t address, uint8_t value) {
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 @@ void _GBHuC3(struct GB* gb, uint16_t address, uint8_t 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));
}

View File

@ -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);

View File

@ -142,6 +142,7 @@ struct GBMemory {
int activeRtcReg;
bool rtcLatched;
uint8_t rtcRegs[5];
time_t rtcLastLatch;
struct mRTCSource* rtc;
struct mRotationSource* rotation;
struct mRumble* rumble;

View File

@ -31,8 +31,8 @@ mLOG_DECLARE_CATEGORY(GB_STATE);
* | 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 @@ struct GBSerializedState {
struct {
uint32_t mode;
} mbc1;
struct {
uint64_t lastLatch;
} rtc;
struct {
int8_t machineState;
GBMBC7Field field;