diff options
author | 2021-12-25 00:05:48 +0000 | |
---|---|---|
committer | 2021-12-24 19:05:48 -0500 | |
commit | 76152ba35ecbf68bdac14e3aae75ea23b4529092 (patch) | |
tree | 5cb2663f3a5835525b5ea9b85bdd99c0c1ad104e /src/devices/bus | |
parent | 1e32a43b50742c418fc169535f6add2ba725875a (diff) |
gb.cpp: Improved MBC3 RTC handling. (#9035)
Diffstat (limited to 'src/devices/bus')
-rw-r--r-- | src/devices/bus/gameboy/mbc.cpp | 123 | ||||
-rw-r--r-- | src/devices/bus/gameboy/mbc.h | 16 |
2 files changed, 107 insertions, 32 deletions
diff --git a/src/devices/bus/gameboy/mbc.cpp b/src/devices/bus/gameboy/mbc.cpp index bb052f9be57..92730506d1b 100644 --- a/src/devices/bus/gameboy/mbc.cpp +++ b/src/devices/bus/gameboy/mbc.cpp @@ -4,8 +4,8 @@ Game Boy carts with MBC (Memory Bank Controller) - - TODO: RTC runs too fast while in-game, in MBC-3 games... find the problem! +TODO: +- The MBC3 RTC should count the time that passed since the game was last played. ***********************************************************************************************************/ @@ -189,12 +189,26 @@ void gb_rom_mbc3_device::device_start() shared_start(); save_item(NAME(m_rtc_regs)); save_item(NAME(m_rtc_ready)); + save_item(NAME(m_rtc_ticks)); + save_item(NAME(m_latched_regs)); + + m_rtc_ticks = 0; + system_time curtime; + machine().current_datetime(curtime); + + m_rtc_regs[REG_SECONDS] = curtime.local_time.second; + m_rtc_regs[REG_MINUTES] = curtime.local_time.minute; + m_rtc_regs[REG_HOURS] = curtime.local_time.hour; + m_rtc_regs[REG_DAYS] = curtime.local_time.day & 0xff; + m_rtc_regs[REG_CONTROL] = (curtime.local_time.day >> 8) & 0x01; + + m_rtc_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gb_rom_mbc3_device::rtc_tick),this)); + m_rtc_timer->adjust(attotime::from_hz(RTC_FREQUENCY), 0, attotime::from_hz(RTC_FREQUENCY)); } void gb_rom_mbc3_device::device_reset() { shared_reset(); - memset(m_rtc_regs, 0, sizeof(m_rtc_regs)); m_rtc_ready = 0; } @@ -468,16 +482,39 @@ void gb_rom_mbc2_device::write_ram(offs_t offset, uint8_t data) // MBC3 -void gb_rom_mbc3_device::update_rtc() +TIMER_CALLBACK_MEMBER(gb_rom_mbc3_device::rtc_tick) { - system_time curtime; - machine().current_datetime(curtime); - - m_rtc_regs[0] = curtime.local_time.second; - m_rtc_regs[1] = curtime.local_time.minute; - m_rtc_regs[2] = curtime.local_time.hour; - m_rtc_regs[3] = curtime.local_time.day & 0xff; - m_rtc_regs[4] = (m_rtc_regs[4] & 0xf0) | (curtime.local_time.day >> 8); + if (BIT(m_rtc_regs[REG_CONTROL], 6)) { + return; + } + m_rtc_ticks++; + if (m_rtc_ticks >= RTC_FREQUENCY) + { + m_rtc_ticks = 0; + m_rtc_regs[REG_SECONDS] = (m_rtc_regs[REG_SECONDS] + 1) & 0x3f; + if (m_rtc_regs[REG_SECONDS] == 60) + { + m_rtc_regs[REG_SECONDS] = 0; + m_rtc_regs[REG_MINUTES] = (m_rtc_regs[REG_MINUTES] + 1) & 0x3f; + if (m_rtc_regs[REG_MINUTES] == 60) + { + m_rtc_regs[REG_MINUTES] -= 60; + m_rtc_regs[REG_HOURS] = (m_rtc_regs[REG_HOURS] + 1) & 0x1f; + if (m_rtc_regs[REG_HOURS] == 24) + { + m_rtc_regs[REG_HOURS] = 0; + m_rtc_regs[REG_DAYS]++; + if (m_rtc_regs[REG_DAYS] == 0) + { + m_rtc_regs[REG_CONTROL] = (m_rtc_regs[REG_CONTROL] & 0xfe) | ((m_rtc_regs[REG_CONTROL] + 1) & 0x01); + if (!(m_rtc_regs[REG_CONTROL] & 0x01)) { + m_rtc_regs[REG_CONTROL] |= RTC_CARRY; + } + } + } + } + } + } } uint8_t gb_rom_mbc3_device::read_rom(offs_t offset) @@ -512,42 +549,66 @@ void gb_rom_mbc3_device::write_bank(offs_t offset, uint8_t data) m_rtc_ready = 0; if (m_rtc_ready == 0 && data == 1) { + m_latched_regs[REG_SECONDS] = m_rtc_regs[REG_SECONDS]; + m_latched_regs[REG_MINUTES] = m_rtc_regs[REG_MINUTES]; + m_latched_regs[REG_HOURS] = m_rtc_regs[REG_HOURS]; + m_latched_regs[REG_DAYS] = m_rtc_regs[REG_DAYS]; + m_latched_regs[REG_CONTROL] = m_rtc_regs[REG_CONTROL]; m_rtc_ready = 1; - update_rtc(); } } } uint8_t gb_rom_mbc3_device::read_ram(offs_t offset) { - if (m_ram_bank < 4 && m_ram_enable) + if (m_ram_enable) { - // RAM - if (!m_ram.empty()) + if (m_ram_bank < 4 && !m_ram.empty()) + { + // RAM return m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)]; - } - if (m_ram_bank >= 0x8 && m_ram_bank <= 0xc) - { - // RTC registers - if (has_timer) - return m_rtc_regs[m_ram_bank - 8]; + } + else if (m_ram_bank >= 0x08 && m_ram_bank <= 0x0c && has_timer) + { + // RTC registers + return m_latched_regs[m_ram_bank - 8]; + } } return 0xff; } void gb_rom_mbc3_device::write_ram(offs_t offset, uint8_t data) { - if (m_ram_bank < 4 && m_ram_enable) + if (m_ram_enable) { - // RAM - if (!m_ram.empty()) + if (m_ram_bank < 4 && !m_ram.empty()) + { + // RAM m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)] = data; - } - if (m_ram_bank >= 0x8 && m_ram_bank <= 0xc && m_ram_enable) - { - // RTC registers are writeable too - if (has_timer) - m_rtc_regs[m_ram_bank - 8] = data; + } + else if (m_ram_bank >= 0x08 && m_ram_bank <= 0x0c && has_timer) + { + // RTC registers are writeable too + uint8_t reg = m_ram_bank - 8; + switch(reg) + { + case REG_SECONDS: + case REG_MINUTES: + data &= 0x3f; + break; + case REG_HOURS: + data &= 0x1f; + break; + case REG_CONTROL: + data &= 0xc1; + break; + } + m_rtc_regs[reg] = data; + if (reg == REG_SECONDS) + { + m_rtc_ticks = 0; + } + } } } diff --git a/src/devices/bus/gameboy/mbc.h b/src/devices/bus/gameboy/mbc.h index 52e5e56b583..cbd2150e752 100644 --- a/src/devices/bus/gameboy/mbc.h +++ b/src/devices/bus/gameboy/mbc.h @@ -100,9 +100,23 @@ protected: virtual void device_reset() override; private: - void update_rtc(); + static constexpr uint32_t RTC_FREQUENCY = 32'768; + static constexpr uint8_t RTC_CARRY = 0x80; + static constexpr uint8_t RTC_HALT = 0x40; + static constexpr uint8_t REG_SECONDS = 0; + static constexpr uint8_t REG_MINUTES = 1; + static constexpr uint8_t REG_HOURS = 2; + static constexpr uint8_t REG_DAYS = 3; + static constexpr uint8_t REG_CONTROL = 4; + + + emu_timer *m_rtc_timer; + uint32_t m_rtc_ticks; uint8_t m_rtc_regs[5]; + uint8_t m_latched_regs[5]; int m_rtc_ready; + + TIMER_CALLBACK_MEMBER(rtc_tick); }; // ======================> gb_rom_mbc5_device |