summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/h8/h8_timer8.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/h8/h8_timer8.c')
-rw-r--r--src/devices/cpu/h8/h8_timer8.c373
1 files changed, 373 insertions, 0 deletions
diff --git a/src/devices/cpu/h8/h8_timer8.c b/src/devices/cpu/h8/h8_timer8.c
new file mode 100644
index 00000000000..b652f091c07
--- /dev/null
+++ b/src/devices/cpu/h8/h8_timer8.c
@@ -0,0 +1,373 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+#include "emu.h"
+#include "h8_timer8.h"
+
+const device_type H8_TIMER8_CHANNEL = &device_creator<h8_timer8_channel_device>;
+const device_type H8H_TIMER8_CHANNEL = &device_creator<h8h_timer8_channel_device>;
+
+h8_timer8_channel_device::h8_timer8_channel_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, H8_TIMER8_CHANNEL, "H8 8-bits timer channel", tag, owner, clock, "h8_8bits_timer_channel", __FILE__),
+ cpu(*this, "^")
+{
+}
+
+h8_timer8_channel_device::h8_timer8_channel_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) :
+ device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ cpu(*this, "^")
+{
+}
+
+void h8_timer8_channel_device::set_info(const char *intc, int _irq_ca, int _irq_cb, int _irq_v, int div1, int div2, int div3, int div4, int div5, int div6)
+{
+ intc_tag = intc;
+ irq_ca = _irq_ca;
+ irq_cb = _irq_cb;
+ irq_v = _irq_v;
+ chain_tag = NULL;
+ chain_type = STOPPED;
+ has_adte = false;
+ has_ice = false;
+ div_tab[0] = div1;
+ div_tab[1] = div2;
+ div_tab[2] = div3;
+ div_tab[3] = div4;
+ div_tab[4] = div5;
+ div_tab[5] = div6;
+}
+
+READ8_MEMBER(h8_timer8_channel_device::tcr_r)
+{
+ return tcr;
+}
+
+WRITE8_MEMBER(h8_timer8_channel_device::tcr_w)
+{
+ update_counter();
+ tcr = data;
+ update_tcr();
+ recalc_event();
+}
+
+void h8_timer8_channel_device::set_extra_clock_bit(bool bit)
+{
+ update_counter();
+ extra_clock_bit = bit;
+ update_tcr();
+ recalc_event();
+}
+
+void h8_timer8_channel_device::update_tcr()
+{
+ switch(tcr & TCR_CKS) {
+ case 0:
+ clock_type = STOPPED;
+ clock_divider = 0;
+ logerror("%s: clock stopped", tag());
+ break;
+
+ case 1: case 2: case 3:
+ clock_type = DIV;
+ clock_divider = div_tab[((tcr & TCR_CKS)-1)*2 + extra_clock_bit];
+ logerror("%s: clock %dHz", tag(), cpu->clock()/clock_divider);
+ break;
+
+ case 4:
+ clock_type = chain_type;
+ clock_divider = 0;
+ logerror("%s: clock chained %s", tag(), clock_type == CHAIN_A ? "tcora" : "overflow");
+ break;
+
+ case 5:
+ clock_type = INPUT_UP;
+ clock_divider = 0;
+ logerror("%s: clock external raising edge", tag());
+ break;
+
+ case 6:
+ clock_type = INPUT_DOWN;
+ clock_divider = 0;
+ logerror("%s: clock external falling edge", tag());
+ break;
+
+ case 7:
+ clock_type = INPUT_UPDOWN;
+ clock_divider = 0;
+ logerror("%s: clock external both edges", tag());
+ break;
+ }
+
+ switch(tcr & TCR_CCLR) {
+ case 0x00:
+ clear_type = CLEAR_NONE;
+ logerror(", no clear");
+ break;
+
+ case 0x08:
+ clear_type = CLEAR_A;
+ logerror(", clear on tcora");
+ break;
+
+ case 0x10:
+ clear_type = CLEAR_B;
+ logerror(", clear on tcorb");
+ break;
+
+ case 0x18:
+ clear_type = CLEAR_EXTERNAL;
+ logerror(", clear on external");
+ break;
+ }
+
+ logerror(", irq=%c%c%c\n",
+ tcr & TCR_CMIEB ? 'b' : '-',
+ tcr & TCR_CMIEA ? 'a' : '-',
+ tcr & TCR_OVIE ? 'o' : '-');
+}
+
+READ8_MEMBER(h8_timer8_channel_device::tcsr_r)
+{
+ return tcsr;
+}
+
+WRITE8_MEMBER(h8_timer8_channel_device::tcsr_w)
+{
+ update_counter();
+
+ UINT8 mask = has_adte || has_ice ? 0x1f : 0x0f;
+ tcsr = (tcsr & ~mask) | (data & mask);
+ tcsr &= data | 0x1f;
+
+ logerror("%s: tcsr_w %02x\n", tag(), tcsr);
+
+ recalc_event();
+}
+
+READ8_MEMBER(h8_timer8_channel_device::tcor_r)
+{
+ return tcor[offset];
+}
+
+WRITE8_MEMBER(h8_timer8_channel_device::tcor_w)
+{
+ update_counter();
+ tcor[offset] = data;
+ logerror("%s: tcor%c_w %02x\n", tag(), 'a'+offset, data);
+ recalc_event();
+}
+
+READ8_MEMBER(h8_timer8_channel_device::tcnt_r)
+{
+ update_counter();
+ recalc_event();
+ return tcnt;
+}
+
+WRITE8_MEMBER(h8_timer8_channel_device::tcnt_w)
+{
+ update_counter();
+ tcnt = data;
+ logerror("%s: tcnt_w %02x\n", tag(), data);
+ recalc_event();
+}
+
+void h8_timer8_channel_device::device_start()
+{
+ intc = siblingdevice<h8_intc_device>(intc_tag);
+ if(chain_tag)
+ chained_timer = siblingdevice<h8_timer8_channel_device>(chain_tag);
+ else
+ chained_timer = NULL;
+}
+
+void h8_timer8_channel_device::device_reset()
+{
+ tcr = 0x00;
+ tcsr = has_adte || has_ice ? 0x00 : 0x10;
+ tcor[0] = 0xff;
+ tcor[1] = 0xff;
+ tcnt = 0x00;
+ counter_cycle = 0x100;
+ clock_type = STOPPED;
+ clock_divider = 0;
+ clear_type = CLEAR_NONE;
+ last_clock_update = 0;
+ event_time = 0;
+ extra_clock_bit = false;
+}
+
+UINT64 h8_timer8_channel_device::internal_update(UINT64 current_time)
+{
+ if(event_time && current_time >= event_time) {
+ update_counter(current_time);
+ if(0)
+ logerror("%s: Reached event time (%ld), counter=%02x, dt=%d\n", tag(), long(current_time), tcnt, int(current_time - event_time));
+ recalc_event(current_time);
+ }
+
+ return event_time;
+}
+
+void h8_timer8_channel_device::update_counter(UINT64 cur_time)
+{
+ if(clock_type != DIV)
+ return;
+
+ if(!cur_time)
+ cur_time = cpu->total_cycles();
+
+ UINT64 base_time = (last_clock_update + clock_divider/2) / clock_divider;
+ UINT64 new_time = (cur_time + clock_divider/2) / clock_divider;
+
+ int tt = tcnt + new_time - base_time;
+ tcnt = tt % counter_cycle;
+
+ if(tt == tcor[0] || tcnt == tcor[0]) {
+ if(chained_timer)
+ chained_timer->chained_timer_tcora();
+
+ if(!(tcsr & TCSR_CMFA)) {
+ tcsr |= TCSR_CMFA;
+ if(tcr & TCR_CMIEA)
+ intc->internal_interrupt(irq_ca);
+ }
+ }
+
+ if(!(tcsr & TCSR_CMFB) && (tt == tcor[1] || tcnt == tcor[1])) {
+ tcsr |= TCSR_CMFB;
+ if(tcr & TCR_CMIEB)
+ intc->internal_interrupt(irq_cb);
+ }
+
+ if(tt >= 0x100) {
+ if(chained_timer)
+ chained_timer->chained_timer_overflow();
+ if(!(tcsr & TCSR_OVF)) {
+ tcsr |= TCSR_OVF;
+ if(tcr & TCR_OVIE)
+ intc->internal_interrupt(irq_v);
+ }
+ }
+ last_clock_update = cur_time;
+}
+
+void h8_timer8_channel_device::recalc_event(UINT64 cur_time)
+{
+ bool update_cpu = cur_time == 0;
+ UINT64 old_event_time = event_time;
+
+ if(clock_type != DIV) {
+ event_time = 0;
+ if(old_event_time && update_cpu)
+ cpu->internal_update();
+ return;
+ }
+
+ if(!cur_time)
+ cur_time = cpu->total_cycles();
+
+ UINT32 event_delay = 0xffffffff;
+ if(clear_type == CLEAR_A || clear_type == CLEAR_B)
+ counter_cycle = tcor[clear_type - CLEAR_A];
+ else {
+ counter_cycle = 0x100;
+ event_delay = counter_cycle - tcnt;
+ if(!event_delay)
+ event_delay = counter_cycle;
+ }
+
+ for(int i=0; i<2; i++) {
+ UINT32 new_delay = 0xffffffff;
+ if(tcor[i] > tcnt) {
+ if(tcnt >= counter_cycle || tcor[i] <= counter_cycle)
+ new_delay = tcor[i] - tcnt;
+ } else if(tcor[i] <= counter_cycle) {
+ if(tcnt < counter_cycle)
+ new_delay = (counter_cycle - tcnt) + tcor[i];
+ else
+ new_delay = (0x100 - tcnt) + tcor[i];
+ }
+ if(event_delay > new_delay)
+ event_delay = new_delay;
+ }
+
+ if(event_delay != 0xffffffff)
+ event_time = ((((cur_time + clock_divider) / clock_divider) + event_delay - 1) * clock_divider) + clock_divider/2;
+ else
+ event_time = 0;
+
+ if(old_event_time != event_time && update_cpu)
+ cpu->internal_update();
+}
+
+void h8_timer8_channel_device::chained_timer_overflow()
+{
+ if(clock_type == CHAIN_OVERFLOW)
+ timer_tick();
+}
+
+void h8_timer8_channel_device::chained_timer_tcora()
+{
+ if(clock_type == CHAIN_A)
+ timer_tick();
+}
+
+void h8_timer8_channel_device::timer_tick()
+{
+ tcnt++;
+
+ if(tcnt == tcor[0]) {
+ if(chained_timer)
+ chained_timer->chained_timer_tcora();
+
+ if(!(tcsr & TCSR_CMFA)) {
+ tcsr |= TCSR_CMFA;
+ if(tcr & TCR_CMIEA)
+ intc->internal_interrupt(irq_ca);
+ }
+ }
+
+ if(!(tcsr & TCSR_CMFB) && tcnt == tcor[1]) {
+ tcsr |= TCSR_CMFB;
+ if(tcr & TCR_CMIEB)
+ intc->internal_interrupt(irq_cb);
+ }
+
+ if(tcnt == 0x00) {
+ if(chained_timer)
+ chained_timer->chained_timer_overflow();
+ if(!(tcsr & TCSR_OVF)) {
+ tcsr |= TCSR_OVF;
+ if(tcr & TCR_OVIE)
+ intc->internal_interrupt(irq_v);
+ }
+ }
+}
+
+h8h_timer8_channel_device::h8h_timer8_channel_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ h8_timer8_channel_device(mconfig, H8H_TIMER8_CHANNEL, "H8H 8-bits timer channel", tag, owner, clock, "h8h_8bits_timer_channel", __FILE__)
+{
+}
+
+h8h_timer8_channel_device::~h8h_timer8_channel_device()
+{
+}
+
+void h8h_timer8_channel_device::set_info(const char *intc, int _irq_ca, int _irq_cb, int _irq_v, const char *_chain_tag, int _chain_type, bool _has_adte, bool _has_ice)
+{
+ intc_tag = intc;
+ irq_ca = _irq_ca;
+ irq_cb = _irq_cb;
+ irq_v = _irq_v;
+ chain_tag = _chain_tag;
+ chain_type = _chain_type;
+ has_adte = _has_adte;
+ has_ice = _has_ice;
+ // The extra clock bit is not used for h8h+
+ div_tab[0] = 8;
+ div_tab[1] = 8;
+ div_tab[2] = 64;
+ div_tab[3] = 64;
+ div_tab[4] = 8192;
+ div_tab[5] = 8192;
+}