summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/cpu/sh2/sh2comn.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/cpu/sh2/sh2comn.c')
-rw-r--r--trunk/src/emu/cpu/sh2/sh2comn.c1011
1 files changed, 1011 insertions, 0 deletions
diff --git a/trunk/src/emu/cpu/sh2/sh2comn.c b/trunk/src/emu/cpu/sh2/sh2comn.c
new file mode 100644
index 00000000000..220a38166ae
--- /dev/null
+++ b/trunk/src/emu/cpu/sh2/sh2comn.c
@@ -0,0 +1,1011 @@
+/*****************************************************************************
+ *
+ * sh2common.c
+ *
+ * SH-2 non-specific components
+ *
+ *****************************************************************************/
+
+#include "emu.h"
+#include "debugger.h"
+#include "sh2.h"
+#include "sh2comn.h"
+
+#define VERBOSE 0
+
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+#ifdef USE_SH2DRC
+#define GET_SH2(dev) *(sh2_state **)downcast<legacy_cpu_device *>(dev)->token()
+#else
+#define GET_SH2(dev) (sh2_state *)downcast<legacy_cpu_device *>(dev)->token()
+#endif
+
+
+static const int div_tab[4] = { 3, 5, 7, 0 };
+
+INLINE UINT32 RL(sh2_state *sh2, offs_t A)
+{
+ if (A >= 0xe0000000)
+ return sh2_internal_r(sh2->internal, (A & 0x1fc)>>2, 0xffffffff);
+
+ if (A >= 0xc0000000)
+ return sh2->program->read_dword(A);
+
+ if (A >= 0x40000000)
+ return 0xa5a5a5a5;
+
+ return sh2->program->read_dword(A & AM);
+}
+
+INLINE void WL(sh2_state *sh2, offs_t A, UINT32 V)
+{
+ if (A >= 0xe0000000)
+ {
+ sh2_internal_w(sh2->internal, (A & 0x1fc)>>2, V, 0xffffffff);
+ return;
+ }
+
+ if (A >= 0xc0000000)
+ {
+ sh2->program->write_dword(A,V);
+ return;
+ }
+
+ if (A >= 0x40000000)
+ return;
+
+ sh2->program->write_dword(A & AM,V);
+}
+
+static void sh2_timer_resync(sh2_state *sh2)
+{
+ int divider = div_tab[(sh2->m[5] >> 8) & 3];
+ UINT64 cur_time = sh2->device->total_cycles();
+ UINT64 add = (cur_time - sh2->frc_base) >> divider;
+
+ if (add > 0)
+ {
+ if(divider)
+ sh2->frc += add;
+
+ sh2->frc_base = cur_time;
+ }
+}
+
+static void sh2_timer_activate(sh2_state *sh2)
+{
+ int max_delta = 0xfffff;
+ UINT16 frc;
+
+ sh2->timer->adjust(attotime::never);
+
+ frc = sh2->frc;
+ if(!(sh2->m[4] & OCFA)) {
+ UINT16 delta = sh2->ocra - frc;
+ if(delta < max_delta)
+ max_delta = delta;
+ }
+
+ if(!(sh2->m[4] & OCFB) && (sh2->ocra <= sh2->ocrb || !(sh2->m[4] & 0x010000))) {
+ UINT16 delta = sh2->ocrb - frc;
+ if(delta < max_delta)
+ max_delta = delta;
+ }
+
+ if(!(sh2->m[4] & OVF) && !(sh2->m[4] & 0x010000)) {
+ int delta = 0x10000 - frc;
+ if(delta < max_delta)
+ max_delta = delta;
+ }
+
+ if(max_delta != 0xfffff) {
+ int divider = div_tab[(sh2->m[5] >> 8) & 3];
+ if(divider) {
+ max_delta <<= divider;
+ sh2->frc_base = sh2->device->total_cycles();
+ sh2->timer->adjust(sh2->device->cycles_to_attotime(max_delta));
+ } else {
+ logerror("SH2.%s: Timer event in %d cycles of external clock", sh2->device->tag(), max_delta);
+ }
+ }
+}
+
+
+static TIMER_CALLBACK( sh2_timer_callback )
+{
+ sh2_state *sh2 = (sh2_state *)ptr;
+ UINT16 frc;
+
+ sh2_timer_resync(sh2);
+
+ frc = sh2->frc;
+
+ if(frc == sh2->ocrb)
+ sh2->m[4] |= OCFB;
+
+ if(frc == 0x0000)
+ sh2->m[4] |= OVF;
+
+ if(frc == sh2->ocra)
+ {
+ sh2->m[4] |= OCFA;
+
+ if(sh2->m[4] & 0x010000)
+ sh2->frc = 0;
+ }
+
+ sh2_recalc_irq(sh2);
+ sh2_timer_activate(sh2);
+}
+
+
+/*
+ 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_notify_dma_data_available(device_t *device)
+{
+ sh2_state *sh2 = GET_SH2(device);
+ //printf("call notify\n");
+
+ for (int dma=0;dma<2;dma++)
+ {
+ //printf("sh2->dma_timer_active[dma] %04x\n",sh2->dma_timer_active[dma]);
+
+ if (sh2->dma_timer_active[dma]==2) // 2 = stalled
+ {
+ // printf("resuming stalled dma\n");
+ sh2->dma_timer_active[dma]=1;
+ sh2->dma_current_active_timer[dma]->adjust(attotime::zero, dma);
+ }
+ }
+
+}
+
+void sh2_do_dma(sh2_state *sh2, int dma)
+{
+ UINT32 dmadata;
+
+ UINT32 tempsrc, tempdst;
+
+ if (sh2->active_dma_count[dma] > 0)
+ {
+
+
+ // process current DMA
+ switch(sh2->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(sh2->active_dma_incs[dma] == 2)
+ tempsrc = sh2->active_dma_src[dma] - 1;
+ else
+ tempsrc = sh2->active_dma_src[dma];
+
+ if(sh2->active_dma_incd[dma] == 2)
+ tempdst = sh2->active_dma_dst[dma] - 1;
+ else
+ tempdst = sh2->active_dma_dst[dma];
+
+ if (sh2->dma_callback_fifo_data_available)
+ {
+ int available = sh2->dma_callback_fifo_data_available(sh2->device, tempsrc, tempdst, 0, sh2->active_dma_size[dma]);
+
+ if (!available)
+ {
+ //printf("dma stalled\n");
+ sh2->dma_timer_active[dma]=2;// mark as stalled
+ return;
+ }
+ }
+
+ #ifdef USE_TIMER_FOR_DMA
+ //schedule next DMA callback
+ sh2->dma_current_active_timer[dma]->adjust(sh2->device->cycles_to_attotime(2), dma);
+ #endif
+
+
+ dmadata = sh2->program->read_byte(tempsrc);
+ if (sh2->dma_callback_kludge) dmadata = sh2->dma_callback_kludge(sh2->device, tempsrc, tempdst, dmadata, sh2->active_dma_size[dma]);
+ sh2->program->write_byte(tempdst, dmadata);
+
+ if(sh2->active_dma_incs[dma] == 2)
+ sh2->active_dma_src[dma] --;
+ if(sh2->active_dma_incd[dma] == 2)
+ sh2->active_dma_dst[dma] --;
+
+
+ if(sh2->active_dma_incs[dma] == 1)
+ sh2->active_dma_src[dma] ++;
+ if(sh2->active_dma_incd[dma] == 1)
+ sh2->active_dma_dst[dma] ++;
+
+ sh2->active_dma_count[dma] --;
+ }
+ break;
+ case 1:
+ {
+ if(sh2->active_dma_incs[dma] == 2)
+ tempsrc = sh2->active_dma_src[dma] - 2;
+ else
+ tempsrc = sh2->active_dma_src[dma];
+
+ if(sh2->active_dma_incd[dma] == 2)
+ tempdst = sh2->active_dma_dst[dma] - 2;
+ else
+ tempdst = sh2->active_dma_dst[dma];
+
+ if (sh2->dma_callback_fifo_data_available)
+ {
+ int available = sh2->dma_callback_fifo_data_available(sh2->device, tempsrc, tempdst, 0, sh2->active_dma_size[dma]);
+
+ if (!available)
+ {
+ //printf("dma stalled\n");
+ sh2->dma_timer_active[dma]=2;// mark as stalled
+ return;
+ }
+ }
+
+ #ifdef USE_TIMER_FOR_DMA
+ //schedule next DMA callback
+ sh2->dma_current_active_timer[dma]->adjust(sh2->device->cycles_to_attotime(2), dma);
+ #endif
+
+ // check: should this really be using read_word_32 / write_word_32?
+ dmadata = sh2->program->read_word(tempsrc);
+ if (sh2->dma_callback_kludge) dmadata = sh2->dma_callback_kludge(sh2->device, tempsrc, tempdst, dmadata, sh2->active_dma_size[dma]);
+ sh2->program->write_word(tempdst, dmadata);
+
+ if(sh2->active_dma_incs[dma] == 2)
+ sh2->active_dma_src[dma] -= 2;
+ if(sh2->active_dma_incd[dma] == 2)
+ sh2->active_dma_dst[dma] -= 2;
+
+ if(sh2->active_dma_incs[dma] == 1)
+ sh2->active_dma_src[dma] += 2;
+ if(sh2->active_dma_incd[dma] == 1)
+ sh2->active_dma_dst[dma] += 2;
+
+ sh2->active_dma_count[dma] --;
+ }
+ break;
+ case 2:
+ {
+ if(sh2->active_dma_incs[dma] == 2)
+ tempsrc = sh2->active_dma_src[dma] - 4;
+ else
+ tempsrc = sh2->active_dma_src[dma];
+
+ if(sh2->active_dma_incd[dma] == 2)
+ tempdst = sh2->active_dma_dst[dma] - 4;
+ else
+ tempdst = sh2->active_dma_dst[dma];
+
+ if (sh2->dma_callback_fifo_data_available)
+ {
+ int available = sh2->dma_callback_fifo_data_available(sh2->device, tempsrc, tempdst, 0, sh2->active_dma_size[dma]);
+
+ if (!available)
+ {
+ //printf("dma stalled\n");
+ sh2->dma_timer_active[dma]=2;// mark as stalled
+ return;
+ }
+ }
+
+ #ifdef USE_TIMER_FOR_DMA
+ //schedule next DMA callback
+ sh2->dma_current_active_timer[dma]->adjust(sh2->device->cycles_to_attotime(2), dma);
+ #endif
+
+ dmadata = sh2->program->read_dword(tempsrc);
+ if (sh2->dma_callback_kludge) dmadata = sh2->dma_callback_kludge(sh2->device, tempsrc, tempdst, dmadata, sh2->active_dma_size[dma]);
+ sh2->program->write_dword(tempdst, dmadata);
+
+ if(sh2->active_dma_incs[dma] == 2)
+ sh2->active_dma_src[dma] -= 4;
+ if(sh2->active_dma_incd[dma] == 2)
+ sh2->active_dma_dst[dma] -= 4;
+
+ if(sh2->active_dma_incs[dma] == 1)
+ sh2->active_dma_src[dma] += 4;
+ if(sh2->active_dma_incd[dma] == 1)
+ sh2->active_dma_dst[dma] += 4;
+
+ sh2->active_dma_count[dma] --;
+ }
+ break;
+ case 3:
+ {
+ // shouldn't this really be 4 calls here instead?
+
+ tempsrc = sh2->active_dma_src[dma];
+
+ if(sh2->active_dma_incd[dma] == 2)
+ tempdst = sh2->active_dma_dst[dma] - 16;
+ else
+ tempdst = sh2->active_dma_dst[dma];
+
+ if (sh2->dma_callback_fifo_data_available)
+ {
+ int available = sh2->dma_callback_fifo_data_available(sh2->device, tempsrc, tempdst, 0, sh2->active_dma_size[dma]);
+
+ if (!available)
+ {
+ //printf("dma stalled\n");
+ sh2->dma_timer_active[dma]=2;// mark as stalled
+ fatalerror("SH2 dma_callback_fifo_data_available == 0 in unsupported mode");
+ }
+ }
+
+ #ifdef USE_TIMER_FOR_DMA
+ //schedule next DMA callback
+ sh2->dma_current_active_timer[dma]->adjust(sh2->device->cycles_to_attotime(2), dma);
+ #endif
+
+ dmadata = sh2->program->read_dword(tempsrc);
+ if (sh2->dma_callback_kludge) dmadata = sh2->dma_callback_kludge(sh2->device, tempsrc, tempdst, dmadata, sh2->active_dma_size[dma]);
+ sh2->program->write_dword(tempdst, dmadata);
+
+ dmadata = sh2->program->read_dword(tempsrc+4);
+ if (sh2->dma_callback_kludge) dmadata = sh2->dma_callback_kludge(sh2->device, tempsrc, tempdst, dmadata, sh2->active_dma_size[dma]);
+ sh2->program->write_dword(tempdst+4, dmadata);
+
+ dmadata = sh2->program->read_dword(tempsrc+8);
+ if (sh2->dma_callback_kludge) dmadata = sh2->dma_callback_kludge(sh2->device, tempsrc, tempdst, dmadata, sh2->active_dma_size[dma]);
+ sh2->program->write_dword(tempdst+8, dmadata);
+
+ dmadata = sh2->program->read_dword(tempsrc+12);
+ if (sh2->dma_callback_kludge) dmadata = sh2->dma_callback_kludge(sh2->device, tempsrc, tempdst, dmadata, sh2->active_dma_size[dma]);
+ sh2->program->write_dword(tempdst+12, dmadata);
+
+ if(sh2->active_dma_incd[dma] == 2)
+ sh2->active_dma_dst[dma] -= 16;
+
+ sh2->active_dma_src[dma] += 16;
+ if(sh2->active_dma_incd[dma] == 1)
+ sh2->active_dma_dst[dma] += 16;
+
+ sh2->active_dma_count[dma]-=4;
+ }
+ break;
+ }
+ }
+ else // the dma is complete
+ {
+ // int dma = param & 1;
+ // sh2_state *sh2 = (sh2_state *)ptr;
+
+ // fever soccer uses cycle-stealing mode, resume the CPU now DMA has finished
+ if (sh2->active_dma_steal[dma])
+ {
+ sh2->device->resume(SUSPEND_REASON_HALT );
+ }
+
+
+ LOG(("SH2.%s: DMA %d complete\n", sh2->device->tag(), dma));
+ sh2->m[0x63+4*dma] |= 2;
+ sh2->dma_timer_active[dma] = 0;
+ sh2_recalc_irq(sh2);
+
+ }
+}
+
+static TIMER_CALLBACK( sh2_dma_current_active_callback )
+{
+ int dma = param & 1;
+ sh2_state *sh2 = (sh2_state *)ptr;
+
+ sh2_do_dma(sh2, dma);
+}
+
+
+static void sh2_dmac_check(sh2_state *sh2, int dma)
+{
+ if(sh2->m[0x63+4*dma] & sh2->m[0x6c] & 1)
+ {
+ if(!sh2->dma_timer_active[dma] && !(sh2->m[0x63+4*dma] & 2))
+ {
+
+ sh2->active_dma_incd[dma] = (sh2->m[0x63+4*dma] >> 14) & 3;
+ sh2->active_dma_incs[dma] = (sh2->m[0x63+4*dma] >> 12) & 3;
+ sh2->active_dma_size[dma] = (sh2->m[0x63+4*dma] >> 10) & 3;
+ sh2->active_dma_steal[dma] = (sh2->m[0x63+4*dma] &0x10);
+
+ if(sh2->active_dma_incd[dma] == 3 || sh2->active_dma_incs[dma] == 3)
+ {
+ logerror("SH2: DMA: bad increment values (%d, %d, %d, %04x)\n", sh2->active_dma_incd[dma], sh2->active_dma_incs[dma], sh2->active_dma_size[dma], sh2->m[0x63+4*dma]);
+ return;
+ }
+ sh2->active_dma_src[dma] = sh2->m[0x60+4*dma];
+ sh2->active_dma_dst[dma] = sh2->m[0x61+4*dma];
+ sh2->active_dma_count[dma] = sh2->m[0x62+4*dma];
+ if(!sh2->active_dma_count[dma])
+ sh2->active_dma_count[dma] = 0x1000000;
+
+ LOG(("SH2: DMA %d start %x, %x, %x, %04x, %d, %d, %d\n", dma, sh2->active_dma_src[dma], sh2->active_dma_dst[dma], sh2->active_dma_count[dma], sh2->m[0x63+4*dma], sh2->active_dma_incs[dma], sh2->active_dma_incd[dma], sh2->active_dma_size[dma]));
+
+ sh2->dma_timer_active[dma] = 1;
+
+ sh2->active_dma_src[dma] &= AM;
+ sh2->active_dma_dst[dma] &= AM;
+
+ switch(sh2->active_dma_size[dma])
+ {
+ case 0:
+ break;
+ case 1:
+ sh2->active_dma_src[dma] &= ~1;
+ sh2->active_dma_dst[dma] &= ~1;
+ break;
+ case 2:
+ sh2->active_dma_src[dma] &= ~3;
+ sh2->active_dma_dst[dma] &= ~3;
+ break;
+ case 3:
+ sh2->active_dma_src[dma] &= ~3;
+ sh2->active_dma_dst[dma] &= ~3;
+ sh2->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 (sh2->active_dma_steal[dma])
+ {
+ //printf("cycle stealing DMA\n");
+ sh2->device->suspend(SUSPEND_REASON_HALT, 1 );
+ }
+
+ sh2->dma_current_active_timer[dma]->adjust(sh2->device->cycles_to_attotime(2), dma);
+#endif
+
+ }
+ }
+ else
+ {
+ if(sh2->dma_timer_active[dma])
+ {
+ logerror("SH2: DMA %d cancelled in-flight\n", dma);
+ //sh2->dma_complete_timer[dma]->adjust(attotime::never);
+ sh2->dma_current_active_timer[dma]->adjust(attotime::never);
+
+ sh2->dma_timer_active[dma] = 0;
+ }
+ }
+}
+
+WRITE32_HANDLER( sh2_internal_w )
+{
+ sh2_state *sh2 = GET_SH2(&space->device());
+ UINT32 old;
+
+#ifdef USE_SH2DRC
+ offset &= 0x7f;
+#endif
+
+ old = sh2->m[offset];
+ COMBINE_DATA(sh2->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, cpu_get_pc(&space->device()));
+
+ switch( offset )
+ {
+ // Timers
+ case 0x04: // TIER, FTCSR, FRC
+ if((mem_mask & 0x00ffffff) != 0)
+ {
+ sh2_timer_resync(sh2);
+ }
+// printf("SH2.%s: TIER write %04x @ %04x\n", sh2->device->tag(), data >> 16, mem_mask>>16);
+ sh2->m[4] = (sh2->m[4] & ~(ICF|OCFA|OCFB|OVF)) | (old & sh2->m[4] & (ICF|OCFA|OCFB|OVF));
+ COMBINE_DATA(&sh2->frc);
+ if((mem_mask & 0x00ffffff) != 0)
+ sh2_timer_activate(sh2);
+ sh2_recalc_irq(sh2);
+ break;
+ case 0x05: // OCRx, TCR, TOCR
+// printf("SH2.%s: TCR write %08x @ %08x\n", sh2->device->tag(), data, mem_mask);
+ sh2_timer_resync(sh2);
+ if(sh2->m[5] & 0x10)
+ sh2->ocrb = (sh2->ocrb & (~mem_mask >> 16)) | ((data & mem_mask) >> 16);
+ else
+ sh2->ocra = (sh2->ocra & (~mem_mask >> 16)) | ((data & mem_mask) >> 16);
+ sh2_timer_activate(sh2);
+ break;
+
+ case 0x06: // ICR
+ break;
+
+ // Interrupt vectors
+ case 0x18: // IPRB, VCRA
+ case 0x19: // VCRB, VCRC
+ case 0x1a: // VCRD
+ sh2_recalc_irq(sh2);
+ break;
+
+ // DMA
+ case 0x1c: // DRCR0, DRCR1
+ break;
+
+ // Watchdog
+ case 0x20: // WTCNT, RSTCSR
+ break;
+
+ // Standby and cache
+ case 0x24: // SBYCR, CCR
+ break;
+
+ // Interrupt vectors cont.
+ case 0x38: // ICR, IRPA
+ break;
+ case 0x39: // VCRWDT
+ break;
+
+ // Division box
+ case 0x40: // DVSR
+ break;
+ case 0x41: // DVDNT
+ {
+ INT32 a = sh2->m[0x41];
+ INT32 b = sh2->m[0x40];
+ LOG(("SH2 '%s' div+mod %d/%d\n", sh2->device->tag(), a, b));
+ if (b)
+ {
+ sh2->m[0x45] = a / b;
+ sh2->m[0x44] = a % b;
+ }
+ else
+ {
+ sh2->m[0x42] |= 0x00010000;
+ sh2->m[0x45] = 0x7fffffff;
+ sh2->m[0x44] = 0x7fffffff;
+ sh2_recalc_irq(sh2);
+ }
+ break;
+ }
+ case 0x42: // DVCR
+ sh2->m[0x42] = (sh2->m[0x42] & ~0x00001000) | (old & sh2->m[0x42] & 0x00010000);
+ sh2_recalc_irq(sh2);
+ break;
+ case 0x43: // VCRDIV
+ sh2_recalc_irq(sh2);
+ break;
+ case 0x44: // DVDNTH
+ break;
+ case 0x45: // DVDNTL
+ {
+ INT64 a = sh2->m[0x45] | ((UINT64)(sh2->m[0x44]) << 32);
+ INT64 b = (INT32)sh2->m[0x40];
+ LOG(("SH2 '%s' div+mod %" I64FMT "d/%" I64FMT "d\n", sh2->device->tag(), a, b));
+ if (b)
+ {
+ INT64 q = a / b;
+ if (q != (INT32)q)
+ {
+ sh2->m[0x42] |= 0x00010000;
+ sh2->m[0x45] = 0x7fffffff;
+ sh2->m[0x44] = 0x7fffffff;
+ sh2_recalc_irq(sh2);
+ }
+ else
+ {
+ sh2->m[0x45] = q;
+ sh2->m[0x44] = a % b;
+ }
+ }
+ else
+ {
+ sh2->m[0x42] |= 0x00010000;
+ sh2->m[0x45] = 0x7fffffff;
+ sh2->m[0x44] = 0x7fffffff;
+ sh2_recalc_irq(sh2);
+ }
+ break;
+ }
+
+ // DMA controller
+ case 0x60: // SAR0
+ case 0x61: // DAR0
+ break;
+ case 0x62: // DTCR0
+ sh2->m[0x62] &= 0xffffff;
+ break;
+ case 0x63: // CHCR0
+ sh2->m[0x63] = (sh2->m[0x63] & ~2) | (old & sh2->m[0x63] & 2);
+ sh2_dmac_check(sh2, 0);
+ break;
+ case 0x64: // SAR1
+ case 0x65: // DAR1
+ break;
+ case 0x66: // DTCR1
+ sh2->m[0x66] &= 0xffffff;
+ break;
+ case 0x67: // CHCR1
+ sh2->m[0x67] = (sh2->m[0x67] & ~2) | (old & sh2->m[0x67] & 2);
+ sh2_dmac_check(sh2, 1);
+ break;
+ case 0x68: // VCRDMA0
+ case 0x6a: // VCRDMA1
+ sh2_recalc_irq(sh2);
+ break;
+ case 0x6c: // DMAOR
+ sh2->m[0x6c] = (sh2->m[0x6c] & ~6) | (old & sh2->m[0x6c] & 6);
+ sh2_dmac_check(sh2, 0);
+ sh2_dmac_check(sh2, 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_HANDLER( sh2_internal_r )
+{
+ sh2_state *sh2 = GET_SH2(&space->device());
+
+#ifdef USE_SH2DRC
+ offset &= 0x7f;
+#endif
+// logerror("sh2_internal_r: Read %08x (%x) @ %08x\n", 0xfffffe00+offset*4, offset, mem_mask);
+ switch( offset )
+ {
+ case 0x04: // TIER, FTCSR, FRC
+ if ( mem_mask == 0x00ff0000 )
+ {
+ if ( sh2->ftcsr_read_callback != NULL )
+ {
+ sh2->ftcsr_read_callback( (sh2->m[4] & 0xffff0000) | sh2->frc );
+ }
+ }
+ sh2_timer_resync(sh2);
+ return (sh2->m[4] & 0xffff0000) | sh2->frc;
+ case 0x05: // OCRx, TCR, TOCR
+ if(sh2->m[5] & 0x10)
+ return (sh2->ocrb << 16) | (sh2->m[5] & 0xffff);
+ else
+ return (sh2->ocra << 16) | (sh2->m[5] & 0xffff);
+ case 0x06: // ICR
+ return sh2->icr << 16;
+
+ case 0x38: // ICR, IPRA
+ return (sh2->m[0x38] & 0x7fffffff) | (sh2->nmi_line_state == ASSERT_LINE ? 0 : 0x80000000);
+
+ case 0x78: // BCR1
+ return sh2->is_slave ? 0x00008000 : 0;
+
+ case 0x41: // dvdntl mirrors
+ case 0x47:
+ return sh2->m[0x45];
+
+ case 0x46: // dvdnth mirror
+ return sh2->m[0x44];
+ }
+ return sh2->m[offset];
+}
+
+void sh2_set_ftcsr_read_callback(device_t *device, void (*callback)(UINT32))
+{
+ sh2_state *sh2 = GET_SH2(device);
+ sh2->ftcsr_read_callback = callback;
+}
+
+void sh2_set_frt_input(device_t *device, int state)
+{
+ sh2_state *sh2 = GET_SH2(device);
+
+ if(state == PULSE_LINE)
+ {
+ sh2_set_frt_input(device, ASSERT_LINE);
+ sh2_set_frt_input(device, CLEAR_LINE);
+ return;
+ }
+
+ if(sh2->frt_input == state) {
+ return;
+ }
+
+ sh2->frt_input = state;
+
+ if(sh2->m[5] & 0x8000) {
+ if(state == CLEAR_LINE) {
+ return;
+ }
+ } else {
+ if(state == ASSERT_LINE) {
+ return;
+ }
+ }
+
+ sh2_timer_resync(sh2);
+ sh2->icr = sh2->frc;
+ sh2->m[4] |= ICF;
+ //logerror("SH2.%s: ICF activated (%x)\n", sh2->device->tag(), sh2->pc & AM);
+ sh2_recalc_irq(sh2);
+}
+
+void sh2_set_irq_line(sh2_state *sh2, int irqline, int state)
+{
+ if (irqline == INPUT_LINE_NMI)
+ {
+ if (sh2->nmi_line_state == state)
+ return;
+ sh2->nmi_line_state = state;
+
+ if( state == CLEAR_LINE )
+ {
+ LOG(("SH-2 '%s' cleared nmi\n", sh2->device->tag()));
+ }
+ else
+ {
+ LOG(("SH-2 '%s' assert nmi\n", sh2->device->tag()));
+
+ sh2_exception(sh2, "Set IRQ line", 16);
+
+ #ifdef USE_SH2DRC
+ sh2->pending_nmi = 1;
+ #endif
+ }
+ }
+ else
+ {
+ if (sh2->irq_line_state[irqline] == state)
+ return;
+ sh2->irq_line_state[irqline] = state;
+
+ if( state == CLEAR_LINE )
+ {
+ LOG(("SH-2 '%s' cleared irq #%d\n", sh2->device->tag(), irqline));
+ sh2->pending_irq &= ~(1 << irqline);
+ }
+ else
+ {
+ LOG(("SH-2 '%s' assert irq #%d\n", sh2->device->tag(), irqline));
+ sh2->pending_irq |= 1 << irqline;
+ #ifdef USE_SH2DRC
+ sh2->test_irq = 1;
+ #else
+ if(sh2->delay)
+ sh2->test_irq = 1;
+ else
+ CHECK_PENDING_IRQ("sh2_set_irq_line");
+ #endif
+ }
+ }
+}
+
+void sh2_recalc_irq(sh2_state *sh2)
+{
+ int irq = 0, vector = -1;
+ int level;
+
+ // Timer irqs
+ if((sh2->m[4]>>8) & sh2->m[4] & (ICF|OCFA|OCFB|OVF))
+ {
+ level = (sh2->m[0x18] >> 24) & 15;
+ if(level > irq)
+ {
+ int mask = (sh2->m[4]>>8) & sh2->m[4];
+ irq = level;
+ if(mask & ICF)
+ vector = (sh2->m[0x19] >> 8) & 0x7f;
+ else if(mask & (OCFA|OCFB))
+ vector = sh2->m[0x19] & 0x7f;
+ else
+ vector = (sh2->m[0x1a] >> 24) & 0x7f;
+ }
+ }
+
+ // DMA irqs
+ if((sh2->m[0x63] & 6) == 6) {
+ level = (sh2->m[0x38] >> 8) & 15;
+ if(level > irq) {
+ irq = level;
+ vector = (sh2->m[0x68] >> 24) & 0x7f;
+ }
+ }
+
+ if((sh2->m[0x67] & 6) == 6) {
+ level = (sh2->m[0x38] >> 8) & 15;
+ if(level > irq) {
+ irq = level;
+ vector = (sh2->m[0x6a] >> 24) & 0x7f;
+ }
+ }
+
+ sh2->internal_irq_level = irq;
+ sh2->internal_irq_vector = vector;
+ sh2->test_irq = 1;
+}
+
+void sh2_exception(sh2_state *sh2, const char *message, int irqline)
+{
+ int vector;
+
+ if (irqline != 16)
+ {
+ if (irqline <= ((sh2->sr >> 4) & 15)) /* If the cpu forbids this interrupt */
+ return;
+
+ // if this is an sh2 internal irq, use its vector
+ if (sh2->internal_irq_level == irqline)
+ {
+ vector = sh2->internal_irq_vector;
+ LOG(("SH-2 '%s' exception #%d (internal vector: $%x) after [%s]\n", sh2->device->tag(), irqline, vector, message));
+ }
+ else
+ {
+ if(sh2->m[0x38] & 0x00010000)
+ {
+ vector = sh2->irq_callback(sh2->device, irqline);
+ LOG(("SH-2 '%s' exception #%d (external vector: $%x) after [%s]\n", sh2->device->tag(), irqline, vector, message));
+ }
+ else
+ {
+ sh2->irq_callback(sh2->device, irqline);
+ vector = 64 + irqline/2;
+ LOG(("SH-2 '%s' exception #%d (autovector: $%x) after [%s]\n", sh2->device->tag(), irqline, vector, message));
+ }
+ }
+ }
+ else
+ {
+ vector = 11;
+ LOG(("SH-2 '%s' nmi exception (autovector: $%x) after [%s]\n", sh2->device->tag(), vector, message));
+ }
+
+ #ifdef USE_SH2DRC
+ sh2->evec = RL( sh2, sh2->vbr + vector * 4 );
+ sh2->evec &= AM;
+ sh2->irqsr = sh2->sr;
+
+ /* set I flags in SR */
+ if (irqline > SH2_INT_15)
+ sh2->sr = sh2->sr | I;
+ else
+ sh2->sr = (sh2->sr & ~I) | (irqline << 4);
+
+// printf("sh2_exception [%s] irqline %x evec %x save SR %x new SR %x\n", message, irqline, sh2->evec, sh2->irqsr, sh2->sr);
+ #else
+ sh2->r[15] -= 4;
+ WL( sh2, sh2->r[15], sh2->sr ); /* push SR onto stack */
+ sh2->r[15] -= 4;
+ WL( sh2, sh2->r[15], sh2->pc ); /* push PC onto stack */
+
+ /* set I flags in SR */
+ if (irqline > SH2_INT_15)
+ sh2->sr = sh2->sr | I;
+ else
+ sh2->sr = (sh2->sr & ~I) | (irqline << 4);
+
+ /* fetch PC */
+ sh2->pc = RL( sh2, sh2->vbr + vector * 4 );
+ #endif
+}
+
+void sh2_common_init(sh2_state *sh2, legacy_cpu_device *device, device_irq_callback irqcallback)
+{
+ const sh2_cpu_core *conf = (const sh2_cpu_core *)device->static_config();
+ int i;
+
+ sh2->timer = device->machine().scheduler().timer_alloc(FUNC(sh2_timer_callback), sh2);
+ sh2->timer->adjust(attotime::never);
+
+ sh2->dma_current_active_timer[0] = device->machine().scheduler().timer_alloc(FUNC(sh2_dma_current_active_callback), sh2);
+ sh2->dma_current_active_timer[0]->adjust(attotime::never);
+
+ sh2->dma_current_active_timer[1] = device->machine().scheduler().timer_alloc(FUNC(sh2_dma_current_active_callback), sh2);
+ sh2->dma_current_active_timer[1]->adjust(attotime::never);
+
+
+ sh2->m = auto_alloc_array(device->machine(), UINT32, 0x200/4);
+
+ if(conf)
+ {
+ sh2->is_slave = conf->is_slave;
+ sh2->dma_callback_kludge = conf->dma_callback_kludge;
+ sh2->dma_callback_fifo_data_available = conf->dma_callback_fifo_data_available;
+ }
+ else
+ {
+ sh2->is_slave = 0;
+ sh2->dma_callback_kludge = NULL;
+ sh2->dma_callback_fifo_data_available = NULL;
+
+ }
+ sh2->irq_callback = irqcallback;
+ sh2->device = device;
+ sh2->program = device->space(AS_PROGRAM);
+ sh2->direct = &sh2->program->direct();
+ sh2->internal = device->space(AS_PROGRAM);
+
+ device->save_item(NAME(sh2->pc));
+ device->save_item(NAME(sh2->sr));
+ device->save_item(NAME(sh2->pr));
+ device->save_item(NAME(sh2->gbr));
+ device->save_item(NAME(sh2->vbr));
+ device->save_item(NAME(sh2->mach));
+ device->save_item(NAME(sh2->macl));
+ device->save_item(NAME(sh2->r));
+ device->save_item(NAME(sh2->ea));
+ device->save_item(NAME(sh2->delay));
+ device->save_item(NAME(sh2->cpu_off));
+ device->save_item(NAME(sh2->dvsr));
+ device->save_item(NAME(sh2->dvdnth));
+ device->save_item(NAME(sh2->dvdntl));
+ device->save_item(NAME(sh2->dvcr));
+ device->save_item(NAME(sh2->pending_irq));
+ device->save_item(NAME(sh2->test_irq));
+ device->save_item(NAME(sh2->pending_nmi));
+ device->save_item(NAME(sh2->irqline));
+ device->save_item(NAME(sh2->evec));
+ device->save_item(NAME(sh2->irqsr));
+ device->save_item(NAME(sh2->target));
+ for (i = 0; i < 16; ++i)
+ {
+ device->save_item(NAME(sh2->irq_queue[i].irq_vector), i);
+ device->save_item(NAME(sh2->irq_queue[i].irq_priority), i);
+ }
+ device->save_item(NAME(sh2->pcfsel));
+ device->save_item(NAME(sh2->maxpcfsel));
+ device->save_item(NAME(sh2->pcflushes));
+ device->save_item(NAME(sh2->irq_line_state));
+ device->save_pointer(NAME(sh2->m), 0x200/4);
+ device->save_item(NAME(sh2->nmi_line_state));
+ device->save_item(NAME(sh2->frc));
+ device->save_item(NAME(sh2->ocra));
+ device->save_item(NAME(sh2->ocrb));
+ device->save_item(NAME(sh2->icr));
+ device->save_item(NAME(sh2->frc_base));
+ device->save_item(NAME(sh2->frt_input));
+ device->save_item(NAME(sh2->internal_irq_level));
+ device->save_item(NAME(sh2->internal_irq_vector));
+ device->save_item(NAME(sh2->dma_timer_active));
+}
+