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@740: #include nkeynes@617: #include nkeynes@671: #include "lxdream.h" nkeynes@422: #include "dreamcast.h" nkeynes@998: #include "cpu.h" nkeynes@669: #include "mem.h" nkeynes@669: #include "clock.h" nkeynes@669: #include "eventq.h" nkeynes@669: #include "syscall.h" nkeynes@669: #include "sh4/intc.h" nkeynes@968: #include "sh4/mmu.h" nkeynes@378: #include "sh4/sh4core.h" nkeynes@998: #include "sh4/sh4dasm.h" nkeynes@378: #include "sh4/sh4mmio.h" nkeynes@422: #include "sh4/sh4stat.h" nkeynes@617: #include "sh4/sh4trans.h" nkeynes@991: #include "xlat/xltcache.h" nkeynes@378: nkeynes@984: #ifndef M_PI nkeynes@984: #define M_PI 3.14159265358979323846264338327950288 nkeynes@984: #endif nkeynes@378: nkeynes@378: void sh4_init( void ); nkeynes@951: void sh4_poweron_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@998: size_t sh4_debug_read_phys( unsigned char *buf, uint32_t addr, size_t length ); nkeynes@998: size_t sh4_debug_write_phys( uint32_t addr, unsigned char *buf, size_t length ); nkeynes@998: size_t sh4_debug_read_vma( unsigned char *buf, uint32_t addr, size_t length ); nkeynes@998: size_t sh4_debug_write_vma( uint32_t addr, unsigned char *buf, size_t length ); nkeynes@378: nkeynes@378: uint32_t sh4_run_slice( uint32_t ); nkeynes@378: nkeynes@998: /* Note: this must match GDB's ordering */ nkeynes@998: const struct reg_desc_struct sh4_reg_map[] = nkeynes@1091: { {"R0", REG_TYPE_INT, &sh4r.r[0]}, {"R1", REG_TYPE_INT, &sh4r.r[1]}, nkeynes@1091: {"R2", REG_TYPE_INT, &sh4r.r[2]}, {"R3", REG_TYPE_INT, &sh4r.r[3]}, nkeynes@1091: {"R4", REG_TYPE_INT, &sh4r.r[4]}, {"R5", REG_TYPE_INT, &sh4r.r[5]}, nkeynes@1091: {"R6", REG_TYPE_INT, &sh4r.r[6]}, {"R7", REG_TYPE_INT, &sh4r.r[7]}, nkeynes@1091: {"R8", REG_TYPE_INT, &sh4r.r[8]}, {"R9", REG_TYPE_INT, &sh4r.r[9]}, nkeynes@1091: {"R10",REG_TYPE_INT, &sh4r.r[10]}, {"R11",REG_TYPE_INT, &sh4r.r[11]}, nkeynes@1091: {"R12",REG_TYPE_INT, &sh4r.r[12]}, {"R13",REG_TYPE_INT, &sh4r.r[13]}, nkeynes@1091: {"R14",REG_TYPE_INT, &sh4r.r[14]}, {"R15",REG_TYPE_INT, &sh4r.r[15]}, nkeynes@1091: {"PC", REG_TYPE_INT, &sh4r.pc}, {"PR", REG_TYPE_INT, &sh4r.pr}, nkeynes@1091: {"GBR", REG_TYPE_INT, &sh4r.gbr}, {"VBR",REG_TYPE_INT, &sh4r.vbr}, nkeynes@1091: {"MACH",REG_TYPE_INT, ((uint32_t *)&sh4r.mac)+1}, {"MACL",REG_TYPE_INT, &sh4r.mac}, nkeynes@1091: {"SR", REG_TYPE_INT, &sh4r.sr}, nkeynes@1091: {"FPUL", REG_TYPE_INT, &sh4r.fpul.i}, {"FPSCR", REG_TYPE_INT, &sh4r.fpscr}, nkeynes@998: nkeynes@1091: {"FR0", REG_TYPE_FLOAT, &sh4r.fr[0][1] },{"FR1", REG_TYPE_FLOAT, &sh4r.fr[0][0]}, nkeynes@1091: {"FR2", REG_TYPE_FLOAT, &sh4r.fr[0][3] },{"FR3", REG_TYPE_FLOAT, &sh4r.fr[0][2]}, nkeynes@1091: {"FR4", REG_TYPE_FLOAT, &sh4r.fr[0][5] },{"FR5", REG_TYPE_FLOAT, &sh4r.fr[0][4]}, nkeynes@1091: {"FR6", REG_TYPE_FLOAT, &sh4r.fr[0][7] },{"FR7", REG_TYPE_FLOAT, &sh4r.fr[0][6]}, nkeynes@1091: {"FR8", REG_TYPE_FLOAT, &sh4r.fr[0][9] },{"FR9", REG_TYPE_FLOAT, &sh4r.fr[0][8]}, nkeynes@1091: {"FR10", REG_TYPE_FLOAT, &sh4r.fr[0][11] },{"FR11", REG_TYPE_FLOAT, &sh4r.fr[0][10]}, nkeynes@1091: {"FR12", REG_TYPE_FLOAT, &sh4r.fr[0][13] },{"FR13", REG_TYPE_FLOAT, &sh4r.fr[0][12]}, nkeynes@1091: {"FR14", REG_TYPE_FLOAT, &sh4r.fr[0][15] },{"FR15", REG_TYPE_FLOAT, &sh4r.fr[0][14]}, nkeynes@378: nkeynes@1091: {"SSR",REG_TYPE_INT, &sh4r.ssr}, {"SPC", REG_TYPE_INT, &sh4r.spc}, nkeynes@998: nkeynes@1091: {"R0B0", REG_TYPE_INT, NULL}, {"R1B0", REG_TYPE_INT, NULL}, nkeynes@1091: {"R2B0", REG_TYPE_INT, NULL}, {"R3B0", REG_TYPE_INT, NULL}, nkeynes@1091: {"R4B0", REG_TYPE_INT, NULL}, {"R5B0", REG_TYPE_INT, NULL}, nkeynes@1091: {"R6B0", REG_TYPE_INT, NULL}, {"R7B0", REG_TYPE_INT, NULL}, nkeynes@1091: {"R0B1", REG_TYPE_INT, NULL}, {"R1B1", REG_TYPE_INT, NULL}, nkeynes@1091: {"R2B1", REG_TYPE_INT, NULL}, {"R3B1", REG_TYPE_INT, NULL}, nkeynes@1091: {"R4B1", REG_TYPE_INT, NULL}, {"R5B1", REG_TYPE_INT, NULL}, nkeynes@1091: {"R6B1", REG_TYPE_INT, NULL}, {"R7B1", REG_TYPE_INT, NULL}, nkeynes@998: nkeynes@1091: {"SGR",REG_TYPE_INT, &sh4r.sgr}, {"DBR", REG_TYPE_INT, &sh4r.dbr}, nkeynes@998: nkeynes@1091: {"XF0", REG_TYPE_FLOAT, &sh4r.fr[1][1] },{"XF1", REG_TYPE_FLOAT, &sh4r.fr[1][0]}, nkeynes@1091: {"XF2", REG_TYPE_FLOAT, &sh4r.fr[1][3] },{"XF3", REG_TYPE_FLOAT, &sh4r.fr[1][2]}, nkeynes@1091: {"XF4", REG_TYPE_FLOAT, &sh4r.fr[1][5] },{"XF5", REG_TYPE_FLOAT, &sh4r.fr[1][4]}, nkeynes@1091: {"XF6", REG_TYPE_FLOAT, &sh4r.fr[1][7] },{"XF7", REG_TYPE_FLOAT, &sh4r.fr[1][6]}, nkeynes@1091: {"XF8", REG_TYPE_FLOAT, &sh4r.fr[1][9] },{"XF9", REG_TYPE_FLOAT, &sh4r.fr[1][8]}, nkeynes@1091: {"XF10", REG_TYPE_FLOAT, &sh4r.fr[1][11] },{"XF11", REG_TYPE_FLOAT, &sh4r.fr[1][10]}, nkeynes@1091: {"XF12", REG_TYPE_FLOAT, &sh4r.fr[1][13] },{"XF13", REG_TYPE_FLOAT, &sh4r.fr[1][12]}, nkeynes@1091: {"XF14", REG_TYPE_FLOAT, &sh4r.fr[1][15] },{"XF15", REG_TYPE_FLOAT, &sh4r.fr[1][14]}, nkeynes@998: nkeynes@998: {NULL, 0, NULL} }; nkeynes@998: nkeynes@998: void *sh4_get_register( int reg ) nkeynes@998: { nkeynes@998: if( reg < 0 || reg >= 94 ) { nkeynes@998: return NULL; nkeynes@998: } else if( reg < 43 ) { nkeynes@998: return sh4_reg_map[reg].value; nkeynes@998: } else if( reg < 51 ) { nkeynes@998: /* r0b0..r7b0 */ nkeynes@998: if( (sh4r.sr & SR_MDRB) == SR_MDRB ) { nkeynes@998: /* bank 1 is primary */ nkeynes@998: return &sh4r.r_bank[reg-43]; nkeynes@998: } else { nkeynes@998: return &sh4r.r[reg-43]; nkeynes@998: } nkeynes@998: } else if( reg < 59 ) { nkeynes@998: /* r0b1..r7b1 */ nkeynes@998: if( (sh4r.sr & SR_MDRB) == SR_MDRB ) { nkeynes@998: /* bank 1 is primary */ nkeynes@998: return &sh4r.r[reg-43]; nkeynes@998: } else { nkeynes@998: return &sh4r.r_bank[reg-43]; nkeynes@998: } nkeynes@998: } else { nkeynes@998: return NULL; /* not supported at the moment */ nkeynes@998: } nkeynes@998: } nkeynes@998: nkeynes@998: nkeynes@998: const struct cpu_desc_struct sh4_cpu_desc = nkeynes@998: { "SH4", sh4_disasm_instruction, sh4_get_register, sh4_has_page, nkeynes@998: sh4_debug_read_phys, sh4_debug_write_phys, sh4_debug_read_vma, sh4_debug_write_vma, nkeynes@998: sh4_execute_instruction, nkeynes@998: sh4_set_breakpoint, sh4_clear_breakpoint, sh4_get_breakpoint, 2, nkeynes@998: (char *)&sh4r, sizeof(sh4r), sh4_reg_map, 23, 59, nkeynes@998: &sh4r.pc }; nkeynes@998: nkeynes@951: struct dreamcast_module sh4_module = { "SH4", sh4_init, sh4_poweron_reset, nkeynes@736: sh4_start, sh4_run_slice, sh4_stop, nkeynes@736: sh4_save_state, sh4_load_state }; nkeynes@378: nkeynes@903: struct sh4_registers sh4r __attribute__((aligned(16))); nkeynes@378: struct breakpoint_struct sh4_breakpoints[MAX_BREAKPOINTS]; nkeynes@378: int sh4_breakpoint_count = 0; nkeynes@929: nkeynes@591: gboolean sh4_starting = FALSE; nkeynes@1218: gboolean sh4_profile_blocks = FALSE; nkeynes@526: static gboolean sh4_use_translator = FALSE; nkeynes@740: static jmp_buf sh4_exit_jmp_buf; nkeynes@740: static gboolean sh4_running = FALSE; nkeynes@586: struct sh4_icache_struct sh4_icache = { NULL, -1, -1, 0 }; nkeynes@378: nkeynes@1112: /* At the moment this is a dummy event to mark the end of the nkeynes@1112: * timeslice nkeynes@1112: */ nkeynes@1112: void sh4_dummy_event(int eventid) nkeynes@1112: { nkeynes@1112: } nkeynes@1112: nkeynes@1125: void sh4_set_core( sh4core_t core ) nkeynes@378: { nkeynes@736: // No-op if the translator was not built nkeynes@526: #ifdef SH4_TRANSLATOR nkeynes@1125: if( core != SH4_INTERPRET ) { nkeynes@736: sh4_translate_init(); nkeynes@1125: sh4_use_translator = TRUE; nkeynes@1125: if( core == SH4_SHADOW ) { nkeynes@1125: sh4_shadow_init(); nkeynes@1125: } nkeynes@1125: } else { nkeynes@1125: sh4_use_translator = FALSE; nkeynes@378: } nkeynes@526: #endif nkeynes@378: } nkeynes@378: nkeynes@740: gboolean sh4_translate_is_enabled() 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@1112: register_event_callback( EVENT_ENDTIMESLICE, sh4_dummy_event ); nkeynes@939: MMU_init(); nkeynes@619: TMU_init(); nkeynes@935: xlat_cache_init(); nkeynes@951: sh4_poweron_reset(); nkeynes@671: #ifdef ENABLE_SH4STATS nkeynes@671: sh4_stats_reset(); nkeynes@671: #endif nkeynes@378: } nkeynes@378: nkeynes@591: void sh4_start(void) nkeynes@591: { nkeynes@591: sh4_starting = TRUE; nkeynes@591: } nkeynes@591: nkeynes@951: void sh4_poweron_reset(void) nkeynes@378: { nkeynes@951: /* zero everything out, for the sake of having a consistent state. */ nkeynes@951: memset( &sh4r, 0, sizeof(sh4r) ); nkeynes@526: if( sh4_use_translator ) { nkeynes@736: xlat_flush_cache(); nkeynes@472: } nkeynes@472: 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@937: sh4_write_sr(0x700000F0); 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@841: PMM_reset(); nkeynes@378: TMU_reset(); nkeynes@378: SCIF_reset(); nkeynes@971: CCN_reset(); nkeynes@951: MMU_reset(); nkeynes@378: } nkeynes@378: nkeynes@378: void sh4_stop(void) nkeynes@378: { nkeynes@526: if( sh4_use_translator ) { nkeynes@736: /* If we were running with the translator, update new_pc and in_delay_slot */ nkeynes@736: sh4r.new_pc = sh4r.pc+2; nkeynes@736: sh4r.in_delay_slot = FALSE; nkeynes@1218: #ifdef SH4_TRANSLATOR nkeynes@1218: if( sh4_profile_blocks ) { nkeynes@1188: sh4_translate_dump_cache_by_activity(30); nkeynes@1182: } nkeynes@1218: #endif nkeynes@502: } nkeynes@378: } nkeynes@378: nkeynes@740: /** nkeynes@740: * Execute a timeslice using translated code only (ie translate/execute loop) nkeynes@740: */ nkeynes@740: uint32_t sh4_run_slice( uint32_t nanosecs ) nkeynes@740: { nkeynes@740: sh4r.slice_cycle = 0; nkeynes@740: nkeynes@740: /* Setup for sudden vm exits */ nkeynes@740: switch( setjmp(sh4_exit_jmp_buf) ) { nkeynes@740: case CORE_EXIT_BREAKPOINT: nkeynes@740: sh4_clear_breakpoint( sh4r.pc, BREAK_ONESHOT ); nkeynes@740: /* fallthrough */ nkeynes@740: case CORE_EXIT_HALT: nkeynes@740: if( sh4r.sh4_state != SH4_STATE_STANDBY ) { nkeynes@740: TMU_run_slice( sh4r.slice_cycle ); nkeynes@740: SCIF_run_slice( sh4r.slice_cycle ); nkeynes@841: PMM_run_slice( sh4r.slice_cycle ); nkeynes@740: dreamcast_stop(); nkeynes@740: return sh4r.slice_cycle; nkeynes@740: } nkeynes@740: case CORE_EXIT_SYSRESET: nkeynes@740: dreamcast_reset(); nkeynes@740: break; nkeynes@740: case CORE_EXIT_SLEEP: nkeynes@740: break; nkeynes@740: case CORE_EXIT_FLUSH_ICACHE: nkeynes@740: xlat_flush_cache(); nkeynes@740: break; nkeynes@740: } nkeynes@740: nkeynes@1171: if( sh4r.sh4_state != SH4_STATE_RUNNING ) { nkeynes@1171: sh4_sleep_run_slice(nanosecs); nkeynes@1171: } else { nkeynes@1171: sh4_running = TRUE; nkeynes@1171: nkeynes@1171: /* Execute the core's real slice */ nkeynes@740: #ifdef SH4_TRANSLATOR nkeynes@1171: if( sh4_use_translator ) { nkeynes@1171: sh4_translate_run_slice(nanosecs); nkeynes@1171: } else { nkeynes@1171: sh4_emulate_run_slice(nanosecs); nkeynes@1171: } nkeynes@1171: #else nkeynes@740: sh4_emulate_run_slice(nanosecs); nkeynes@1171: #endif nkeynes@740: } nkeynes@740: nkeynes@740: /* And finish off the peripherals afterwards */ nkeynes@740: nkeynes@740: sh4_running = FALSE; nkeynes@740: sh4_starting = FALSE; nkeynes@740: sh4r.slice_cycle = nanosecs; nkeynes@740: if( sh4r.sh4_state != SH4_STATE_STANDBY ) { nkeynes@740: TMU_run_slice( nanosecs ); nkeynes@740: SCIF_run_slice( nanosecs ); nkeynes@841: PMM_run_slice( sh4r.slice_cycle ); nkeynes@740: } nkeynes@740: return nanosecs; nkeynes@740: } nkeynes@740: nkeynes@740: void sh4_core_exit( int exit_code ) nkeynes@740: { nkeynes@740: if( sh4_running ) { nkeynes@740: #ifdef SH4_TRANSLATOR nkeynes@740: if( sh4_use_translator ) { nkeynes@941: if( exit_code == CORE_EXIT_EXCEPTION ) { nkeynes@941: sh4_translate_exception_exit_recover(); nkeynes@941: } else { nkeynes@941: sh4_translate_exit_recover(); nkeynes@941: } nkeynes@740: } nkeynes@740: #endif nkeynes@971: if( exit_code != CORE_EXIT_EXCEPTION && nkeynes@971: exit_code != CORE_EXIT_BREAKPOINT ) { nkeynes@948: sh4_finalize_instruction(); nkeynes@948: } nkeynes@740: // longjmp back into sh4_run_slice nkeynes@740: sh4_running = FALSE; nkeynes@740: longjmp(sh4_exit_jmp_buf, exit_code); nkeynes@740: } nkeynes@740: } nkeynes@740: nkeynes@378: void sh4_save_state( FILE *f ) nkeynes@378: { nkeynes@526: if( sh4_use_translator ) { nkeynes@736: /* If we were running with the translator, update new_pc and in_delay_slot */ nkeynes@736: sh4r.new_pc = sh4r.pc+2; nkeynes@736: sh4r.in_delay_slot = FALSE; nkeynes@401: } nkeynes@401: nkeynes@936: fwrite( &sh4r, offsetof(struct sh4_registers, xlat_sh4_mode), 1, f ); nkeynes@378: MMU_save_state( f ); nkeynes@931: CCN_save_state( f ); nkeynes@841: PMM_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@736: xlat_flush_cache(); nkeynes@472: } nkeynes@936: fread( &sh4r, offsetof(struct sh4_registers, xlat_sh4_mode), 1, f ); nkeynes@936: sh4r.xlat_sh4_mode = (sh4r.sr & SR_MD) | (sh4r.fpscr & (FPSCR_SZ|FPSCR_PR)); nkeynes@378: MMU_load_state( f ); nkeynes@931: CCN_load_state( f ); nkeynes@841: PMM_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@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@736: 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 (int64_t)0x00007FFFFFFFFFFFLL ) nkeynes@736: sh4r.mac = 0x00007FFFFFFFFFFFLL; nkeynes@401: } nkeynes@401: nkeynes@905: void FASTCALL 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@905: void FASTCALL sh4_sleep(void) nkeynes@401: { nkeynes@401: if( MMIO_READ( CPG, STBCR ) & 0x80 ) { nkeynes@736: sh4r.sh4_state = SH4_STATE_STANDBY; nkeynes@736: /* Bring all running peripheral modules up to date, and then halt them. */ nkeynes@736: TMU_run_slice( sh4r.slice_cycle ); nkeynes@736: SCIF_run_slice( sh4r.slice_cycle ); nkeynes@841: PMM_run_slice( sh4r.slice_cycle ); nkeynes@401: } else { nkeynes@736: if( MMIO_READ( CPG, STBCR2 ) & 0x80 ) { nkeynes@736: sh4r.sh4_state = SH4_STATE_DEEP_SLEEP; nkeynes@736: /* Halt DMAC but other peripherals still running */ nkeynes@736: nkeynes@736: } else { nkeynes@736: sh4r.sh4_state = SH4_STATE_SLEEP; nkeynes@736: } nkeynes@617: } nkeynes@740: sh4_core_exit( CORE_EXIT_SLEEP ); 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@736: break; nkeynes@617: case SH4_STATE_DEEP_SLEEP: nkeynes@736: break; nkeynes@617: case SH4_STATE_SLEEP: nkeynes@736: 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@1171: assert( sh4r.sh4_state != SH4_STATE_RUNNING ); nkeynes@736: nkeynes@617: while( sh4r.event_pending < nanosecs ) { nkeynes@736: sh4r.slice_cycle = sh4r.event_pending; nkeynes@736: if( sh4r.event_types & PENDING_EVENT ) { nkeynes@736: event_execute(); nkeynes@736: } nkeynes@736: if( sh4r.event_types & PENDING_IRQ ) { nkeynes@736: sh4_wakeup(); nkeynes@736: return sh4r.slice_cycle; nkeynes@736: } nkeynes@617: } nkeynes@1171: if( sh4r.slice_cycle < nanosecs ) nkeynes@1171: sh4r.slice_cycle = nanosecs; 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@905: void FASTCALL sh4_ftrv( float *target ) nkeynes@401: { nkeynes@401: float fv[4] = { target[1], target[0], target[3], target[2] }; nkeynes@669: target[1] = sh4r.fr[1][1] * fv[0] + sh4r.fr[1][5]*fv[1] + nkeynes@736: sh4r.fr[1][9]*fv[2] + sh4r.fr[1][13]*fv[3]; nkeynes@669: target[0] = sh4r.fr[1][0] * fv[0] + sh4r.fr[1][4]*fv[1] + nkeynes@736: sh4r.fr[1][8]*fv[2] + sh4r.fr[1][12]*fv[3]; nkeynes@669: target[3] = sh4r.fr[1][3] * fv[0] + sh4r.fr[1][7]*fv[1] + nkeynes@736: sh4r.fr[1][11]*fv[2] + sh4r.fr[1][15]*fv[3]; nkeynes@669: target[2] = sh4r.fr[1][2] * fv[0] + sh4r.fr[1][6]*fv[1] + nkeynes@736: sh4r.fr[1][10]*fv[2] + sh4r.fr[1][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: } nkeynes@998: nkeynes@1187: void sh4_handle_pending_events() { nkeynes@1187: if( sh4r.event_types & PENDING_EVENT ) { nkeynes@1187: event_execute(); nkeynes@1187: } nkeynes@1187: /* Eventq execute may (quite likely) deliver an immediate IRQ */ nkeynes@1187: if( sh4r.event_types & PENDING_IRQ ) { nkeynes@1187: sh4_accept_interrupt(); nkeynes@1187: } nkeynes@1187: } nkeynes@1187: nkeynes@998: /** nkeynes@998: * Go through ext_address_space page by page nkeynes@998: */ nkeynes@998: size_t sh4_debug_read_phys( unsigned char *buf, uint32_t addr, size_t length ) nkeynes@998: { nkeynes@998: /* Quick and very dirty */ nkeynes@998: unsigned char *region = mem_get_region(addr); nkeynes@998: if( region == NULL ) { nkeynes@998: memset( buf, 0, length ); nkeynes@998: } else { nkeynes@998: memcpy( buf, region, length ); nkeynes@998: } nkeynes@998: return length; nkeynes@998: } nkeynes@998: nkeynes@998: size_t sh4_debug_write_phys( uint32_t addr, unsigned char *buf, size_t length ) nkeynes@998: { nkeynes@998: unsigned char *region = mem_get_region(addr); nkeynes@998: if( region != NULL ) { nkeynes@998: memcpy( region, buf, length ); nkeynes@998: } nkeynes@998: return length; nkeynes@998: } nkeynes@998: nkeynes@998: /** nkeynes@998: * Read virtual memory - for now just go 1K at a time nkeynes@998: */ nkeynes@998: size_t sh4_debug_read_vma( unsigned char *buf, uint32_t addr, size_t length ) nkeynes@998: { nkeynes@998: if( IS_TLB_ENABLED() ) { nkeynes@998: size_t read_len = 0; nkeynes@998: while( length > 0 ) { nkeynes@998: sh4addr_t phys = mmu_vma_to_phys_disasm(addr); nkeynes@998: if( phys == MMU_VMA_ERROR ) nkeynes@998: break; nkeynes@998: int next_len = 1024 - (phys&0x000003FF); nkeynes@998: if( next_len >= length ) { nkeynes@998: next_len = length; nkeynes@998: } nkeynes@998: sh4_debug_read_phys( buf, phys, length ); nkeynes@998: buf += next_len; nkeynes@998: addr += next_len; nkeynes@998: read_len += next_len; nkeynes@998: length -= next_len; nkeynes@998: } nkeynes@998: return read_len; nkeynes@998: } else { nkeynes@998: return sh4_debug_read_phys( buf, addr, length ); nkeynes@998: } nkeynes@998: } nkeynes@998: nkeynes@998: size_t sh4_debug_write_vma( uint32_t addr, unsigned char *buf, size_t length ) nkeynes@998: { nkeynes@998: if( IS_TLB_ENABLED() ) { nkeynes@998: size_t read_len = 0; nkeynes@998: while( length > 0 ) { nkeynes@998: sh4addr_t phys = mmu_vma_to_phys_disasm(addr); nkeynes@998: if( phys == MMU_VMA_ERROR ) nkeynes@998: break; nkeynes@998: int next_len = 1024 - (phys&0x000003FF); nkeynes@998: if( next_len >= length ) { nkeynes@998: next_len = length; nkeynes@998: } nkeynes@998: sh4_debug_write_phys( phys, buf, length ); nkeynes@998: buf += next_len; nkeynes@998: addr += next_len; nkeynes@998: read_len += next_len; nkeynes@998: length -= next_len; nkeynes@998: } nkeynes@1071: return read_len; nkeynes@998: } else { nkeynes@998: return sh4_debug_write_phys( addr, buf, length ); nkeynes@998: } nkeynes@998: }