summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author wilbertpol <wilbertpol@users.noreply.github.com>2021-12-25 00:05:48 +0000
committer GitHub <noreply@github.com>2021-12-24 19:05:48 -0500
commit76152ba35ecbf68bdac14e3aae75ea23b4529092 (patch)
tree5cb2663f3a5835525b5ea9b85bdd99c0c1ad104e
parent1e32a43b50742c418fc169535f6add2ba725875a (diff)
gb.cpp: Improved MBC3 RTC handling. (#9035)
-rw-r--r--src/devices/bus/gameboy/mbc.cpp123
-rw-r--r--src/devices/bus/gameboy/mbc.h16
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