/***************************************************************************** * * sh4comn.c * * SH-4 non-specific components * *****************************************************************************/ #include "emu.h" #include "debugger.h" #include "sh4.h" #include "sh4regs.h" #include "sh4comn.h" #include "sh3comn.h" #include "sh4tmu.h" #include "sh4dmac.h" static const int rtcnt_div[8] = { 0, 4, 16, 64, 256, 1024, 2048, 4096 }; static const int daysmonth[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static const UINT32 exception_priority_default[] = { EXPPRI(1,1,0,0), /* Power-on Reset */ EXPPRI(1,2,0,1), /* Manual Reset */ EXPPRI(1,1,0,2), /* H-UDI Reset */ EXPPRI(1,3,0,3), /* Inst TLB Multiple Hit */ EXPPRI(1,4,0,4), /* Data TLB Multiple Hit */ EXPPRI(2,0,0,5), /* User break Before Instruction */ EXPPRI(2,1,0,6), /* Inst Address Error */ EXPPRI(2,2,0,7), /* Inst TLB Miss */ EXPPRI(2,3,0,8), /* Inst TLB Protection Violation */ EXPPRI(2,4,0,9), /* Illegal Instruction */ EXPPRI(2,4,0,10), /* Slot Illegal Instruction */ EXPPRI(2,4,0,11), /* FPU Disable */ EXPPRI(2,4,0,12), /* Slot FPU Disable */ EXPPRI(2,5,0,13), /* Data Address Error (Read) */ EXPPRI(2,5,0,14), /* Data Address Error (Write) */ EXPPRI(2,6,0,15), /* Data TBL Miss Read */ EXPPRI(2,6,0,16), /* Data TBL Miss Write */ EXPPRI(2,7,0,17), /* Data TBL Protection Violation Read */ EXPPRI(2,7,0,18), /* Data TBL Protection Violation Write */ EXPPRI(2,8,0,19), /* FPU Exception */ EXPPRI(2,9,0,20), /* Initial Page Write exception */ EXPPRI(2,4,0,21), /* Unconditional TRAP */ EXPPRI(2,10,0,22), /* User break After Instruction */ EXPPRI(3,0,16,SH4_INTC_NMI) /* NMI */ /* This is copied to a table, and the IRQ priorities filled in later */ }; static const int exception_codes[] = { 0x000, /* Power-on Reset */ 0x020, /* Manual Reset */ 0x000, /* H-UDI Reset */ 0x140, /* Inst TLB Multiple Hit */ 0x140, /* Data TLB Multiple Hit */ 0x1E0, /* User break Before Instruction */ 0x0E0, /* Inst Address Error */ 0x040, /* Inst TLB Miss */ 0x0A0, /* Inst TLB Protection Violation */ 0x180, /* Illegal Instruction */ 0x1A0, /* Slot Illegal Instruction */ 0x800, /* FPU Disable */ 0x820, /* Slot FPU Disable */ 0x0E0, /* Data Address Error (Read) */ 0x100, /* Data Address Error (Write) */ 0x040, /* Data TBL Miss Read */ 0x060, /* Data TBL Miss Write */ 0x0A0, /* Data TBL Protection Violation Read */ 0x0C0, /* Data TBL Protection Violation Write */ 0x120, /* FPU Exception */ 0x080, /* Initial Page Write exception */ 0x160, /* Unconditional TRAP */ 0x1E0, /* User break After Instruction */ 0x1C0, /* NMI */ /* SH4_INTC_NMI=23 represents this location in this list.. */ 0x200, /* EX Irq 0 */ 0x220, /* 1 */ 0x240, /* 2 */ 0x260, /* 3 */ 0x280, /* 4 */ 0x2A0, /* 5 */ 0x2C0, /* 6 */ 0x2E0, /* 7 */ 0x300, /* 8 */ 0x320, /* 9 */ 0x340, /* A */ 0x360, /* B */ 0x380, /* C */ 0x3A0, /* D */ 0x3C0, /* E */ 0x240, /* SH4_INTC_IRL0 */ 0x2A0, /* SH4_INTC_IRL1 */ 0x300, /* SH4_INTC_IRL2 */ 0x360, /* SH4_INTC_IRL3 */ 0x600, /* HUDI */ 0x620, /* SH4_INTC_GPOI */ 0x640, /* SH4_INTC_DMTE0 */ 0x660, /* SH4_INTC_DMTE1 */ 0x680, /* SH4_INTC_DMTE2 */ 0x6A0, /* SH4_INTC_DMTE3 */ 0x780, /* SH4_INTC_DMTE4 */ 0x7A0, /* SH4_INTC_DMTE5 */ 0x7C0, /* SH4_INTC_DMTE6 */ 0x7E0, /* SH4_INTC_DMTE7 */ 0x6C0, /* SH4_INTC_DMAE */ 0xB00, /* SH4_INTC_TUNI3 */ 0xB80, /* SH4_INTC_TUNI4 */ 0x400, /* SH4_INTC_TUNI0 */ 0x420, /* SH4_INTC_TUNI1 */ 0x440, /* SH4_INTC_TUNI2 */ 0x460, /* SH4_INTC_TICPI2 */ 0x480, /* SH4_INTC_ATI */ 0x4A0, /* SH4_INTC_PRI */ 0x4C0, /* SH4_INTC_CUI */ 0x4E0, /* SH4_INTC_SCI1ERI */ 0x500, /* SH4_INTC_SCI1RXI */ 0x520, /* SH4_INTC_SCI1TXI */ 0x540, /* SH4_INTC_SCI1TEI */ 0x700, /* SH4_INTC_SCIFERI */ 0x720, /* SH4_INTC_SCIFRXI */ 0x740, /* SH4_INTC_SCIFBRI */ 0x760, /* SH4_INTC_SCIFTXI */ 0x560, /* SH4_INTC_ITI */ 0x580, /* SH4_INTC_RCMI */ 0x5A0 /* SH4_INTC_ROVI */ }; /* SH3 INTEVT2 uses a different table - values of -1 aren't filled in yet, some may not exist on the sh3. */ /* The above table should differ too, some things depend on the interrupt level rather than beign fixed values */ static const int sh3_intevt2_exception_codes[] = { 0x000, /* Power-on Reset */ -1, /* Manual Reset */ -1, /* H-UDI Reset */ -1, /* Inst TLB Multiple Hit */ -1, /* Data TLB Multiple Hit */ -1, /* User break Before Instruction */ -1, /* Inst Address Error */ -1, /* Inst TLB Miss */ -1, /* Inst TLB Protection Violation */ -1, /* Illegal Instruction */ -1, /* Slot Illegal Instruction */ -1, /* FPU Disable */ -1, /* Slot FPU Disable */ -1, /* Data Address Error (Read) */ -1, /* Data Address Error (Write) */ -1, /* Data TBL Miss Read */ -1, /* Data TBL Miss Write */ -1, /* Data TBL Protection Violation Read */ -1, /* Data TBL Protection Violation Write */ -1, /* FPU Exception */ -1, /* Initial Page Write exception */ -1, /* Unconditional TRAP */ -1, /* User break After Instruction */ -1, /* NMI */ /* SH4_INTC_NMI=23 represents this location in this list.. */ -1, /* EX Irq 0 */ -1, /* 1 */ -1, /* 2 */ -1, /* 3 */ -1, /* 4 */ -1, /* 5 */ -1, /* 6 */ -1, /* 7 */ -1, /* 8 */ -1, /* 9 */ -1, /* A */ -1, /* B */ -1, /* C */ -1, /* D */ -1, /* E */ 0x600, /* SH4_INTC_IRL0 */ 0x620, /* SH4_INTC_IRL1 */ 0x640, /* SH4_INTC_IRL2 */ 0x660, /* SH4_INTC_IRL3 */ /* todo: SH3 should have lines 4+5 too? */ -1, /* HUDI */ -1, /* SH4_INTC_GPOI */ -1, /* SH4_INTC_DMTE0 */ -1, /* SH4_INTC_DMTE1 */ -1, /* SH4_INTC_DMTE2 */ -1, /* SH4_INTC_DMTE3 */ -1, /* SH4_INTC_DMTE4 */ -1, /* SH4_INTC_DMTE5 */ -1, /* SH4_INTC_DMTE6 */ -1, /* SH4_INTC_DMTE7 */ -1, /* SH4_INTC_DMAE */ -1, /* SH4_INTC_TUNI3 */ -1, /* SH4_INTC_TUNI4 */ 0x400, /* SH4_INTC_TUNI0 */ 0x420, /* SH4_INTC_TUNI1 */ 0x440, /* SH4_INTC_TUNI2 */ 0x460, /* SH4_INTC_TICPI2 */ -1, /* SH4_INTC_ATI */ -1, /* SH4_INTC_PRI */ -1, /* SH4_INTC_CUI */ -1, /* SH4_INTC_SCI1ERI */ -1, /* SH4_INTC_SCI1RXI */ -1, /* SH4_INTC_SCI1TXI */ -1, /* SH4_INTC_SCI1TEI */ -1, /* SH4_INTC_SCIFERI */ -1, /* SH4_INTC_SCIFRXI */ -1, /* SH4_INTC_SCIFBRI */ -1, /* SH4_INTC_SCIFTXI */ -1, /* SH4_INTC_ITI */ -1, /* SH4_INTC_RCMI */ -1 /* SH4_INTC_ROVI */ }; void sh34_base_device::sh4_change_register_bank(int to) { int s; if (to) // 0 -> 1 { for (s = 0;s < 8;s++) { m_rbnk[0][s] = m_r[s]; m_r[s] = m_rbnk[1][s]; } } else // 1 -> 0 { for (s = 0;s < 8;s++) { m_rbnk[1][s] = m_r[s]; m_r[s] = m_rbnk[0][s]; } } } void sh34_base_device::sh4_swap_fp_registers() { int s; UINT32 z; for (s = 0;s <= 15;s++) { z = m_fr[s]; m_fr[s] = m_xf[s]; m_xf[s] = z; } } void sh34_base_device::sh4_swap_fp_couples() { int s; UINT32 z; for (s = 0;s <= 15;s = s+2) { z = m_fr[s]; m_fr[s] = m_fr[s + 1]; m_fr[s + 1] = z; z = m_xf[s]; m_xf[s] = m_xf[s + 1]; m_xf[s + 1] = z; } } void sh34_base_device::sh4_syncronize_register_bank(int to) { int s; for (s = 0;s < 8;s++) { m_rbnk[to][s] = m_r[s]; } } void sh34_base_device::sh4_default_exception_priorities() // setup default priorities for exceptions { int a; for (a=0;a <= SH4_INTC_NMI;a++) m_exception_priority[a] = exception_priority_default[a]; for (a=SH4_INTC_IRLn0;a <= SH4_INTC_IRLnE;a++) m_exception_priority[a] = INTPRI(15-(a-SH4_INTC_IRLn0), a); m_exception_priority[SH4_INTC_IRL0] = INTPRI(13, SH4_INTC_IRL0); m_exception_priority[SH4_INTC_IRL1] = INTPRI(10, SH4_INTC_IRL1); m_exception_priority[SH4_INTC_IRL2] = INTPRI(7, SH4_INTC_IRL2); m_exception_priority[SH4_INTC_IRL3] = INTPRI(4, SH4_INTC_IRL3); for (a=SH4_INTC_HUDI;a <= SH4_INTC_ROVI;a++) m_exception_priority[a] = INTPRI(0, a); } void sh34_base_device::sh4_exception_recompute() // checks if there is any interrupt with high enough priority { int a,z; m_test_irq = 0; if ((!m_pending_irq) || ((m_sr & BL) && (m_exception_requesting[SH4_INTC_NMI] == 0))) return; z = (m_sr >> 4) & 15; for (a=0;a <= SH4_INTC_ROVI;a++) { if (m_exception_requesting[a]) { int pri = (((int)m_exception_priority[a] >> 8) & 255); //logerror("pri is %02x z is %02x\n",pri,z); if (pri > z) { //logerror("will test\n"); m_test_irq = 1; // will check for exception at end of instructions break; } } } } void sh34_base_device::sh4_exception_request(int exception) // start requesting an exception { //logerror("sh4_exception_request a\n"); if (!m_exception_requesting[exception]) { //logerror("sh4_exception_request b\n"); m_exception_requesting[exception] = 1; m_pending_irq++; sh4_exception_recompute(); } } void sh34_base_device::sh4_exception_unrequest(int exception) // stop requesting an exception { if (m_exception_requesting[exception]) { m_exception_requesting[exception] = 0; m_pending_irq--; sh4_exception_recompute(); } } void sh34_base_device::sh4_exception_checkunrequest(int exception) { if (exception == SH4_INTC_NMI) sh4_exception_unrequest(exception); if ((exception == SH4_INTC_DMTE0) || (exception == SH4_INTC_DMTE1) || (exception == SH4_INTC_DMTE2) || (exception == SH4_INTC_DMTE3)) sh4_exception_unrequest(exception); } void sh34_base_device::sh4_exception(const char *message, int exception) // handle exception { UINT32 vector; if (m_cpu_type == CPU_TYPE_SH4) { if (exception < SH4_INTC_NMI) return; // Not yet supported if (exception == SH4_INTC_NMI) { if ((m_sr & BL) && (!(m_m[ICR] & 0x200))) return; m_m[ICR] &= ~0x200; m_m[INTEVT] = 0x1c0; vector = 0x600; standard_irq_callback(INPUT_LINE_NMI); LOG(("SH-4 '%s' nmi exception after [%s]\n", tag(), message)); } else { // if ((m_m[ICR] & 0x4000) && (m_nmi_line_state == ASSERT_LINE)) // return; if (m_sr & BL) return; if (((m_exception_priority[exception] >> 8) & 255) <= ((m_sr >> 4) & 15)) return; m_m[INTEVT] = exception_codes[exception]; vector = 0x600; if ((exception >= SH4_INTC_IRL0) && (exception <= SH4_INTC_IRL3)) standard_irq_callback((exception-SH4_INTC_IRL0)+SH4_IRL0); else standard_irq_callback(SH4_IRL3+1); LOG(("SH-4 '%s' interrupt exception #%d after [%s]\n", tag(), exception, message)); } } else /* SH3 exceptions */ { /***** ASSUME THIS TO BE WRONG FOR NOW *****/ if (exception < SH4_INTC_NMI) return; // Not yet supported if (exception == SH4_INTC_NMI) { return; } else { if (m_sr & BL) return; if (((m_exception_priority[exception] >> 8) & 255) <= ((m_sr >> 4) & 15)) return; vector = 0x600; if ((exception >= SH4_INTC_IRL0) && (exception <= SH4_INTC_IRL3)) standard_irq_callback((exception-SH4_INTC_IRL0)+SH4_IRL0); else standard_irq_callback(SH4_IRL3+1); if (sh3_intevt2_exception_codes[exception]==-1) fatalerror("sh3_intevt2_exception_codes unpopulated for exception %02x\n", exception); m_sh3internal_lower[INTEVT2] = sh3_intevt2_exception_codes[exception]; m_sh3internal_upper[SH3_EXPEVT_ADDR] = exception_codes[exception]; LOG(("SH-3 '%s' interrupt exception #%d after [%s]\n", tag(), exception, message)); } /***** END ASSUME THIS TO BE WRONG FOR NOW *****/ } sh4_exception_checkunrequest(exception); m_spc = m_pc; m_ssr = m_sr; m_sgr = m_r[15]; m_sr |= MD; if ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0) sh4_syncronize_register_bank((m_sr & sRB) >> 29); if (!(m_sr & sRB)) sh4_change_register_bank(1); m_sr |= sRB; m_sr |= BL; sh4_exception_recompute(); /* fetch PC */ m_pc = m_vbr + vector; /* wake up if a sleep opcode is triggered */ if(m_sleep_mode == 1) { m_sleep_mode = 2; } } UINT32 sh34_base_device::compute_ticks_refresh_timer(emu_timer *timer, int hertz, int base, int divisor) { // elapsed:total = x : ticks // x=elapsed*tics/total -> x=elapsed*(double)100000000/rtcnt_div[(m_m[RTCSR] >> 3) & 7] // ticks/total=ticks / ((rtcnt_div[(m_m[RTCSR] >> 3) & 7] * ticks) / 100000000)=1/((rtcnt_div[(m_m[RTCSR] >> 3) & 7] / 100000000)=100000000/rtcnt_div[(m_m[RTCSR] >> 3) & 7] return base + (UINT32)((timer->elapsed().as_double() * (double)hertz) / (double)divisor); } void sh34_base_device::sh4_refresh_timer_recompute() { UINT32 ticks; if (m_cpu_type != CPU_TYPE_SH4) fatalerror("sh4_refresh_timer_recompute uses m_m[] with SH3\n"); //if rtcnt < rtcor then rtcor-rtcnt //if rtcnt >= rtcor then 256-rtcnt+rtcor=256+rtcor-rtcnt ticks = m_m[RTCOR]-m_m[RTCNT]; if (ticks <= 0) ticks = 256 + ticks; m_refresh_timer->adjust(attotime::from_hz(m_bus_clock) * rtcnt_div[(m_m[RTCSR] >> 3) & 7] * ticks); m_refresh_timer_base = m_m[RTCNT]; } TIMER_CALLBACK_MEMBER( sh34_base_device::sh4_refresh_timer_callback ) { if (m_cpu_type != CPU_TYPE_SH4) fatalerror("sh4_refresh_timer_callback uses m_m[] with SH3\n"); m_m[RTCNT] = 0; sh4_refresh_timer_recompute(); m_m[RTCSR] |= 128; if ((m_m[MCR] & 4) && !(m_m[MCR] & 2)) { m_m[RFCR] = (m_m[RFCR] + 1) & 1023; if (((m_m[RTCSR] & 1) && (m_m[RFCR] == 512)) || (m_m[RFCR] == 0)) { m_m[RFCR] = 0; m_m[RTCSR] |= 4; } } } void sh34_base_device::increment_rtc_time(int mode) { int carry, year, leap, days; if (m_cpu_type != CPU_TYPE_SH4) fatalerror("increment_rtc_time uses m_m[] with SH3\n"); if (mode == 0) { carry = 0; m_m[RSECCNT] = m_m[RSECCNT] + 1; if ((m_m[RSECCNT] & 0xf) == 0xa) m_m[RSECCNT] = m_m[RSECCNT] + 6; if (m_m[RSECCNT] == 0x60) { m_m[RSECCNT] = 0; carry=1; } else return; } else carry = 1; m_m[RMINCNT] = m_m[RMINCNT] + carry; if ((m_m[RMINCNT] & 0xf) == 0xa) m_m[RMINCNT] = m_m[RMINCNT] + 6; carry=0; if (m_m[RMINCNT] == 0x60) { m_m[RMINCNT] = 0; carry = 1; } m_m[RHRCNT] = m_m[RHRCNT] + carry; if ((m_m[RHRCNT] & 0xf) == 0xa) m_m[RHRCNT] = m_m[RHRCNT] + 6; carry = 0; if (m_m[RHRCNT] == 0x24) { m_m[RHRCNT] = 0; carry = 1; } m_m[RWKCNT] = m_m[RWKCNT] + carry; if (m_m[RWKCNT] == 0x7) { m_m[RWKCNT] = 0; } days = 0; year = (m_m[RYRCNT] & 0xf) + ((m_m[RYRCNT] & 0xf0) >> 4)*10 + ((m_m[RYRCNT] & 0xf00) >> 8)*100 + ((m_m[RYRCNT] & 0xf000) >> 12)*1000; leap = 0; if (!(year%100)) { if (!(year%400)) leap = 1; } else if (!(year%4)) leap = 1; if (m_m[RMONCNT] != 2) leap = 0; if (m_m[RMONCNT]) days = daysmonth[(m_m[RMONCNT] & 0xf) + ((m_m[RMONCNT] & 0xf0) >> 4)*10 - 1]; m_m[RDAYCNT] = m_m[RDAYCNT] + carry; if ((m_m[RDAYCNT] & 0xf) == 0xa) m_m[RDAYCNT] = m_m[RDAYCNT] + 6; carry = 0; if (m_m[RDAYCNT] > (days+leap)) { m_m[RDAYCNT] = 1; carry = 1; } m_m[RMONCNT] = m_m[RMONCNT] + carry; if ((m_m[RMONCNT] & 0xf) == 0xa) m_m[RMONCNT] = m_m[RMONCNT] + 6; carry=0; if (m_m[RMONCNT] == 0x13) { m_m[RMONCNT] = 1; carry = 1; } m_m[RYRCNT] = m_m[RYRCNT] + carry; if ((m_m[RYRCNT] & 0xf) >= 0xa) m_m[RYRCNT] = m_m[RYRCNT] + 6; if ((m_m[RYRCNT] & 0xf0) >= 0xa0) m_m[RYRCNT] = m_m[RYRCNT] + 0x60; if ((m_m[RYRCNT] & 0xf00) >= 0xa00) m_m[RYRCNT] = m_m[RYRCNT] + 0x600; if ((m_m[RYRCNT] & 0xf000) >= 0xa000) m_m[RYRCNT] = 0; } TIMER_CALLBACK_MEMBER( sh34_base_device::sh4_rtc_timer_callback ) { if (m_cpu_type != CPU_TYPE_SH4) { logerror("sh4_rtc_timer_callback uses m_m[] with SH3\n"); return; } m_rtc_timer->adjust(attotime::from_hz(128)); m_m[R64CNT] = (m_m[R64CNT]+1) & 0x7f; if (m_m[R64CNT] == 64) { m_m[RCR1] |= 0x80; increment_rtc_time(0); //sh4_exception_request(SH4_INTC_NMI); // TEST } } void sh34_base_device::sh4_dmac_nmi() // manage dma when nmi gets asserted { int s; m_SH4_DMAOR |= DMAOR_NMIF; for (s = 0;s < 4;s++) { if (m_dma_timer_active[s]) { logerror("SH4: DMA %d cancelled due to NMI but all data transferred", s); m_dma_timer[s]->adjust(attotime::never, s); m_dma_timer_active[s] = 0; } } } void sh34_base_device::sh4_handler_ipra_w(UINT32 data, UINT32 mem_mask) { COMBINE_DATA(&m_SH4_IPRA); /* 15 - 12 TMU0 */ /* 11 - 8 TMU1 */ /* 7 - 4 TMU2 */ /* 3 - 0 RTC */ m_exception_priority[SH4_INTC_ATI] = INTPRI(m_SH4_IPRA & 0x000f, SH4_INTC_ATI); m_exception_priority[SH4_INTC_PRI] = INTPRI(m_SH4_IPRA & 0x000f, SH4_INTC_PRI); m_exception_priority[SH4_INTC_CUI] = INTPRI(m_SH4_IPRA & 0x000f, SH4_INTC_CUI); m_exception_priority[SH4_INTC_TUNI2] = INTPRI((m_SH4_IPRA & 0x00f0) >> 4, SH4_INTC_TUNI2); m_exception_priority[SH4_INTC_TICPI2] = INTPRI((m_SH4_IPRA & 0x00f0) >> 4, SH4_INTC_TICPI2); m_exception_priority[SH4_INTC_TUNI1] = INTPRI((m_SH4_IPRA & 0x0f00) >> 8, SH4_INTC_TUNI1); m_exception_priority[SH4_INTC_TUNI0] = INTPRI((m_SH4_IPRA & 0xf000) >> 12, SH4_INTC_TUNI0); logerror("setting priorities TMU0 %01x TMU1 %01x TMU2 %01x RTC %01x\n", (m_SH4_IPRA & 0xf000)>>12, (m_SH4_IPRA & 0x0f00)>>8, (m_SH4_IPRA & 0x00f0)>>4, (m_SH4_IPRA & 0x000f)>>0); sh4_exception_recompute(); } WRITE32_MEMBER( sh4_base_device::sh4_internal_w ) { int a; UINT32 addr = (offset << 2) + 0xfe000000; offset = ((addr & 0xfc) >> 2) | ((addr & 0x1fe0000) >> 11); if (m_cpu_type != CPU_TYPE_SH4) fatalerror("sh4_internal_w uses m_m[] with SH3\n"); UINT32 old = m_m[offset]; COMBINE_DATA(m_m+offset); // printf("sh4_internal_w: Write %08x (%x), %08x @ %08x\n", 0xfe000000+((offset & 0x3fc0) << 11)+((offset & 0x3f) << 2), offset, data, mem_mask); switch( offset ) { case MMUCR: // MMU Control if (data & MMUCR_AT) { printf("SH4 MMU Enabled\n"); printf("If you're seeing this, but running something other than a Naomi GD-ROM game then chances are it won't work\n"); printf("The MMU emulation is a hack specific to that system\n"); m_sh4_mmu_enabled = 1; // should be a different bit! { int i; for (i=0;i<64;i++) { m_sh4_tlb_address[i] = 0; m_sh4_tlb_data[i] = 0; } } } else { m_sh4_mmu_enabled = 0; } break; // Memory refresh case RTCSR: m_m[RTCSR] &= 255; if ((old >> 3) & 7) m_m[RTCNT] = compute_ticks_refresh_timer(m_refresh_timer, m_bus_clock, m_refresh_timer_base, rtcnt_div[(old >> 3) & 7]) & 0xff; if ((m_m[RTCSR] >> 3) & 7) { // activated sh4_refresh_timer_recompute(); } else { m_refresh_timer->adjust(attotime::never); } break; case RTCNT: m_m[RTCNT] &= 255; if ((m_m[RTCSR] >> 3) & 7) { // active sh4_refresh_timer_recompute(); } break; case RTCOR: m_m[RTCOR] &= 255; if ((m_m[RTCSR] >> 3) & 7) { // active m_m[RTCNT] = compute_ticks_refresh_timer(m_refresh_timer, m_bus_clock, m_refresh_timer_base, rtcnt_div[(m_m[RTCSR] >> 3) & 7]) & 0xff; sh4_refresh_timer_recompute(); } break; case RFCR: m_m[RFCR] &= 1023; break; // RTC case RCR1: if ((m_m[RCR1] & 8) && (~old & 8)) // 0 -> 1 m_m[RCR1] ^= 1; break; case RCR2: if (m_m[RCR2] & 2) { m_m[R64CNT] = 0; m_m[RCR2] ^= 2; } if (m_m[RCR2] & 4) { m_m[R64CNT] = 0; if (m_m[RSECCNT] >= 30) increment_rtc_time(1); m_m[RSECCNT] = 0; } if ((m_m[RCR2] & 8) && (~old & 8)) { // 0 -> 1 m_rtc_timer->adjust(attotime::from_hz(128)); } else if (~(m_m[RCR2]) & 8) { // 0 m_rtc_timer->adjust(attotime::never); } break; /********************************************************************************************************************* TMU (Timer Unit) *********************************************************************************************************************/ case SH4_TSTR_ADDR: sh4_handle_tstr_addr_w(data,mem_mask); break; case SH4_TCR0_ADDR: sh4_handle_tcr0_addr_w(data,mem_mask); break; case SH4_TCR1_ADDR: sh4_handle_tcr1_addr_w(data,mem_mask); break; case SH4_TCR2_ADDR: sh4_handle_tcr2_addr_w(data,mem_mask); break; case SH4_TCOR0_ADDR: sh4_handle_tcor0_addr_w(data,mem_mask); break; case SH4_TCNT0_ADDR: sh4_handle_tcnt0_addr_w(data,mem_mask); break; case SH4_TCOR1_ADDR: sh4_handle_tcor1_addr_w(data,mem_mask); break; case SH4_TCNT1_ADDR: sh4_handle_tcnt1_addr_w(data,mem_mask); break; case SH4_TCOR2_ADDR: sh4_handle_tcor2_addr_w(data,mem_mask); break; case SH4_TCNT2_ADDR: sh4_handle_tcnt2_addr_w(data,mem_mask); break; case SH4_TOCR_ADDR: sh4_handle_tocr_addr_w(data,mem_mask); break; // not supported case SH4_TCPR2_ADDR: sh4_handle_tcpr2_addr_w(data,mem_mask); break; // not supported /********************************************************************************************************************* INTC (Interrupt Controller) *********************************************************************************************************************/ case ICR: m_m[ICR] = (m_m[ICR] & 0x7fff) | (old & 0x8000); break; case IPRA: sh4_handler_ipra_w(data, mem_mask); break; case IPRB: m_exception_priority[SH4_INTC_SCI1ERI] = INTPRI((m_m[IPRB] & 0x00f0) >> 4, SH4_INTC_SCI1ERI); m_exception_priority[SH4_INTC_SCI1RXI] = INTPRI((m_m[IPRB] & 0x00f0) >> 4, SH4_INTC_SCI1RXI); m_exception_priority[SH4_INTC_SCI1TXI] = INTPRI((m_m[IPRB] & 0x00f0) >> 4, SH4_INTC_SCI1TXI); m_exception_priority[SH4_INTC_SCI1TEI] = INTPRI((m_m[IPRB] & 0x00f0) >> 4, SH4_INTC_SCI1TEI); m_exception_priority[SH4_INTC_RCMI] = INTPRI((m_m[IPRB] & 0x0f00) >> 8, SH4_INTC_RCMI); m_exception_priority[SH4_INTC_ROVI] = INTPRI((m_m[IPRB] & 0x0f00) >> 8, SH4_INTC_ROVI); m_exception_priority[SH4_INTC_ITI] = INTPRI((m_m[IPRB] & 0xf000) >> 12, SH4_INTC_ITI); sh4_exception_recompute(); break; case IPRC: m_exception_priority[SH4_INTC_HUDI] = INTPRI(m_m[IPRC] & 0x000f, SH4_INTC_HUDI); m_exception_priority[SH4_INTC_SCIFERI] = INTPRI((m_m[IPRC] & 0x00f0) >> 4, SH4_INTC_SCIFERI); m_exception_priority[SH4_INTC_SCIFRXI] = INTPRI((m_m[IPRC] & 0x00f0) >> 4, SH4_INTC_SCIFRXI); m_exception_priority[SH4_INTC_SCIFBRI] = INTPRI((m_m[IPRC] & 0x00f0) >> 4, SH4_INTC_SCIFBRI); m_exception_priority[SH4_INTC_SCIFTXI] = INTPRI((m_m[IPRC] & 0x00f0) >> 4, SH4_INTC_SCIFTXI); m_exception_priority[SH4_INTC_DMTE0] = INTPRI((m_m[IPRC] & 0x0f00) >> 8, SH4_INTC_DMTE0); m_exception_priority[SH4_INTC_DMTE1] = INTPRI((m_m[IPRC] & 0x0f00) >> 8, SH4_INTC_DMTE1); m_exception_priority[SH4_INTC_DMTE2] = INTPRI((m_m[IPRC] & 0x0f00) >> 8, SH4_INTC_DMTE2); m_exception_priority[SH4_INTC_DMTE3] = INTPRI((m_m[IPRC] & 0x0f00) >> 8, SH4_INTC_DMTE3); m_exception_priority[SH4_INTC_DMAE] = INTPRI((m_m[IPRC] & 0x0f00) >> 8, SH4_INTC_DMAE); m_exception_priority[SH4_INTC_GPOI] = INTPRI((m_m[IPRC] & 0xf000) >> 12, SH4_INTC_GPOI); sh4_exception_recompute(); break; /********************************************************************************************************************* DMAC (DMA Controller) *********************************************************************************************************************/ case SH4_SAR0_ADDR: sh4_handle_sar0_addr_w(data,mem_mask); break; case SH4_SAR1_ADDR: sh4_handle_sar1_addr_w(data,mem_mask); break; case SH4_SAR2_ADDR: sh4_handle_sar2_addr_w(data,mem_mask); break; case SH4_SAR3_ADDR: sh4_handle_sar3_addr_w(data,mem_mask); break; case SH4_DAR0_ADDR: sh4_handle_dar0_addr_w(data,mem_mask); break; case SH4_DAR1_ADDR: sh4_handle_dar1_addr_w(data,mem_mask); break; case SH4_DAR2_ADDR: sh4_handle_dar2_addr_w(data,mem_mask); break; case SH4_DAR3_ADDR: sh4_handle_dar3_addr_w(data,mem_mask); break; case SH4_DMATCR0_ADDR: sh4_handle_dmatcr0_addr_w(data,mem_mask); break; case SH4_DMATCR1_ADDR: sh4_handle_dmatcr1_addr_w(data,mem_mask); break; case SH4_DMATCR2_ADDR: sh4_handle_dmatcr2_addr_w(data,mem_mask); break; case SH4_DMATCR3_ADDR: sh4_handle_dmatcr3_addr_w(data,mem_mask); break; case SH4_CHCR0_ADDR: sh4_handle_chcr0_addr_w(data,mem_mask); break; case SH4_CHCR1_ADDR: sh4_handle_chcr1_addr_w(data,mem_mask); break; case SH4_CHCR2_ADDR: sh4_handle_chcr2_addr_w(data,mem_mask); break; case SH4_CHCR3_ADDR: sh4_handle_chcr3_addr_w(data,mem_mask); break; case SH4_DMAOR_ADDR: sh4_handle_dmaor_addr_w(data,mem_mask); break; /********************************************************************************************************************* Store Queues *********************************************************************************************************************/ case QACR0: case QACR1: break; /********************************************************************************************************************* I/O *********************************************************************************************************************/ case PCTRA: m_ioport16_pullup = 0; m_ioport16_direction = 0; for (a=0;a < 16;a++) { m_ioport16_direction |= (m_m[PCTRA] & (1 << (a*2))) >> a; m_ioport16_pullup |= (m_m[PCTRA] & (1 << (a*2+1))) >> (a+1); } m_ioport16_direction &= 0xffff; m_ioport16_pullup = (m_ioport16_pullup | m_ioport16_direction) ^ 0xffff; if (m_m[BCR2] & 1) m_io->write_dword(SH4_IOPORT_16, (UINT64)(m_m[PDTRA] & m_ioport16_direction) | ((UINT64)m_m[PCTRA] << 16)); break; case PDTRA: if (m_m[BCR2] & 1) m_io->write_dword(SH4_IOPORT_16, (UINT64)(m_m[PDTRA] & m_ioport16_direction) | ((UINT64)m_m[PCTRA] << 16)); break; case PCTRB: m_ioport4_pullup = 0; m_ioport4_direction = 0; for (a=0;a < 4;a++) { m_ioport4_direction |= (m_m[PCTRB] & (1 << (a*2))) >> a; m_ioport4_pullup |= (m_m[PCTRB] & (1 << (a*2+1))) >> (a+1); } m_ioport4_direction &= 0xf; m_ioport4_pullup = (m_ioport4_pullup | m_ioport4_direction) ^ 0xf; if (m_m[BCR2] & 1) m_io->write_dword(SH4_IOPORT_4, (m_m[PDTRB] & m_ioport4_direction) | (m_m[PCTRB] << 16)); break; case PDTRB: if (m_m[BCR2] & 1) m_io->write_dword(SH4_IOPORT_4, (m_m[PDTRB] & m_ioport4_direction) | (m_m[PCTRB] << 16)); break; case SCBRR2: break; case SCSPTR2: //trips often in aristocrat mk-6 break; default: logerror("sh4_internal_w: Unmapped write %08x, %08x @ %08x\n", 0xfe000000+((offset & 0x3fc0) << 11)+((offset & 0x3f) << 2), data, mem_mask); break; } } READ32_MEMBER( sh4_base_device::sh4_internal_r ) { if (m_cpu_type != CPU_TYPE_SH4) fatalerror("sh4_internal_r uses m_m[] with SH3\n"); UINT32 addr = (offset << 2) + 0xfe000000; offset = ((addr & 0xfc) >> 2) | ((addr & 0x1fe0000) >> 11); // printf("sh4_internal_r: Read %08x (%x) @ %08x\n", 0xfe000000+((offset & 0x3fc0) << 11)+((offset & 0x3f) << 2), offset, mem_mask); switch( offset ) { case VERSION: return PVR_SH7091; // 0x040205c1, this is what a real SH7091 in a Dreamcast returns - the later Naomi BIOSes check and care! case PRR: return 0; case IPRD: return 0x00000000; // SH7750 ignores writes here and always returns zero case RTCNT: if ((m_m[RTCSR] >> 3) & 7) { // activated //((double)rtcnt_div[(m_m[RTCSR] >> 3) & 7] / (double)100000000) //return (refresh_timer_base + (m_refresh_timer->elapsed() * (double)100000000) / (double)rtcnt_div[(m_m[RTCSR] >> 3) & 7]) & 0xff; return compute_ticks_refresh_timer(m_refresh_timer, m_bus_clock, m_refresh_timer_base, rtcnt_div[(m_m[RTCSR] >> 3) & 7]) & 0xff; } else return m_m[RTCNT]; /********************************************************************************************************************* INTC (Interrupt Controller) *********************************************************************************************************************/ case IPRA: return m_SH4_IPRA; /********************************************************************************************************************* TMU (Timer Unit) *********************************************************************************************************************/ case SH4_TSTR_ADDR: return sh4_handle_tstr_addr_r(mem_mask); case SH4_TCR0_ADDR: return sh4_handle_tcr0_addr_r(mem_mask); case SH4_TCR1_ADDR: return sh4_handle_tcr1_addr_r(mem_mask); case SH4_TCR2_ADDR: return sh4_handle_tcr2_addr_r(mem_mask); case SH4_TCNT0_ADDR: return sh4_handle_tcnt0_addr_r(mem_mask); case SH4_TCNT1_ADDR: return sh4_handle_tcnt1_addr_r(mem_mask); case SH4_TCNT2_ADDR: return sh4_handle_tcnt2_addr_r(mem_mask); case SH4_TCOR0_ADDR: return sh4_handle_tcor0_addr_r(mem_mask); case SH4_TCOR1_ADDR: return sh4_handle_tcor1_addr_r(mem_mask); case SH4_TCOR2_ADDR: return sh4_handle_tcor2_addr_r(mem_mask); case SH4_TOCR_ADDR: return sh4_handle_tocr_addr_r(mem_mask); // not supported case SH4_TCPR2_ADDR: return sh4_handle_tcpr2_addr_r(mem_mask); // not supported /********************************************************************************************************************* DMAC (DMA Controller) *********************************************************************************************************************/ case SH4_SAR0_ADDR: return sh4_handle_sar0_addr_r(mem_mask); case SH4_SAR1_ADDR: return sh4_handle_sar1_addr_r(mem_mask); case SH4_SAR2_ADDR: return sh4_handle_sar2_addr_r(mem_mask); case SH4_SAR3_ADDR: return sh4_handle_sar3_addr_r(mem_mask); case SH4_DAR0_ADDR: return sh4_handle_dar0_addr_r(mem_mask); case SH4_DAR1_ADDR: return sh4_handle_dar1_addr_r(mem_mask); case SH4_DAR2_ADDR: return sh4_handle_dar2_addr_r(mem_mask); case SH4_DAR3_ADDR: return sh4_handle_dar3_addr_r(mem_mask); case SH4_DMATCR0_ADDR: return sh4_handle_dmatcr0_addr_r(mem_mask); case SH4_DMATCR1_ADDR: return sh4_handle_dmatcr1_addr_r(mem_mask); case SH4_DMATCR2_ADDR: return sh4_handle_dmatcr2_addr_r(mem_mask); case SH4_DMATCR3_ADDR: return sh4_handle_dmatcr3_addr_r(mem_mask); case SH4_CHCR0_ADDR: return sh4_handle_chcr0_addr_r(mem_mask); case SH4_CHCR1_ADDR: return sh4_handle_chcr1_addr_r(mem_mask); case SH4_CHCR2_ADDR: return sh4_handle_chcr2_addr_r(mem_mask); case SH4_CHCR3_ADDR: return sh4_handle_chcr3_addr_r(mem_mask); case SH4_DMAOR_ADDR: return sh4_handle_dmaor_addr_r(mem_mask); /********************************************************************************************************************* I/O Ports *********************************************************************************************************************/ case PDTRA: if (m_m[BCR2] & 1) return (m_io->read_dword(SH4_IOPORT_16) & ~m_ioport16_direction) | (m_m[PDTRA] & m_ioport16_direction); break; case PDTRB: if (m_m[BCR2] & 1) return (m_io->read_dword(SH4_IOPORT_4) & ~m_ioport4_direction) | (m_m[PDTRB] & m_ioport4_direction); break; // SCIF (UART with FIFO) case SCFSR2: return 0x60; //read-only status register } return m_m[offset]; } void sh34_base_device::sh4_set_frt_input(int state) { if (m_cpu_type != CPU_TYPE_SH4) fatalerror("sh4_set_frt_input uses m_m[] with SH3\n"); if(state == PULSE_LINE) { sh4_set_frt_input(ASSERT_LINE); sh4_set_frt_input(CLEAR_LINE); return; } if(m_frt_input == state) { return; } m_frt_input = state; if (m_cpu_type == CPU_TYPE_SH4) { if(m_m[5] & 0x8000) { if(state == CLEAR_LINE) { return; } } else { if(state == ASSERT_LINE) { return; } } } else { fatalerror("sh4_set_frt_input uses m_m[] with SH3\n"); } #if 0 sh4_timer_resync(); m_icr = m_frc; m_m[4] |= ICF; logerror("SH4 '%s': ICF activated (%x)\n", tag(), m_pc & AM); sh4_recalc_irq(); #endif } void sh34_base_device::sh4_set_irln_input(int value) { if (m_irln == value) return; m_irln = value; set_input_line(SH4_IRLn, ASSERT_LINE); set_input_line(SH4_IRLn, CLEAR_LINE); } void sh34_base_device::execute_set_input(int irqline, int state) // set state of external interrupt line { if (m_cpu_type == CPU_TYPE_SH3) { /***** ASSUME THIS TO BE WRONG FOR NOW *****/ if (irqline == INPUT_LINE_NMI) { fatalerror("SH3 NMI Unimplemented\n"); } else { //if (irqline > SH4_IRL3) // return; if (m_irq_line_state[irqline] == state) return; m_irq_line_state[irqline] = state; if( state == CLEAR_LINE ) { LOG(("SH-4 '%s' cleared external irq IRL%d\n", tag(), irqline)); sh4_exception_unrequest(SH4_INTC_IRL0+irqline-SH4_IRL0); } else { LOG(("SH-4 '%s' assert external irq IRL%d\n", tag(), irqline)); sh4_exception_request(SH4_INTC_IRL0+irqline-SH4_IRL0); } } /***** END ASSUME THIS TO BE WRONG FOR NOW *****/ } else { int s; if (irqline == INPUT_LINE_NMI) { if (m_nmi_line_state == state) return; if (m_m[ICR] & 0x100) { if ((state == CLEAR_LINE) && (m_nmi_line_state == ASSERT_LINE)) // rising { LOG(("SH-4 '%s' assert nmi\n", tag())); sh4_exception_request(SH4_INTC_NMI); sh4_dmac_nmi(); } } else { if ((state == ASSERT_LINE) && (m_nmi_line_state == CLEAR_LINE)) // falling { LOG(("SH-4 '%s' assert nmi\n", tag())); sh4_exception_request(SH4_INTC_NMI); sh4_dmac_nmi(); } } if (state == CLEAR_LINE) m_m[ICR] ^= 0x8000; else m_m[ICR] |= 0x8000; m_nmi_line_state = state; } else { if (m_m[ICR] & 0x80) // four independent external interrupt sources { if (irqline > SH4_IRL3) return; if (m_irq_line_state[irqline] == state) return; m_irq_line_state[irqline] = state; if( state == CLEAR_LINE ) { LOG(("SH-4 '%s' cleared external irq IRL%d\n", tag(), irqline)); sh4_exception_unrequest(SH4_INTC_IRL0+irqline-SH4_IRL0); } else { LOG(("SH-4 '%s' assert external irq IRL%d\n", tag(), irqline)); sh4_exception_request(SH4_INTC_IRL0+irqline-SH4_IRL0); } } else // level-encoded interrupt { if (irqline != SH4_IRLn) return; if ((m_irln > 15) || (m_irln < 0)) return; for (s = 0; s < 15; s++) sh4_exception_unrequest(SH4_INTC_IRLn0+s); if (m_irln < 15) sh4_exception_request(SH4_INTC_IRLn0+m_irln); LOG(("SH-4 '%s' IRLn0-IRLn3 level #%d\n", tag(), m_irln)); } } if (m_test_irq && (!m_delay)) sh4_check_pending_irq("sh4_set_irq_line"); } } void sh34_base_device::sh4_parse_configuration() { if(c_clock > 0) { switch((c_md2 << 2) | (c_md1 << 1) | (c_md0)) { case 0: m_cpu_clock = c_clock; m_bus_clock = c_clock / 4; m_pm_clock = c_clock / 4; break; case 1: m_cpu_clock = c_clock; m_bus_clock = c_clock / 6; m_pm_clock = c_clock / 6; break; case 2: m_cpu_clock = c_clock; m_bus_clock = c_clock / 3; m_pm_clock = c_clock / 6; break; case 3: m_cpu_clock = c_clock; m_bus_clock = c_clock / 3; m_pm_clock = c_clock / 6; break; case 4: m_cpu_clock = c_clock; m_bus_clock = c_clock / 2; m_pm_clock = c_clock / 4; break; case 5: m_cpu_clock = c_clock; m_bus_clock = c_clock / 2; m_pm_clock = c_clock / 4; break; } m_is_slave = (~(c_md7)) & 1; } else { m_cpu_clock = 200000000; m_bus_clock = 100000000; m_pm_clock = 50000000; m_is_slave = 0; } } UINT32 sh34_base_device::sh4_getsqremap(UINT32 address) { if (!m_sh4_mmu_enabled) return address; else { int i; UINT32 topaddr = address&0xfff00000; for (i=0;i<64;i++) { UINT32 topcmp = m_sh4_tlb_address[i]&0xfff00000; if (topcmp==topaddr) return (address&0x000fffff) | ((m_sh4_tlb_data[i])&0xfff00000); } } return address; } READ64_MEMBER( sh4_base_device::sh4_tlb_r ) { int offs = offset*8; if (offs >= 0x01000000) { UINT8 i = (offs>>8)&63; return m_sh4_tlb_data[i]; } else { UINT8 i = (offs>>8)&63; return m_sh4_tlb_address[i]; } } WRITE64_MEMBER( sh4_base_device::sh4_tlb_w ) { int offs = offset*8; if (offs >= 0x01000000) { UINT8 i = (offs>>8)&63; m_sh4_tlb_data[i] = data&0xffffffff; } else { UINT8 i = (offs>>8)&63; m_sh4_tlb_address[i] = data&0xffffffff; } }