diff options
Diffstat (limited to 'src/devices/cpu/f2mc16/f2mc16_reload.cpp')
-rw-r--r-- | src/devices/cpu/f2mc16/f2mc16_reload.cpp | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/src/devices/cpu/f2mc16/f2mc16_reload.cpp b/src/devices/cpu/f2mc16/f2mc16_reload.cpp new file mode 100644 index 00000000000..33ff2aa53b5 --- /dev/null +++ b/src/devices/cpu/f2mc16/f2mc16_reload.cpp @@ -0,0 +1,381 @@ +// license:BSD-3-Clause +// copyright-holders:smf +/*************************************************************************** + + Fujitsu Micro F2MC-16 series 16-bit timer + +***************************************************************************/ + +#include "emu.h" +#include "f2mc16_reload.h" + +namespace { + +struct TMCSR { enum : uint16_t +{ + CSL = 3 << 10, + CSL_DIV2 = 0 << 10, + CSL_DIV8 = 1 << 10, + CSL_DIV32 = 2 << 10, + CSL_EXTERNAL = 3 << 10, + MOD2 = 1 << 9, + MOD1 = 1 << 8, + MOD0 = 1 << 7, + MOD_INT = 7 << 7, + MOD_EXT = 3 << 7, + MOD_DISABLE = 0 << 7, + MOD_RISING = 1 << 7, + MOD_FALLING = 2 << 7, + MOD_BOTH = 3 << 7, + MOD_GATE = 5 << 7, + MOD_GATE_LOW = 4 << 7, + MOD_GATE_HIGH = 5 << 7, + OUTE = 1 << 6, + OUTL = 1 << 5, + RELD = 1 << 4, + INTE = 1 << 3, + UF = 1 << 2, + CNTE = 1 << 1, + TRG = 1 << 0 +}; }; + +} // anonymous namespace + +DEFINE_DEVICE_TYPE(F2MC16_RELOAD_TIMER, f2mc16_reload_timer_device, "f2mc16_reload_timer", "F2MC16 16-bit reload timer") + +f2mc16_reload_timer_device::f2mc16_reload_timer_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, required_device<f2mc16_intc_device> &intc, uint8_t vector) : + f2mc16_reload_timer_device(mconfig, tag, owner, clock) +{ + m_cpu = downcast<f2mc16_device *>(owner); + m_intc.set_tag(intc); + m_vector = vector; +} + +f2mc16_reload_timer_device::f2mc16_reload_timer_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, F2MC16_RELOAD_TIMER, tag, owner, clock), + m_cpu(nullptr), + m_intc(*this, finder_base::DUMMY_TAG), + m_vector(0), + m_internal_hz_cb(*this), + m_tot_hz_cb(*this), + m_peripheral_clock_changed(attotime::zero), + m_tin_changed(attotime::zero), + m_start_time(attotime::never), + m_underflow_time(attotime::never), + m_clocksel(0), + m_peripheral_clock_hz(0), + m_tin_hz(0), + m_internal_hz(0), + m_tot_hz(0), + m_tin(1), + m_i2osclr(0), + m_tmcsr(TMCSR::UF), + m_tmr(0), + m_tmrlr(0) +{ +} + +void f2mc16_reload_timer_device::device_start() +{ + m_timer = timer_alloc(FUNC(f2mc16_reload_timer_device::timer_callback), this); + + save_item(NAME(m_peripheral_clock_changed)); + save_item(NAME(m_tin_changed)); + save_item(NAME(m_start_time)); + save_item(NAME(m_underflow_time)); + save_item(NAME(m_clocksel)); + save_item(NAME(m_peripheral_clock_hz)); + save_item(NAME(m_tin_hz)); + save_item(NAME(m_internal_hz)); + save_item(NAME(m_tot_hz)); + save_item(NAME(m_tin)); + save_item(NAME(m_i2osclr)); + save_item(NAME(m_tmcsr)); + save_item(NAME(m_tmr)); + save_item(NAME(m_tmrlr)); +} + +void f2mc16_reload_timer_device::device_clock_changed() +{ + if (machine().scheduler().currently_executing()) + machine().scheduler().synchronize(timer_expired_delegate(FUNC(f2mc16_reload_timer_device::update_peripheral_clock), this), clock()); + else + update_peripheral_clock(clock()); +} + +void f2mc16_reload_timer_device::device_reset() +{ + update_tmr(); + m_tmcsr = TMCSR::UF; + update(); +} + +void f2mc16_reload_timer_device::tin_hz(uint32_t hz) +{ + if (started() && machine().scheduler().currently_executing()) + machine().scheduler().synchronize(timer_expired_delegate(FUNC(f2mc16_reload_timer_device::update_tin_hz), this), hz); + else + update_tin_hz(hz); +} + +void f2mc16_reload_timer_device::tin(int state) +{ + bool rising_edge = (state && !m_tin); + bool falling_edge = (!state && m_tin); + + if (m_tin_hz == 0 && (m_tmcsr & TMCSR::CNTE) && (rising_edge || falling_edge)) + { + if ((m_tmcsr & TMCSR::CSL) == TMCSR::CSL_EXTERNAL) + { + if ((((m_tmcsr & TMCSR::MOD_EXT) == TMCSR::MOD_RISING) && rising_edge) || + (((m_tmcsr & TMCSR::MOD_EXT) == TMCSR::MOD_RISING) && falling_edge) || + ((m_tmcsr & TMCSR::MOD_EXT) == TMCSR::MOD_BOTH)) + { + m_tmr--; + + if (m_tmr == 0xffff) + { + if (m_tmcsr & TMCSR::RELD) + m_tmr = m_tmrlr; + else + m_start_time = attotime::never; + + m_underflow_time = machine().time(); + update(); + } + } + } + else + { + if ((((m_tmcsr & TMCSR::MOD_INT) == TMCSR::MOD_RISING) && rising_edge) || + (((m_tmcsr & TMCSR::MOD_INT) == TMCSR::MOD_RISING) && falling_edge) || + ((m_tmcsr & TMCSR::MOD_INT) == TMCSR::MOD_BOTH)) + { + trigger(); + update(); + } + else if ((m_tmcsr & TMCSR::MOD_GATE) == TMCSR::MOD_GATE_LOW || + (m_tmcsr & TMCSR::MOD_GATE) == TMCSR::MOD_GATE_HIGH) + { + update_tmr(); + update(); + } + } + } + + m_tin = state; +} + +void f2mc16_reload_timer_device::i2osclr(int state) +{ + if (state && !m_i2osclr) + { + update_tmr(); + + m_underflow_time = attotime::never; + + update(); + } + + m_i2osclr = state; +} + +uint16_t f2mc16_reload_timer_device::tmcsr_r(offs_t offset, uint16_t mem_mask) +{ + if (ACCESSING_BITS_0_7 && !m_cpu->rmw() && m_underflow_time > machine().time()) + return m_tmcsr & ~TMCSR::UF; + + return m_tmcsr; +} + +void f2mc16_reload_timer_device::tmcsr_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + uint16_t tmcsr = m_tmcsr; + COMBINE_DATA(&tmcsr); + tmcsr &= TMCSR::CSL | TMCSR::MOD_INT | TMCSR::OUTE | TMCSR::OUTL | TMCSR::RELD | TMCSR::INTE | TMCSR::UF | TMCSR::CNTE | TMCSR::TRG; + + if (m_tmcsr != tmcsr) + { + update_tmr(); + + m_tmcsr = tmcsr; + + if (ACCESSING_BITS_0_7) + { + if (m_tmcsr & TMCSR::TRG) + { + if (m_tmcsr & TMCSR::CNTE) + trigger(); + + m_tmcsr &= ~TMCSR::TRG; + } + + if (!(m_tmcsr & TMCSR::UF)) + { + if (m_underflow_time <= machine().time()) + m_underflow_time = attotime::never; + + m_tmcsr |= TMCSR::UF; + } + } + + update(); + } +} + +uint16_t f2mc16_reload_timer_device::tmr_r() +{ + return calculate_tmr(); +} + +void f2mc16_reload_timer_device::tmrlr_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + uint32_t tmrlr = m_tmrlr; + COMBINE_DATA(&tmrlr); + + if (m_tmrlr != tmrlr) + { + if (m_tmcsr & TMCSR::RELD) + update_tmr(); + + m_tmrlr = tmrlr; + + if (m_tmcsr & TMCSR::RELD) + update(); + } +} + +void f2mc16_reload_timer_device::update() +{ + attotime now = machine().time(); + + if (!(m_tmcsr & TMCSR::CNTE)) + m_start_time = attotime::never; + + m_clocksel = m_start_time.is_never() ? 0 : + ((m_tmcsr & TMCSR::CSL) == TMCSR::CSL_EXTERNAL) ? + (((m_tmcsr & TMCSR::MOD_EXT) == TMCSR::MOD_DISABLE) ? 0 : + ((m_tmcsr & TMCSR::MOD_EXT) == TMCSR::MOD_BOTH) ? m_tin_hz * 2 : + m_tin_hz) : + (((m_tmcsr & TMCSR::MOD_GATE) == TMCSR::MOD_GATE_LOW && m_tin) ? 0 : + ((m_tmcsr & TMCSR::MOD_GATE) == TMCSR::MOD_GATE_HIGH && !m_tin) ? 0 : + ((m_tmcsr & TMCSR::CSL) == TMCSR::CSL_DIV2) ? clock() / 2 : + ((m_tmcsr & TMCSR::CSL) == TMCSR::CSL_DIV8) ? clock() / 8 : + ((m_tmcsr & TMCSR::CSL) == TMCSR::CSL_DIV32) ? clock() / 32 : + 0); + + attotime event_time = attotime::never; + + attotime uf = (m_start_time.is_never() || !m_clocksel) ? attotime::never : + m_start_time + attotime::from_ticks(m_tmr + 1, m_clocksel); + + if (m_underflow_time > now) + { + m_underflow_time = uf; + + if ((m_tmcsr & TMCSR::INTE) && event_time > uf) + event_time = uf; + } + + uint32_t internal_hz = (m_tmcsr & TMCSR::RELD) ? m_clocksel / (m_tmrlr + 1) / 2: 0; + uint32_t tot_hz = (m_tmcsr & TMCSR::OUTE) ? internal_hz : 0; + + if ((m_internal_hz != internal_hz && !m_internal_hz_cb.isunset()) || + (m_tot_hz != tot_hz && !m_tot_hz_cb.isunset())) + { + if (m_tmr != m_tmrlr && (m_tmcsr & TMCSR::RELD) && internal_hz) + { + if (event_time > uf) + event_time = uf; + } + else + { + if (m_internal_hz != internal_hz && !m_internal_hz_cb.isunset()) + { + m_internal_hz_cb(internal_hz); + m_internal_hz = internal_hz; + } + + if (m_tot_hz != tot_hz && !m_tot_hz_cb.isunset()) + { + m_tot_hz_cb(tot_hz); + m_tot_hz = tot_hz; + } + } + } + + m_timer->adjust(event_time.is_never() ? event_time : event_time - now); + m_intc->set_irq(m_vector, ((m_tmcsr & TMCSR::INTE) && m_underflow_time <= now) ? 1 : 0); +} + +void f2mc16_reload_timer_device::trigger() +{ + m_tmr = m_tmrlr; + m_start_time = machine().time(); +} + +void f2mc16_reload_timer_device::update_tmr() +{ + m_tmr = calculate_tmr(); + if (m_start_time != attotime::never) + m_start_time = machine().time(); +} + +uint16_t f2mc16_reload_timer_device::calculate_tmr() +{ + if (m_start_time.is_never() || m_clocksel == 0) + return m_tmr; + + uint64_t ticks = (machine().time() - m_start_time).as_ticks(m_clocksel); + if (m_tmr >= ticks) + return m_tmr - ticks; + + if (m_tmcsr & TMCSR::RELD) + return m_tmrlr - ((ticks - m_tmr - 1) % (m_tmrlr + 1)); + + return 0xffff; +} + +TIMER_CALLBACK_MEMBER(f2mc16_reload_timer_device::update_tin_hz) +{ + if ((m_tmcsr & TMCSR::CSL) == TMCSR::CSL_EXTERNAL) + update_tmr(); + + m_tin_hz = param; + + if (started()) + { + m_tin_changed = machine().time(); + + if ((m_tmcsr & TMCSR::CSL) == TMCSR::CSL_EXTERNAL) + update(); + } +} + +TIMER_CALLBACK_MEMBER(f2mc16_reload_timer_device::update_peripheral_clock) +{ + if ((m_tmcsr & TMCSR::CSL) != TMCSR::CSL_EXTERNAL) + update_tmr(); + + m_peripheral_clock_hz = param; + m_peripheral_clock_changed = machine().time(); + + if ((m_tmcsr & TMCSR::CSL) != TMCSR::CSL_EXTERNAL) + update(); +} + +TIMER_CALLBACK_MEMBER(f2mc16_reload_timer_device::timer_callback) +{ + if (m_tmcsr & TMCSR::RELD) + { + m_tmr = m_tmrlr; + m_start_time = machine().time(); + } + else + { + m_tmr = 0xffff; + m_start_time = attotime::never; + } + + update(); +} |