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@430: #include "eventq.h" nkeynes@430: #include "syscall.h" nkeynes@586: #include "clock.h" nkeynes@669: #include "dreamcast.h" nkeynes@430: #include "sh4/sh4core.h" nkeynes@430: #include "sh4/sh4trans.h" nkeynes@430: #include "sh4/xltcache.h" nkeynes@359: nkeynes@586: nkeynes@359: /** nkeynes@359: * Execute a timeslice using translated code only (ie translate/execute loop) nkeynes@359: */ nkeynes@740: uint32_t sh4_translate_run_slice( uint32_t nanosecs ) nkeynes@359: { nkeynes@408: void * (*code)() = NULL; nkeynes@368: while( sh4r.slice_cycle < nanosecs ) { nkeynes@736: if( sh4r.event_pending <= sh4r.slice_cycle ) { nkeynes@736: if( sh4r.event_types & PENDING_EVENT ) { nkeynes@736: event_execute(); nkeynes@736: } nkeynes@736: /* Eventq execute may (quite likely) deliver an immediate IRQ */ nkeynes@736: if( sh4r.event_types & PENDING_IRQ ) { nkeynes@736: sh4_accept_interrupt(); nkeynes@736: code = NULL; nkeynes@736: } nkeynes@736: } nkeynes@359: nkeynes@736: if( code == NULL ) { nkeynes@736: if( sh4r.pc > 0xFFFFFF00 ) { nkeynes@736: syscall_invoke( sh4r.pc ); nkeynes@736: sh4r.in_delay_slot = 0; nkeynes@736: sh4r.pc = sh4r.pr; nkeynes@736: } nkeynes@736: nkeynes@736: code = xlat_get_code_by_vma( sh4r.pc ); nkeynes@736: if( code == NULL ) { nkeynes@736: code = sh4_translate_basic_block( sh4r.pc ); nkeynes@736: } nkeynes@736: } nkeynes@736: code = code(); nkeynes@359: } nkeynes@359: return nanosecs; nkeynes@359: } nkeynes@359: nkeynes@359: uint8_t *xlat_output; nkeynes@596: xlat_cache_block_t xlat_current_block; nkeynes@586: struct xlat_recovery_record xlat_recovery[MAX_RECOVERY_SIZE]; nkeynes@586: uint32_t xlat_recovery_posn; nkeynes@359: nkeynes@596: void sh4_translate_add_recovery( uint32_t icount ) nkeynes@596: { nkeynes@596: xlat_recovery[xlat_recovery_posn].xlat_offset = nkeynes@736: ((uintptr_t)xlat_output) - ((uintptr_t)xlat_current_block->code); nkeynes@596: xlat_recovery[xlat_recovery_posn].sh4_icount = icount; nkeynes@596: xlat_recovery_posn++; nkeynes@596: } nkeynes@596: 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@596: xlat_current_block = xlat_start_block( start ); nkeynes@596: xlat_output = (uint8_t *)xlat_current_block->code; nkeynes@586: xlat_recovery_posn = 0; nkeynes@596: uint8_t *eob = xlat_output + xlat_current_block->size; nkeynes@588: nkeynes@588: if( GET_ICACHE_END() < lastpc ) { nkeynes@711: lastpc = GET_ICACHE_END(); nkeynes@588: } nkeynes@588: nkeynes@408: sh4_translate_begin_block(pc); nkeynes@359: nkeynes@408: do { nkeynes@711: /* check for breakpoints at this pc */ nkeynes@711: for( i=0; icode; nkeynes@711: xlat_current_block = xlat_extend_block( xlat_output - oldstart + MAX_INSTRUCTION_SIZE ); nkeynes@711: xlat_output = xlat_current_block->code + (xlat_output - oldstart); nkeynes@711: eob = xlat_current_block->code + xlat_current_block->size; nkeynes@711: } nkeynes@711: done = sh4_translate_instruction( pc ); nkeynes@711: assert( xlat_output <= eob ); nkeynes@711: pc += 2; nkeynes@711: if ( pc >= lastpc ) { nkeynes@711: done = 2; nkeynes@711: } nkeynes@408: } while( !done ); nkeynes@408: pc += (done - 2); nkeynes@617: nkeynes@617: // Add end-of-block recovery for post-instruction checks nkeynes@617: sh4_translate_add_recovery( (pc - start)>>1 ); nkeynes@617: nkeynes@593: int epilogue_size = sh4_translate_end_block_size(); nkeynes@593: uint32_t recovery_size = sizeof(struct xlat_recovery_record)*xlat_recovery_posn; nkeynes@711: uint32_t finalsize = (xlat_output - xlat_current_block->code) + epilogue_size + recovery_size; nkeynes@711: if( xlat_current_block->size < finalsize ) { nkeynes@711: uint8_t *oldstart = xlat_current_block->code; nkeynes@711: xlat_current_block = xlat_extend_block( finalsize ); nkeynes@711: xlat_output = xlat_current_block->code + (xlat_output - oldstart); nkeynes@410: } nkeynes@368: sh4_translate_end_block(pc); nkeynes@711: assert( xlat_output <= (xlat_current_block->code + xlat_current_block->size - recovery_size) ); nkeynes@736: nkeynes@586: /* Write the recovery records onto the end of the code block */ nkeynes@586: memcpy( xlat_output, xlat_recovery, recovery_size); nkeynes@596: xlat_current_block->recover_table_offset = xlat_output - (uint8_t *)xlat_current_block->code; nkeynes@596: xlat_current_block->recover_table_size = xlat_recovery_posn; nkeynes@586: xlat_commit_block( finalsize, pc-start ); nkeynes@596: return xlat_current_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@740: void sh4_translate_exit_recover( ) nkeynes@586: { nkeynes@586: void *pc = xlat_get_native_pc(); nkeynes@586: if( pc != NULL ) { nkeynes@736: // could be null if we're not actually running inside the translator nkeynes@736: void *code = xlat_get_code( sh4r.pc ); nkeynes@809: xlat_recovery_record_t recover = xlat_get_post_recovery(code, pc, TRUE); nkeynes@736: if( recover != NULL ) { nkeynes@736: // Can be null if there is no recovery necessary nkeynes@736: sh4_translate_run_recovery(recover); nkeynes@736: } nkeynes@398: } 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@736: return; nkeynes@591: } nkeynes@740: sh4_core_exit( CORE_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@740: gboolean 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@809: xlat_recovery_record_t recover = xlat_get_post_recovery(code, pc, FALSE); nkeynes@586: if( recover != NULL ) { nkeynes@736: // Can be null if there is no recovery necessary nkeynes@736: sh4_translate_run_recovery(recover); nkeynes@736: xlat_flush_cache(); nkeynes@740: return TRUE; nkeynes@586: } else { nkeynes@736: xlat_flush_cache(); nkeynes@740: return FALSE; 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@736: return xlat_get_code( GET_ICACHE_PHYS(vma) ); nkeynes@586: } nkeynes@586: nkeynes@588: if( vma > 0xFFFFFF00 ) { nkeynes@736: // lxdream hook nkeynes@736: return NULL; nkeynes@588: } nkeynes@588: nkeynes@588: if( !mmu_update_icache(vma) ) { nkeynes@736: // fault - off to the fault handler nkeynes@736: if( !mmu_update_icache(sh4r.pc) ) { nkeynes@736: // double fault - halt nkeynes@736: ERROR( "Double fault - halting" ); nkeynes@740: sh4_core_exit(CORE_EXIT_HALT); nkeynes@736: return NULL; nkeynes@736: } 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: