Fixed a bug where accurate RTC emulation halted while SameBoy wasn't running

This commit is contained in:
Lior Halphon 2023-07-29 22:27:50 +03:00
parent 4ab256d896
commit 048da6e6d1
3 changed files with 81 additions and 71 deletions

View File

@ -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. */

View File

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

View File

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