summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/sh
diff options
context:
space:
mode:
author MetalliC <0vetal0@gmail.com>2019-11-07 10:25:21 +0200
committer MetalliC <0vetal0@gmail.com>2019-11-07 10:25:21 +0200
commit80aa59a46e4da52fce22bc4bf9c07f6baa497d36 (patch)
treea8ef0bbf1fb2688de8787a0076cd49378a3a5180 /src/devices/cpu/sh
parent9404607605f82e64af0933123b800ce46b604aa5 (diff)
sh2: implement watchdog timer
Diffstat (limited to 'src/devices/cpu/sh')
-rw-r--r--src/devices/cpu/sh/sh2.cpp2
-rw-r--r--src/devices/cpu/sh/sh2.h4
-rw-r--r--src/devices/cpu/sh/sh2comn.cpp53
3 files changed, 58 insertions, 1 deletions
diff --git a/src/devices/cpu/sh/sh2.cpp b/src/devices/cpu/sh/sh2.cpp
index b9be01f3c7e..eddf593b2db 100644
--- a/src/devices/cpu/sh/sh2.cpp
+++ b/src/devices/cpu/sh/sh2.cpp
@@ -515,6 +515,8 @@ void sh2_device::device_start()
m_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(sh2_device::sh2_timer_callback), this));
m_timer->adjust(attotime::never);
+ m_wdtimer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(sh2_device::sh2_wdtimer_callback), this));
+ m_wdtimer->adjust(attotime::never);
m_dma_current_active_timer[0] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(sh2_device::sh2_dma_current_active_callback), this));
m_dma_current_active_timer[0]->adjust(attotime::never);
diff --git a/src/devices/cpu/sh/sh2.h b/src/devices/cpu/sh/sh2.h
index fd4e9c01bcd..a83bea94646 100644
--- a/src/devices/cpu/sh/sh2.h
+++ b/src/devices/cpu/sh/sh2.h
@@ -302,6 +302,7 @@ private:
int m_internal_irq_vector;
emu_timer *m_timer;
+ emu_timer *m_wdtimer;
emu_timer *m_dma_current_active_timer[2];
int m_dma_timer_active[2];
uint8_t m_dma_irq[2];
@@ -339,9 +340,12 @@ private:
virtual void execute_one_f000(uint16_t opcode) override;
TIMER_CALLBACK_MEMBER( sh2_timer_callback );
+ TIMER_CALLBACK_MEMBER( sh2_wdtimer_callback );
TIMER_CALLBACK_MEMBER( sh2_dma_current_active_callback );
void sh2_timer_resync();
void sh2_timer_activate();
+ void sh2_wtcnt_recalc();
+ void sh2_wdt_activate();
void sh2_do_dma(int dmach);
virtual void sh2_exception(const char *message, int irqline) override;
void sh2_dmac_check(int dma);
diff --git a/src/devices/cpu/sh/sh2comn.cpp b/src/devices/cpu/sh/sh2comn.cpp
index bb17a818c17..fc39aacd1f0 100644
--- a/src/devices/cpu/sh/sh2comn.cpp
+++ b/src/devices/cpu/sh/sh2comn.cpp
@@ -18,6 +18,7 @@
#include "logmacro.h"
static const int div_tab[4] = { 3, 5, 7, 0 };
+static const int wdtclk_tab[8] = { 1, 6, 7, 8, 9, 10, 12, 13 };
void sh2_device::sh2_timer_resync()
@@ -100,6 +101,32 @@ TIMER_CALLBACK_MEMBER( sh2_device::sh2_timer_callback )
sh2_timer_activate();
}
+void sh2_device::sh2_wtcnt_recalc()
+{
+ if (m_wdtimer->expire() != attotime::never)
+ m_wtcnt = 0x100 - (attotime_to_cycles(m_wdtimer->remaining()) >> wdtclk_tab[m_wtcsr & 7]);
+}
+
+void sh2_device::sh2_wdt_activate()
+{
+ m_wdtimer->adjust(cycles_to_attotime((0x100 - m_wtcnt) << wdtclk_tab[m_wtcsr & 7]));
+}
+
+TIMER_CALLBACK_MEMBER(sh2_device::sh2_wdtimer_callback)
+{
+ m_wtcnt = 0;
+ if (!(m_wtcsr & 0x40)) // timer mode
+ {
+ m_wtcsr |= 0x80;
+ sh2_recalc_irq();
+ sh2_wdt_activate();
+ }
+ else // watchdog mode
+ {
+ m_rstcsr |= 0x80;
+ // TODO reset and /WDTOVF out
+ }
+}
/*
We have to do DMA on a timer (or at least, in chunks) due to the way some systems use it.
@@ -841,6 +868,7 @@ WRITE32_MEMBER( sh2_device::dvdntl_w )
READ16_MEMBER( sh2_device::wtcnt_r )
{
+ sh2_wtcnt_recalc();
return ((m_wtcsr | 0x18) << 8) | (m_wtcnt & 0xff);
}
@@ -856,6 +884,8 @@ WRITE16_MEMBER( sh2_device::wtcnt_w )
{
case 0x5a00:
m_wtcnt = m_wtcw[0] & 0xff;
+ if (m_wtcsr & 0x20)
+ sh2_wdt_activate();
break;
case 0xa500:
/*
@@ -866,7 +896,17 @@ WRITE16_MEMBER( sh2_device::wtcnt_w )
---1 1---
---- -xxx Clock select
*/
- m_wtcsr = m_wtcw[0] & 0xff;
+ sh2_wtcnt_recalc();
+ m_wtcsr &= m_wtcw[0] & 0x80;
+ m_wtcsr |= m_wtcw[0] & 0x7f;
+ if (m_wtcsr & 0x20)
+ sh2_wdt_activate();
+ else
+ {
+ m_wtcnt = 0;
+ m_wdtimer->adjust(attotime::never);
+ }
+ sh2_recalc_irq();
break;
}
}
@@ -1049,6 +1089,17 @@ void sh2_device::sh2_recalc_irq()
}
}
+ // WDT irqs
+ if (m_wtcsr & 0x80)
+ {
+ level = m_irq_level.wdt & 15;
+ if (level > irq)
+ {
+ irq = level;
+ vector = (m_vcrwdt >> 8) & 0x7f;
+ }
+ }
+
// DMA irqs
if((m_dmac[0].chcr & 6) == 6 && m_dma_irq[0]) {
level = m_irq_level.dmac & 15;