mirror of https://github.com/mgba-emu/mgba.git
GBA Savedata: Store RTC data in savegames (closes #240)
This commit is contained in:
parent
e2e22e2218
commit
bb711d311f
3
CHANGES
3
CHANGES
|
@ -87,8 +87,9 @@ Misc:
|
|||
- GBA: Automatically skip BIOS if ROM has invalid logo
|
||||
- GBA: Refine multiboot detection (fixes mgba.io/i/2192)
|
||||
- GBA Cheats: Implement "never" type codes (closes mgba.io/i/915)
|
||||
- GBA Memory: Implement adjustable EWRAM waitstates (closes mgba.io/i/1276)
|
||||
- GBA DMA: Enhanced logging (closes mgba.io/i/2454)
|
||||
- GBA Memory: Implement adjustable EWRAM waitstates (closes mgba.io/i/1276)
|
||||
- GBA Savedata: Store RTC data in savegames (closes mgba.io/i/240)
|
||||
- GBA Video: Implement layer placement for OpenGL renderer (fixes mgba.io/i/1962)
|
||||
- GBA Video: Fix highlighting for sprites with mid-frame palette changes
|
||||
- mGUI: Add margin to right-aligned menu text (fixes mgba.io/i/871)
|
||||
|
|
|
@ -42,7 +42,7 @@ enum GPIODirection {
|
|||
GPIO_READ_WRITE = 1
|
||||
};
|
||||
|
||||
DECL_BITFIELD(RTCControl, uint32_t);
|
||||
DECL_BITFIELD(RTCControl, uint8_t);
|
||||
DECL_BIT(RTCControl, MinIRQ, 3);
|
||||
DECL_BIT(RTCControl, Hour24, 6);
|
||||
DECL_BIT(RTCControl, Poweroff, 7);
|
||||
|
@ -69,6 +69,8 @@ struct GBARTC {
|
|||
RTCCommandData command;
|
||||
RTCControl control;
|
||||
uint8_t time[7];
|
||||
time_t lastLatch;
|
||||
time_t offset;
|
||||
};
|
||||
|
||||
DECL_BITFIELD(GPIOPin, uint16_t);
|
||||
|
|
|
@ -72,6 +72,7 @@ struct GBASavedata {
|
|||
uint8_t* data;
|
||||
enum SavedataCommand command;
|
||||
struct VFile* vf;
|
||||
struct GBACartridgeHardware* gpio;
|
||||
|
||||
int mapMode;
|
||||
bool maskWriteback;
|
||||
|
@ -93,6 +94,12 @@ struct GBASavedata {
|
|||
enum FlashStateMachine flashState;
|
||||
};
|
||||
|
||||
struct GBASavedataRTCBuffer {
|
||||
uint8_t time[7];
|
||||
uint8_t control;
|
||||
uint64_t lastLatch;
|
||||
};
|
||||
|
||||
void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf);
|
||||
void GBASavedataDeinit(struct GBASavedata* savedata);
|
||||
|
||||
|
@ -116,6 +123,9 @@ void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32
|
|||
|
||||
void GBASavedataClean(struct GBASavedata* savedata, uint32_t frameCount);
|
||||
|
||||
void GBASavedataRTCRead(struct GBASavedata* savedata);
|
||||
void GBASavedataRTCWrite(struct GBASavedata* savedata);
|
||||
|
||||
struct GBASerializedState;
|
||||
void GBASavedataSerialize(const struct GBASavedata* savedata, struct GBASerializedState* state);
|
||||
void GBASavedataDeserialize(struct GBASavedata* savedata, const struct GBASerializedState* state);
|
||||
|
|
|
@ -348,7 +348,8 @@ struct GBASerializedState {
|
|||
int32_t rtcBits;
|
||||
int32_t rtcCommandActive;
|
||||
RTCCommandData rtcCommand;
|
||||
RTCControl rtcControl;
|
||||
uint8_t rtcControl;
|
||||
uint8_t reserved[3];
|
||||
uint8_t time[7];
|
||||
uint8_t devices;
|
||||
uint16_t gyroSample;
|
||||
|
|
|
@ -100,6 +100,9 @@ void GBAHardwareInitRTC(struct GBACartridgeHardware* hw) {
|
|||
hw->rtc.command = 0;
|
||||
hw->rtc.control = 0x40;
|
||||
memset(hw->rtc.time, 0, sizeof(hw->rtc.time));
|
||||
|
||||
hw->rtc.lastLatch = 0;
|
||||
hw->rtc.offset = 0;
|
||||
}
|
||||
|
||||
void _readPins(struct GBACartridgeHardware* hw) {
|
||||
|
@ -278,6 +281,9 @@ void _rtcUpdateClock(struct GBACartridgeHardware* hw) {
|
|||
} else {
|
||||
t = time(0);
|
||||
}
|
||||
hw->rtc.lastLatch = t;
|
||||
t -= hw->rtc.offset;
|
||||
|
||||
struct tm date;
|
||||
localtime_r(&t, &date);
|
||||
hw->rtc.time[0] = _rtcBCD(date.tm_year - 100);
|
||||
|
|
|
@ -77,6 +77,7 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) {
|
|||
gba->memory.savedata.timing = &gba->timing;
|
||||
gba->memory.savedata.vf = NULL;
|
||||
gba->memory.savedata.realVf = NULL;
|
||||
gba->memory.savedata.gpio = &gba->memory.hw;
|
||||
GBASavedataInit(&gba->memory.savedata, NULL);
|
||||
|
||||
gba->video.p = gba;
|
||||
|
|
|
@ -341,6 +341,7 @@ void GBAOverrideApply(struct GBA* gba, const struct GBACartridgeOverride* overri
|
|||
|
||||
if (override->hardware & HW_RTC) {
|
||||
GBAHardwareInitRTC(&gba->memory.hw);
|
||||
GBASavedataRTCRead(&gba->memory.savedata);
|
||||
}
|
||||
|
||||
if (override->hardware & HW_GYRO) {
|
||||
|
|
|
@ -575,6 +575,7 @@ void GBASavedataClean(struct GBASavedata* savedata, uint32_t frameCount) {
|
|||
if (savedata->mapMode & MAP_WRITE) {
|
||||
size_t size = GBASavedataSize(savedata);
|
||||
if (savedata->data && savedata->vf->sync(savedata->vf, savedata->data, size)) {
|
||||
GBASavedataRTCWrite(savedata);
|
||||
mLOG(GBA_SAVE, INFO, "Savedata synced");
|
||||
} else {
|
||||
mLOG(GBA_SAVE, INFO, "Savedata failed to sync!");
|
||||
|
@ -583,6 +584,58 @@ void GBASavedataClean(struct GBASavedata* savedata, uint32_t frameCount) {
|
|||
}
|
||||
}
|
||||
|
||||
void GBASavedataRTCWrite(struct GBASavedata* savedata) {
|
||||
if (!(savedata->gpio->devices & HW_RTC)) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct GBASavedataRTCBuffer buffer;
|
||||
|
||||
memcpy(&buffer.time, savedata->gpio->rtc.time, 7);
|
||||
buffer.control = savedata->gpio->rtc.control;
|
||||
STORE_64LE(savedata->gpio->rtc.lastLatch, 0, &buffer.lastLatch);
|
||||
|
||||
size_t size = GBASavedataSize(savedata) & ~0xFF;
|
||||
savedata->vf->seek(savedata->vf, size, SEEK_SET);
|
||||
savedata->vf->write(savedata->vf, &buffer, sizeof(buffer));
|
||||
}
|
||||
|
||||
static uint8_t _unBCD(uint8_t byte) {
|
||||
return (byte >> 4) * 10 + (byte & 0xF);
|
||||
}
|
||||
|
||||
void GBASavedataRTCRead(struct GBASavedata* savedata) {
|
||||
struct GBASavedataRTCBuffer buffer;
|
||||
|
||||
size_t size = GBASavedataSize(savedata) & ~0xFF;
|
||||
savedata->vf->seek(savedata->vf, size, SEEK_SET);
|
||||
size = savedata->vf->read(savedata->vf, &buffer, sizeof(buffer));
|
||||
if (size < sizeof(buffer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(savedata->gpio->rtc.time, &buffer.time, 7);
|
||||
|
||||
// Older FlashGBX sets this to 0x01 instead of the control flag.
|
||||
// Since that bit is invalid on hardware, we can check for != 0x01
|
||||
// to see if it's a valid value instead of just a filler value.
|
||||
if (buffer.control != 1) {
|
||||
savedata->gpio->rtc.control = buffer.control;
|
||||
}
|
||||
LOAD_64LE(savedata->gpio->rtc.lastLatch, 0, &buffer.lastLatch);
|
||||
|
||||
struct tm date;
|
||||
date.tm_year = _unBCD(savedata->gpio->rtc.time[0]) + 100;
|
||||
date.tm_mon = _unBCD(savedata->gpio->rtc.time[1]) - 1;
|
||||
date.tm_mday = _unBCD(savedata->gpio->rtc.time[2]);
|
||||
date.tm_hour = _unBCD(savedata->gpio->rtc.time[4]);
|
||||
date.tm_min = _unBCD(savedata->gpio->rtc.time[5]);
|
||||
date.tm_sec = _unBCD(savedata->gpio->rtc.time[6]);
|
||||
date.tm_isdst = -1;
|
||||
|
||||
savedata->gpio->rtc.offset = savedata->gpio->rtc.lastLatch - mktime(&date);
|
||||
}
|
||||
|
||||
void GBASavedataSerialize(const struct GBASavedata* savedata, struct GBASerializedState* state) {
|
||||
state->savedata.type = savedata->type;
|
||||
state->savedata.command = savedata->command;
|
||||
|
|
Loading…
Reference in New Issue