// license:BSD-3-Clause
// copyright-holders:R. Belmont,Carl
/**********************************************************************
cdp1879.c - RCA CDP1879 real-time clock emulation
**********************************************************************/
#include "emu.h"
#include "cdp1879.h"
#include "machine/timehelp.h"
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
// device type definition
DEFINE_DEVICE_TYPE(CDP1879, cdp1879_device, "cdp1879", "RCA CDP1879 RTC")
//-------------------------------------------------
// cdp1879_device - constructor
//-------------------------------------------------
cdp1879_device::cdp1879_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, CDP1879, tag, owner, clock),
device_rtc_interface(mconfig, *this),
m_irq_w(*this)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void cdp1879_device::device_start()
{
// allocate timers
m_clock_timer = timer_alloc();
m_clock_timer->adjust(attotime::from_seconds(1), 0, attotime::from_seconds(1));
m_irq_w.resolve_safe();
// state saving
save_item(NAME(m_regs));
save_item(NAME(m_comparator_state));
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void cdp1879_device::device_reset()
{
m_regs[0] = m_regs[1] = 0;
m_regs[R_CTL_IRQSTATUS] = 0;
m_regs[R_CTL_CONTROL] = 0;
m_comparator_state = false;
}
//-------------------------------------------------
// device_timer - handler timer events
//-------------------------------------------------
void cdp1879_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
advance_seconds();
// comparator IRQ
bool new_state = true;
for (int i = R_CNT_SECONDS; i <= R_CNT_HOURS; i++)
{
if(m_regs[i] != m_regs[i + 6])
{
new_state = false;
break;
}
}
if (!m_comparator_state && new_state) // positive-edge-triggered
set_irq(7);
m_comparator_state = new_state;
}
//-------------------------------------------------
// rtc_clock_updated -
//-------------------------------------------------
void cdp1879_device::rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second)
{
m_regs[R_CNT_SECONDS] = time_helper::make_bcd(second); // seconds (BCD)
m_regs[R_CNT_MINUTES] = time_helper::make_bcd(minute); // minutes (BCD)
m_regs[R_CNT_HOURS] = time_helper::make_bcd(hour); // hour (BCD)
m_regs[R_CNT_DAYOFMONTH] = time_helper::make_bcd(day); // day of the month (BCD)
m_regs[R_CNT_MONTH] = time_helper::make_bcd(month); // month (BCD)
}
void cdp1879_device::set_irq(int bit)
{
m_regs[R_CTL_IRQSTATUS] |= (1 << bit);
m_irq_w(ASSERT_LINE);
}
void cdp1879_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_MONTH, bcd_to_integer(m_regs[R_CNT_MONTH]));
}
READ8_MEMBER(cdp1879_device::read)
{
if (offset == R_CTL_IRQSTATUS && !machine().side_effects_disabled())
{
// reading the IRQ status clears IRQ line and IRQ status
uint8_t data = m_regs[offset];
m_regs[R_CTL_IRQSTATUS] = 0;
m_irq_w(CLEAR_LINE);
return data;
}
return m_regs[offset];
}
WRITE8_MEMBER(cdp1879_device::write)
{
switch (offset)
{
case R_CNT_SECONDS:
case R_CNT_MINUTES:
case R_CNT_HOURS:
if(BIT(m_regs[R_CTL_CONTROL], 3))
{
m_regs[offset + 6] = data;
break;
}
case R_CNT_DAYOFMONTH:
case R_CNT_MONTH:
m_regs[offset] = data;
update_rtc();
break;
case R_CTL_CONTROL:
m_regs[offset] = data;
break;
}
}