summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/sh/sh2comn.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/sh/sh2comn.cpp')
-rw-r--r--src/devices/cpu/sh/sh2comn.cpp946
1 files changed, 946 insertions, 0 deletions
diff --git a/src/devices/cpu/sh/sh2comn.cpp b/src/devices/cpu/sh/sh2comn.cpp
new file mode 100644
index 00000000000..b6ac0e1cc65
--- /dev/null
+++ b/src/devices/cpu/sh/sh2comn.cpp
@@ -0,0 +1,946 @@
+// license:BSD-3-Clause
+// copyright-holders:R. Belmont
+/*****************************************************************************
+ *
+ * sh2common.c
+ *
+ * SH-2 non-specific components
+ *
+ *****************************************************************************/
+
+#include "emu.h"
+#include "sh2.h"
+#include "sh2comn.h"
+
+#include "debugger.h"
+
+//#define VERBOSE 1
+#include "logmacro.h"
+
+static const int div_tab[4] = { 3, 5, 7, 0 };
+
+
+void sh2_device::sh2_timer_resync()
+{
+ int divider = div_tab[(m_m[5] >> 8) & 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 sh2_device::sh2_timer_activate()
+{
+ int max_delta = 0xfffff;
+ uint16_t frc;
+
+ m_timer->adjust(attotime::never);
+
+ frc = m_frc;
+ if(!(m_m[4] & OCFA)) {
+ uint16_t delta = m_ocra - frc;
+ if(delta < max_delta)
+ max_delta = delta;
+ }
+
+ if(!(m_m[4] & OCFB) && (m_ocra <= m_ocrb || !(m_m[4] & 0x010000))) {
+ uint16_t delta = m_ocrb - frc;
+ if(delta < max_delta)
+ max_delta = delta;
+ }
+
+ if(!(m_m[4] & OVF) && !(m_m[4] & 0x010000)) {
+ int delta = 0x10000 - frc;
+ if(delta < max_delta)
+ max_delta = delta;
+ }
+
+ if(max_delta != 0xfffff) {
+ int divider = div_tab[(m_m[5] >> 8) & 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( sh2_device::sh2_timer_callback )
+{
+ uint16_t frc;
+
+ sh2_timer_resync();
+
+ frc = m_frc;
+
+ if(frc == m_ocrb)
+ m_m[4] |= OCFB;
+
+ if(frc == 0x0000)
+ m_m[4] |= OVF;
+
+ if(frc == m_ocra)
+ {
+ m_m[4] |= OCFA;
+
+ if(m_m[4] & 0x010000)
+ m_frc = 0;
+ }
+
+ sh2_recalc_irq();
+ sh2_timer_activate();
+}
+
+
+/*
+ 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 sh2_device::sh2_notify_dma_data_available()
+{
+ //printf("call notify\n");
+
+ for (int dma=0;dma<2;dma++)
+ {
+ //printf("m_dma_timer_active[dma] %04x\n",m_dma_timer_active[dma]);
+
+ if (m_dma_timer_active[dma]==2) // 2 = stalled
+ {
+ // printf("resuming stalled dma\n");
+ m_dma_timer_active[dma]=1;
+ m_dma_current_active_timer[dma]->adjust(attotime::zero, dma);
+ }
+ }
+
+}
+
+void sh2_device::sh2_do_dma(int dma)
+{
+ uint32_t dmadata;
+
+ uint32_t tempsrc, tempdst;
+
+ if (m_active_dma_count[dma] > 0)
+ {
+ // process current DMA
+ switch(m_active_dma_size[dma])
+ {
+ 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)
+
+ if(m_active_dma_incs[dma] == 2)
+ tempsrc = m_active_dma_src[dma] - 1;
+ else
+ tempsrc = m_active_dma_src[dma];
+
+ if(m_active_dma_incd[dma] == 2)
+ tempdst = m_active_dma_dst[dma] - 1;
+ else
+ tempdst = m_active_dma_dst[dma];
+
+ if (!m_dma_fifo_data_available_cb.isnull())
+ {
+ int available = m_dma_fifo_data_available_cb(tempsrc, tempdst, 0, m_active_dma_size[dma]);
+
+ if (!available)
+ {
+ //printf("dma stalled\n");
+ m_dma_timer_active[dma]=2;// mark as stalled
+ return;
+ }
+ }
+
+ #ifdef USE_TIMER_FOR_DMA
+ //schedule next DMA callback
+ m_dma_current_active_timer[dma]->adjust(cycles_to_attotime(2), dma);
+ #endif
+
+
+ dmadata = m_program->read_byte(tempsrc);
+ if (!m_dma_kludge_cb.isnull()) dmadata = m_dma_kludge_cb(tempsrc, tempdst, dmadata, m_active_dma_size[dma]);
+ m_program->write_byte(tempdst, dmadata);
+
+ if(m_active_dma_incs[dma] == 2)
+ m_active_dma_src[dma] --;
+ if(m_active_dma_incd[dma] == 2)
+ m_active_dma_dst[dma] --;
+
+
+ if(m_active_dma_incs[dma] == 1)
+ m_active_dma_src[dma] ++;
+ if(m_active_dma_incd[dma] == 1)
+ m_active_dma_dst[dma] ++;
+
+ m_active_dma_count[dma] --;
+ }
+ break;
+ case 1:
+ {
+ if(m_active_dma_incs[dma] == 2)
+ tempsrc = m_active_dma_src[dma] - 2;
+ else
+ tempsrc = m_active_dma_src[dma];
+
+ if(m_active_dma_incd[dma] == 2)
+ tempdst = m_active_dma_dst[dma] - 2;
+ else
+ tempdst = m_active_dma_dst[dma];
+
+ if (!m_dma_fifo_data_available_cb.isnull())
+ {
+ int available = m_dma_fifo_data_available_cb(tempsrc, tempdst, 0, m_active_dma_size[dma]);
+
+ if (!available)
+ {
+ //printf("dma stalled\n");
+ m_dma_timer_active[dma]=2;// mark as stalled
+ return;
+ }
+ }
+
+ #ifdef USE_TIMER_FOR_DMA
+ //schedule next DMA callback
+ m_dma_current_active_timer[dma]->adjust(cycles_to_attotime(2), dma);
+ #endif
+
+ // check: should this really be using read_word_32 / write_word_32?
+ dmadata = m_program->read_word(tempsrc);
+ if (!m_dma_kludge_cb.isnull()) dmadata = m_dma_kludge_cb(tempsrc, tempdst, dmadata, m_active_dma_size[dma]);
+ m_program->write_word(tempdst, dmadata);
+
+ if(m_active_dma_incs[dma] == 2)
+ m_active_dma_src[dma] -= 2;
+ if(m_active_dma_incd[dma] == 2)
+ m_active_dma_dst[dma] -= 2;
+
+ if(m_active_dma_incs[dma] == 1)
+ m_active_dma_src[dma] += 2;
+ if(m_active_dma_incd[dma] == 1)
+ m_active_dma_dst[dma] += 2;
+
+ m_active_dma_count[dma] --;
+ }
+ break;
+ case 2:
+ {
+ if(m_active_dma_incs[dma] == 2)
+ tempsrc = m_active_dma_src[dma] - 4;
+ else
+ tempsrc = m_active_dma_src[dma];
+
+ if(m_active_dma_incd[dma] == 2)
+ tempdst = m_active_dma_dst[dma] - 4;
+ else
+ tempdst = m_active_dma_dst[dma];
+
+ if (!m_dma_fifo_data_available_cb.isnull())
+ {
+ int available = m_dma_fifo_data_available_cb(tempsrc, tempdst, 0, m_active_dma_size[dma]);
+
+ if (!available)
+ {
+ //printf("dma stalled\n");
+ m_dma_timer_active[dma]=2;// mark as stalled
+ return;
+ }
+ }
+
+ #ifdef USE_TIMER_FOR_DMA
+ //schedule next DMA callback
+ m_dma_current_active_timer[dma]->adjust(cycles_to_attotime(2), dma);
+ #endif
+
+ dmadata = m_program->read_dword(tempsrc);
+ if (!m_dma_kludge_cb.isnull()) dmadata = m_dma_kludge_cb(tempsrc, tempdst, dmadata, m_active_dma_size[dma]);
+ m_program->write_dword(tempdst, dmadata);
+
+ if(m_active_dma_incs[dma] == 2)
+ m_active_dma_src[dma] -= 4;
+ if(m_active_dma_incd[dma] == 2)
+ m_active_dma_dst[dma] -= 4;
+
+ if(m_active_dma_incs[dma] == 1)
+ m_active_dma_src[dma] += 4;
+ if(m_active_dma_incd[dma] == 1)
+ m_active_dma_dst[dma] += 4;
+
+ m_active_dma_count[dma] --;
+ }
+ break;
+ case 3:
+ {
+ // shouldn't this really be 4 calls here instead?
+
+ tempsrc = m_active_dma_src[dma];
+
+ if(m_active_dma_incd[dma] == 2)
+ tempdst = m_active_dma_dst[dma] - 16;
+ else
+ tempdst = m_active_dma_dst[dma];
+
+ if (!m_dma_fifo_data_available_cb.isnull())
+ {
+ int available = m_dma_fifo_data_available_cb(tempsrc, tempdst, 0, m_active_dma_size[dma]);
+
+ if (!available)
+ {
+ //printf("dma stalled\n");
+ m_dma_timer_active[dma]=2;// mark as stalled
+ fatalerror("SH2 dma_callback_fifo_data_available == 0 in unsupported mode\n");
+ }
+ }
+
+ #ifdef USE_TIMER_FOR_DMA
+ //schedule next DMA callback
+ m_dma_current_active_timer[dma]->adjust(cycles_to_attotime(2), dma);
+ #endif
+
+ dmadata = m_program->read_dword(tempsrc);
+ if (!m_dma_kludge_cb.isnull()) dmadata = m_dma_kludge_cb(tempsrc, tempdst, dmadata, m_active_dma_size[dma]);
+ 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[dma]);
+ 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[dma]);
+ 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[dma]);
+ m_program->write_dword(tempdst+12, dmadata);
+
+ if(m_active_dma_incd[dma] == 2)
+ m_active_dma_dst[dma] -= 16;
+
+ m_active_dma_src[dma] += 16;
+ if(m_active_dma_incd[dma] == 1)
+ m_active_dma_dst[dma] += 16;
+
+ m_active_dma_count[dma]-=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[dma])
+ {
+ resume(SUSPEND_REASON_HALT );
+ }
+
+
+ LOG("SH2: DMA %d complete\n", dma);
+ m_m[0x62+4*dma] = 0;
+ m_m[0x63+4*dma] |= 2;
+ m_dma_timer_active[dma] = 0;
+ m_dma_irq[dma] |= 1;
+ sh2_recalc_irq();
+
+ }
+}
+
+TIMER_CALLBACK_MEMBER( sh2_device::sh2_dma_current_active_callback )
+{
+ int dma = param & 1;
+
+ sh2_do_dma(dma);
+}
+
+
+void sh2_device::sh2_dmac_check(int dma)
+{
+ if(m_m[0x63+4*dma] & m_m[0x6c] & 1)
+ {
+ if(!m_dma_timer_active[dma] && !(m_m[0x63+4*dma] & 2))
+ {
+ m_active_dma_incd[dma] = (m_m[0x63+4*dma] >> 14) & 3;
+ m_active_dma_incs[dma] = (m_m[0x63+4*dma] >> 12) & 3;
+ m_active_dma_size[dma] = (m_m[0x63+4*dma] >> 10) & 3;
+ m_active_dma_steal[dma] = (m_m[0x63+4*dma] &0x10);
+
+ if(m_active_dma_incd[dma] == 3 || m_active_dma_incs[dma] == 3)
+ {
+ logerror("SH2: DMA: bad increment values (%d, %d, %d, %04x)\n", m_active_dma_incd[dma], m_active_dma_incs[dma], m_active_dma_size[dma], m_m[0x63+4*dma]);
+ return;
+ }
+ m_active_dma_src[dma] = m_m[0x60+4*dma];
+ m_active_dma_dst[dma] = m_m[0x61+4*dma];
+ m_active_dma_count[dma] = m_m[0x62+4*dma];
+ if(!m_active_dma_count[dma])
+ m_active_dma_count[dma] = 0x1000000;
+
+ LOG("SH2: DMA %d start %x, %x, %x, %04x, %d, %d, %d\n", dma, m_active_dma_src[dma], m_active_dma_dst[dma], m_active_dma_count[dma], m_m[0x63+4*dma], m_active_dma_incs[dma], m_active_dma_incd[dma], m_active_dma_size[dma]);
+
+ m_dma_timer_active[dma] = 1;
+
+ m_active_dma_src[dma] &= AM;
+ m_active_dma_dst[dma] &= AM;
+
+ switch(m_active_dma_size[dma])
+ {
+ case 0:
+ break;
+ case 1:
+ m_active_dma_src[dma] &= ~1;
+ m_active_dma_dst[dma] &= ~1;
+ break;
+ case 2:
+ m_active_dma_src[dma] &= ~3;
+ m_active_dma_dst[dma] &= ~3;
+ break;
+ case 3:
+ m_active_dma_src[dma] &= ~3;
+ m_active_dma_dst[dma] &= ~3;
+ m_active_dma_count[dma] &= ~3;
+ break;
+ }
+
+
+
+
+#ifdef USE_TIMER_FOR_DMA
+ // start DMA timer
+
+ // fever soccer uses cycle-stealing mode, requiring the CPU to be halted
+ if (m_active_dma_steal[dma])
+ {
+ //printf("cycle stealing DMA\n");
+ suspend(SUSPEND_REASON_HALT, 1 );
+ }
+
+ m_dma_current_active_timer[dma]->adjust(cycles_to_attotime(2), dma);
+#endif
+
+ }
+ }
+ else
+ {
+ if(m_dma_timer_active[dma])
+ {
+ logerror("SH2: DMA %d cancelled in-flight\n", dma);
+ //m_dma_complete_timer[dma]->adjust(attotime::never);
+ m_dma_current_active_timer[dma]->adjust(attotime::never);
+
+ m_dma_timer_active[dma] = 0;
+ }
+ }
+}
+
+
+WRITE32_MEMBER( sh2_device::sh7604_w )
+{
+ uint32_t old;
+
+ old = m_m[offset];
+ COMBINE_DATA(m_m+offset);
+
+ // if(offset != 0x20)
+ // logerror("sh2_internal_w: Write %08x (%x), %08x @ %08x\n", 0xfffffe00+offset*4, offset, data, mem_mask);
+
+// if(offset != 0x20)
+// printf("sh2_internal_w: Write %08x (%x), %08x @ %08x (PC %x)\n", 0xfffffe00+offset*4, offset, data, mem_mask, space.device().safe_pc());
+
+ switch( offset )
+ {
+ case 0x00:
+ //if(mem_mask == 0xff)
+ // printf("%c",data & 0xff);
+ break;
+ case 0x01:
+ //printf("%08x %02x %02x\n",mem_mask,offset,data);
+ break;
+ // Timers
+ case 0x04: // TIER, FTCSR, FRC
+ if((mem_mask & 0x00ffffff) != 0)
+ {
+ sh2_timer_resync();
+ }
+// printf("SH2.%s: TIER write %04x @ %04x\n", m_device->tag(), data >> 16, mem_mask>>16);
+ m_m[4] = (m_m[4] & ~(ICF|OCFA|OCFB|OVF)) | (old & m_m[4] & (ICF|OCFA|OCFB|OVF));
+ COMBINE_DATA(&m_frc);
+ if((mem_mask & 0x00ffffff) != 0)
+ sh2_timer_activate();
+ sh2_recalc_irq();
+ break;
+ case 0x05: // OCRx, TCR, TOCR
+// printf("SH2.%s: TCR write %08x @ %08x\n", m_device->tag(), data, mem_mask);
+ sh2_timer_resync();
+ if(m_m[5] & 0x10)
+ m_ocrb = (m_ocrb & (~mem_mask >> 16)) | ((data & mem_mask) >> 16);
+ else
+ m_ocra = (m_ocra & (~mem_mask >> 16)) | ((data & mem_mask) >> 16);
+ sh2_timer_activate();
+ break;
+
+ case 0x06: // ICR
+ break;
+
+ // Interrupt vectors
+ case 0x18: // IPRB, VCRA
+ case 0x19: // VCRB, VCRC
+ case 0x1a: // VCRD
+ sh2_recalc_irq();
+ break;
+
+ // DMA
+ case 0x1c: // DRCR0, DRCR1
+ break;
+
+ // Watchdog
+ case 0x20: // WTCNT, RSTCSR
+ if((m_m[0x20] & 0xff000000) == 0x5a000000)
+ m_wtcnt = (m_m[0x20] >> 16) & 0xff;
+
+ if((m_m[0x20] & 0xff000000) == 0xa5000000)
+ {
+ /*
+ WTCSR
+ x--- ---- Overflow in IT mode
+ -x-- ---- Timer mode (0: IT 1: watchdog)
+ --x- ---- Timer enable
+ ---1 1---
+ ---- -xxx Clock select
+ */
+
+ m_wtcsr = (m_m[0x20] >> 16) & 0xff;
+ }
+
+ if((m_m[0x20] & 0x0000ff00) == 0x00005a00)
+ {
+ // -x-- ---- RSTE (1: resets wtcnt when overflows 0: no reset)
+ // --x- ---- RSTS (0: power-on reset 1: Manual reset)
+ // ...
+ }
+
+ if((m_m[0x20] & 0x0000ff00) == 0x0000a500)
+ {
+ // clear WOVF
+ // ...
+ }
+
+
+
+ break;
+
+ // Standby and cache
+ case 0x24: // SBYCR, CCR
+ /*
+ CCR
+ xx-- ---- ---- ---- Way 0/1
+ ---x ---- ---- ---- Cache Purge (CP)
+ ---- x--- ---- ---- Two-Way Mode (TW)
+ ---- -x-- ---- ---- Data Replacement Disable (OD)
+ ---- --x- ---- ---- Instruction Replacement Disable (ID)
+ ---- ---x ---- ---- Cache Enable (CE)
+ */
+ break;
+
+ // Interrupt vectors cont.
+ case 0x38: // ICR, IRPA
+ break;
+ case 0x39: // VCRWDT
+ break;
+
+ // Division box
+ case 0x40: // DVSR
+ break;
+ case 0x41: // DVDNT
+ {
+ int32_t a = m_m[0x41];
+ int32_t b = m_m[0x40];
+ LOG("SH2 div+mod %d/%d\n", a, b);
+ if (b)
+ {
+ m_m[0x45] = a / b;
+ m_m[0x44] = a % b;
+ }
+ else
+ {
+ m_m[0x42] |= 0x00010000;
+ m_m[0x45] = 0x7fffffff;
+ m_m[0x44] = 0x7fffffff;
+ sh2_recalc_irq();
+ }
+ break;
+ }
+ case 0x42: // DVCR
+ m_m[0x42] = (m_m[0x42] & ~0x00001000) | (old & m_m[0x42] & 0x00010000);
+ sh2_recalc_irq();
+ break;
+ case 0x43: // VCRDIV
+ sh2_recalc_irq();
+ break;
+ case 0x44: // DVDNTH
+ break;
+ case 0x45: // DVDNTL
+ {
+ int64_t a = m_m[0x45] | ((uint64_t)(m_m[0x44]) << 32);
+ int64_t b = (int32_t)m_m[0x40];
+ LOG("SH2 div+mod %d/%d\n", a, b);
+ if (b)
+ {
+ int64_t q = a / b;
+ if (q != (int32_t)q)
+ {
+ m_m[0x42] |= 0x00010000;
+ m_m[0x45] = 0x7fffffff;
+ m_m[0x44] = 0x7fffffff;
+ sh2_recalc_irq();
+ }
+ else
+ {
+ m_m[0x45] = q;
+ m_m[0x44] = a % b;
+ }
+ }
+ else
+ {
+ m_m[0x42] |= 0x00010000;
+ m_m[0x45] = 0x7fffffff;
+ m_m[0x44] = 0x7fffffff;
+ sh2_recalc_irq();
+ }
+ break;
+ }
+
+ // DMA controller
+ case 0x60: // SAR0
+ case 0x61: // DAR0
+ break;
+ case 0x62: // DTCR0
+ m_m[0x62] &= 0xffffff;
+ break;
+ case 0x63: // CHCR0
+ m_m[0x63] = (m_m[0x63] & ~2) | (old & m_m[0x63] & 2);
+ sh2_dmac_check(0);
+ break;
+ case 0x64: // SAR1
+ case 0x65: // DAR1
+ break;
+ case 0x66: // DTCR1
+ m_m[0x66] &= 0xffffff;
+ break;
+ case 0x67: // CHCR1
+ m_m[0x67] = (m_m[0x67] & ~2) | (old & m_m[0x67] & 2);
+ sh2_dmac_check(1);
+ break;
+ case 0x68: // VCRDMA0
+ case 0x6a: // VCRDMA1
+ sh2_recalc_irq();
+ break;
+ case 0x6c: // DMAOR
+ m_m[0x6c] = (m_m[0x6c] & ~6) | (old & m_m[0x6c] & 6);
+ sh2_dmac_check(0);
+ sh2_dmac_check(1);
+ break;
+
+ // Bus controller
+ case 0x78: // BCR1
+ case 0x79: // BCR2
+ case 0x7a: // WCR
+ case 0x7b: // MCR
+ case 0x7c: // RTCSR
+ case 0x7d: // RTCNT
+ case 0x7e: // RTCOR
+ break;
+
+ default:
+ logerror("sh2_internal_w: Unmapped write %08x, %08x @ %08x\n", 0xfffffe00+offset*4, data, mem_mask);
+ break;
+ }
+}
+
+READ32_MEMBER( sh2_device::sh7604_r )
+{
+// logerror("sh2_internal_r: Read %08x (%x) @ %08x\n", 0xfffffe00+offset*4, offset, mem_mask);
+ switch( offset )
+ {
+ case 0x00:
+ break;
+ case 0x01:
+// return m_m[1] | 0; // bit31 is TDRE: Trasmit Data Register Empty. Forcing it to be '1' breaks Saturn ...
+ return m_m[1] | (0x84 << 24); // ... but this is actually needed to make EGWord on SS to boot?
+
+ case 0x04: // TIER, FTCSR, FRC
+ if ( mem_mask == 0x00ff0000 )
+ {
+ if (!m_ftcsr_read_cb.isnull())
+ {
+ m_ftcsr_read_cb((m_m[4] & 0xffff0000) | m_frc);
+ }
+ }
+ sh2_timer_resync();
+ return (m_m[4] & 0xffff0000) | m_frc;
+ case 0x05: // OCRx, TCR, TOCR
+ if(m_m[5] & 0x10)
+ return (m_ocrb << 16) | (m_m[5] & 0xffff);
+ else
+ return (m_ocra << 16) | (m_m[5] & 0xffff);
+ case 0x06: // ICR
+ return m_icr << 16;
+
+ case 0x20:
+ return (((m_wtcsr | 0x18) & 0xff) << 24) | ((m_wtcnt & 0xff) << 16);
+
+ case 0x24: // SBYCR, CCR
+ return m_m[0x24] & ~0x3000; /* bit 4-5 of CCR are always zero */
+
+ case 0x38: // ICR, IPRA
+ return (m_m[0x38] & 0x7fffffff) | (m_nmi_line_state == ASSERT_LINE ? 0 : 0x80000000);
+
+ case 0x78: // BCR1
+ return (m_is_slave ? 0x00008000 : 0) | (m_m[0x78] & 0x7fff);
+
+ case 0x41: // dvdntl mirrors
+ case 0x47:
+ return m_m[0x45];
+
+ case 0x46: // dvdnth mirror
+ return m_m[0x44];
+ }
+ return m_m[offset];
+}
+
+void sh2_device::sh2_set_frt_input(int state)
+{
+ if(state == PULSE_LINE)
+ {
+ sh2_set_frt_input(ASSERT_LINE);
+ sh2_set_frt_input(CLEAR_LINE);
+ return;
+ }
+
+ if(m_frt_input == state) {
+ return;
+ }
+
+ m_frt_input = state;
+
+ if(m_m[5] & 0x8000) {
+ if(state == CLEAR_LINE) {
+ return;
+ }
+ } else {
+ if(state == ASSERT_LINE) {
+ return;
+ }
+ }
+
+ sh2_timer_resync();
+ m_icr = m_frc;
+ m_m[4] |= ICF;
+ //logerror("SH2.%s: ICF activated (%x)\n", tag(), m_sh2_state->pc & AM);
+ sh2_recalc_irq();
+}
+
+void sh2_device::sh2_recalc_irq()
+{
+ int irq = 0, vector = -1;
+ int level;
+
+ // Timer irqs
+ if((m_m[4]>>8) & m_m[4] & (ICF|OCFA|OCFB|OVF))
+ {
+ level = (m_m[0x18] >> 24) & 15;
+ if(level > irq)
+ {
+ int mask = (m_m[4]>>8) & m_m[4];
+ irq = level;
+ if(mask & ICF)
+ vector = (m_m[0x19] >> 8) & 0x7f;
+ else if(mask & (OCFA|OCFB))
+ vector = m_m[0x19] & 0x7f;
+ else
+ vector = (m_m[0x1a] >> 24) & 0x7f;
+ }
+ }
+
+ // DMA irqs
+ if((m_m[0x63] & 6) == 6 && m_dma_irq[0]) {
+ level = (m_m[0x38] >> 8) & 15;
+ if(level > irq) {
+ irq = level;
+ m_dma_irq[0] &= ~1;
+ vector = (m_m[0x68]) & 0x7f;
+ }
+ }
+ else if((m_m[0x67] & 6) == 6 && m_dma_irq[1]) {
+ level = (m_m[0x38] >> 8) & 15;
+ if(level > irq) {
+ irq = level;
+ m_dma_irq[1] &= ~1;
+ vector = (m_m[0x6a]) & 0x7f;
+ }
+ }
+
+ m_sh2_state->internal_irq_level = irq;
+ m_internal_irq_vector = vector;
+ m_test_irq = 1;
+}
+
+/*
+ SH-7021 on-chip device
+ */
+
+void sh2a_device::sh7032_dma_exec(int ch)
+{
+ const short dma_word_size[4] = { 0, +1, -1, 0 };
+ uint8_t rs = (m_dma[ch].chcr >> 8) & 0xf; /**< Resource Select bits */
+ if(rs != 0xc) // Auto-Request
+ {
+ logerror("Warning: SH7032 DMA enables non auto-request transfer\n");
+ return;
+ }
+
+ // channel enable & master enable
+ if((m_dma[ch].chcr & 1) == 0 || (m_dmaor & 1) == 0)
+ return;
+
+ printf("%08x %08x %04x\n",m_dma[ch].sar,m_dma[ch].dar,m_dma[ch].chcr);
+ uint8_t dm = (m_dma[ch].chcr >> 14) & 3; /**< Destination Address Mode bits */
+ uint8_t sm = (m_dma[ch].chcr >> 12) & 3; /**< Source Address Mode bits */
+ bool ts = (m_dma[ch].chcr & 8); /**< Transfer Size bit */
+ int src_word_size = dma_word_size[sm] * ((ts == true) ? 2 : 1);
+ int dst_word_size = dma_word_size[dm] * ((ts == true) ? 2 : 1);
+ uint32_t src_addr = m_dma[ch].sar;
+ uint32_t dst_addr = m_dma[ch].dar;
+ uint32_t size_index = m_dma[ch].tcr;
+ if(size_index == 0)
+ size_index = 0x10000;
+
+ if(ts == false)
+ logerror("SH7032: DMA byte mode check\n");
+
+ for(int index = size_index;index>-1;index--)
+ {
+ if(ts == true)
+ m_program->write_word(dst_addr,m_program->read_word(src_addr));
+ else
+ m_program->write_byte(dst_addr,m_program->read_byte(src_addr));
+
+ src_addr += src_word_size;
+ dst_addr += dst_word_size;
+ }
+
+ m_dma[ch].chcr &= ~1; /**< @todo non-instant DMA */
+ printf("%02x %02x %02x %1d\n",sm,dm,rs,ts);
+}
+
+READ32_MEMBER(sh2a_device::dma_sar0_r)
+{
+ return m_dma[0].sar;
+}
+
+WRITE32_MEMBER(sh2a_device::dma_sar0_w)
+{
+ COMBINE_DATA(&m_dma[0].sar);
+}
+
+READ32_MEMBER(sh2a_device::dma_dar0_r)
+{
+ return m_dma[0].dar;
+}
+
+WRITE32_MEMBER(sh2a_device::dma_dar0_w)
+{
+ COMBINE_DATA(&m_dma[0].dar);
+}
+
+READ16_MEMBER(sh2a_device::dma_tcr0_r)
+{
+ return m_dma[0].tcr;
+}
+
+WRITE16_MEMBER(sh2a_device::dma_tcr0_w)
+{
+ //printf("%04x\n",data);
+ COMBINE_DATA(&m_dma[0].tcr);
+}
+
+READ16_MEMBER(sh2a_device::dma_chcr0_r)
+{
+ return m_dma[0].chcr;
+}
+
+WRITE16_MEMBER(sh2a_device::dma_chcr0_w)
+{
+ //printf("%04x CHCR0\n",data);
+ COMBINE_DATA(&m_dma[0].chcr);
+ sh7032_dma_exec(0);
+}
+
+READ16_MEMBER(sh2a_device::dmaor_r)
+{
+ return m_dmaor;
+}
+
+WRITE16_MEMBER(sh2a_device::dmaor_w)
+{
+ COMBINE_DATA(&m_dmaor);
+ sh7032_dma_exec(0);
+}
+
+/*!
+ @brief Dummy debug interface
+ */
+READ16_MEMBER(sh1_device::sh7032_r)
+{
+ return m_sh7032_regs[offset];
+}
+
+/*!
+ @brief Dummy debug interface
+ */
+WRITE16_MEMBER(sh1_device::sh7032_w)
+{
+ COMBINE_DATA(&m_sh7032_regs[offset]);
+}
+
+READ16_MEMBER(sh2a_device::sh7021_r)
+{
+ return m_sh7021_regs[offset];
+}
+
+/*!
+ @brief Dummy debug interface
+ */
+WRITE16_MEMBER(sh2a_device::sh7021_w)
+{
+ COMBINE_DATA(&m_sh7021_regs[offset]);
+}