sh2: implement watchdog timer

This commit is contained in:
MetalliC 2019-11-07 10:25:21 +02:00
parent 9404607605
commit 80aa59a46e
3 changed files with 58 additions and 1 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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;