summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/f2mc16/f2mc16_clock.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/f2mc16/f2mc16_clock.cpp')
-rw-r--r--src/devices/cpu/f2mc16/f2mc16_clock.cpp355
1 files changed, 355 insertions, 0 deletions
diff --git a/src/devices/cpu/f2mc16/f2mc16_clock.cpp b/src/devices/cpu/f2mc16/f2mc16_clock.cpp
new file mode 100644
index 00000000000..82111f60299
--- /dev/null
+++ b/src/devices/cpu/f2mc16/f2mc16_clock.cpp
@@ -0,0 +1,355 @@
+// license:BSD-3-Clause
+// copyright-holders:smf
+/***************************************************************************
+
+ Fujitsu Micro F2MC-16 series Clock Generator
+
+***************************************************************************/
+
+#include "emu.h"
+#include "f2mc16_clock.h"
+
+namespace {
+
+struct CKSCR { enum : uint8_t
+{
+ RESERVED1 = 1 << 7,
+ MCM = 1 << 6,
+ WS = 3 << 4,
+ RESERVED2 = 1 << 3,
+ MCS = 1 << 2,
+ CS = 3 << 0
+}; };
+
+struct LPMCR { enum : uint8_t
+{
+ RESERVED3 = (1 << 0),
+ CG = 3 << 1,
+ RESERVED4 = (1 << 3),
+ RST = 1 << 4,
+ SPL = 1 << 5,
+ SLP = 1 << 6,
+ STP = 1 << 7
+}; };
+
+struct TBTC { enum : uint8_t
+{
+ TEST = 1 << 7,
+ TBIE = 1 << 4,
+ TBOF = 1 << 3,
+ TBR = 1 << 2,
+ TBC = 3 << 0,
+ TBC_2_12 = 0 << 0,
+ TBC_2_14 = 1 << 0,
+ TBC_2_16 = 2 << 0,
+ TBC_2_19 = 3 << 0,
+}; };
+
+struct WDTC { enum : uint8_t
+{
+ WT = 3 << 0,
+ WTE = 1 << 2,
+ SRST = 1 << 3,
+ ERST = 1 << 4,
+ WRST = 1 << 5,
+ STBR = 1 << 6,
+ PONR = 1 << 7
+}; };
+
+} // anonymous namespace
+
+DEFINE_DEVICE_TYPE(F2MC16_CLOCK_GENERATOR, f2mc16_clock_generator_device, "f2mc16_clock_generator", "F2MC16 Clock Generator")
+
+f2mc16_clock_generator_device::f2mc16_clock_generator_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, required_device<f2mc16_intc_device> &intc, uint8_t tbtc_vector) :
+ f2mc16_clock_generator_device(mconfig, tag, owner, clock)
+{
+ m_cpu = downcast<f2mc16_device *>(owner);
+ m_intc.set_tag(intc);
+ m_tbtc_vector = tbtc_vector;
+}
+
+f2mc16_clock_generator_device::f2mc16_clock_generator_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, F2MC16_CLOCK_GENERATOR, tag, owner, clock),
+ m_cpu(nullptr),
+ m_intc(*this, finder_base::DUMMY_TAG),
+ m_tbtc_vector(0),
+ m_timebase_hz_cb(*this),
+ m_timebase_start_time(attotime::zero),
+ m_tbtc_overflow_time(attotime::never),
+ m_watchdog_overflow_time(attotime::never),
+ m_mainclock_changed(attotime::zero),
+ m_timebase_counter(0),
+ m_tbtc_hz(0),
+ m_mainclock_hz(0),
+ m_reset_reason(WDTC::PONR),
+ m_watchdog_overflow_count(0),
+ m_wt(0),
+ m_ckscr(0),
+ m_lpmcr(0),
+ m_tbtc(0),
+ m_wdtc(WDTC::WTE | WDTC::WT)
+{
+}
+
+void f2mc16_clock_generator_device::device_start()
+{
+ m_timebase_timer = timer_alloc(FUNC(f2mc16_clock_generator_device::timebase_timer_callback), this);
+
+ save_item(NAME(m_timebase_start_time));
+ save_item(NAME(m_tbtc_overflow_time));
+ save_item(NAME(m_mainclock_changed));
+ save_item(NAME(m_timebase_counter));
+ save_item(NAME(m_tbtc_hz));
+ save_item(NAME(m_mainclock_hz));
+ save_item(NAME(m_reset_reason));
+ save_item(NAME(m_ckscr));
+ save_item(NAME(m_lpmcr));
+ save_item(NAME(m_tbtc));
+ save_item(NAME(m_wdtc));
+}
+
+void f2mc16_clock_generator_device::device_clock_changed()
+{
+ if (machine().scheduler().currently_executing())
+ machine().scheduler().synchronize(timer_expired_delegate(FUNC(f2mc16_clock_generator_device::update_mainclock_hz), this), m_cpu->unscaled_clock());
+ else
+ update_mainclock_hz(m_cpu->unscaled_clock());
+}
+
+void f2mc16_clock_generator_device::device_reset()
+{
+ if (!m_reset_reason)
+ m_reset_reason = WDTC::ERST;
+
+ m_ckscr = ~CKSCR::MCS;
+ m_lpmcr = LPMCR::RESERVED4 | LPMCR::RESERVED3 | LPMCR::RST;
+ m_wdtc |= m_reset_reason | WDTC::WTE | WDTC::WT;
+ m_tbtc = TBTC::TEST | TBTC::TBR;
+ m_reset_reason = 0;
+ m_watchdog_overflow_time = attotime::never;
+
+ timebase_reset();
+ timebase_update();
+}
+
+uint8_t f2mc16_clock_generator_device::ckscr_r()
+{
+ return m_ckscr;
+}
+
+void f2mc16_clock_generator_device::ckscr_w(uint8_t data)
+{
+ data = (m_ckscr & ~(CKSCR::WS | CKSCR::MCS | CKSCR::CS)) | (data & (CKSCR::WS | CKSCR::MCS | CKSCR::CS));
+
+ if (m_ckscr != data)
+ {
+ if (!(m_ckscr & CKSCR::MCS) && (data & CKSCR::MCS))
+ {
+ timebase_reset();
+ timebase_update();
+ }
+
+ m_ckscr = data;
+
+ m_cpu->set_clock_scale((m_ckscr & CKSCR::MCS) ? 1 : (m_ckscr & CKSCR::CS) + 1);
+ }
+}
+
+uint8_t f2mc16_clock_generator_device::lpmcr_r()
+{
+ return m_lpmcr & (LPMCR::SPL | LPMCR::CG);
+}
+
+void f2mc16_clock_generator_device::lpmcr_w(uint8_t data)
+{
+ data |= LPMCR::RESERVED4 | LPMCR::RESERVED3;
+
+ if (m_lpmcr != data)
+ {
+ m_lpmcr = data;
+
+ if (!(m_lpmcr & LPMCR::RST))
+ {
+ m_lpmcr |= LPMCR::RST;
+ m_reset_reason |= WDTC::SRST;
+ machine().scheduler().synchronize(timer_expired_delegate(FUNC(f2mc16_clock_generator_device::soft_reset), this));
+ }
+ }
+}
+
+TIMER_CALLBACK_MEMBER(f2mc16_clock_generator_device::soft_reset)
+{
+ m_cpu->reset();
+}
+
+uint8_t f2mc16_clock_generator_device::wdtc_r()
+{
+ uint8_t data = m_wdtc;
+
+ if (!machine().side_effects_disabled())
+ m_wdtc &= ~(WDTC::PONR | WDTC::STBR | WDTC::WRST | WDTC::SRST);
+
+ return data | WDTC::WTE | WDTC::WT;
+}
+
+void f2mc16_clock_generator_device::wdtc_w(uint8_t data)
+{
+ uint8_t prev = m_wdtc;
+ m_wdtc = data & (m_wdtc | ~WDTC::WTE);
+
+ if (!(data & WDTC::WTE))
+ {
+ m_watchdog_overflow_count = 0;
+
+ if (prev & WDTC::WTE)
+ {
+ m_wt = m_wdtc & WDTC::WT;
+
+ timebase_update_counter();
+ timebase_update();
+ }
+ }
+}
+
+uint8_t f2mc16_clock_generator_device::tbtc_r()
+{
+ if (!m_cpu->rmw() && m_tbtc_overflow_time > machine().time())
+ return m_tbtc & ~TBTC::TBOF;
+
+ return m_tbtc;
+}
+
+void f2mc16_clock_generator_device::tbtc_w(uint8_t data)
+{
+ if (m_tbtc != data)
+ {
+ timebase_update_counter();
+
+ m_tbtc = data;
+
+ if (!(data & TBTC::TBR))
+ {
+ timebase_reset();
+ m_tbtc |= TBTC::TBR;
+ }
+
+ if (!(data & TBTC::TBOF))
+ {
+ if (m_tbtc_overflow_time <= machine().time())
+ m_tbtc_overflow_time = attotime::never;
+
+ m_tbtc |= TBTC::TBOF;
+ }
+
+ timebase_update();
+ }
+}
+
+void f2mc16_clock_generator_device::timebase_reset()
+{
+ m_timebase_counter = 0;
+ m_timebase_start_time = attotime::never;
+ m_tbtc_overflow_time = attotime::never;
+ m_tbtc_hz = 0;
+}
+
+void f2mc16_clock_generator_device::timebase_update()
+{
+ static const int cycles[4] = { 1 << 12, 1 << 14, 1 << 16, 1 << 19 };
+ attotime now = machine().time();
+
+ if (m_timebase_start_time == attotime::never)
+ m_timebase_start_time = now;
+
+ uint32_t event_ticks = 0;
+ attotime event_time = attotime::never;
+
+ if (m_tbtc_overflow_time > now)
+ {
+ uint32_t tbtc_ticks = cycles[m_tbtc & TBTC::TBC] - (m_timebase_counter & (cycles[m_tbtc & TBTC::TBC] - 1));
+ m_tbtc_overflow_time = m_timebase_start_time.is_never() || !m_mainclock_hz ? attotime::never :
+ m_timebase_start_time + attotime::from_ticks(tbtc_ticks, m_mainclock_hz);
+
+ if ((m_tbtc & TBTC::TBIE) && event_time > m_tbtc_overflow_time)
+ {
+ event_time = m_tbtc_overflow_time;
+ event_ticks = tbtc_ticks;
+ }
+ }
+
+ if (!(m_wdtc & WDTC::WTE))
+ {
+ if (m_watchdog_overflow_time < now)
+ {
+ m_watchdog_overflow_count++;
+ if (m_watchdog_overflow_count == 4)
+ {
+ m_reset_reason |= WDTC::WRST;
+ m_cpu->reset();
+ }
+ }
+
+ uint32_t watchdog_ticks = cycles[m_wt] - (m_timebase_counter & (cycles[m_wt] - 1));
+ attotime wdof = m_timebase_start_time.is_never() || !m_mainclock_hz ? attotime::never :
+ m_timebase_start_time + attotime::from_ticks(watchdog_ticks, m_mainclock_hz);
+ m_watchdog_overflow_time = wdof;
+
+ if (event_time > wdof)
+ {
+ event_time = wdof;
+ event_ticks = watchdog_ticks;
+ }
+ }
+
+ if (m_tbtc_hz != m_mainclock_hz && !m_timebase_hz_cb.isunset())
+ {
+ if ((m_timebase_counter % 512) != 0 && m_mainclock_hz)
+ {
+ uint32_t sync_ticks = 512 - (m_timebase_counter & (512 - 1));
+ attotime sync_time = m_timebase_start_time.is_never() || !m_mainclock_hz ? attotime::never :
+ m_timebase_start_time + attotime::from_ticks(sync_ticks, m_mainclock_hz);
+
+ if (event_time > sync_time)
+ {
+ event_time = sync_time;
+ event_ticks = sync_ticks;
+ }
+ }
+ else
+ {
+ m_timebase_hz_cb(m_mainclock_hz / 512);
+ m_tbtc_hz = m_mainclock_hz;
+ }
+ }
+
+ m_timebase_timer->adjust(event_time.is_never() ? event_time : event_time - now, event_ticks);
+ m_intc->set_irq(m_tbtc_vector, ((m_tbtc & TBTC::TBIE) && m_tbtc_overflow_time <= now) ? 1 : 0);
+}
+
+void f2mc16_clock_generator_device::timebase_update_counter()
+{
+ attotime now = machine().time();
+ uint64_t ticks = ((now - m_timebase_start_time) + attotime::zero).as_ticks(m_mainclock_hz);
+ m_timebase_counter += ticks;
+ m_timebase_counter &= 0x3ffff;
+ m_timebase_start_time = now;
+}
+
+TIMER_CALLBACK_MEMBER(f2mc16_clock_generator_device::update_mainclock_hz)
+{
+ timebase_update_counter();
+
+ m_mainclock_hz = param;
+ m_mainclock_changed = machine().time();
+
+ timebase_update();
+}
+
+TIMER_CALLBACK_MEMBER(f2mc16_clock_generator_device::timebase_timer_callback)
+{
+ m_timebase_counter += param;
+ m_timebase_counter &= 0x3ffff;
+ m_timebase_start_time = machine().time();
+
+ timebase_update();
+}