nkeynes@359: /** nkeynes@586: * $Id$ nkeynes@359: * nkeynes@359: * SH4 translation core module. This part handles the non-target-specific nkeynes@359: * section of the translation. nkeynes@359: * nkeynes@359: * Copyright (c) 2005 Nathan Keynes. nkeynes@359: * nkeynes@359: * This program is free software; you can redistribute it and/or modify nkeynes@359: * it under the terms of the GNU General Public License as published by nkeynes@359: * the Free Software Foundation; either version 2 of the License, or nkeynes@359: * (at your option) any later version. nkeynes@359: * nkeynes@359: * This program is distributed in the hope that it will be useful, nkeynes@359: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@359: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@359: * GNU General Public License for more details. nkeynes@359: */ nkeynes@398: #include nkeynes@586: #include nkeynes@430: #include "eventq.h" nkeynes@430: #include "syscall.h" nkeynes@586: #include "clock.h" nkeynes@430: #include "sh4/sh4core.h" nkeynes@430: #include "sh4/sh4trans.h" nkeynes@430: #include "sh4/xltcache.h" nkeynes@359: nkeynes@586: nkeynes@586: static jmp_buf xlat_jmp_buf; nkeynes@586: static gboolean xlat_running = FALSE; nkeynes@586: nkeynes@586: gboolean sh4_xlat_is_running() nkeynes@586: { nkeynes@586: return xlat_running; nkeynes@586: } nkeynes@586: nkeynes@359: /** nkeynes@359: * Execute a timeslice using translated code only (ie translate/execute loop) nkeynes@359: */ nkeynes@359: uint32_t sh4_xlat_run_slice( uint32_t nanosecs ) nkeynes@359: { nkeynes@359: sh4r.slice_cycle = 0; nkeynes@359: nkeynes@359: if( sh4r.sh4_state != SH4_STATE_RUNNING ) { nkeynes@359: if( sh4r.event_pending < nanosecs ) { nkeynes@359: sh4r.sh4_state = SH4_STATE_RUNNING; nkeynes@359: sh4r.slice_cycle = sh4r.event_pending; nkeynes@359: } nkeynes@359: } nkeynes@359: nkeynes@586: switch( setjmp(xlat_jmp_buf) ) { nkeynes@586: case XLAT_EXIT_BREAKPOINT: nkeynes@586: sh4_clear_breakpoint( sh4r.pc, BREAK_ONESHOT ); nkeynes@586: /* fallthrough */ nkeynes@586: case XLAT_EXIT_HALT: nkeynes@586: if( sh4r.sh4_state != SH4_STATE_STANDBY ) { nkeynes@586: TMU_run_slice( sh4r.slice_cycle ); nkeynes@586: SCIF_run_slice( sh4r.slice_cycle ); nkeynes@586: dreamcast_stop(); nkeynes@586: return sh4r.slice_cycle; nkeynes@586: } nkeynes@586: case XLAT_EXIT_SYSRESET: nkeynes@586: dreamcast_reset(); nkeynes@586: break; nkeynes@586: } nkeynes@586: nkeynes@586: xlat_running = TRUE; nkeynes@408: void * (*code)() = NULL; nkeynes@368: while( sh4r.slice_cycle < nanosecs ) { nkeynes@408: if( sh4r.event_pending <= sh4r.slice_cycle ) { nkeynes@359: if( sh4r.event_types & PENDING_EVENT ) { nkeynes@359: event_execute(); nkeynes@359: } nkeynes@359: /* Eventq execute may (quite likely) deliver an immediate IRQ */ nkeynes@359: if( sh4r.event_types & PENDING_IRQ ) { nkeynes@359: sh4_accept_interrupt(); nkeynes@408: code = NULL; nkeynes@359: } nkeynes@359: } nkeynes@408: nkeynes@417: if( code == NULL ) { nkeynes@408: if( sh4r.pc > 0xFFFFFF00 ) { nkeynes@408: syscall_invoke( sh4r.pc ); nkeynes@408: sh4r.in_delay_slot = 0; nkeynes@408: sh4r.pc = sh4r.pr; nkeynes@408: } nkeynes@359: nkeynes@586: code = xlat_get_code_by_vma( sh4r.pc ); nkeynes@408: if( code == NULL ) { nkeynes@408: code = sh4_translate_basic_block( sh4r.pc ); nkeynes@408: } nkeynes@390: } nkeynes@417: code = code(); nkeynes@359: } nkeynes@359: nkeynes@586: xlat_running = FALSE; nkeynes@591: sh4_starting = FALSE; nkeynes@586: nkeynes@359: if( sh4r.sh4_state != SH4_STATE_STANDBY ) { nkeynes@359: TMU_run_slice( nanosecs ); nkeynes@359: SCIF_run_slice( nanosecs ); nkeynes@359: } nkeynes@359: return nanosecs; nkeynes@359: } nkeynes@359: nkeynes@359: uint8_t *xlat_output; nkeynes@586: struct xlat_recovery_record xlat_recovery[MAX_RECOVERY_SIZE]; nkeynes@586: uint32_t xlat_recovery_posn; nkeynes@359: nkeynes@359: /** nkeynes@359: * Translate a linear basic block, ie all instructions from the start address nkeynes@359: * (inclusive) until the next branch/jump instruction or the end of the page nkeynes@359: * is reached. nkeynes@359: * @return the address of the translated block nkeynes@359: * eg due to lack of buffer space. nkeynes@359: */ nkeynes@359: void * sh4_translate_basic_block( sh4addr_t start ) nkeynes@359: { nkeynes@408: sh4addr_t pc = start; nkeynes@410: sh4addr_t lastpc = (pc&0xFFFFF000)+0x1000; nkeynes@586: int done, i; nkeynes@359: xlat_cache_block_t block = xlat_start_block( start ); nkeynes@359: xlat_output = (uint8_t *)block->code; nkeynes@586: xlat_recovery_posn = 0; nkeynes@359: uint8_t *eob = xlat_output + block->size; nkeynes@588: nkeynes@588: if( GET_ICACHE_END() < lastpc ) { nkeynes@588: lastpc = GET_ICACHE_END(); nkeynes@588: } nkeynes@588: nkeynes@408: sh4_translate_begin_block(pc); nkeynes@359: nkeynes@408: do { nkeynes@586: /* check for breakpoints at this pc */ nkeynes@586: for( i=0; icode; nkeynes@410: block = xlat_extend_block( xlat_output - oldstart + MAX_INSTRUCTION_SIZE ); nkeynes@359: xlat_output = block->code + (xlat_output - oldstart); nkeynes@359: eob = block->code + block->size; nkeynes@359: } nkeynes@527: done = sh4_translate_instruction( pc ); nkeynes@410: assert( xlat_output <= eob ); nkeynes@359: pc += 2; nkeynes@410: if ( pc >= lastpc ) { nkeynes@410: done = 2; nkeynes@410: } nkeynes@408: } while( !done ); nkeynes@408: pc += (done - 2); nkeynes@410: if( eob - xlat_output < EPILOGUE_SIZE ) { nkeynes@410: uint8_t *oldstart = block->code; nkeynes@410: block = xlat_extend_block( xlat_output - oldstart + EPILOGUE_SIZE ); nkeynes@410: xlat_output = block->code + (xlat_output - oldstart); nkeynes@410: } nkeynes@368: sh4_translate_end_block(pc); nkeynes@586: nkeynes@586: /* Write the recovery records onto the end of the code block */ nkeynes@586: uint32_t recovery_size = sizeof(struct xlat_recovery_record)*xlat_recovery_posn; nkeynes@586: uint32_t finalsize = xlat_output - block->code + recovery_size; nkeynes@586: if( finalsize > block->size ) { nkeynes@586: uint8_t *oldstart = block->code; nkeynes@586: block = xlat_extend_block( finalsize ); nkeynes@586: xlat_output = block->code + (xlat_output - oldstart); nkeynes@586: } nkeynes@586: memcpy( xlat_output, xlat_recovery, recovery_size); nkeynes@586: block->recover_table = (xlat_recovery_record_t)xlat_output; nkeynes@586: block->recover_table_size = xlat_recovery_posn; nkeynes@586: xlat_commit_block( finalsize, pc-start ); nkeynes@359: return block->code; nkeynes@359: } nkeynes@359: nkeynes@398: /** nkeynes@586: * "Execute" the supplied recovery record. Currently this only updates nkeynes@586: * sh4r.pc and sh4r.slice_cycle according to the currently executing nkeynes@586: * instruction. In future this may be more sophisticated (ie will nkeynes@586: * call into generated code). nkeynes@398: */ nkeynes@586: void sh4_translate_run_recovery( xlat_recovery_record_t recovery ) nkeynes@398: { nkeynes@586: sh4r.slice_cycle += (recovery->sh4_icount * sh4_cpu_period); nkeynes@586: sh4r.pc += (recovery->sh4_icount<<1); nkeynes@586: } nkeynes@359: nkeynes@586: void sh4_translate_unwind_stack( gboolean abort_after, unwind_thunk_t thunk ) nkeynes@586: { nkeynes@586: void *pc = xlat_get_native_pc(); nkeynes@398: nkeynes@586: assert( pc != NULL ); nkeynes@586: void *code = xlat_get_code( sh4r.pc ); nkeynes@586: xlat_recovery_record_t recover = xlat_get_recovery(code, pc, TRUE); nkeynes@586: if( recover != NULL ) { nkeynes@586: // Can be null if there is no recovery necessary nkeynes@586: sh4_translate_run_recovery(recover); nkeynes@586: } nkeynes@586: if( thunk != NULL ) { nkeynes@586: thunk(); nkeynes@586: } nkeynes@586: // finally longjmp back into sh4_xlat_run_slice nkeynes@586: xlat_running = FALSE; nkeynes@586: longjmp(xlat_jmp_buf, XLAT_EXIT_CONTINUE); nkeynes@586: } nkeynes@398: nkeynes@586: void sh4_translate_exit( int exit_code ) nkeynes@586: { nkeynes@586: void *pc = xlat_get_native_pc(); nkeynes@586: if( pc != NULL ) { nkeynes@586: // could be null if we're not actually running inside the translator nkeynes@586: void *code = xlat_get_code( sh4r.pc ); nkeynes@586: xlat_recovery_record_t recover = xlat_get_recovery(code, pc, TRUE); nkeynes@586: if( recover != NULL ) { nkeynes@586: // Can be null if there is no recovery necessary nkeynes@586: sh4_translate_run_recovery(recover); nkeynes@586: } nkeynes@398: } nkeynes@586: // finally longjmp back into sh4_xlat_run_slice nkeynes@586: xlat_running = FALSE; nkeynes@586: longjmp(xlat_jmp_buf, exit_code); nkeynes@586: } nkeynes@398: nkeynes@591: void sh4_translate_breakpoint_hit(uint32_t pc) nkeynes@591: { nkeynes@591: if( sh4_starting && sh4r.slice_cycle == 0 && pc == sh4r.pc ) { nkeynes@591: return; nkeynes@591: } nkeynes@591: sh4_translate_exit( XLAT_EXIT_BREAKPOINT ); nkeynes@591: } nkeynes@591: nkeynes@586: /** nkeynes@586: * Exit the current block at the end of the current instruction, flush the nkeynes@586: * translation cache (completely) and return control to sh4_xlat_run_slice. nkeynes@586: * nkeynes@586: * As a special case, if the current instruction is actually the last nkeynes@586: * instruction in the block (ie it's in a delay slot), this function nkeynes@586: * returns to allow normal completion of the translation block. Otherwise nkeynes@586: * this function never returns. nkeynes@586: * nkeynes@586: * Must only be invoked (indirectly) from within translated code. nkeynes@586: */ nkeynes@586: void sh4_translate_flush_cache() nkeynes@586: { nkeynes@586: void *pc = xlat_get_native_pc(); nkeynes@586: assert( pc != NULL ); nkeynes@586: nkeynes@586: void *code = xlat_get_code( sh4r.pc ); nkeynes@586: xlat_recovery_record_t recover = xlat_get_recovery(code, pc, TRUE); nkeynes@586: if( recover != NULL ) { nkeynes@586: // Can be null if there is no recovery necessary nkeynes@586: sh4_translate_run_recovery(recover); nkeynes@586: xlat_flush_cache(); nkeynes@586: xlat_running = FALSE; nkeynes@586: longjmp(xlat_jmp_buf, XLAT_EXIT_CONTINUE); nkeynes@586: } else { nkeynes@586: xlat_flush_cache(); nkeynes@586: return; nkeynes@586: } nkeynes@398: } nkeynes@586: nkeynes@586: void *xlat_get_code_by_vma( sh4vma_t vma ) nkeynes@586: { nkeynes@586: void *result = NULL; nkeynes@586: nkeynes@588: if( IS_IN_ICACHE(vma) ) { nkeynes@586: result = xlat_get_code( GET_ICACHE_PHYS(vma) ); nkeynes@586: } nkeynes@586: nkeynes@588: if( vma > 0xFFFFFF00 ) { nkeynes@588: // lxdream hook nkeynes@588: return NULL; nkeynes@588: } nkeynes@588: nkeynes@588: if( !mmu_update_icache(vma) ) { nkeynes@588: // fault - off to the fault handler nkeynes@588: if( !mmu_update_icache(sh4r.pc) ) { nkeynes@588: // double fault - halt nkeynes@588: ERROR( "Double fault - halting" ); nkeynes@588: dreamcast_stop(); nkeynes@588: return NULL; nkeynes@588: } nkeynes@588: } nkeynes@588: nkeynes@588: assert( IS_IN_ICACHE(sh4r.pc) ); nkeynes@588: result = xlat_get_code( GET_ICACHE_PHYS(sh4r.pc) ); nkeynes@586: return result; nkeynes@586: } nkeynes@586: