summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/h8/h8_timer8.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/h8/h8_timer8.cpp')
-rw-r--r--src/devices/cpu/h8/h8_timer8.cpp208
1 files changed, 114 insertions, 94 deletions
diff --git a/src/devices/cpu/h8/h8_timer8.cpp b/src/devices/cpu/h8/h8_timer8.cpp
index 6976adfbeca..c6ba496ea27 100644
--- a/src/devices/cpu/h8/h8_timer8.cpp
+++ b/src/devices/cpu/h8/h8_timer8.cpp
@@ -1,5 +1,22 @@
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
+/***************************************************************************
+
+ h8_timer8.cpp
+
+ H8 8 bits timer
+
+ TODO:
+ - IRQs are level triggered? eg. when an interrupt enable flag gets set
+ while an overflow or compare match flag is 1, will it trigger an IRQ?
+ Or if it's edge triggered, will it trigger an IRQ on rising edge of
+ (irq_enable & flag)?
+ - When writing 0 to the status register(s), the overflow/compare match
+ flags will only be cleared after a read access was done while they
+ were set? It's how the databook explains it, similar to HD6301.
+
+***************************************************************************/
+
#include "emu.h"
#include "h8_timer8.h"
@@ -12,12 +29,12 @@ static constexpr int V = 1;
DEFINE_DEVICE_TYPE(H8_TIMER8_CHANNEL, h8_timer8_channel_device, "h8_timer8_channel", "H8 8-bit timer channel")
DEFINE_DEVICE_TYPE(H8H_TIMER8_CHANNEL, h8h_timer8_channel_device, "h8h_timer8_channel", "H8H 8-bit timer channel")
-h8_timer8_channel_device::h8_timer8_channel_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+h8_timer8_channel_device::h8_timer8_channel_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
h8_timer8_channel_device(mconfig, H8_TIMER8_CHANNEL, tag, owner, clock)
{
}
-h8_timer8_channel_device::h8_timer8_channel_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
+h8_timer8_channel_device::h8_timer8_channel_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) :
device_t(mconfig, type, tag, owner, clock),
m_cpu(*this, finder_base::DUMMY_TAG),
m_intc(*this, finder_base::DUMMY_TAG),
@@ -30,12 +47,12 @@ h8_timer8_channel_device::h8_timer8_channel_device(const machine_config &mconfig
m_has_ice = false;
}
-uint8_t h8_timer8_channel_device::tcr_r()
+u8 h8_timer8_channel_device::tcr_r()
{
return m_tcr;
}
-void h8_timer8_channel_device::tcr_w(uint8_t data)
+void h8_timer8_channel_device::tcr_w(u8 data)
{
update_counter();
m_tcr = data;
@@ -64,7 +81,7 @@ void h8_timer8_channel_device::update_tcr()
case 1: case 2: case 3:
m_clock_type = DIV;
m_clock_divider = m_div_tab[((m_tcr & TCR_CKS)-1)*2 + m_extra_clock_bit];
- if(V>=1) util::stream_format(message, "clock %dHz", m_cpu->clock()/m_clock_divider);
+ if(V>=1) util::stream_format(message, "clock %dHz", m_cpu->system_clock()/m_clock_divider);
break;
case 4:
@@ -114,24 +131,26 @@ void h8_timer8_channel_device::update_tcr()
break;
}
- if(V>=1) util::stream_format(message, ", irq=%c%c%c\n",
- m_tcr & TCR_CMIEB ? 'b' : '-',
- m_tcr & TCR_CMIEA ? 'a' : '-',
- m_tcr & TCR_OVIE ? 'o' : '-');
+ if(V>=1) {
+ util::stream_format(message, ", irq=%c%c%c\n",
+ m_tcr & TCR_CMIEB ? 'b' : '-',
+ m_tcr & TCR_CMIEA ? 'a' : '-',
+ m_tcr & TCR_OVIE ? 'o' : '-');
- logerror(std::move(message).str());
+ logerror(std::move(message).str());
+ }
}
-uint8_t h8_timer8_channel_device::tcsr_r()
+u8 h8_timer8_channel_device::tcsr_r()
{
return m_tcsr;
}
-void h8_timer8_channel_device::tcsr_w(uint8_t data)
+void h8_timer8_channel_device::tcsr_w(u8 data)
{
update_counter();
- uint8_t mask = m_has_adte || m_has_ice ? 0x1f : 0x0f;
+ u8 mask = m_has_adte || m_has_ice ? 0x1f : 0x0f;
m_tcsr = (m_tcsr & ~mask) | (data & mask);
m_tcsr &= data | 0x1f;
@@ -140,12 +159,12 @@ void h8_timer8_channel_device::tcsr_w(uint8_t data)
recalc_event();
}
-uint8_t h8_timer8_channel_device::tcor_r(offs_t offset)
+u8 h8_timer8_channel_device::tcor_r(offs_t offset)
{
return m_tcor[offset];
}
-void h8_timer8_channel_device::tcor_w(offs_t offset, uint8_t data)
+void h8_timer8_channel_device::tcor_w(offs_t offset, u8 data)
{
update_counter();
m_tcor[offset] = data;
@@ -153,14 +172,16 @@ void h8_timer8_channel_device::tcor_w(offs_t offset, uint8_t data)
recalc_event();
}
-uint8_t h8_timer8_channel_device::tcnt_r()
+u8 h8_timer8_channel_device::tcnt_r()
{
- update_counter();
- recalc_event();
+ if(!machine().side_effects_disabled()) {
+ update_counter();
+ recalc_event();
+ }
return m_tcnt;
}
-void h8_timer8_channel_device::tcnt_w(uint8_t data)
+void h8_timer8_channel_device::tcnt_w(u8 data)
{
update_counter();
m_tcnt = data;
@@ -170,6 +191,17 @@ void h8_timer8_channel_device::tcnt_w(uint8_t data)
void h8_timer8_channel_device::device_start()
{
+ save_item(NAME(m_tcor));
+ save_item(NAME(m_tcr));
+ save_item(NAME(m_tcsr));
+ save_item(NAME(m_tcnt));
+ save_item(NAME(m_extra_clock_bit));
+ save_item(NAME(m_clock_type));
+ save_item(NAME(m_clock_divider));
+ save_item(NAME(m_clear_type));
+ save_item(NAME(m_counter_cycle));
+ save_item(NAME(m_last_clock_update));
+ save_item(NAME(m_event_time));
}
void h8_timer8_channel_device::device_reset()
@@ -188,34 +220,54 @@ void h8_timer8_channel_device::device_reset()
m_extra_clock_bit = false;
}
-uint64_t h8_timer8_channel_device::internal_update(uint64_t current_time)
+u64 h8_timer8_channel_device::internal_update(u64 current_time)
{
- if(m_event_time && current_time >= m_event_time) {
- update_counter(current_time);
- recalc_event(current_time);
+ while(m_event_time && current_time >= m_event_time) {
+ update_counter(m_event_time);
+ recalc_event(m_event_time);
}
return m_event_time;
}
-void h8_timer8_channel_device::update_counter(uint64_t cur_time)
+void h8_timer8_channel_device::notify_standby(int state)
{
- if(m_clock_type != DIV)
- return;
+ if(!state && m_event_time) {
+ u64 delta = m_cpu->total_cycles() - m_cpu->standby_time();
+ m_event_time += delta;
+ m_last_clock_update += delta;
+ }
+}
- if(!cur_time)
- cur_time = m_cpu->total_cycles();
+void h8_timer8_channel_device::update_counter(u64 cur_time, u64 delta)
+{
+ if(m_clock_type == DIV) {
+ if(!cur_time)
+ cur_time = m_cpu->total_cycles();
+
+ u64 base_time = (m_last_clock_update + m_clock_divider/2) / m_clock_divider;
+ m_last_clock_update = cur_time;
+ u64 new_time = (cur_time + m_clock_divider/2) / m_clock_divider;
+ delta = new_time - base_time;
+ }
+
+ if(!delta)
+ return;
- uint64_t base_time = (m_last_clock_update + m_clock_divider/2) / m_clock_divider;
- uint64_t new_time = (cur_time + m_clock_divider/2) / m_clock_divider;
+ u8 prev = m_tcnt;
+ u64 tt = m_tcnt + delta;
- int tt = m_tcnt + new_time - base_time;
- m_tcnt = tt % m_counter_cycle;
+ if(prev >= m_counter_cycle) {
+ if(tt >= 0x100)
+ m_tcnt = (tt - 0x100) % m_counter_cycle;
+ else
+ m_tcnt = tt;
+ } else
+ m_tcnt = tt % m_counter_cycle;
- if(tt == m_tcor[0] || m_tcnt == m_tcor[0]) {
+ if(u8 cmp = m_tcor[0] + 1; m_tcnt == cmp || (tt == cmp && tt == m_counter_cycle)) {
if(m_chained_timer)
m_chained_timer->chained_timer_tcora();
-
if(!(m_tcsr & TCSR_CMFA)) {
m_tcsr |= TCSR_CMFA;
if(m_tcr & TCR_CMIEA)
@@ -223,13 +275,15 @@ void h8_timer8_channel_device::update_counter(uint64_t cur_time)
}
}
- if(!(m_tcsr & TCSR_CMFB) && (tt == m_tcor[1] || m_tcnt == m_tcor[1])) {
- m_tcsr |= TCSR_CMFB;
- if(m_tcr & TCR_CMIEB)
- m_intc->internal_interrupt(m_irq_cb);
+ if(u8 cmp = m_tcor[1] + 1; m_tcnt == cmp || (tt == cmp && tt == m_counter_cycle)) {
+ if(!(m_tcsr & TCSR_CMFB)) {
+ m_tcsr |= TCSR_CMFB;
+ if(m_tcr & TCR_CMIEB)
+ m_intc->internal_interrupt(m_irq_cb);
+ }
}
- if(tt >= 0x100) {
+ if(tt >= 0x100 && (m_counter_cycle == 0x100 || prev >= m_counter_cycle)) {
if(m_chained_timer)
m_chained_timer->chained_timer_overflow();
if(!(m_tcsr & TCSR_OVF)) {
@@ -238,13 +292,12 @@ void h8_timer8_channel_device::update_counter(uint64_t cur_time)
m_intc->internal_interrupt(m_irq_v);
}
}
- m_last_clock_update = cur_time;
}
-void h8_timer8_channel_device::recalc_event(uint64_t cur_time)
+void h8_timer8_channel_device::recalc_event(u64 cur_time)
{
bool update_cpu = cur_time == 0;
- uint64_t old_event_time = m_event_time;
+ u64 old_event_time = m_event_time;
if(m_clock_type != DIV) {
m_event_time = 0;
@@ -256,33 +309,32 @@ void h8_timer8_channel_device::recalc_event(uint64_t cur_time)
if(!cur_time)
cur_time = m_cpu->total_cycles();
- uint32_t event_delay = 0xffffffff;
+ u32 event_delay = 0xffffffff;
if(m_clear_type == CLEAR_A || m_clear_type == CLEAR_B)
- m_counter_cycle = m_tcor[m_clear_type - CLEAR_A];
- else {
+ m_counter_cycle = m_tcor[m_clear_type - CLEAR_A] + 1;
+ else
m_counter_cycle = 0x100;
- event_delay = m_counter_cycle - m_tcnt;
- if(!event_delay)
- event_delay = m_counter_cycle;
- }
-
- for(auto &elem : m_tcor) {
- uint32_t new_delay = 0xffffffff;
- if(elem > m_tcnt) {
- if(m_tcnt >= m_counter_cycle || elem <= m_counter_cycle)
- new_delay = elem - m_tcnt;
- } else if(elem <= m_counter_cycle) {
+ if(m_counter_cycle == 0x100 || m_tcnt >= m_counter_cycle)
+ event_delay = 0x100 - m_tcnt;
+
+ for(auto &tcor : m_tcor) {
+ u32 new_delay = 0xffffffff;
+ u8 cmp = tcor + 1;
+ if(cmp > m_tcnt) {
+ if(m_tcnt >= m_counter_cycle || cmp <= m_counter_cycle)
+ new_delay = cmp - m_tcnt;
+ } else if(cmp <= m_counter_cycle) {
if(m_tcnt < m_counter_cycle)
- new_delay = (m_counter_cycle - m_tcnt) + elem;
+ new_delay = (m_counter_cycle - m_tcnt) + cmp;
else
- new_delay = (0x100 - m_tcnt) + elem;
+ new_delay = (0x100 - m_tcnt) + cmp;
}
if(event_delay > new_delay)
event_delay = new_delay;
}
if(event_delay != 0xffffffff)
- m_event_time = ((((cur_time + m_clock_divider) / m_clock_divider) + event_delay - 1) * m_clock_divider) + m_clock_divider/2;
+ m_event_time = ((((cur_time + m_clock_divider/2) / m_clock_divider) + event_delay - 1) * m_clock_divider) + m_clock_divider/2;
else
m_event_time = 0;
@@ -293,48 +345,16 @@ void h8_timer8_channel_device::recalc_event(uint64_t cur_time)
void h8_timer8_channel_device::chained_timer_overflow()
{
if(m_clock_type == CHAIN_OVERFLOW)
- timer_tick();
+ update_counter(0, 1);
}
void h8_timer8_channel_device::chained_timer_tcora()
{
if(m_clock_type == CHAIN_A)
- timer_tick();
-}
-
-void h8_timer8_channel_device::timer_tick()
-{
- m_tcnt++;
-
- if(m_tcnt == m_tcor[0]) {
- if(m_chained_timer)
- m_chained_timer->chained_timer_tcora();
-
- if(!(m_tcsr & TCSR_CMFA)) {
- m_tcsr |= TCSR_CMFA;
- if(m_tcr & TCR_CMIEA)
- m_intc->internal_interrupt(m_irq_ca);
- }
- }
-
- if(!(m_tcsr & TCSR_CMFB) && m_tcnt == m_tcor[1]) {
- m_tcsr |= TCSR_CMFB;
- if(m_tcr & TCR_CMIEB)
- m_intc->internal_interrupt(m_irq_cb);
- }
-
- if(m_tcnt == 0x00) {
- if(m_chained_timer)
- m_chained_timer->chained_timer_overflow();
- if(!(m_tcsr & TCSR_OVF)) {
- m_tcsr |= TCSR_OVF;
- if(m_tcr & TCR_OVIE)
- m_intc->internal_interrupt(m_irq_v);
- }
- }
+ update_counter(0, 1);
}
-h8h_timer8_channel_device::h8h_timer8_channel_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+h8h_timer8_channel_device::h8h_timer8_channel_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
h8_timer8_channel_device(mconfig, H8H_TIMER8_CHANNEL, tag, owner, clock)
{
// The extra clock bit is not used for h8h+