filename | src/sh4/sh4trans.c |
changeset | 914:72abecf5a315 |
prev | 906:268ea359f884 |
next | 936:f394309c399a |
next | 953:f4a156508ad1 |
author | nkeynes |
date | Wed Nov 05 10:05:08 2008 +0000 (15 years ago) |
permissions | -rw-r--r-- |
last change | Fix (extremely boneheaded) failure to convert pc to physical address before storing in the translation cache (in other words, the translation cache was effectively disabled for MMU code). MMU code is now about 3 times faster... |
file | annotate | diff | log | raw |
nkeynes@359 | 1 | /** |
nkeynes@586 | 2 | * $Id$ |
nkeynes@359 | 3 | * |
nkeynes@359 | 4 | * SH4 translation core module. This part handles the non-target-specific |
nkeynes@359 | 5 | * section of the translation. |
nkeynes@359 | 6 | * |
nkeynes@359 | 7 | * Copyright (c) 2005 Nathan Keynes. |
nkeynes@359 | 8 | * |
nkeynes@359 | 9 | * This program is free software; you can redistribute it and/or modify |
nkeynes@359 | 10 | * it under the terms of the GNU General Public License as published by |
nkeynes@359 | 11 | * the Free Software Foundation; either version 2 of the License, or |
nkeynes@359 | 12 | * (at your option) any later version. |
nkeynes@359 | 13 | * |
nkeynes@359 | 14 | * This program is distributed in the hope that it will be useful, |
nkeynes@359 | 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
nkeynes@359 | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
nkeynes@359 | 17 | * GNU General Public License for more details. |
nkeynes@359 | 18 | */ |
nkeynes@398 | 19 | #include <assert.h> |
nkeynes@430 | 20 | #include "eventq.h" |
nkeynes@430 | 21 | #include "syscall.h" |
nkeynes@586 | 22 | #include "clock.h" |
nkeynes@669 | 23 | #include "dreamcast.h" |
nkeynes@430 | 24 | #include "sh4/sh4core.h" |
nkeynes@430 | 25 | #include "sh4/sh4trans.h" |
nkeynes@430 | 26 | #include "sh4/xltcache.h" |
nkeynes@359 | 27 | |
nkeynes@586 | 28 | |
nkeynes@359 | 29 | /** |
nkeynes@359 | 30 | * Execute a timeslice using translated code only (ie translate/execute loop) |
nkeynes@359 | 31 | */ |
nkeynes@740 | 32 | uint32_t sh4_translate_run_slice( uint32_t nanosecs ) |
nkeynes@359 | 33 | { |
nkeynes@408 | 34 | void * (*code)() = NULL; |
nkeynes@368 | 35 | while( sh4r.slice_cycle < nanosecs ) { |
nkeynes@736 | 36 | if( sh4r.event_pending <= sh4r.slice_cycle ) { |
nkeynes@736 | 37 | if( sh4r.event_types & PENDING_EVENT ) { |
nkeynes@736 | 38 | event_execute(); |
nkeynes@736 | 39 | } |
nkeynes@736 | 40 | /* Eventq execute may (quite likely) deliver an immediate IRQ */ |
nkeynes@736 | 41 | if( sh4r.event_types & PENDING_IRQ ) { |
nkeynes@736 | 42 | sh4_accept_interrupt(); |
nkeynes@736 | 43 | code = NULL; |
nkeynes@736 | 44 | } |
nkeynes@736 | 45 | } |
nkeynes@359 | 46 | |
nkeynes@736 | 47 | if( code == NULL ) { |
nkeynes@736 | 48 | if( sh4r.pc > 0xFFFFFF00 ) { |
nkeynes@736 | 49 | syscall_invoke( sh4r.pc ); |
nkeynes@736 | 50 | sh4r.in_delay_slot = 0; |
nkeynes@736 | 51 | sh4r.pc = sh4r.pr; |
nkeynes@736 | 52 | } |
nkeynes@736 | 53 | |
nkeynes@736 | 54 | code = xlat_get_code_by_vma( sh4r.pc ); |
nkeynes@901 | 55 | if( code == NULL || (sh4r.fpscr & (FPSCR_PR|FPSCR_SZ)) != XLAT_BLOCK_FPSCR(code) ) { |
nkeynes@736 | 56 | code = sh4_translate_basic_block( sh4r.pc ); |
nkeynes@736 | 57 | } |
nkeynes@736 | 58 | } |
nkeynes@736 | 59 | code = code(); |
nkeynes@359 | 60 | } |
nkeynes@359 | 61 | return nanosecs; |
nkeynes@359 | 62 | } |
nkeynes@359 | 63 | |
nkeynes@359 | 64 | uint8_t *xlat_output; |
nkeynes@596 | 65 | xlat_cache_block_t xlat_current_block; |
nkeynes@586 | 66 | struct xlat_recovery_record xlat_recovery[MAX_RECOVERY_SIZE]; |
nkeynes@586 | 67 | uint32_t xlat_recovery_posn; |
nkeynes@359 | 68 | |
nkeynes@596 | 69 | void sh4_translate_add_recovery( uint32_t icount ) |
nkeynes@596 | 70 | { |
nkeynes@596 | 71 | xlat_recovery[xlat_recovery_posn].xlat_offset = |
nkeynes@736 | 72 | ((uintptr_t)xlat_output) - ((uintptr_t)xlat_current_block->code); |
nkeynes@596 | 73 | xlat_recovery[xlat_recovery_posn].sh4_icount = icount; |
nkeynes@596 | 74 | xlat_recovery_posn++; |
nkeynes@596 | 75 | } |
nkeynes@596 | 76 | |
nkeynes@359 | 77 | /** |
nkeynes@359 | 78 | * Translate a linear basic block, ie all instructions from the start address |
nkeynes@359 | 79 | * (inclusive) until the next branch/jump instruction or the end of the page |
nkeynes@359 | 80 | * is reached. |
nkeynes@914 | 81 | * @param start VMA of the block start (which must already be in the icache) |
nkeynes@359 | 82 | * @return the address of the translated block |
nkeynes@359 | 83 | * eg due to lack of buffer space. |
nkeynes@359 | 84 | */ |
nkeynes@359 | 85 | void * sh4_translate_basic_block( sh4addr_t start ) |
nkeynes@359 | 86 | { |
nkeynes@408 | 87 | sh4addr_t pc = start; |
nkeynes@410 | 88 | sh4addr_t lastpc = (pc&0xFFFFF000)+0x1000; |
nkeynes@586 | 89 | int done, i; |
nkeynes@914 | 90 | xlat_current_block = xlat_start_block( GET_ICACHE_PHYS(start) ); |
nkeynes@596 | 91 | xlat_output = (uint8_t *)xlat_current_block->code; |
nkeynes@586 | 92 | xlat_recovery_posn = 0; |
nkeynes@596 | 93 | uint8_t *eob = xlat_output + xlat_current_block->size; |
nkeynes@588 | 94 | |
nkeynes@588 | 95 | if( GET_ICACHE_END() < lastpc ) { |
nkeynes@711 | 96 | lastpc = GET_ICACHE_END(); |
nkeynes@588 | 97 | } |
nkeynes@588 | 98 | |
nkeynes@408 | 99 | sh4_translate_begin_block(pc); |
nkeynes@359 | 100 | |
nkeynes@408 | 101 | do { |
nkeynes@711 | 102 | /* check for breakpoints at this pc */ |
nkeynes@711 | 103 | for( i=0; i<sh4_breakpoint_count; i++ ) { |
nkeynes@711 | 104 | if( sh4_breakpoints[i].address == pc ) { |
nkeynes@711 | 105 | sh4_translate_emit_breakpoint(pc); |
nkeynes@711 | 106 | break; |
nkeynes@711 | 107 | } |
nkeynes@711 | 108 | } |
nkeynes@711 | 109 | if( eob - xlat_output < MAX_INSTRUCTION_SIZE ) { |
nkeynes@711 | 110 | uint8_t *oldstart = xlat_current_block->code; |
nkeynes@711 | 111 | xlat_current_block = xlat_extend_block( xlat_output - oldstart + MAX_INSTRUCTION_SIZE ); |
nkeynes@711 | 112 | xlat_output = xlat_current_block->code + (xlat_output - oldstart); |
nkeynes@711 | 113 | eob = xlat_current_block->code + xlat_current_block->size; |
nkeynes@711 | 114 | } |
nkeynes@711 | 115 | done = sh4_translate_instruction( pc ); |
nkeynes@711 | 116 | assert( xlat_output <= eob ); |
nkeynes@711 | 117 | pc += 2; |
nkeynes@711 | 118 | if ( pc >= lastpc ) { |
nkeynes@711 | 119 | done = 2; |
nkeynes@711 | 120 | } |
nkeynes@408 | 121 | } while( !done ); |
nkeynes@408 | 122 | pc += (done - 2); |
nkeynes@617 | 123 | |
nkeynes@617 | 124 | // Add end-of-block recovery for post-instruction checks |
nkeynes@617 | 125 | sh4_translate_add_recovery( (pc - start)>>1 ); |
nkeynes@617 | 126 | |
nkeynes@593 | 127 | int epilogue_size = sh4_translate_end_block_size(); |
nkeynes@593 | 128 | uint32_t recovery_size = sizeof(struct xlat_recovery_record)*xlat_recovery_posn; |
nkeynes@711 | 129 | uint32_t finalsize = (xlat_output - xlat_current_block->code) + epilogue_size + recovery_size; |
nkeynes@711 | 130 | if( xlat_current_block->size < finalsize ) { |
nkeynes@711 | 131 | uint8_t *oldstart = xlat_current_block->code; |
nkeynes@711 | 132 | xlat_current_block = xlat_extend_block( finalsize ); |
nkeynes@711 | 133 | xlat_output = xlat_current_block->code + (xlat_output - oldstart); |
nkeynes@410 | 134 | } |
nkeynes@368 | 135 | sh4_translate_end_block(pc); |
nkeynes@711 | 136 | assert( xlat_output <= (xlat_current_block->code + xlat_current_block->size - recovery_size) ); |
nkeynes@736 | 137 | |
nkeynes@586 | 138 | /* Write the recovery records onto the end of the code block */ |
nkeynes@586 | 139 | memcpy( xlat_output, xlat_recovery, recovery_size); |
nkeynes@596 | 140 | xlat_current_block->recover_table_offset = xlat_output - (uint8_t *)xlat_current_block->code; |
nkeynes@596 | 141 | xlat_current_block->recover_table_size = xlat_recovery_posn; |
nkeynes@901 | 142 | xlat_current_block->fpscr = sh4r.fpscr & (FPSCR_PR|FPSCR_SZ); |
nkeynes@901 | 143 | xlat_current_block->fpscr_mask = (FPSCR_PR|FPSCR_SZ); |
nkeynes@586 | 144 | xlat_commit_block( finalsize, pc-start ); |
nkeynes@596 | 145 | return xlat_current_block->code; |
nkeynes@359 | 146 | } |
nkeynes@359 | 147 | |
nkeynes@398 | 148 | /** |
nkeynes@586 | 149 | * "Execute" the supplied recovery record. Currently this only updates |
nkeynes@586 | 150 | * sh4r.pc and sh4r.slice_cycle according to the currently executing |
nkeynes@586 | 151 | * instruction. In future this may be more sophisticated (ie will |
nkeynes@586 | 152 | * call into generated code). |
nkeynes@398 | 153 | */ |
nkeynes@586 | 154 | void sh4_translate_run_recovery( xlat_recovery_record_t recovery ) |
nkeynes@398 | 155 | { |
nkeynes@586 | 156 | sh4r.slice_cycle += (recovery->sh4_icount * sh4_cpu_period); |
nkeynes@586 | 157 | sh4r.pc += (recovery->sh4_icount<<1); |
nkeynes@586 | 158 | } |
nkeynes@359 | 159 | |
nkeynes@740 | 160 | void sh4_translate_exit_recover( ) |
nkeynes@586 | 161 | { |
nkeynes@906 | 162 | void *code = xlat_get_code_by_vma( sh4r.pc ); |
nkeynes@906 | 163 | if( code != NULL ) { |
nkeynes@906 | 164 | uint32_t size = xlat_get_code_size( code ); |
nkeynes@906 | 165 | void *pc = xlat_get_native_pc( code, size ); |
nkeynes@906 | 166 | if( pc != NULL ) { |
nkeynes@906 | 167 | // could be null if we're not actually running inside the translator |
nkeynes@906 | 168 | xlat_recovery_record_t recover = xlat_get_post_recovery(code, pc, TRUE); |
nkeynes@906 | 169 | if( recover != NULL ) { |
nkeynes@906 | 170 | // Can be null if there is no recovery necessary |
nkeynes@906 | 171 | sh4_translate_run_recovery(recover); |
nkeynes@906 | 172 | } |
nkeynes@736 | 173 | } |
nkeynes@398 | 174 | } |
nkeynes@586 | 175 | } |
nkeynes@398 | 176 | |
nkeynes@905 | 177 | void FASTCALL sh4_translate_breakpoint_hit(uint32_t pc) |
nkeynes@591 | 178 | { |
nkeynes@591 | 179 | if( sh4_starting && sh4r.slice_cycle == 0 && pc == sh4r.pc ) { |
nkeynes@736 | 180 | return; |
nkeynes@591 | 181 | } |
nkeynes@740 | 182 | sh4_core_exit( CORE_EXIT_BREAKPOINT ); |
nkeynes@591 | 183 | } |
nkeynes@591 | 184 | |
nkeynes@586 | 185 | /** |
nkeynes@586 | 186 | * Exit the current block at the end of the current instruction, flush the |
nkeynes@586 | 187 | * translation cache (completely) and return control to sh4_xlat_run_slice. |
nkeynes@586 | 188 | * |
nkeynes@586 | 189 | * As a special case, if the current instruction is actually the last |
nkeynes@586 | 190 | * instruction in the block (ie it's in a delay slot), this function |
nkeynes@586 | 191 | * returns to allow normal completion of the translation block. Otherwise |
nkeynes@586 | 192 | * this function never returns. |
nkeynes@586 | 193 | * |
nkeynes@586 | 194 | * Must only be invoked (indirectly) from within translated code. |
nkeynes@586 | 195 | */ |
nkeynes@740 | 196 | gboolean sh4_translate_flush_cache() |
nkeynes@586 | 197 | { |
nkeynes@906 | 198 | void *code = xlat_get_code_by_vma( sh4r.pc ); |
nkeynes@906 | 199 | if( code != NULL ) { |
nkeynes@906 | 200 | uint32_t size = xlat_get_code_size( code ); |
nkeynes@906 | 201 | void *pc = xlat_get_native_pc( code, size ); |
nkeynes@906 | 202 | assert( pc != NULL ); |
nkeynes@586 | 203 | |
nkeynes@906 | 204 | xlat_recovery_record_t recover = xlat_get_post_recovery(code, pc, FALSE); |
nkeynes@906 | 205 | if( recover != NULL ) { |
nkeynes@906 | 206 | // Can be null if there is no recovery necessary |
nkeynes@906 | 207 | sh4_translate_run_recovery(recover); |
nkeynes@906 | 208 | xlat_flush_cache(); |
nkeynes@906 | 209 | return TRUE; |
nkeynes@906 | 210 | } else { |
nkeynes@906 | 211 | xlat_flush_cache(); |
nkeynes@906 | 212 | return FALSE; |
nkeynes@906 | 213 | } |
nkeynes@586 | 214 | } |
nkeynes@398 | 215 | } |
nkeynes@586 | 216 | |
nkeynes@905 | 217 | void * FASTCALL xlat_get_code_by_vma( sh4vma_t vma ) |
nkeynes@586 | 218 | { |
nkeynes@586 | 219 | void *result = NULL; |
nkeynes@586 | 220 | |
nkeynes@588 | 221 | if( IS_IN_ICACHE(vma) ) { |
nkeynes@736 | 222 | return xlat_get_code( GET_ICACHE_PHYS(vma) ); |
nkeynes@586 | 223 | } |
nkeynes@586 | 224 | |
nkeynes@588 | 225 | if( vma > 0xFFFFFF00 ) { |
nkeynes@736 | 226 | // lxdream hook |
nkeynes@736 | 227 | return NULL; |
nkeynes@588 | 228 | } |
nkeynes@588 | 229 | |
nkeynes@588 | 230 | if( !mmu_update_icache(vma) ) { |
nkeynes@736 | 231 | // fault - off to the fault handler |
nkeynes@736 | 232 | if( !mmu_update_icache(sh4r.pc) ) { |
nkeynes@736 | 233 | // double fault - halt |
nkeynes@736 | 234 | ERROR( "Double fault - halting" ); |
nkeynes@740 | 235 | sh4_core_exit(CORE_EXIT_HALT); |
nkeynes@736 | 236 | return NULL; |
nkeynes@736 | 237 | } |
nkeynes@588 | 238 | } |
nkeynes@588 | 239 | |
nkeynes@588 | 240 | assert( IS_IN_ICACHE(sh4r.pc) ); |
nkeynes@588 | 241 | result = xlat_get_code( GET_ICACHE_PHYS(sh4r.pc) ); |
nkeynes@586 | 242 | return result; |
nkeynes@586 | 243 | } |
nkeynes@586 | 244 |
.