nkeynes@378: /** nkeynes@586: * $Id$ nkeynes@378: * nkeynes@378: * SH4 parent module for all CPU modes and SH4 peripheral nkeynes@378: * modules. nkeynes@378: * nkeynes@378: * Copyright (c) 2005 Nathan Keynes. nkeynes@378: * nkeynes@378: * This program is free software; you can redistribute it and/or modify nkeynes@378: * it under the terms of the GNU General Public License as published by nkeynes@378: * the Free Software Foundation; either version 2 of the License, or nkeynes@378: * (at your option) any later version. nkeynes@378: * nkeynes@378: * This program is distributed in the hope that it will be useful, nkeynes@378: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@378: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@378: * GNU General Public License for more details. nkeynes@378: */ nkeynes@378: nkeynes@378: #define MODULE sh4_module nkeynes@378: #include nkeynes@617: #include nkeynes@378: #include "dream.h" nkeynes@422: #include "dreamcast.h" nkeynes@378: #include "sh4/sh4core.h" nkeynes@378: #include "sh4/sh4mmio.h" nkeynes@378: #include "sh4/intc.h" nkeynes@422: #include "sh4/xltcache.h" nkeynes@422: #include "sh4/sh4stat.h" nkeynes@617: #include "sh4/sh4trans.h" nkeynes@378: #include "mem.h" nkeynes@378: #include "clock.h" nkeynes@378: #include "syscall.h" nkeynes@378: nkeynes@378: void sh4_init( void ); nkeynes@526: void sh4_xlat_init( void ); nkeynes@378: void sh4_reset( void ); nkeynes@378: void sh4_start( void ); nkeynes@378: void sh4_stop( void ); nkeynes@378: void sh4_save_state( FILE *f ); nkeynes@378: int sh4_load_state( FILE *f ); nkeynes@378: nkeynes@378: uint32_t sh4_run_slice( uint32_t ); nkeynes@378: uint32_t sh4_xlat_run_slice( uint32_t ); nkeynes@378: nkeynes@378: struct dreamcast_module sh4_module = { "SH4", sh4_init, sh4_reset, nkeynes@591: sh4_start, sh4_run_slice, sh4_stop, nkeynes@378: sh4_save_state, sh4_load_state }; nkeynes@378: nkeynes@378: struct sh4_registers sh4r; nkeynes@378: struct breakpoint_struct sh4_breakpoints[MAX_BREAKPOINTS]; nkeynes@378: int sh4_breakpoint_count = 0; nkeynes@586: sh4ptr_t sh4_main_ram; nkeynes@591: gboolean sh4_starting = FALSE; nkeynes@526: static gboolean sh4_use_translator = FALSE; nkeynes@586: struct sh4_icache_struct sh4_icache = { NULL, -1, -1, 0 }; nkeynes@378: nkeynes@378: void sh4_set_use_xlat( gboolean use ) nkeynes@378: { nkeynes@526: // No-op if the translator was not built nkeynes@526: #ifdef SH4_TRANSLATOR nkeynes@378: if( use ) { nkeynes@378: xlat_cache_init(); nkeynes@378: sh4_x86_init(); nkeynes@378: sh4_module.run_time_slice = sh4_xlat_run_slice; nkeynes@378: } else { nkeynes@378: sh4_module.run_time_slice = sh4_run_slice; nkeynes@378: } nkeynes@526: sh4_use_translator = use; nkeynes@526: #endif nkeynes@378: } nkeynes@378: nkeynes@586: gboolean sh4_is_using_xlat() nkeynes@586: { nkeynes@586: return sh4_use_translator; nkeynes@586: } nkeynes@586: nkeynes@378: void sh4_init(void) nkeynes@378: { nkeynes@378: register_io_regions( mmio_list_sh4mmio ); nkeynes@418: sh4_main_ram = mem_get_region_by_name(MEM_REGION_MAIN); nkeynes@378: MMU_init(); nkeynes@378: sh4_reset(); nkeynes@378: } nkeynes@378: nkeynes@591: void sh4_start(void) nkeynes@591: { nkeynes@591: sh4_starting = TRUE; nkeynes@591: } nkeynes@591: nkeynes@378: void sh4_reset(void) nkeynes@378: { nkeynes@526: if( sh4_use_translator ) { nkeynes@472: xlat_flush_cache(); nkeynes@472: } nkeynes@472: nkeynes@378: /* zero everything out, for the sake of having a consistent state. */ nkeynes@378: memset( &sh4r, 0, sizeof(sh4r) ); nkeynes@378: nkeynes@378: /* Resume running if we were halted */ nkeynes@378: sh4r.sh4_state = SH4_STATE_RUNNING; nkeynes@378: nkeynes@378: sh4r.pc = 0xA0000000; nkeynes@378: sh4r.new_pc= 0xA0000002; nkeynes@378: sh4r.vbr = 0x00000000; nkeynes@378: sh4r.fpscr = 0x00040001; nkeynes@378: sh4r.sr = 0x700000F0; nkeynes@378: sh4r.fr_bank = &sh4r.fr[0][0]; nkeynes@378: nkeynes@378: /* Mem reset will do this, but if we want to reset _just_ the SH4... */ nkeynes@378: MMIO_WRITE( MMU, EXPEVT, EXC_POWER_RESET ); nkeynes@378: nkeynes@378: /* Peripheral modules */ nkeynes@378: CPG_reset(); nkeynes@378: INTC_reset(); nkeynes@378: MMU_reset(); nkeynes@378: TMU_reset(); nkeynes@378: SCIF_reset(); nkeynes@401: sh4_stats_reset(); nkeynes@378: } nkeynes@378: nkeynes@378: void sh4_stop(void) nkeynes@378: { nkeynes@526: if( sh4_use_translator ) { nkeynes@502: /* If we were running with the translator, update new_pc and in_delay_slot */ nkeynes@502: sh4r.new_pc = sh4r.pc+2; nkeynes@502: sh4r.in_delay_slot = FALSE; nkeynes@502: } nkeynes@378: nkeynes@378: } nkeynes@378: nkeynes@378: void sh4_save_state( FILE *f ) nkeynes@378: { nkeynes@526: if( sh4_use_translator ) { nkeynes@401: /* If we were running with the translator, update new_pc and in_delay_slot */ nkeynes@401: sh4r.new_pc = sh4r.pc+2; nkeynes@401: sh4r.in_delay_slot = FALSE; nkeynes@401: } nkeynes@401: nkeynes@378: fwrite( &sh4r, sizeof(sh4r), 1, f ); nkeynes@378: MMU_save_state( f ); nkeynes@378: INTC_save_state( f ); nkeynes@378: TMU_save_state( f ); nkeynes@378: SCIF_save_state( f ); nkeynes@378: } nkeynes@378: nkeynes@378: int sh4_load_state( FILE * f ) nkeynes@378: { nkeynes@526: if( sh4_use_translator ) { nkeynes@472: xlat_flush_cache(); nkeynes@472: } nkeynes@378: fread( &sh4r, sizeof(sh4r), 1, f ); nkeynes@412: sh4r.fr_bank = &sh4r.fr[(sh4r.fpscr&FPSCR_FR)>>21][0]; // Fixup internal FR pointer nkeynes@378: MMU_load_state( f ); nkeynes@378: INTC_load_state( f ); nkeynes@378: TMU_load_state( f ); nkeynes@378: return SCIF_load_state( f ); nkeynes@378: } nkeynes@378: nkeynes@378: nkeynes@586: void sh4_set_breakpoint( uint32_t pc, breakpoint_type_t type ) nkeynes@378: { nkeynes@378: sh4_breakpoints[sh4_breakpoint_count].address = pc; nkeynes@378: sh4_breakpoints[sh4_breakpoint_count].type = type; nkeynes@586: if( sh4_use_translator ) { nkeynes@586: xlat_invalidate_word( pc ); nkeynes@586: } nkeynes@378: sh4_breakpoint_count++; nkeynes@378: } nkeynes@378: nkeynes@586: gboolean sh4_clear_breakpoint( uint32_t pc, breakpoint_type_t type ) nkeynes@378: { nkeynes@378: int i; nkeynes@378: nkeynes@378: for( i=0; i %08X", code, sh4r.spc, sh4r.pc ); nkeynes@401: } nkeynes@401: nkeynes@401: void signsat48( void ) nkeynes@401: { nkeynes@401: if( ((int64_t)sh4r.mac) < (int64_t)0xFFFF800000000000LL ) nkeynes@401: sh4r.mac = 0xFFFF800000000000LL; nkeynes@401: else if( ((int64_t)sh4r.mac) > (int64_t)0x00007FFFFFFFFFFFLL ) nkeynes@401: sh4r.mac = 0x00007FFFFFFFFFFFLL; nkeynes@401: } nkeynes@401: nkeynes@401: void sh4_fsca( uint32_t anglei, float *fr ) nkeynes@401: { nkeynes@401: float angle = (((float)(anglei&0xFFFF))/65536.0) * 2 * M_PI; nkeynes@401: *fr++ = cosf(angle); nkeynes@401: *fr = sinf(angle); nkeynes@401: } nkeynes@401: nkeynes@617: /** nkeynes@617: * Enter sleep mode (eg by executing a SLEEP instruction). nkeynes@617: * Sets sh4_state appropriately and ensures any stopping peripheral modules nkeynes@617: * are up to date. nkeynes@617: */ nkeynes@401: void sh4_sleep(void) nkeynes@401: { nkeynes@401: if( MMIO_READ( CPG, STBCR ) & 0x80 ) { nkeynes@401: sh4r.sh4_state = SH4_STATE_STANDBY; nkeynes@617: /* Bring all running peripheral modules up to date, and then halt them. */ nkeynes@617: TMU_run_slice( sh4r.slice_cycle ); nkeynes@617: SCIF_run_slice( sh4r.slice_cycle ); nkeynes@401: } else { nkeynes@617: if( MMIO_READ( CPG, STBCR2 ) & 0x80 ) { nkeynes@617: sh4r.sh4_state = SH4_STATE_DEEP_SLEEP; nkeynes@617: /* Halt DMAC but other peripherals still running */ nkeynes@617: nkeynes@617: } else { nkeynes@617: sh4r.sh4_state = SH4_STATE_SLEEP; nkeynes@617: } nkeynes@617: } nkeynes@617: if( sh4_xlat_is_running() ) { nkeynes@617: sh4_translate_exit( XLAT_EXIT_SLEEP ); nkeynes@401: } nkeynes@401: } nkeynes@401: nkeynes@401: /** nkeynes@617: * Wakeup following sleep mode (IRQ or reset). Sets state back to running, nkeynes@617: * and restarts any peripheral devices that were stopped. nkeynes@617: */ nkeynes@617: void sh4_wakeup(void) nkeynes@617: { nkeynes@617: switch( sh4r.sh4_state ) { nkeynes@617: case SH4_STATE_STANDBY: nkeynes@617: break; nkeynes@617: case SH4_STATE_DEEP_SLEEP: nkeynes@617: break; nkeynes@617: case SH4_STATE_SLEEP: nkeynes@617: break; nkeynes@617: } nkeynes@617: sh4r.sh4_state = SH4_STATE_RUNNING; nkeynes@617: } nkeynes@617: nkeynes@617: /** nkeynes@617: * Run a time slice (or portion of a timeslice) while the SH4 is sleeping. nkeynes@617: * Returns when either the SH4 wakes up (interrupt received) or the end of nkeynes@617: * the slice is reached. Updates sh4.slice_cycle with the exit time and nkeynes@617: * returns the same value. nkeynes@617: */ nkeynes@617: uint32_t sh4_sleep_run_slice( uint32_t nanosecs ) nkeynes@617: { nkeynes@617: int sleep_state = sh4r.sh4_state; nkeynes@617: assert( sleep_state != SH4_STATE_RUNNING ); nkeynes@617: while( sh4r.event_pending < nanosecs ) { nkeynes@617: sh4r.slice_cycle = sh4r.event_pending; nkeynes@617: if( sh4r.event_types & PENDING_EVENT ) { nkeynes@617: event_execute(); nkeynes@617: } nkeynes@617: if( sh4r.event_types & PENDING_IRQ ) { nkeynes@617: sh4_wakeup(); nkeynes@617: nanosecs = sh4r.event_pending; nkeynes@617: break; nkeynes@617: } nkeynes@617: } nkeynes@617: sh4r.slice_cycle = nanosecs; nkeynes@617: if( sleep_state != SH4_STATE_STANDBY ) { nkeynes@617: TMU_run_slice( nanosecs ); nkeynes@617: SCIF_run_slice( nanosecs ); nkeynes@617: } nkeynes@617: return sh4r.slice_cycle; nkeynes@617: } nkeynes@617: nkeynes@617: nkeynes@617: /** nkeynes@401: * Compute the matrix tranform of fv given the matrix xf. nkeynes@401: * Both fv and xf are word-swapped as per the sh4r.fr banks nkeynes@401: */ nkeynes@401: void sh4_ftrv( float *target, float *xf ) nkeynes@401: { nkeynes@401: float fv[4] = { target[1], target[0], target[3], target[2] }; nkeynes@401: target[1] = xf[1] * fv[0] + xf[5]*fv[1] + nkeynes@401: xf[9]*fv[2] + xf[13]*fv[3]; nkeynes@401: target[0] = xf[0] * fv[0] + xf[4]*fv[1] + nkeynes@401: xf[8]*fv[2] + xf[12]*fv[3]; nkeynes@401: target[3] = xf[3] * fv[0] + xf[7]*fv[1] + nkeynes@401: xf[11]*fv[2] + xf[15]*fv[3]; nkeynes@401: target[2] = xf[2] * fv[0] + xf[6]*fv[1] + nkeynes@401: xf[10]*fv[2] + xf[14]*fv[3]; nkeynes@401: } nkeynes@401: nkeynes@597: gboolean sh4_has_page( sh4vma_t vma ) nkeynes@597: { nkeynes@597: sh4addr_t addr = mmu_vma_to_phys_disasm(vma); nkeynes@597: return addr != MMU_VMA_ERROR && mem_has_page(addr); nkeynes@597: }