mirror of https://github.com/mgba-emu/mgba.git
GB: Fix RTC saving
This commit is contained in:
parent
ab70312c0a
commit
1c9db6058c
25
src/gb/gb.c
25
src/gb/gb.c
|
@ -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");
|
||||
|
|
97
src/gb/mbc.c
97
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 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));
|
||||
}
|
||||
|
|
16
src/gb/mbc.h
16
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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue