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@975: #include "sh4/sh4mmio.h" nkeynes@975: #include "sh4/mmu.h" nkeynes@991: #include "xlat/xltcache.h" nkeynes@359: 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@1014: if( IS_SYSCALL(sh4r.pc) ) { 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@953: if( code == NULL || sh4r.xlat_sh4_mode != XLAT_BLOCK_MODE(code) ) { nkeynes@736: code = sh4_translate_basic_block( sh4r.pc ); nkeynes@736: } nkeynes@953: } else if( sh4r.xlat_sh4_mode != XLAT_BLOCK_MODE(code) ) { nkeynes@954: if( !IS_IN_ICACHE(sh4r.pc) ) { nkeynes@954: /* If TLB is off, we may have gotten here without updating nkeynes@954: * the icache, so do it now. This should never fail, so... nkeynes@954: */ nkeynes@954: mmu_update_icache(sh4r.pc); nkeynes@954: assert( IS_IN_ICACHE(sh4r.pc) ); nkeynes@954: } nkeynes@953: code = sh4_translate_basic_block( sh4r.pc ); 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@914: * @param start VMA of the block start (which must already be in the icache) 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@914: xlat_current_block = xlat_start_block( GET_ICACHE_PHYS(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: if( eob - xlat_output < MAX_INSTRUCTION_SIZE ) { nkeynes@711: uint8_t *oldstart = xlat_current_block->code; 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@953: xlat_current_block->xlat_sh4_mode = sh4r.xlat_sh4_mode; 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@953: /** nkeynes@953: * Same as sh4_translate_run_recovery, but is used to recover from a taken nkeynes@953: * exception - that is, it fixes sh4r.spc rather than sh4r.pc nkeynes@953: */ nkeynes@953: void sh4_translate_run_exception_recovery( xlat_recovery_record_t recovery ) nkeynes@953: { nkeynes@953: sh4r.slice_cycle += (recovery->sh4_icount * sh4_cpu_period); nkeynes@953: sh4r.spc += (recovery->sh4_icount<<1); nkeynes@953: } nkeynes@953: nkeynes@740: void sh4_translate_exit_recover( ) nkeynes@586: { nkeynes@906: void *code = xlat_get_code_by_vma( sh4r.pc ); nkeynes@906: if( code != NULL ) { nkeynes@906: uint32_t size = xlat_get_code_size( code ); nkeynes@906: void *pc = xlat_get_native_pc( code, size ); nkeynes@906: if( pc != NULL ) { nkeynes@906: // could be null if we're not actually running inside the translator nkeynes@953: xlat_recovery_record_t recover = xlat_get_pre_recovery(code, pc); nkeynes@906: if( recover != NULL ) { nkeynes@906: // Can be null if there is no recovery necessary nkeynes@906: sh4_translate_run_recovery(recover); nkeynes@906: } nkeynes@736: } nkeynes@398: } nkeynes@586: } nkeynes@398: nkeynes@953: void sh4_translate_exception_exit_recover( ) nkeynes@953: { nkeynes@953: void *code = xlat_get_code_by_vma( sh4r.spc ); nkeynes@953: if( code != NULL ) { nkeynes@953: uint32_t size = xlat_get_code_size( code ); nkeynes@953: void *pc = xlat_get_native_pc( code, size ); nkeynes@953: if( pc != NULL ) { nkeynes@953: // could be null if we're not actually running inside the translator nkeynes@953: xlat_recovery_record_t recover = xlat_get_pre_recovery(code, pc); nkeynes@953: if( recover != NULL ) { nkeynes@953: // Can be null if there is no recovery necessary nkeynes@953: sh4_translate_run_exception_recovery(recover); nkeynes@953: } nkeynes@953: } nkeynes@953: } nkeynes@953: nkeynes@953: } nkeynes@953: nkeynes@905: void FASTCALL 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@905: void * FASTCALL 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@1014: if( IS_SYSCALL(vma) ) { 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: