From 048da6e6d16fed2fecdf7e94142a78320e630134 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 29 Jul 2023 22:27:50 +0300 Subject: [PATCH] Fixed a bug where accurate RTC emulation halted while SameBoy wasn't running --- Core/gb.c | 4 ++ Core/timing.c | 146 ++++++++++++++++++++++++++------------------------ Core/timing.h | 2 +- 3 files changed, 81 insertions(+), 71 deletions(-) diff --git a/Core/gb.c b/Core/gb.c index de84912..a04480b 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -1040,7 +1040,9 @@ void GB_load_battery_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t really RTC data. */ goto reset_rtc; } + GB_rtc_set_time(gb, time(NULL)); goto exit; + reset_rtc: gb->last_rtc_second = time(NULL); gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */ @@ -1150,7 +1152,9 @@ void GB_load_battery(GB_gameboy_t *gb, const char *path) really RTC data. */ goto reset_rtc; } + GB_rtc_set_time(gb, time(NULL)); goto exit; + reset_rtc: gb->last_rtc_second = time(NULL); gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */ diff --git a/Core/timing.c b/Core/timing.c index 993fbbd..b71a083 100644 --- a/Core/timing.c +++ b/Core/timing.c @@ -282,6 +282,81 @@ void GB_set_rtc_multiplier(GB_gameboy_t *gb, double multiplier) gb->rtc_second_length = GB_get_unmultiplied_clock_rate(gb) * 2 * multiplier; } +void GB_rtc_set_time(GB_gameboy_t *gb, uint64_t current_time) +{ + if (gb->cartridge_type->mbc_type == GB_HUC3) { + while (gb->last_rtc_second / 60 < current_time / 60) { + gb->last_rtc_second += 60; + gb->huc3.minutes++; + if (gb->huc3.minutes == 60 * 24) { + gb->huc3.days++; + gb->huc3.minutes = 0; + } + } + return; + } + + bool running = false; + if (gb->cartridge_type->mbc_type == GB_TPP1) { + running = gb->tpp1_mr4 & 0x4; + } + else { + running = (gb->rtc_real.high & 0x40) == 0; + } + + if (!running) return; + + while (gb->last_rtc_second + 60 * 60 * 24 < current_time) { + gb->last_rtc_second += 60 * 60 * 24; + if (gb->cartridge_type->mbc_type == GB_TPP1) { + if (++gb->rtc_real.tpp1.weekday == 7) { + gb->rtc_real.tpp1.weekday = 0; + if (++gb->rtc_real.tpp1.weeks == 0) { + gb->tpp1_mr4 |= 8; /* Overflow bit */ + } + } + } + else if (++gb->rtc_real.days == 0) { + if (gb->rtc_real.high & 1) { /* Bit 8 of days*/ + gb->rtc_real.high |= 0x80; /* Overflow bit */ + } + + gb->rtc_real.high ^= 1; + } + } + + while (gb->last_rtc_second < current_time) { + gb->last_rtc_second++; + if (++gb->rtc_real.seconds != 60) continue; + gb->rtc_real.seconds = 0; + + if (++gb->rtc_real.minutes != 60) continue; + gb->rtc_real.minutes = 0; + + if (gb->cartridge_type->mbc_type == GB_TPP1) { + if (++gb->rtc_real.tpp1.hours != 24) continue; + gb->rtc_real.tpp1.hours = 0; + if (++gb->rtc_real.tpp1.weekday != 7) continue; + gb->rtc_real.tpp1.weekday = 0; + if (++gb->rtc_real.tpp1.weeks == 0) { + gb->tpp1_mr4 |= 8; /* Overflow bit */ + } + } + else { + if (++gb->rtc_real.hours != 24) continue; + gb->rtc_real.hours = 0; + + if (++gb->rtc_real.days != 0) continue; + + if (gb->rtc_real.high & 1) { /* Bit 8 of days*/ + gb->rtc_real.high |= 0x80; /* Overflow bit */ + } + + gb->rtc_real.high ^= 1; + } + } +} + static void rtc_run(GB_gameboy_t *gb, uint8_t cycles) { if (likely(gb->cartridge_type->mbc_type != GB_HUC3 && !gb->cartridge_type->has_rtc)) return; @@ -307,76 +382,7 @@ static void rtc_run(GB_gameboy_t *gb, uint8_t cycles) break; } - if (gb->cartridge_type->mbc_type == GB_HUC3) { - while (gb->last_rtc_second / 60 < current_time / 60) { - gb->last_rtc_second += 60; - gb->huc3.minutes++; - if (gb->huc3.minutes == 60 * 24) { - gb->huc3.days++; - gb->huc3.minutes = 0; - } - } - return; - } - bool running = false; - if (gb->cartridge_type->mbc_type == GB_TPP1) { - running = gb->tpp1_mr4 & 0x4; - } - else { - running = (gb->rtc_real.high & 0x40) == 0; - } - - if (running) { /* is timer running? */ - while (gb->last_rtc_second + 60 * 60 * 24 < current_time) { - gb->last_rtc_second += 60 * 60 * 24; - if (gb->cartridge_type->mbc_type == GB_TPP1) { - if (++gb->rtc_real.tpp1.weekday == 7) { - gb->rtc_real.tpp1.weekday = 0; - if (++gb->rtc_real.tpp1.weeks == 0) { - gb->tpp1_mr4 |= 8; /* Overflow bit */ - } - } - } - else if (++gb->rtc_real.days == 0) { - if (gb->rtc_real.high & 1) { /* Bit 8 of days*/ - gb->rtc_real.high |= 0x80; /* Overflow bit */ - } - - gb->rtc_real.high ^= 1; - } - } - - while (gb->last_rtc_second < current_time) { - gb->last_rtc_second++; - if (++gb->rtc_real.seconds != 60) continue; - gb->rtc_real.seconds = 0; - - if (++gb->rtc_real.minutes != 60) continue; - gb->rtc_real.minutes = 0; - - if (gb->cartridge_type->mbc_type == GB_TPP1) { - if (++gb->rtc_real.tpp1.hours != 24) continue; - gb->rtc_real.tpp1.hours = 0; - if (++gb->rtc_real.tpp1.weekday != 7) continue; - gb->rtc_real.tpp1.weekday = 0; - if (++gb->rtc_real.tpp1.weeks == 0) { - gb->tpp1_mr4 |= 8; /* Overflow bit */ - } - } - else { - if (++gb->rtc_real.hours != 24) continue; - gb->rtc_real.hours = 0; - - if (++gb->rtc_real.days != 0) continue; - - if (gb->rtc_real.high & 1) { /* Bit 8 of days*/ - gb->rtc_real.high |= 0x80; /* Overflow bit */ - } - - gb->rtc_real.high ^= 1; - } - } - } + GB_rtc_set_time(gb, current_time); } static void camera_run(GB_gameboy_t *gb, uint8_t cycles) diff --git a/Core/timing.h b/Core/timing.h index b9ab4d7..b89bf16 100644 --- a/Core/timing.h +++ b/Core/timing.h @@ -20,7 +20,7 @@ internal bool GB_timing_sync_turbo(GB_gameboy_t *gb); /* Returns true if should internal void GB_timing_sync(GB_gameboy_t *gb); internal void GB_set_internal_div_counter(GB_gameboy_t *gb, uint16_t value); internal void GB_serial_master_edge(GB_gameboy_t *gb); - +internal void GB_rtc_set_time(GB_gameboy_t *gb, uint64_t time); #define GB_SLEEP(gb, unit, state, cycles) do {\ (gb)->unit##_cycles -= (cycles) * __state_machine_divisor; \