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@586: nkeynes@1125: //#define SINGLESTEP 1 nkeynes@1125: 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@1112: event_schedule( EVENT_ENDTIMESLICE, nanosecs ); nkeynes@1112: for(;;) { nkeynes@736: if( sh4r.event_pending <= sh4r.slice_cycle ) { nkeynes@1187: sh4_handle_pending_events(); nkeynes@1112: if( sh4r.slice_cycle >= nanosecs ) nkeynes@1112: return nanosecs; nkeynes@736: } nkeynes@359: nkeynes@1112: if( IS_SYSCALL(sh4r.pc) ) { nkeynes@1112: uint32_t pc = sh4r.pc; nkeynes@1112: sh4r.pc = sh4r.pr; nkeynes@1112: sh4r.in_delay_slot = 0; nkeynes@1112: syscall_invoke( pc ); nkeynes@1112: } nkeynes@736: nkeynes@1187: void * (*code)() = xlat_get_code_by_vma( sh4r.pc ); nkeynes@1149: if( code != NULL ) { nkeynes@1149: while( sh4r.xlat_sh4_mode != XLAT_BLOCK_MODE(code) ) { nkeynes@1149: code = XLAT_BLOCK_CHAIN(code); nkeynes@1149: if( code == NULL ) { nkeynes@1149: code = sh4_translate_basic_block( sh4r.pc ); nkeynes@1149: break; nkeynes@1149: } nkeynes@1149: } nkeynes@1149: } else { nkeynes@936: code = sh4_translate_basic_block( sh4r.pc ); nkeynes@736: } nkeynes@1112: code(); nkeynes@359: } 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@1125: #ifdef SINGLESTEP nkeynes@1125: if( !done ) done = 2; nkeynes@1125: #endif 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@936: 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@941: /** nkeynes@941: * Same as sh4_translate_run_recovery, but is used to recover from a taken nkeynes@941: * exception - that is, it fixes sh4r.spc rather than sh4r.pc nkeynes@941: */ nkeynes@941: void sh4_translate_run_exception_recovery( xlat_recovery_record_t recovery ) nkeynes@941: { nkeynes@941: sh4r.slice_cycle += (recovery->sh4_icount * sh4_cpu_period); nkeynes@941: sh4r.spc += (recovery->sh4_icount<<1); nkeynes@941: } nkeynes@941: 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@949: 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@941: void sh4_translate_exception_exit_recover( ) nkeynes@941: { nkeynes@941: void *code = xlat_get_code_by_vma( sh4r.spc ); nkeynes@941: if( code != NULL ) { nkeynes@941: uint32_t size = xlat_get_code_size( code ); nkeynes@941: void *pc = xlat_get_native_pc( code, size ); nkeynes@941: if( pc != NULL ) { nkeynes@941: // could be null if we're not actually running inside the translator nkeynes@941: xlat_recovery_record_t recover = xlat_get_pre_recovery(code, pc); nkeynes@941: if( recover != NULL ) { nkeynes@941: // Can be null if there is no recovery necessary nkeynes@941: sh4_translate_run_exception_recovery(recover); nkeynes@941: } nkeynes@941: } nkeynes@941: } nkeynes@941: nkeynes@941: } nkeynes@941: 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: nkeynes@1091: /** nkeynes@1091: * Crashdump translation information. nkeynes@1091: * nkeynes@1091: * Print out the currently executing block (if any), in source and target nkeynes@1091: * assembly. nkeynes@1091: * nkeynes@1091: * Note: we want to be _really_ careful not to cause a second-level crash nkeynes@1091: * at this point (e.g. if the lookup tables are corrupted...) nkeynes@1091: */ nkeynes@1091: void sh4_translate_crashdump() nkeynes@1091: { nkeynes@1091: if( !IS_IN_ICACHE(sh4r.pc) ) { nkeynes@1091: /** If we're crashing due to an icache lookup failure, we'll probably nkeynes@1091: * hit this case - just complain and return. nkeynes@1091: */ nkeynes@1091: fprintf( stderr, "** SH4 PC not in current instruction region **\n" ); nkeynes@1091: return; nkeynes@1091: } nkeynes@1091: uint32_t pma = GET_ICACHE_PHYS(sh4r.pc); nkeynes@1091: void *code = xlat_get_code( pma ); nkeynes@1091: if( code == NULL ) { nkeynes@1091: fprintf( stderr, "** No translated block for current SH4 PC **\n" ); nkeynes@1091: return; nkeynes@1091: } nkeynes@1091: nkeynes@1091: /* Sanity check on the code pointer */ nkeynes@1091: if( !xlat_is_code_pointer(code) ) { nkeynes@1091: fprintf( stderr, "** Possibly corrupt translation cache **\n" ); nkeynes@1091: return; nkeynes@1091: } nkeynes@1091: nkeynes@1091: void *native_pc = xlat_get_native_pc( code, xlat_get_code_size(code) ); nkeynes@1091: sh4_translate_disasm_block( stderr, code, sh4r.pc, native_pc ); nkeynes@1091: } nkeynes@1094: nkeynes@1094: /** nkeynes@1094: * Dual-dump the translated block and original SH4 code for the basic block nkeynes@1094: * starting at sh4_pc. If there is no translated block, this prints an error nkeynes@1094: * and returns. nkeynes@1094: */ nkeynes@1094: void sh4_translate_dump_block( uint32_t sh4_pc ) nkeynes@1094: { nkeynes@1094: if( !IS_IN_ICACHE(sh4_pc) ) { nkeynes@1094: fprintf( stderr, "** Address %08x not in current instruction region **\n", sh4_pc ); nkeynes@1094: return; nkeynes@1094: } nkeynes@1094: uint32_t pma = GET_ICACHE_PHYS(sh4_pc); nkeynes@1094: void *code = xlat_get_code( pma ); nkeynes@1094: if( code == NULL ) { nkeynes@1094: fprintf( stderr, "** No translated block for address %08x **\n", sh4_pc ); nkeynes@1094: return; nkeynes@1094: } nkeynes@1094: sh4_translate_disasm_block( stderr, code, sh4_pc, NULL ); nkeynes@1094: } nkeynes@1094: