diff options
Diffstat (limited to 'src/devices/cpu/sh/sh7604.cpp')
-rw-r--r-- | src/devices/cpu/sh/sh7604.cpp | 1540 |
1 files changed, 1540 insertions, 0 deletions
diff --git a/src/devices/cpu/sh/sh7604.cpp b/src/devices/cpu/sh/sh7604.cpp new file mode 100644 index 00000000000..8bd1e59970c --- /dev/null +++ b/src/devices/cpu/sh/sh7604.cpp @@ -0,0 +1,1540 @@ +// license:BSD-3-Clause +// copyright-holders:Juergen Buchmueller, R. Belmont +/***************************************************************************** + * + * sh7604.cpp + * Portable Hitachi SH-2 (SH7600 family) emulator + * + * This work is based on <tiraniddo@hotmail.com> C/C++ implementation of + * the SH-2 CPU core and was adapted to the MAME CPU core requirements. + * Thanks also go to Chuck Mason <chukjr@sundail.net> and Olivier Galibert + * <galibert@pobox.com> for letting me peek into their SEMU code :-) + * + *****************************************************************************/ +/* +TODO: Test and use sh7604_wdt_device, sh7604_sci_device, and sh7604_bus_device as appropriate +*/ + + +#include "emu.h" + +#include "sh7604.h" + +//#define VERBOSE 1 +#include "logmacro.h" + + +static constexpr int div_tab[4] = { 3, 5, 7, 0 }; +static constexpr int wdtclk_tab[8] = { 1, 6, 7, 8, 9, 10, 12, 13 }; + + +DEFINE_DEVICE_TYPE(SH7604, sh7604_device, "sh2_7604", "Hitachi SH-2 (SH7604)") + + +sh7604_device::sh7604_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : sh2_device(mconfig, SH7604, tag, owner, clock, CPU_TYPE_SH2, address_map_constructor(FUNC(sh7604_device::sh7604_map), this), 32, 0xc7ffffff) + , m_test_irq(0), m_internal_irq_vector(0) + , m_smr(0), m_brr(0), m_scr(0), m_tdr(0), m_ssr(0) + , m_tier(0), m_ftcsr(0), m_frc_tcr(0), m_tocr(0), m_frc(0), m_ocra(0), m_ocrb(0), m_frc_icr(0) + , m_ipra(0), m_iprb(0), m_vcra(0), m_vcrb(0), m_vcrc(0), m_vcrd(0), m_vcrwdt(0), m_vcrdiv(0), m_intc_icr(0), m_vecmd(false), m_nmie(false) + , m_divu_ovf(false), m_divu_ovfie(false), m_dvsr(0), m_dvdntl(0), m_dvdnth(0) + , m_wtcnt(0), m_wtcsr(0), m_rstcsr(0) + , m_dmaor(0) + , m_sbycr(0), m_ccr(0) + , m_bcr1(0), m_bcr2(0), m_wcr(0), m_mcr(0), m_rtcsr(0), m_rtcor(0), m_rtcnt(0) + , m_frc_base(0), m_frt_input(0) + , m_timer(nullptr), m_wdtimer(nullptr) + , m_is_slave(0) + , m_dma_kludge_cb(*this) + , m_dma_fifo_data_available_cb(*this) + , m_ftcsr_read_cb(*this) +{ + std::fill(std::begin(m_vcrdma), std::end(m_vcrdma), 0); + std::fill(std::begin(m_dma_timer_active), std::end(m_dma_timer_active), 0); + std::fill(std::begin(m_dma_irq), std::end(m_dma_irq), 0); + std::fill(std::begin(m_active_dma_incs), std::end(m_active_dma_incs), 0); + std::fill(std::begin(m_active_dma_incd), std::end(m_active_dma_incd), 0); + std::fill(std::begin(m_active_dma_size), std::end(m_active_dma_size), 0); + std::fill(std::begin(m_active_dma_steal), std::end(m_active_dma_steal), 0); + std::fill(std::begin(m_active_dma_src), std::end(m_active_dma_src), 0); + std::fill(std::begin(m_active_dma_dst), std::end(m_active_dma_dst), 0); + std::fill(std::begin(m_active_dma_count), std::end(m_active_dma_count), 0); + std::fill(std::begin(m_wtcw), std::end(m_wtcw), 0); + std::fill(std::begin(m_dma_current_active_timer), std::end(m_dma_current_active_timer), nullptr); + + m_irq_vector.fic = m_irq_vector.foc = m_irq_vector.fov = m_irq_vector.divu = 0; + std::fill(std::begin(m_irq_vector.dmac), std::end(m_irq_vector.dmac), 0); + + m_irq_level.frc = m_irq_level.sci = m_irq_level.divu = m_irq_level.dmac = m_irq_level.wdt = 0; + + for (int i = 0; i < 2; i++) + m_dmac[i].drcr = m_dmac[i].sar = m_dmac[i].dar = m_dmac[i].tcr = m_dmac[i].chcr = 0; +} + +void sh7604_device::device_start() +{ + sh2_device::device_start(); + + m_timer = timer_alloc(FUNC(sh7604_device::sh2_timer_callback), this); + m_timer->adjust(attotime::never); + m_wdtimer = timer_alloc(FUNC(sh7604_device::sh2_wdtimer_callback), this); + m_wdtimer->adjust(attotime::never); + + m_dma_current_active_timer[0] = timer_alloc(FUNC(sh7604_device::sh2_dma_current_active_callback), this); + m_dma_current_active_timer[0]->adjust(attotime::never); + + m_dma_current_active_timer[1] = timer_alloc(FUNC(sh7604_device::sh2_dma_current_active_callback), this); + m_dma_current_active_timer[1]->adjust(attotime::never); + + /* resolve callbacks */ + m_dma_kludge_cb.resolve(); + m_dma_fifo_data_available_cb.resolve(); + m_ftcsr_read_cb.resolve(); + + // SCI + save_item(NAME(m_smr)); + save_item(NAME(m_brr)); + save_item(NAME(m_scr)); + save_item(NAME(m_tdr)); + save_item(NAME(m_ssr)); + + // FRT / FRC + save_item(NAME(m_tier)); + save_item(NAME(m_ftcsr)); + save_item(NAME(m_frc_tcr)); + save_item(NAME(m_tocr)); + save_item(NAME(m_frc)); + save_item(NAME(m_ocra)); + save_item(NAME(m_ocrb)); + save_item(NAME(m_frc_icr)); + save_item(NAME(m_frc_base)); + save_item(NAME(m_frt_input)); + + // INTC + save_item(NAME(m_irq_level.frc)); + save_item(NAME(m_irq_level.sci)); + save_item(NAME(m_irq_level.divu)); + save_item(NAME(m_irq_level.dmac)); + save_item(NAME(m_irq_level.wdt)); + save_item(NAME(m_irq_vector.fic)); + save_item(NAME(m_irq_vector.foc)); + save_item(NAME(m_irq_vector.fov)); + save_item(NAME(m_irq_vector.divu)); + save_item(NAME(m_irq_vector.dmac)); + + save_item(NAME(m_ipra)); + save_item(NAME(m_iprb)); + save_item(NAME(m_vcra)); + save_item(NAME(m_vcrb)); + save_item(NAME(m_vcrc)); + save_item(NAME(m_vcrd)); + save_item(NAME(m_vcrwdt)); + save_item(NAME(m_vcrdiv)); + save_item(NAME(m_intc_icr)); + save_item(NAME(m_vcrdma)); + + save_item(NAME(m_vecmd)); + save_item(NAME(m_nmie)); + + // DIVU + save_item(NAME(m_divu_ovf)); + save_item(NAME(m_divu_ovfie)); + save_item(NAME(m_dvsr)); + save_item(NAME(m_dvdntl)); + save_item(NAME(m_dvdnth)); + + // WTC + save_item(NAME(m_wtcnt)); + save_item(NAME(m_wtcsr)); + save_item(NAME(m_rstcsr)); + save_item(NAME(m_wtcw)); + + // DMAC + save_item(NAME(m_dmaor)); + save_item(STRUCT_MEMBER(m_dmac, drcr)); + save_item(STRUCT_MEMBER(m_dmac, sar)); + save_item(STRUCT_MEMBER(m_dmac, dar)); + save_item(STRUCT_MEMBER(m_dmac, tcr)); + save_item(STRUCT_MEMBER(m_dmac, chcr)); + + // misc + save_item(NAME(m_sbycr)); + save_item(NAME(m_ccr)); + + // BSC + save_item(NAME(m_bcr1)); + save_item(NAME(m_bcr2)); + save_item(NAME(m_wcr)); + save_item(NAME(m_mcr)); + save_item(NAME(m_rtcsr)); + save_item(NAME(m_rtcor)); + save_item(NAME(m_rtcnt)); +} + +void sh7604_device::device_reset() +{ + sh2_device::device_reset(); + + m_frc = 0; + m_ocra = 0; + m_ocrb = 0; + m_frc_icr = 0; + m_frc_base = 0; + m_frt_input = 0; + + for (int i = 0; i < 2; i++) + { + m_dma_timer_active[i] = 0; + m_dma_irq[i] = 0; + m_active_dma_incs[i] = 0; + m_active_dma_incd[i] = 0; + m_active_dma_size[i] = 0; + m_active_dma_steal[i] = 0; + m_active_dma_src[i] = 0; + m_active_dma_dst[i] = 0; + m_active_dma_count[i] = 0; + } + + m_wtcnt = 0; + m_wtcsr = 0; +} + +void sh7604_device::sh7604_map(address_map &map) +{ + map(0x40000000, 0xbfffffff).r(FUNC(sh7604_device::sh2_internal_a5)); + +// TODO: cps3boot breaks with this enabled. Needs callback +// map(0xc0000000, 0xc0000fff).ram(); // cache data array + +// map(0xe0000000, 0xe00001ff).mirror(0x1ffffe00).rw(FUNC(sh7604_device::sh7604_r), FUNC(sh7604_device::sh7604_w)); + // TODO: internal map takes way too much resources if mirrored with 0x1ffffe00 + // we eventually internalize again via trampoline & sh7604_device + // Also area 0xffff8000-0xffffbfff is for synchronous DRAM mode, + // so this isn't actually a full mirror + // SCI + map(0xfffffe00, 0xfffffe00).rw(FUNC(sh7604_device::smr_r), FUNC(sh7604_device::smr_w)); + map(0xfffffe01, 0xfffffe01).rw(FUNC(sh7604_device::brr_r), FUNC(sh7604_device::brr_w)); + map(0xfffffe02, 0xfffffe02).rw(FUNC(sh7604_device::scr_r), FUNC(sh7604_device::scr_w)); + map(0xfffffe03, 0xfffffe03).rw(FUNC(sh7604_device::tdr_r), FUNC(sh7604_device::tdr_w)); + map(0xfffffe04, 0xfffffe04).rw(FUNC(sh7604_device::ssr_r), FUNC(sh7604_device::ssr_w)); + map(0xfffffe05, 0xfffffe05).r(FUNC(sh7604_device::rdr_r)); + + // FRC + map(0xfffffe10, 0xfffffe10).rw(FUNC(sh7604_device::tier_r), FUNC(sh7604_device::tier_w)); + map(0xfffffe11, 0xfffffe11).rw(FUNC(sh7604_device::ftcsr_r), FUNC(sh7604_device::ftcsr_w)); + map(0xfffffe12, 0xfffffe13).rw(FUNC(sh7604_device::frc_r), FUNC(sh7604_device::frc_w)); + map(0xfffffe14, 0xfffffe15).rw(FUNC(sh7604_device::ocra_b_r), FUNC(sh7604_device::ocra_b_w)); + map(0xfffffe16, 0xfffffe16).rw(FUNC(sh7604_device::frc_tcr_r), FUNC(sh7604_device::frc_tcr_w)); + map(0xfffffe17, 0xfffffe17).rw(FUNC(sh7604_device::tocr_r), FUNC(sh7604_device::tocr_w)); + map(0xfffffe18, 0xfffffe19).r(FUNC(sh7604_device::frc_icr_r)); + + // INTC + map(0xfffffe60, 0xfffffe61).rw(FUNC(sh7604_device::iprb_r), FUNC(sh7604_device::iprb_w)); + map(0xfffffe62, 0xfffffe63).rw(FUNC(sh7604_device::vcra_r), FUNC(sh7604_device::vcra_w)); + map(0xfffffe64, 0xfffffe65).rw(FUNC(sh7604_device::vcrb_r), FUNC(sh7604_device::vcrb_w)); + map(0xfffffe66, 0xfffffe67).rw(FUNC(sh7604_device::vcrc_r), FUNC(sh7604_device::vcrc_w)); + map(0xfffffe68, 0xfffffe69).rw(FUNC(sh7604_device::vcrd_r), FUNC(sh7604_device::vcrd_w)); + + map(0xfffffe71, 0xfffffe71).rw(FUNC(sh7604_device::drcr_r<0>), FUNC(sh7604_device::drcr_w<0>)); + map(0xfffffe72, 0xfffffe72).rw(FUNC(sh7604_device::drcr_r<1>), FUNC(sh7604_device::drcr_w<1>)); + + // WTC + map(0xfffffe80, 0xfffffe81).rw(FUNC(sh7604_device::wtcnt_r), FUNC(sh7604_device::wtcnt_w)); + map(0xfffffe82, 0xfffffe83).rw(FUNC(sh7604_device::rstcsr_r), FUNC(sh7604_device::rstcsr_w)); + + // standby and cache control + map(0xfffffe90, 0xfffffe91).rw(FUNC(sh7604_device::fmr_sbycr_r), FUNC(sh7604_device::fmr_sbycr_w)); + map(0xfffffe92, 0xfffffe92).rw(FUNC(sh7604_device::ccr_r), FUNC(sh7604_device::ccr_w)); + + // INTC second section + map(0xfffffee0, 0xfffffee1).rw(FUNC(sh7604_device::intc_icr_r), FUNC(sh7604_device::intc_icr_w)); + map(0xfffffee2, 0xfffffee3).rw(FUNC(sh7604_device::ipra_r), FUNC(sh7604_device::ipra_w)); + map(0xfffffee4, 0xfffffee5).rw(FUNC(sh7604_device::vcrwdt_r), FUNC(sh7604_device::vcrwdt_w)); + + // DIVU + map(0xffffff00, 0xffffff03).rw(FUNC(sh7604_device::dvsr_r), FUNC(sh7604_device::dvsr_w)); + map(0xffffff04, 0xffffff07).rw(FUNC(sh7604_device::dvdnt_r), FUNC(sh7604_device::dvdnt_w)); + map(0xffffff08, 0xffffff0b).rw(FUNC(sh7604_device::dvcr_r), FUNC(sh7604_device::dvcr_w)); + // INTC third section + map(0xffffff0c, 0xffffff0f).rw(FUNC(sh7604_device::vcrdiv_r), FUNC(sh7604_device::vcrdiv_w)); + // DIVU continued (64-bit plus mirrors) + map(0xffffff10, 0xffffff13).rw(FUNC(sh7604_device::dvdnth_r), FUNC(sh7604_device::dvdnth_w)); + map(0xffffff14, 0xffffff17).rw(FUNC(sh7604_device::dvdntl_r), FUNC(sh7604_device::dvdntl_w)); + map(0xffffff18, 0xffffff1b).r(FUNC(sh7604_device::dvdnth_r)); + map(0xffffff1c, 0xffffff1f).r(FUNC(sh7604_device::dvdntl_r)); + + // DMAC + map(0xffffff80, 0xffffff83).rw(FUNC(sh7604_device::sar_r<0>), FUNC(sh7604_device::sar_w<0>)); + map(0xffffff84, 0xffffff87).rw(FUNC(sh7604_device::dar_r<0>), FUNC(sh7604_device::dar_w<0>)); + map(0xffffff88, 0xffffff8b).rw(FUNC(sh7604_device::dmac_tcr_r<0>), FUNC(sh7604_device::dmac_tcr_w<0>)); + map(0xffffff8c, 0xffffff8f).rw(FUNC(sh7604_device::chcr_r<0>), FUNC(sh7604_device::chcr_w<0>)); + + map(0xffffff90, 0xffffff93).rw(FUNC(sh7604_device::sar_r<1>), FUNC(sh7604_device::sar_w<1>)); + map(0xffffff94, 0xffffff97).rw(FUNC(sh7604_device::dar_r<1>), FUNC(sh7604_device::dar_w<1>)); + map(0xffffff98, 0xffffff9b).rw(FUNC(sh7604_device::dmac_tcr_r<1>), FUNC(sh7604_device::dmac_tcr_w<1>)); + map(0xffffff9c, 0xffffff9f).rw(FUNC(sh7604_device::chcr_r<1>), FUNC(sh7604_device::chcr_w<1>)); + + map(0xffffffa0, 0xffffffa3).rw(FUNC(sh7604_device::vcrdma_r<0>), FUNC(sh7604_device::vcrdma_w<0>)); + map(0xffffffa8, 0xffffffab).rw(FUNC(sh7604_device::vcrdma_r<1>), FUNC(sh7604_device::vcrdma_w<1>)); + map(0xffffffb0, 0xffffffb3).rw(FUNC(sh7604_device::dmaor_r), FUNC(sh7604_device::dmaor_w)); + + // BSC + map(0xffffffe0, 0xffffffe3).rw(FUNC(sh7604_device::bcr1_r), FUNC(sh7604_device::bcr1_w)); + map(0xffffffe4, 0xffffffe7).rw(FUNC(sh7604_device::bcr2_r), FUNC(sh7604_device::bcr2_w)); + map(0xffffffe8, 0xffffffeb).rw(FUNC(sh7604_device::wcr_r), FUNC(sh7604_device::wcr_w)); + map(0xffffffec, 0xffffffef).rw(FUNC(sh7604_device::mcr_r), FUNC(sh7604_device::mcr_w)); + map(0xfffffff0, 0xfffffff3).rw(FUNC(sh7604_device::rtcsr_r), FUNC(sh7604_device::rtcsr_w)); + map(0xfffffff4, 0xfffffff7).rw(FUNC(sh7604_device::rtcnt_r), FUNC(sh7604_device::rtcnt_w)); + map(0xfffffff8, 0xfffffffb).rw(FUNC(sh7604_device::rtcor_r), FUNC(sh7604_device::rtcor_w)); +} + + +void sh7604_device::sh2_exception(const char *message, int irqline) +{ + int vector; + + if (irqline != 16) + { + if (irqline <= ((m_sh2_state->sr >> 4) & 15)) /* If the cpu forbids this interrupt */ + return; + + // if this is an sh2 internal irq, use its vector + if (m_sh2_state->internal_irq_level == irqline) + { + vector = m_internal_irq_vector; + /* avoid spurious irqs with this (TODO: needs a better fix) */ + m_sh2_state->internal_irq_level = -1; + LOG("SH-2 exception #%d (internal vector: $%x) after [%s]\n", irqline, vector, message); + } + else + { + if (m_vecmd) + { + vector = standard_irq_callback(irqline, m_sh2_state->pc); + LOG("SH-2 exception #%d (external vector: $%x) after [%s]\n", irqline, vector, message); + } + else + { + standard_irq_callback(irqline, m_sh2_state->pc); + vector = 64 + irqline/2; + LOG("SH-2 exception #%d (autovector: $%x) after [%s]\n", irqline, vector, message); + } + } + } + else + { + vector = 11; + LOG("SH-2 nmi exception (autovector: $%x) after [%s]\n", vector, message); + } + + sh2_exception_internal(message, irqline, vector); +} + +uint32_t sh7604_device::sh2_internal_a5() +{ + return 0xa5a5a5a5; +} + +void sh7604_device::sh2_timer_resync() +{ + // TODO: setting 3 is "External clock: count on rising edge" + int divider = div_tab[m_frc_tcr & 3]; + uint64_t cur_time = total_cycles(); + uint64_t add = (cur_time - m_frc_base) >> divider; + + if (add > 0) + { + if (divider) + m_frc += add; + + m_frc_base = cur_time; + } +} + +void sh7604_device::sh2_timer_activate() +{ + int max_delta = 0xfffff; + + m_timer->adjust(attotime::never); + + uint16_t frc = m_frc; + if (!(m_ftcsr & OCFA)) + { + uint16_t delta = m_ocra - frc; + if (delta < max_delta) + max_delta = delta; + } + + if (!(m_ftcsr & OCFB) && (m_ocra <= m_ocrb || !(m_ftcsr & CCLRA))) + { + uint16_t delta = m_ocrb - frc; + if (delta < max_delta) + max_delta = delta; + } + + if (!(m_ftcsr & OVF) && !(m_ftcsr & CCLRA)) + { + int delta = 0x10000 - frc; + if (delta < max_delta) + max_delta = delta; + } + + if (max_delta != 0xfffff) + { + int divider = div_tab[m_frc_tcr & 3]; + if (divider) + { + max_delta <<= divider; + m_frc_base = total_cycles(); + m_timer->adjust(cycles_to_attotime(max_delta)); + } + else + { + logerror("SH2.%s: Timer event in %d cycles of external clock", tag(), max_delta); + } + } +} + +TIMER_CALLBACK_MEMBER(sh7604_device::sh2_timer_callback) +{ + sh2_timer_resync(); + uint16_t frc = m_frc; + + if (frc == m_ocrb) + m_ftcsr |= OCFB; + + if (frc == 0x0000) + m_ftcsr |= OVF; + + if (frc == m_ocra) + { + m_ftcsr |= OCFA; + + if (m_ftcsr & CCLRA) + m_frc = 0; + } + + sh2_recalc_irq(); + sh2_timer_activate(); +} + +void sh7604_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 sh7604_device::sh2_wdt_activate() +{ + m_wdtimer->adjust(cycles_to_attotime((0x100 - m_wtcnt) << wdtclk_tab[m_wtcsr & 7])); +} + +TIMER_CALLBACK_MEMBER(sh7604_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. + The 32x is a difficult case, they set the SOURCE of the DMA to a FIFO buffer, which at most + can have 8 words in it. Attempting to do an 'instant DMA' in this scenario is impossible + because the game is expecting the 68k of the system to feed data into the FIFO at the same + time as the SH2 is transfering it out via DMA + + There are two ways we can do this + + a) with a high frequency timer (more accurate, but a large performance hit) + + or + + b) in the CPU_EXECUTE loop + + + we're currently doing a) + + b) causes problems with ST-V games + +*/ + + + +void sh7604_device::sh2_notify_dma_data_available() +{ + //printf("call notify\n"); + + for (int dmach=0;dmach<2;dmach++) + { + //printf("m_dma_timer_active[dmach] %04x\n", m_dma_timer_active[dmach]); + + if (m_dma_timer_active[dmach]==2) // 2 = stalled + { + //printf("resuming stalled dma\n"); + m_dma_timer_active[dmach]=1; + m_dma_current_active_timer[dmach]->adjust(attotime::zero, dmach); + } + } + +} + +void sh7604_device::sh2_do_dma(int dmach) +{ + if (m_active_dma_count[dmach] > 0) + { + // process current DMA + switch (m_active_dma_size[dmach]) + { + case 0: + { + // we need to know the src / dest ahead of time without changing them + // to allow for the callback to check if we can process the DMA at this + // time (we need to know where we're reading / writing to/from) + + uint32_t tempsrc = m_active_dma_src[dmach]; + if (m_active_dma_incs[dmach] == 2) + tempsrc--; + + uint32_t tempdst = m_active_dma_dst[dmach]; + if (m_active_dma_incd[dmach] == 2) + tempdst--; + + if (!m_dma_fifo_data_available_cb.isnull()) + { + int available = m_dma_fifo_data_available_cb(tempsrc, tempdst, 0, m_active_dma_size[dmach]); + + if (!available) + { + //printf("dma stalled\n"); + m_dma_timer_active[dmach] = 2; // mark as stalled + return; + } + } + + //schedule next DMA callback + m_dma_current_active_timer[dmach]->adjust(cycles_to_attotime(2), dmach); + + uint32_t dmadata = m_program->read_byte(tempsrc); + if (!m_dma_kludge_cb.isnull()) + dmadata = m_dma_kludge_cb(tempsrc, tempdst, dmadata, m_active_dma_size[dmach]); + m_program->write_byte(tempdst, dmadata); + + if (m_active_dma_incs[dmach] == 2) + m_active_dma_src[dmach]--; + if (m_active_dma_incd[dmach] == 2) + m_active_dma_dst[dmach]--; + + if (m_active_dma_incs[dmach] == 1) + m_active_dma_src[dmach]++; + if (m_active_dma_incd[dmach] == 1) + m_active_dma_dst[dmach]++; + + m_active_dma_count[dmach]--; + break; + } + + case 1: + { + uint32_t tempsrc = m_active_dma_src[dmach]; + if (m_active_dma_incs[dmach] == 2) + tempsrc -= 2; + + uint32_t tempdst = m_active_dma_dst[dmach]; + if (m_active_dma_incd[dmach] == 2) + tempdst -= 2; + + if (!m_dma_fifo_data_available_cb.isnull()) + { + int available = m_dma_fifo_data_available_cb(tempsrc, tempdst, 0, m_active_dma_size[dmach]); + + if (!available) + { + //printf("dma stalled\n"); + m_dma_timer_active[dmach] = 2; // mark as stalled + return; + } + } + + //schedule next DMA callback + m_dma_current_active_timer[dmach]->adjust(cycles_to_attotime(2), dmach); + + // check: should this really be using read_word_32 / write_word_32? + uint32_t dmadata = m_program->read_word(tempsrc); + if (!m_dma_kludge_cb.isnull()) + dmadata = m_dma_kludge_cb(tempsrc, tempdst, dmadata, m_active_dma_size[dmach]); + m_program->write_word(tempdst, dmadata); + + if (m_active_dma_incs[dmach] == 2) + m_active_dma_src[dmach] -= 2; + if (m_active_dma_incd[dmach] == 2) + m_active_dma_dst[dmach] -= 2; + + if (m_active_dma_incs[dmach] == 1) + m_active_dma_src[dmach] += 2; + if (m_active_dma_incd[dmach] == 1) + m_active_dma_dst[dmach] += 2; + + m_active_dma_count[dmach]--; + break; + } + + case 2: + { + uint32_t tempsrc = m_active_dma_src[dmach]; + if (m_active_dma_incs[dmach] == 2) + tempsrc -= 4; + + uint32_t tempdst = m_active_dma_dst[dmach]; + if (m_active_dma_incd[dmach] == 2) + tempdst -= 4; + + if (!m_dma_fifo_data_available_cb.isnull()) + { + int available = m_dma_fifo_data_available_cb(tempsrc, tempdst, 0, m_active_dma_size[dmach]); + + if (!available) + { + //printf("dma stalled\n"); + m_dma_timer_active[dmach] = 2; // mark as stalled + return; + } + } + + //schedule next DMA callback + m_dma_current_active_timer[dmach]->adjust(cycles_to_attotime(2), dmach); + + uint32_t dmadata = m_program->read_dword(tempsrc); + if (!m_dma_kludge_cb.isnull()) + dmadata = m_dma_kludge_cb(tempsrc, tempdst, dmadata, m_active_dma_size[dmach]); + m_program->write_dword(tempdst, dmadata); + + if (m_active_dma_incs[dmach] == 2) + m_active_dma_src[dmach] -= 4; + if (m_active_dma_incd[dmach] == 2) + m_active_dma_dst[dmach] -= 4; + + if (m_active_dma_incs[dmach] == 1) + m_active_dma_src[dmach] += 4; + if (m_active_dma_incd[dmach] == 1) + m_active_dma_dst[dmach] += 4; + + m_active_dma_count[dmach]--; + break; + } + + case 3: + { + // shouldn't this really be 4 calls here instead? + + uint32_t tempsrc = m_active_dma_src[dmach]; + + uint32_t tempdst = m_active_dma_dst[dmach]; + if (m_active_dma_incd[dmach] == 2) + tempdst -= 16; + + if (!m_dma_fifo_data_available_cb.isnull()) + { + int available = m_dma_fifo_data_available_cb(tempsrc, tempdst, 0, m_active_dma_size[dmach]); + + if (!available) + { + //printf("dma stalled\n"); + m_dma_timer_active[dmach] = 2; // mark as stalled + fatalerror("SH2 dma_callback_fifo_data_available == 0 in unsupported mode\n"); + } + } + + //schedule next DMA callback + m_dma_current_active_timer[dmach]->adjust(cycles_to_attotime(2), dmach); + + uint32_t dmadata = m_program->read_dword(tempsrc); + if (!m_dma_kludge_cb.isnull()) + dmadata = m_dma_kludge_cb(tempsrc, tempdst, dmadata, m_active_dma_size[dmach]); + m_program->write_dword(tempdst, dmadata); + + dmadata = m_program->read_dword(tempsrc + 4); + if (!m_dma_kludge_cb.isnull()) + dmadata = m_dma_kludge_cb(tempsrc, tempdst, dmadata, m_active_dma_size[dmach]); + m_program->write_dword(tempdst + 4, dmadata); + + dmadata = m_program->read_dword(tempsrc + 8); + if (!m_dma_kludge_cb.isnull()) + dmadata = m_dma_kludge_cb(tempsrc, tempdst, dmadata, m_active_dma_size[dmach]); + m_program->write_dword(tempdst + 8, dmadata); + + dmadata = m_program->read_dword(tempsrc + 12); + if (!m_dma_kludge_cb.isnull()) + dmadata = m_dma_kludge_cb(tempsrc, tempdst, dmadata, m_active_dma_size[dmach]); + m_program->write_dword(tempdst + 12, dmadata); + + if (m_active_dma_incd[dmach] == 2) + m_active_dma_dst[dmach] -= 16; + + m_active_dma_src[dmach] += 16; + if (m_active_dma_incd[dmach] == 1) + m_active_dma_dst[dmach] += 16; + + m_active_dma_count[dmach] -= 4; + break; + } + } + } + else // the dma is complete + { + // int dma = param & 1; + + // fever soccer uses cycle-stealing mode, resume the CPU now DMA has finished + if (m_active_dma_steal[dmach]) + { + resume(SUSPEND_REASON_HALT); + } + + LOG("SH2: DMA %d complete\n", dmach); + m_dmac[dmach].tcr = 0; + m_dmac[dmach].chcr |= 2; + m_dma_timer_active[dmach] = 0; + m_dma_irq[dmach] |= 1; + sh2_recalc_irq(); + + } +} + +TIMER_CALLBACK_MEMBER(sh7604_device::sh2_dma_current_active_callback) +{ + sh2_do_dma(param & 1); +} + + +void sh7604_device::sh2_dmac_check(int dmach) +{ + if (m_dmac[dmach].chcr & m_dmaor & 1) + { + if (!m_dma_timer_active[dmach] && !(m_dmac[dmach].chcr & 2)) + { + m_active_dma_incd[dmach] = (m_dmac[dmach].chcr >> 14) & 3; + m_active_dma_incs[dmach] = (m_dmac[dmach].chcr >> 12) & 3; + m_active_dma_size[dmach] = (m_dmac[dmach].chcr >> 10) & 3; + m_active_dma_steal[dmach] = (m_dmac[dmach].chcr & 0x10); + + if (m_active_dma_incd[dmach] == 3 || m_active_dma_incs[dmach] == 3) + { + LOG("SH2: DMA: bad increment values (%d, %d, %d, %04x)\n", m_active_dma_incd[dmach], m_active_dma_incs[dmach], m_active_dma_size[dmach], m_dmac[dmach].chcr); + return; + } + m_active_dma_src[dmach] = m_dmac[dmach].sar; + m_active_dma_dst[dmach] = m_dmac[dmach].dar; + m_active_dma_count[dmach] = m_dmac[dmach].tcr; + if (!m_active_dma_count[dmach]) + m_active_dma_count[dmach] = 0x1000000; + + LOG("SH2: DMA %d start %x, %x, %x, %04x, %d, %d, %d\n", dmach, m_active_dma_src[dmach], m_active_dma_dst[dmach], m_active_dma_count[dmach], m_dmac[dmach].chcr, m_active_dma_incs[dmach], m_active_dma_incd[dmach], m_active_dma_size[dmach]); + + m_dma_timer_active[dmach] = 1; + + m_active_dma_src[dmach] &= m_am; + m_active_dma_dst[dmach] &= m_am; + + switch (m_active_dma_size[dmach]) + { + case 0: + break; + case 1: + m_active_dma_src[dmach] &= ~1; + m_active_dma_dst[dmach] &= ~1; + break; + case 2: + m_active_dma_src[dmach] &= ~3; + m_active_dma_dst[dmach] &= ~3; + break; + case 3: + m_active_dma_src[dmach] &= ~3; + m_active_dma_dst[dmach] &= ~3; + m_active_dma_count[dmach] &= ~3; + break; + } + + // start DMA timer + + // fever soccer uses cycle-stealing mode, requiring the CPU to be halted + if (m_active_dma_steal[dmach]) + { + //printf("cycle stealing DMA\n"); + suspend(SUSPEND_REASON_HALT, 1); + } + + m_dma_current_active_timer[dmach]->adjust(cycles_to_attotime(2), dmach); + } + } + else + { + if (m_dma_timer_active[dmach]) + { + LOG("SH2: DMA %d cancelled in-flight\n", dmach); + //m_dma_complete_timer[dmach]->adjust(attotime::never); + m_dma_current_active_timer[dmach]->adjust(attotime::never, dmach); + + m_dma_timer_active[dmach] = 0; + } + } +} + +/* + * SCI + */ +// TODO: identical to H8 counterpart + +uint8_t sh7604_device::smr_r() +{ + return m_smr; +} + +void sh7604_device::smr_w(uint8_t data) +{ + m_smr = data; +} + +uint8_t sh7604_device::brr_r() +{ + return m_brr; +} + +void sh7604_device::brr_w(uint8_t data) +{ + m_brr = data; +} + +uint8_t sh7604_device::scr_r() +{ + return m_scr; +} + +void sh7604_device::scr_w(uint8_t data) +{ + m_scr = data; +} + +uint8_t sh7604_device::tdr_r() +{ + return m_tdr; +} + +void sh7604_device::tdr_w(uint8_t data) +{ + m_tdr = data; + //printf("%c", data & 0xff); +} + +uint8_t sh7604_device::ssr_r() +{ + // 0x84 is needed by EGWord on Saturn to make it to boot for some reason. + return m_ssr | 0x84; +} + +void sh7604_device::ssr_w(uint8_t data) +{ + m_ssr = data; +} + +uint8_t sh7604_device::rdr_r() +{ + return 0; +} + +/* + * FRC + */ + +uint8_t sh7604_device::tier_r() +{ + return m_tier; +} + +void sh7604_device::tier_w(uint8_t data) +{ + sh2_timer_resync(); + m_tier = data; + sh2_timer_activate(); + sh2_recalc_irq(); +} + +uint8_t sh7604_device::ftcsr_r() +{ + // TODO: to be tested + if (!m_ftcsr_read_cb.isnull()) + m_ftcsr_read_cb((((m_tier << 24) | (m_ftcsr << 16)) & 0xffff0000) | m_frc); + + return m_ftcsr; +} + +void sh7604_device::ftcsr_w(uint8_t data) +{ + uint8_t old = m_ftcsr; + + m_ftcsr = data; + sh2_timer_resync(); + m_ftcsr = (m_ftcsr & ~(ICF | OCFA | OCFB | OVF)) | (old & m_ftcsr & (ICF | OCFA | OCFB | OVF)); + sh2_timer_activate(); + sh2_recalc_irq(); +} + +uint16_t sh7604_device::frc_r() +{ + sh2_timer_resync(); + return m_frc; +} + +void sh7604_device::frc_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + sh2_timer_resync(); + COMBINE_DATA(&m_frc); + sh2_timer_activate(); + sh2_recalc_irq(); +} + +uint16_t sh7604_device::ocra_b_r() +{ + return (m_tocr & 0x10) ? m_ocrb : m_ocra; +} + +void sh7604_device::ocra_b_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + sh2_timer_resync(); + if (m_tocr & 0x10) + m_ocrb = (m_ocrb & ~mem_mask) | (data & mem_mask); + else + m_ocra = (m_ocra & ~mem_mask) | (data & mem_mask); + sh2_timer_activate(); + sh2_recalc_irq(); +} + +uint8_t sh7604_device::frc_tcr_r() +{ + return m_frc_tcr & 0x83; +} + +void sh7604_device::frc_tcr_w(uint8_t data) +{ + sh2_timer_resync(); + m_frc_tcr = data & 0x83; + sh2_timer_activate(); + sh2_recalc_irq(); +} + +uint8_t sh7604_device::tocr_r() +{ + return (m_tocr & 0x13) | 0xe0; +} + +void sh7604_device::tocr_w(uint8_t data) +{ + sh2_timer_resync(); + // TODO: output levels A/B (bits 1-0) + m_tocr = data & 0x13; + sh2_timer_activate(); + sh2_recalc_irq(); +} + +uint16_t sh7604_device::frc_icr_r() +{ + return m_frc_icr; +} + +/* + * INTC + */ + +uint16_t sh7604_device::intc_icr_r() +{ + // TODO: flip meaning based off NMI edge select bit (NMIE) + uint16_t nmilv = m_nmi_line_state == ASSERT_LINE ? 0 : 0x8000; + return nmilv | (m_intc_icr & 0x0101); +} + +void sh7604_device::intc_icr_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_intc_icr); + m_nmie = BIT(m_intc_icr, 8); + m_vecmd = BIT(m_intc_icr, 0); +} + +uint16_t sh7604_device::ipra_r() +{ + return m_ipra & 0xfff0; +} + +void sh7604_device::ipra_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_ipra); + m_irq_level.divu = (m_ipra >> 12) & 0xf; + m_irq_level.dmac = (m_ipra >> 8) & 0xf; + m_irq_level.wdt = (m_ipra >> 4) & 0xf; + sh2_recalc_irq(); +} + +uint16_t sh7604_device::iprb_r() +{ + return m_iprb & 0xff00; +} + +void sh7604_device::iprb_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_iprb); + m_irq_level.sci = (m_iprb >> 12) & 0xf; + m_irq_level.frc = (m_iprb >> 8) & 0xf; + sh2_recalc_irq(); +} + +uint16_t sh7604_device::vcra_r() +{ + return m_vcra & 0x7f7f; +} + +void sh7604_device::vcra_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_vcra); + // ... + sh2_recalc_irq(); +} + +uint16_t sh7604_device::vcrb_r() +{ + return m_vcrb; +} + +void sh7604_device::vcrb_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_vcrb); + // ... + sh2_recalc_irq(); +} + +uint16_t sh7604_device::vcrc_r() +{ + return m_vcrc & 0x7f7f; +} + +void sh7604_device::vcrc_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_vcrc); + m_irq_vector.fic = (m_vcrc >> 8) & 0x7f; + m_irq_vector.foc = (m_vcrc >> 0) & 0x7f; + sh2_recalc_irq(); +} + +uint16_t sh7604_device::vcrd_r() +{ + return m_vcrd & 0x7f00; +} + +void sh7604_device::vcrd_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_vcrd); + m_irq_vector.fov = (m_vcrc >> 8) & 0x7f; + sh2_recalc_irq(); +} + +uint16_t sh7604_device::vcrwdt_r() +{ + return m_vcrwdt & 0x7f7f; +} + +void sh7604_device::vcrwdt_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_vcrwdt); + // ... + sh2_recalc_irq(); +} + +uint32_t sh7604_device::vcrdiv_r() +{ + return m_vcrdiv & 0x7f; +} + +void sh7604_device::vcrdiv_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + COMBINE_DATA(&m_vcrdiv); + // TODO: unemulated, level is seemingly not documented/settable? + m_irq_vector.divu = data & 0x7f; + sh2_recalc_irq(); +} + +/* + * DIVU + */ + +uint32_t sh7604_device::dvcr_r() +{ + return (m_divu_ovfie ? 2 : 0) | (m_divu_ovf ? 1 : 0); +} + +void sh7604_device::dvcr_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + if (ACCESSING_BITS_0_7) + { + if (data & 1) + m_divu_ovf = false; + if (data & 2) + { + m_divu_ovfie = BIT(data, 1); + if (m_divu_ovfie) + LOG("SH2: unemulated DIVU OVF interrupt enable\n"); + } + sh2_recalc_irq(); + } +} + +uint32_t sh7604_device::dvsr_r() +{ + return m_dvsr; +} + +void sh7604_device::dvsr_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + COMBINE_DATA(&m_dvsr); +} + +uint32_t sh7604_device::dvdnt_r() +{ + return m_dvdntl; +} + +void sh7604_device::dvdnt_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + COMBINE_DATA(&m_dvdntl); + int32_t a = m_dvdntl; + int32_t b = m_dvsr; + LOG("SH2 div32+mod %d/%d\n", a, b); + if (b) + { + m_dvdntl = a / b; + m_dvdnth = a % b; + } + else + { + m_divu_ovf = true; + m_dvdntl = 0x7fffffff; + m_dvdnth = 0x7fffffff; + sh2_recalc_irq(); + } +} + +uint32_t sh7604_device::dvdnth_r() +{ + return m_dvdnth; +} + +uint32_t sh7604_device::dvdntl_r() +{ + return m_dvdntl; +} + +void sh7604_device::dvdnth_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + COMBINE_DATA(&m_dvdnth); +} + +void sh7604_device::dvdntl_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + COMBINE_DATA(&m_dvdntl); + int64_t a = m_dvdntl | ((uint64_t)m_dvdnth << 32); + int64_t b = (int32_t)m_dvsr; + LOG("SH2 div64+mod %d/%d\n", a, b); + if (b) + { + int64_t q = a / b; + if (q != (int32_t)q) + { + m_divu_ovf = true; + m_dvdntl = 0x7fffffff; + m_dvdnth = 0x7fffffff; + sh2_recalc_irq(); + } + else + { + m_dvdntl = q; + m_dvdnth = a % b; + } + } + else + { + m_divu_ovf = true; + m_dvdntl = 0x7fffffff; + m_dvdnth = 0x7fffffff; + sh2_recalc_irq(); + } +} + +/* + * WTC + */ + +uint16_t sh7604_device::wtcnt_r() +{ + sh2_wtcnt_recalc(); + return ((m_wtcsr | 0x18) << 8) | (m_wtcnt & 0xff); +} + +uint16_t sh7604_device::rstcsr_r() +{ + return (m_rstcsr & 0xe0) | 0x1f; +} + +void sh7604_device::wtcnt_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_wtcw[0]); + switch (m_wtcw[0] & 0xff00) + { + case 0x5a00: + m_wtcnt = m_wtcw[0] & 0xff; + if (m_wtcsr & 0x20) + sh2_wdt_activate(); + break; + case 0xa500: + /* + WTCSR + x--- ---- Overflow in IT mode + -x-- ---- Timer mode (0: IT 1: watchdog) + --x- ---- Timer enable + ---1 1--- + ---- -xxx Clock select + */ + 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; + } +} + +void sh7604_device::rstcsr_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_wtcw[1]); + switch (m_wtcw[1] & 0xff00) + { + case 0xa500: + // clear WOVF flag + if ((m_wtcw[1] & 0x80) == 0) + m_rstcsr &= 0x7f; + break; + case 0x5a00: + m_rstcsr = (m_rstcsr & 0x80) | (m_wtcw[1] & 0x60); + break; + } +} + +uint16_t sh7604_device::fmr_sbycr_r() +{ + return m_sbycr; +} + +void sh7604_device::fmr_sbycr_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + switch (mem_mask) + { + case 0xff00: // FMR 8bit + logerror("SH2 set clock multiplier x%d\n", 1 << ((data >> 8) & 3)); + break; + case 0xffff: // FMR 16bit + // SH7604 docs says FMR register must be set using 8-bit write, however at practice 16-bit works too. + // has been verified for CPS3 custom SH2, SH7604 and SH7095 (clock multiplier feature is not officially documented for SH7095). + logerror("SH2 set clock multiplier x%d\n", 1 << (data & 3)); + break; + case 0x00ff: // SBYCR + m_sbycr = data; + if (data & 0x1f) + logerror("SH2 module stop selected %02x\n", data); + break; + } +} + +uint8_t sh7604_device::ccr_r() +{ + return m_ccr & ~0x30; +} + +void sh7604_device::ccr_w(uint8_t data) +{ + /* + xx-- ---- Way 0/1 + ---x ---- Cache Purge (CP), write only + ---- x--- Two-Way Mode (TW) + ---- -x-- Data Replacement Disable (OD) + ---- --x- Instruction Replacement Disable (ID) + ---- ---x Cache Enable (CE) + */ + m_ccr = data; +} + +uint32_t sh7604_device::bcr1_r() +{ + return (m_bcr1 & ~0xe008) | (m_is_slave ? 0x8000 : 0); +} + +void sh7604_device::bcr1_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + COMBINE_DATA(&m_bcr1); +} + +uint32_t sh7604_device::bcr2_r() +{ + return m_bcr2; +} + +void sh7604_device::bcr2_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + COMBINE_DATA(&m_bcr2); +} + +uint32_t sh7604_device::wcr_r() +{ + return m_wcr; +} + +void sh7604_device::wcr_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + COMBINE_DATA(&m_wcr); +} + +uint32_t sh7604_device::mcr_r() +{ + return m_mcr & ~0x103; +} + +void sh7604_device::mcr_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + COMBINE_DATA(&m_mcr); +} + +uint32_t sh7604_device::rtcsr_r() +{ + return m_rtcsr & 0xf8; +} + +void sh7604_device::rtcsr_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + COMBINE_DATA(&m_rtcsr); +} + +uint32_t sh7604_device::rtcnt_r() +{ + return m_rtcnt & 0xff; +} + +void sh7604_device::rtcnt_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + COMBINE_DATA(&m_rtcnt); + m_rtcnt &= 0xff; +} + +uint32_t sh7604_device::rtcor_r() +{ + return m_rtcor & 0xff; +} + +void sh7604_device::rtcor_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + COMBINE_DATA(&m_rtcor); + m_rtcor &= 0xff; +} + +void sh7604_device::set_frt_input(int state) +{ + if (m_frt_input == state) + return; + + m_frt_input = state; + + if (m_frc_tcr & 0x80) + { + if (state == CLEAR_LINE) + return; + } + else + { + if (state == ASSERT_LINE) + return; + } + + sh2_timer_resync(); + m_frc_icr = m_frc; + m_ftcsr |= ICF; + //logerror("SH2.%s: ICF activated (%x)\n", tag(), m_sh2_state->pc & AM); + sh2_recalc_irq(); +} + +void sh7604_device::sh2_recalc_irq() +{ + int irq = 0; + int vector = -1; + int level; + + // Timer irqs + if (m_tier & m_ftcsr & (ICF | OCFA | OCFB | OVF)) + { + level = (m_irq_level.frc & 15); + if (level > irq) + { + int mask = m_tier & m_ftcsr; + irq = level; + if (mask & ICF) + vector = m_irq_vector.fic & 0x7f; + else if (mask & (OCFA | OCFB)) + vector = m_irq_vector.foc & 0x7f; + else + vector = m_irq_vector.fov & 0x7f; + } + } + + // 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; + if (level > irq) + { + irq = level; + m_dma_irq[0] &= ~1; + vector = m_irq_vector.dmac[0] & 0x7f; + } + } + else if ((m_dmac[1].chcr & 6) == 6 && m_dma_irq[1]) + { + level = m_irq_level.dmac & 15; + if (level > irq) + { + irq = level; + m_dma_irq[1] &= ~1; + vector = m_irq_vector.dmac[1] & 0x7f; + } + } + + m_sh2_state->internal_irq_level = irq; + m_internal_irq_vector = vector; + m_test_irq = 1; +} + +/* + * DMAC + */ + +template <int Channel> +uint32_t sh7604_device::vcrdma_r() +{ + return m_vcrdma[Channel] & 0x7f; +} + +template <int Channel> +void sh7604_device::vcrdma_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + COMBINE_DATA(&m_vcrdma[Channel]); + m_irq_vector.dmac[Channel] = m_vcrdma[Channel] & 0x7f; + sh2_recalc_irq(); +} + +template <int Channel> +uint8_t sh7604_device::drcr_r() +{ + return m_dmac[Channel].drcr & 3; +} + +template <int Channel> +void sh7604_device::drcr_w(uint8_t data) +{ + m_dmac[Channel].drcr = data & 3; + sh2_recalc_irq(); +} + +template <int Channel> +uint32_t sh7604_device::sar_r() +{ + return m_dmac[Channel].sar; +} + +template <int Channel> +void sh7604_device::sar_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + COMBINE_DATA(&m_dmac[Channel].sar); +} + +template <int Channel> +uint32_t sh7604_device::dar_r() +{ + return m_dmac[Channel].dar; +} + +template <int Channel> +void sh7604_device::dar_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + COMBINE_DATA(&m_dmac[Channel].dar); +} + +template <int Channel> +uint32_t sh7604_device::dmac_tcr_r() +{ + return m_dmac[Channel].tcr; +} + +template <int Channel> +void sh7604_device::dmac_tcr_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + COMBINE_DATA(&m_dmac[Channel].tcr); + m_dmac[Channel].tcr &= 0xffffff; +} + +template <int Channel> +uint32_t sh7604_device::chcr_r() +{ + return m_dmac[Channel].chcr; +} + +template <int Channel> +void sh7604_device::chcr_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + uint32_t old; + old = m_dmac[Channel].chcr; + COMBINE_DATA(&m_dmac[Channel].chcr); + m_dmac[Channel].chcr = (data & ~2) | (old & m_dmac[Channel].chcr & 2); + sh2_dmac_check(Channel); +} + +uint32_t sh7604_device::dmaor_r() +{ + return m_dmaor & 0xf; +} + +void sh7604_device::dmaor_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + if (ACCESSING_BITS_0_7) + { + uint8_t old = m_dmaor & 0xf; + m_dmaor = (data & ~6) | (old & m_dmaor & 6); // TODO: should this be old & data & 6? bug? + sh2_dmac_check(0); + sh2_dmac_check(1); + } +} |