summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/machine/mm58167.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/machine/mm58167.c')
-rw-r--r--src/emu/machine/mm58167.c94
1 files changed, 93 insertions, 1 deletions
diff --git a/src/emu/machine/mm58167.c b/src/emu/machine/mm58167.c
index 40a93285480..962b681f39d 100644
--- a/src/emu/machine/mm58167.c
+++ b/src/emu/machine/mm58167.c
@@ -2,7 +2,7 @@
mm58167.c - National Semiconductor MM58167 real-time clock emulation
- TODO: alarms and IRQs not used by the Apple /// and aren't implemented.
+ TODO: standby interrupt
**********************************************************************/
@@ -70,6 +70,8 @@ void mm58167_device::device_start()
// state saving
save_item(NAME(m_regs));
+ save_item(NAME(m_milliseconds));
+ save_item(NAME(m_comparator_state));
}
@@ -82,7 +84,9 @@ void mm58167_device::device_reset()
set_current_time(machine());
m_regs[R_CTL_STATUS] = 0; // not busy
+ m_regs[R_CTL_IRQSTATUS] = 0;
m_milliseconds = 0;
+ m_comparator_state = false;
}
@@ -101,12 +105,49 @@ void mm58167_device::device_timer(emu_timer &timer, device_timer_id id, int para
if (m_milliseconds >= 999)
{
+ int old_seconds = m_regs[R_CNT_SECONDS];
+ int old_minutes = m_regs[R_CNT_MINUTES];
+ int old_hours = m_regs[R_CNT_HOURS];
+ int old_dayofmonth = m_regs[R_CNT_DAYOFMONTH];
+ int old_dayofweek = m_regs[R_CNT_DAYOFWEEK];
+ int old_month = m_regs[R_CNT_MONTH];
+
advance_seconds();
m_milliseconds = 0;
+
+ if ((m_regs[R_CTL_IRQCONTROL] & 0x04) && m_regs[R_CNT_SECONDS] != old_seconds) set_irq(2); // every second
+ if ((m_regs[R_CTL_IRQCONTROL] & 0x08) && m_regs[R_CNT_MINUTES] != old_minutes) set_irq(3); // every minute
+ if ((m_regs[R_CTL_IRQCONTROL] & 0x10) && m_regs[R_CNT_HOURS] != old_hours) set_irq(4); // every hour
+ if ((m_regs[R_CTL_IRQCONTROL] & 0x20) && m_regs[R_CNT_DAYOFMONTH] != old_dayofmonth) set_irq(5); // every day
+ if ((m_regs[R_CTL_IRQCONTROL] & 0x40) && m_regs[R_CNT_DAYOFWEEK] < old_dayofweek) set_irq(6); // every week
+ if ((m_regs[R_CTL_IRQCONTROL] & 0x80) && m_regs[R_CNT_MONTH] != old_month) set_irq(7); // every month
}
m_regs[R_CNT_MILLISECONDS] = make_bcd(m_milliseconds % 10);
m_regs[R_CNT_HUNDTENTHS] = make_bcd(m_milliseconds / 10);
+
+ // 10Hz IRQ
+ if ((m_regs[R_CTL_IRQCONTROL] & 0x02) && (m_milliseconds % 100) == 0)
+ set_irq(1);
+
+ // comparator IRQ
+ bool new_state = true;
+ for (int i = R_CNT_MILLISECONDS; i <= R_CNT_MONTH; i++)
+ {
+ // nibbles that have the 2 MSB set always compares true
+ // Milliseconds use only the high nibble and Day of Week only the low nibble
+ if ((i != R_CNT_MILLISECONDS && (m_regs[i + 8] & 0x0c) != 0x0c && (m_regs[i + 8] & 0x0f) != (m_regs[i] & 0x0f)) ||
+ (i != R_CNT_DAYOFWEEK && (m_regs[i + 8] & 0xc0) != 0xc0 && (m_regs[i + 8] & 0xf0) != (m_regs[i] & 0xf0)))
+ {
+ new_state = false;
+ break;
+ }
+ }
+
+ if ((m_regs[R_CTL_IRQCONTROL] & 0x01) && !m_comparator_state && new_state) // positive-edge-triggered
+ set_irq(0);
+
+ m_comparator_state = new_state;
}
@@ -124,9 +165,36 @@ void mm58167_device::rtc_clock_updated(int year, int month, int day, int day_of_
m_regs[R_CNT_MONTH] = make_bcd(month); // month (BCD)
}
+void mm58167_device::set_irq(int bit)
+{
+ m_regs[R_CTL_IRQSTATUS] |= (1 << bit);
+ m_irq_w(ASSERT_LINE);
+}
+
+void mm58167_device::update_rtc()
+{
+ set_clock_register(RTC_SECOND, bcd_to_integer(m_regs[R_CNT_SECONDS]));
+ set_clock_register(RTC_MINUTE, bcd_to_integer(m_regs[R_CNT_MINUTES]));
+ set_clock_register(RTC_HOUR, bcd_to_integer(m_regs[R_CNT_HOURS]));
+ set_clock_register(RTC_DAY, bcd_to_integer(m_regs[R_CNT_DAYOFMONTH]));
+ set_clock_register(RTC_DAY_OF_WEEK, bcd_to_integer(m_regs[R_CNT_DAYOFWEEK]));
+ set_clock_register(RTC_MONTH, bcd_to_integer(m_regs[R_CNT_MONTH]));
+ m_milliseconds = (bcd_to_integer(m_regs[R_CNT_HUNDTENTHS]) * 10) + (bcd_to_integer(m_regs[R_CNT_MILLISECONDS] >> 4) % 10);
+}
+
READ8_MEMBER(mm58167_device::read)
{
// printf("read reg %x = %02x\n", offset, m_regs[offset]);
+
+ if (offset == R_CTL_IRQSTATUS && !space.debugger_access())
+ {
+ // reading the IRQ status clears IRQ line and IRQ status
+ UINT8 data = m_regs[offset];
+ m_regs[R_CTL_IRQSTATUS] = 0;
+ m_irq_w(CLEAR_LINE);
+ return data;
+ }
+
return m_regs[offset];
}
@@ -141,11 +209,35 @@ WRITE8_MEMBER(mm58167_device::write)
switch (offset)
{
+ case R_CNT_MILLISECONDS:
+ case R_CNT_HUNDTENTHS:
+ case R_CNT_SECONDS:
+ case R_CNT_MINUTES:
+ case R_CNT_HOURS:
+ case R_CNT_DAYOFWEEK:
+ case R_CNT_DAYOFMONTH:
+ case R_CNT_MONTH:
+ m_regs[offset] = data;
+ update_rtc();
+ break;
+
// any write to this starts at the current time and zero milliseconds
case R_CTL_GOCMD:
m_milliseconds = 0;
break;
+ case R_CTL_RESETCOUNTERS:
+ if (data == 0xff)
+ {
+ for (int i = R_CNT_MILLISECONDS; i <= R_CNT_MONTH; i++)
+ {
+ m_regs[i] = 0;
+ }
+
+ update_rtc();
+ }
+ break;
+
case R_CTL_RESETRAM:
if (data == 0xff)
{