Cross emulator compatibility with RTC saves

This commit is contained in:
Lior Halphon 2018-11-03 01:31:14 +02:00
parent 64922fff4b
commit c9d6a1381f
2 changed files with 116 additions and 27 deletions

103
Core/gb.c
View File

@ -195,6 +195,36 @@ int GB_load_rom(GB_gameboy_t *gb, const char *path)
return 0; return 0;
} }
typedef struct {
uint8_t seconds;
uint8_t padding1[3];
uint8_t minutes;
uint8_t padding2[3];
uint8_t hours;
uint8_t padding3[3];
uint8_t days;
uint8_t padding4[3];
uint8_t high;
uint8_t padding5[3];
} GB_vba_rtc_time_t;
typedef union {
struct __attribute__((packed)) {
GB_rtc_time_t rtc_real;
time_t last_rtc_second; /* Platform specific endianess and size */
} sameboy_legacy;
struct {
/* Used by VBA versions with 32-bit timestamp*/
GB_vba_rtc_time_t rtc_real, rtc_latched;
uint32_t last_rtc_second; /* Always little endian */
} vba32;
struct {
/* Used by BGB and VBA versions with 64-bit timestamp*/
GB_vba_rtc_time_t rtc_real, rtc_latched;
uint64_t last_rtc_second; /* Always little endian */
} vba64;
} GB_rtc_save_t;
int GB_save_battery(GB_gameboy_t *gb, const char *path) int GB_save_battery(GB_gameboy_t *gb, const char *path)
{ {
if (!gb->cartridge_type->has_battery) return 0; // Nothing to save. if (!gb->cartridge_type->has_battery) return 0; // Nothing to save.
@ -210,15 +240,27 @@ int GB_save_battery(GB_gameboy_t *gb, const char *path)
return EIO; return EIO;
} }
if (gb->cartridge_type->has_rtc) { if (gb->cartridge_type->has_rtc) {
if (fwrite(&gb->rtc_real, 1, sizeof(gb->rtc_real), f) != sizeof(gb->rtc_real)) { GB_rtc_save_t rtc_save = {{{{0,}},},};
rtc_save.vba64.rtc_real.seconds = gb->rtc_real.seconds;
rtc_save.vba64.rtc_real.minutes = gb->rtc_real.minutes;
rtc_save.vba64.rtc_real.hours = gb->rtc_real.hours;
rtc_save.vba64.rtc_real.days = gb->rtc_real.days;
rtc_save.vba64.rtc_real.high = gb->rtc_real.high;
rtc_save.vba64.rtc_latched.seconds = gb->rtc_latched.seconds;
rtc_save.vba64.rtc_latched.minutes = gb->rtc_latched.minutes;
rtc_save.vba64.rtc_latched.hours = gb->rtc_latched.hours;
rtc_save.vba64.rtc_latched.days = gb->rtc_latched.days;
rtc_save.vba64.rtc_latched.high = gb->rtc_latched.high;
#ifdef GB_BIG_ENDIAN
rtc_save.vba64.last_rtc_second = __builtin_bswap64(gb->last_rtc_second);
#else
rtc_save.vba64.last_rtc_second = gb->last_rtc_second;
#endif
if (fwrite(&rtc_save.vba64, 1, sizeof(rtc_save.vba64), f) != sizeof(rtc_save.vba64)) {
fclose(f); fclose(f);
return EIO; return EIO;
} }
if (fwrite(&gb->last_rtc_second, 1, sizeof(gb->last_rtc_second), f) != sizeof(gb->last_rtc_second)) {
fclose(f);
return EIO;
}
} }
errno = 0; errno = 0;
@ -238,14 +280,53 @@ void GB_load_battery(GB_gameboy_t *gb, const char *path)
goto reset_rtc; goto reset_rtc;
} }
if (fread(&gb->rtc_real, 1, sizeof(gb->rtc_real), f) != sizeof(gb->rtc_real)) { GB_rtc_save_t rtc_save;
switch (fread(&rtc_save, 1, sizeof(rtc_save), f)) {
case sizeof(rtc_save.sameboy_legacy):
memcpy(&gb->rtc_real, &rtc_save.sameboy_legacy.rtc_real, sizeof(gb->rtc_real));
memcpy(&gb->rtc_latched, &rtc_save.sameboy_legacy.rtc_real, sizeof(gb->rtc_real));
gb->last_rtc_second = rtc_save.sameboy_legacy.last_rtc_second;
break;
case sizeof(rtc_save.vba32):
gb->rtc_real.seconds = rtc_save.vba32.rtc_real.seconds;
gb->rtc_real.minutes = rtc_save.vba32.rtc_real.minutes;
gb->rtc_real.hours = rtc_save.vba32.rtc_real.hours;
gb->rtc_real.days = rtc_save.vba32.rtc_real.days;
gb->rtc_real.high = rtc_save.vba32.rtc_real.high;
gb->rtc_latched.seconds = rtc_save.vba32.rtc_latched.seconds;
gb->rtc_latched.minutes = rtc_save.vba32.rtc_latched.minutes;
gb->rtc_latched.hours = rtc_save.vba32.rtc_latched.hours;
gb->rtc_latched.days = rtc_save.vba32.rtc_latched.days;
gb->rtc_latched.high = rtc_save.vba32.rtc_latched.high;
#ifdef GB_BIG_ENDIAN
gb->last_rtc_second = __builtin_bswap32(rtc_save.vba32.last_rtc_second);
#else
gb->last_rtc_second = rtc_save.vba32.last_rtc_second;
#endif
break;
case sizeof(rtc_save.vba64):
gb->rtc_real.seconds = rtc_save.vba64.rtc_real.seconds;
gb->rtc_real.minutes = rtc_save.vba64.rtc_real.minutes;
gb->rtc_real.hours = rtc_save.vba64.rtc_real.hours;
gb->rtc_real.days = rtc_save.vba64.rtc_real.days;
gb->rtc_real.high = rtc_save.vba64.rtc_real.high;
gb->rtc_latched.seconds = rtc_save.vba64.rtc_latched.seconds;
gb->rtc_latched.minutes = rtc_save.vba64.rtc_latched.minutes;
gb->rtc_latched.hours = rtc_save.vba64.rtc_latched.hours;
gb->rtc_latched.days = rtc_save.vba64.rtc_latched.days;
gb->rtc_latched.high = rtc_save.vba64.rtc_latched.high;
#ifdef GB_BIG_ENDIAN
gb->last_rtc_second = __builtin_bswap64(rtc_save.vba64.last_rtc_second);
#else
gb->last_rtc_second = rtc_save.vba64.last_rtc_second;
#endif
break;
default:
goto reset_rtc; goto reset_rtc;
} }
if (fread(&gb->last_rtc_second, 1, sizeof(gb->last_rtc_second), f) != sizeof(gb->last_rtc_second)) {
goto reset_rtc;
}
if (gb->last_rtc_second > time(NULL)) { if (gb->last_rtc_second > time(NULL)) {
/* We must reset RTC here, or it will not advance. */ /* We must reset RTC here, or it will not advance. */
goto reset_rtc; goto reset_rtc;

View File

@ -29,6 +29,25 @@
#define GB_MODEL_CGB_FAMILY 0x200 #define GB_MODEL_CGB_FAMILY 0x200
#endif #endif
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define GB_BIG_ENDIAN
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define GB_LITTLE_ENDIAN
#else
#error Unable to detect endianess
#endif
typedef union {
struct {
uint8_t seconds;
uint8_t minutes;
uint8_t hours;
uint8_t days;
uint8_t high;
};
uint8_t data[5];
} GB_rtc_time_t;
typedef enum { typedef enum {
// GB_MODEL_DMG_0 = 0x000, // GB_MODEL_DMG_0 = 0x000,
@ -265,18 +284,16 @@ struct GB_gameboy_internal_s {
sp; sp;
}; };
struct { struct {
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #ifdef GB_BIG_ENDIAN
uint8_t a, f, uint8_t a, f,
b, c, b, c,
d, e, d, e,
h, l; h, l;
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #else
uint8_t f, a, uint8_t f, a,
c, b, c, b,
e, d, e, d,
l, h; l, h;
#else
#error Unable to detect endianess
#endif #endif
}; };
@ -389,17 +406,8 @@ struct GB_gameboy_internal_s {
/* RTC */ /* RTC */
GB_SECTION(rtc, GB_SECTION(rtc,
union { GB_rtc_time_t rtc_real, rtc_latched;
struct { uint64_t last_rtc_second;
uint8_t seconds;
uint8_t minutes;
uint8_t hours;
uint8_t days;
uint8_t high;
};
uint8_t data[5];
} rtc_real, rtc_latched;
time_t last_rtc_second;
bool rtc_latch; bool rtc_latch;
); );