Support for RTC latching. Fixes #4.

This commit is contained in:
Lior Halphon 2016-08-21 22:33:57 +03:00
parent 92c2b22735
commit ee4907949b
4 changed files with 31 additions and 26 deletions

View File

@ -369,7 +369,7 @@ 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_data, 1, sizeof(gb->rtc_data), f) != sizeof(gb->rtc_data)) { if (fwrite(&gb->rtc_real, 1, sizeof(gb->rtc_real), f) != sizeof(gb->rtc_real)) {
fclose(f); fclose(f);
return EIO; return EIO;
} }
@ -397,7 +397,7 @@ void GB_load_battery(GB_gameboy_t *gb, const char *path)
goto reset_rtc; goto reset_rtc;
} }
if (fread(gb->rtc_data, 1, sizeof(gb->rtc_data), f) != sizeof(gb->rtc_data)) { if (fread(&gb->rtc_real, 1, sizeof(gb->rtc_real), f) != sizeof(gb->rtc_real)) {
goto reset_rtc; goto reset_rtc;
} }
@ -418,7 +418,7 @@ void GB_load_battery(GB_gameboy_t *gb, const char *path)
goto exit; goto exit;
reset_rtc: reset_rtc:
gb->last_rtc_second = time(NULL); gb->last_rtc_second = time(NULL);
gb->rtc_high |= 0x80; /* This gives the game a hint that the clock should be reset. */ gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */
exit: exit:
fclose(f); fclose(f);
return; return;

View File

@ -306,15 +306,16 @@ typedef struct GB_gameboy_s {
GB_SECTION(rtc, GB_SECTION(rtc,
union { union {
struct { struct {
uint8_t rtc_seconds; uint8_t seconds;
uint8_t rtc_minutes; uint8_t minutes;
uint8_t rtc_hours; uint8_t hours;
uint8_t rtc_days; uint8_t days;
uint8_t rtc_high; uint8_t high;
};
uint8_t rtc_data[5];
}; };
uint8_t data[5];
} rtc_real, rtc_latched;
time_t last_rtc_second; time_t last_rtc_second;
bool rtc_latch;
); );
/* Video Display */ /* Video Display */

View File

@ -78,8 +78,8 @@ static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr)
if (gb->cartridge_type->has_rtc && gb->mbc_ram_bank >= 8 && gb->mbc_ram_bank <= 0xC) { if (gb->cartridge_type->has_rtc && gb->mbc_ram_bank >= 8 && gb->mbc_ram_bank <= 0xC) {
/* RTC read */ /* RTC read */
gb->rtc_high |= ~0xC1; /* Not all bytes in RTC high are used. */ gb->rtc_latched.high |= ~0xC1; /* Not all bytes in RTC high are used. */
return gb->rtc_data[gb->mbc_ram_bank - 8]; return gb->rtc_latched.data[gb->mbc_ram_bank - 8];
} }
if (!gb->mbc_ram) { if (!gb->mbc_ram) {
@ -284,7 +284,12 @@ static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break; case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break;
case 0x2000: case 0x3000: gb->mbc3.rom_bank = value; break; case 0x2000: case 0x3000: gb->mbc3.rom_bank = value; break;
case 0x4000: case 0x5000: gb->mbc3.ram_bank = value; break; case 0x4000: case 0x5000: gb->mbc3.ram_bank = value; break;
case 0x6000: case 0x7000: /* Todo: Clock latching support */ break; case 0x6000: case 0x7000:
if (!gb->rtc_latch && (value & 1)) { /* Todo: verify condition is correct*/
memcpy(&gb->rtc_latched, &gb->rtc_real, sizeof(gb->rtc_real));
}
gb->rtc_latch = value & 1;
break;
} }
break; break;
case GB_MBC5: case GB_MBC5:
@ -314,8 +319,7 @@ static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
if (gb->cartridge_type->has_rtc && gb->mbc_ram_bank >= 8 && gb->mbc_ram_bank <= 0xC) { if (gb->cartridge_type->has_rtc && gb->mbc_ram_bank >= 8 && gb->mbc_ram_bank <= 0xC) {
/* RTC read */ /* RTC read */
gb->rtc_data[gb->mbc_ram_bank - 8] = value; gb->rtc_latched.data[gb->mbc_ram_bank - 8] = gb->rtc_real.data[gb->mbc_ram_bank - 8] = value; /* Todo: does it really write both? */
gb->rtc_high |= ~0xC1; /* Not all bytes in RTC high are used. */
} }
if (gb->cartridge_type->mbc_type == GB_MBC2) { if (gb->cartridge_type->mbc_type == GB_MBC2) {

View File

@ -114,26 +114,26 @@ void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac)
void GB_rtc_run(GB_gameboy_t *gb) void GB_rtc_run(GB_gameboy_t *gb)
{ {
if ((gb->rtc_high & 0x40) == 0) { /* is timer running? */ if ((gb->rtc_real.high & 0x40) == 0) { /* is timer running? */
time_t current_time = time(NULL); time_t current_time = time(NULL);
while (gb->last_rtc_second < current_time) { while (gb->last_rtc_second < current_time) {
gb->last_rtc_second++; gb->last_rtc_second++;
if (++gb->rtc_seconds == 60) if (++gb->rtc_real.seconds == 60)
{ {
gb->rtc_seconds = 0; gb->rtc_real.seconds = 0;
if (++gb->rtc_minutes == 60) if (++gb->rtc_real.minutes == 60)
{ {
gb->rtc_minutes = 0; gb->rtc_real.minutes = 0;
if (++gb->rtc_hours == 24) if (++gb->rtc_real.hours == 24)
{ {
gb->rtc_hours = 0; gb->rtc_real.hours = 0;
if (++gb->rtc_days == 0) if (++gb->rtc_real.days == 0)
{ {
if (gb->rtc_high & 1) /* Bit 8 of days*/ if (gb->rtc_real.high & 1) /* Bit 8 of days*/
{ {
gb->rtc_high |= 0x80; /* Overflow bit */ gb->rtc_real.high |= 0x80; /* Overflow bit */
} }
gb->rtc_high ^= 1; gb->rtc_real.high ^= 1;
} }
} }
} }