nkeynes@359 | 1 | /**
|
nkeynes@561 | 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@571 | 20 | #include <setjmp.h>
|
nkeynes@430 | 21 | #include "eventq.h"
|
nkeynes@430 | 22 | #include "syscall.h"
|
nkeynes@571 | 23 | #include "clock.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@559 | 28 |
|
nkeynes@571 | 29 | static jmp_buf xlat_jmp_buf;
|
nkeynes@359 | 30 | /**
|
nkeynes@359 | 31 | * Execute a timeslice using translated code only (ie translate/execute loop)
|
nkeynes@359 | 32 | * Note this version does not support breakpoints
|
nkeynes@359 | 33 | */
|
nkeynes@359 | 34 | uint32_t sh4_xlat_run_slice( uint32_t nanosecs )
|
nkeynes@359 | 35 | {
|
nkeynes@359 | 36 | sh4r.slice_cycle = 0;
|
nkeynes@359 | 37 |
|
nkeynes@359 | 38 | if( sh4r.sh4_state != SH4_STATE_RUNNING ) {
|
nkeynes@359 | 39 | if( sh4r.event_pending < nanosecs ) {
|
nkeynes@359 | 40 | sh4r.sh4_state = SH4_STATE_RUNNING;
|
nkeynes@359 | 41 | sh4r.slice_cycle = sh4r.event_pending;
|
nkeynes@359 | 42 | }
|
nkeynes@359 | 43 | }
|
nkeynes@359 | 44 |
|
nkeynes@571 | 45 | int jmp = setjmp(xlat_jmp_buf);
|
nkeynes@571 | 46 |
|
nkeynes@408 | 47 | void * (*code)() = NULL;
|
nkeynes@368 | 48 | while( sh4r.slice_cycle < nanosecs ) {
|
nkeynes@408 | 49 | if( sh4r.event_pending <= sh4r.slice_cycle ) {
|
nkeynes@359 | 50 | if( sh4r.event_types & PENDING_EVENT ) {
|
nkeynes@359 | 51 | event_execute();
|
nkeynes@359 | 52 | }
|
nkeynes@359 | 53 | /* Eventq execute may (quite likely) deliver an immediate IRQ */
|
nkeynes@359 | 54 | if( sh4r.event_types & PENDING_IRQ ) {
|
nkeynes@359 | 55 | sh4_accept_interrupt();
|
nkeynes@408 | 56 | code = NULL;
|
nkeynes@359 | 57 | }
|
nkeynes@359 | 58 | }
|
nkeynes@408 | 59 |
|
nkeynes@417 | 60 | if( code == NULL ) {
|
nkeynes@408 | 61 | if( sh4r.pc > 0xFFFFFF00 ) {
|
nkeynes@408 | 62 | syscall_invoke( sh4r.pc );
|
nkeynes@408 | 63 | sh4r.in_delay_slot = 0;
|
nkeynes@408 | 64 | sh4r.pc = sh4r.pr;
|
nkeynes@408 | 65 | }
|
nkeynes@359 | 66 |
|
nkeynes@569 | 67 | code = xlat_get_code_by_vma( sh4r.pc );
|
nkeynes@408 | 68 | if( code == NULL ) {
|
nkeynes@408 | 69 | code = sh4_translate_basic_block( sh4r.pc );
|
nkeynes@408 | 70 | }
|
nkeynes@390 | 71 | }
|
nkeynes@417 | 72 | code = code();
|
nkeynes@359 | 73 | }
|
nkeynes@359 | 74 |
|
nkeynes@359 | 75 | if( sh4r.sh4_state != SH4_STATE_STANDBY ) {
|
nkeynes@359 | 76 | TMU_run_slice( nanosecs );
|
nkeynes@359 | 77 | SCIF_run_slice( nanosecs );
|
nkeynes@359 | 78 | }
|
nkeynes@359 | 79 | return nanosecs;
|
nkeynes@359 | 80 | }
|
nkeynes@359 | 81 |
|
nkeynes@359 | 82 | uint8_t *xlat_output;
|
nkeynes@571 | 83 | struct xlat_recovery_record xlat_recovery[MAX_RECOVERY_SIZE];
|
nkeynes@571 | 84 | uint32_t xlat_recovery_posn;
|
nkeynes@359 | 85 |
|
nkeynes@359 | 86 | /**
|
nkeynes@359 | 87 | * Translate a linear basic block, ie all instructions from the start address
|
nkeynes@359 | 88 | * (inclusive) until the next branch/jump instruction or the end of the page
|
nkeynes@359 | 89 | * is reached.
|
nkeynes@359 | 90 | * @return the address of the translated block
|
nkeynes@359 | 91 | * eg due to lack of buffer space.
|
nkeynes@359 | 92 | */
|
nkeynes@359 | 93 | void * sh4_translate_basic_block( sh4addr_t start )
|
nkeynes@359 | 94 | {
|
nkeynes@408 | 95 | sh4addr_t pc = start;
|
nkeynes@410 | 96 | sh4addr_t lastpc = (pc&0xFFFFF000)+0x1000;
|
nkeynes@359 | 97 | int done;
|
nkeynes@359 | 98 | xlat_cache_block_t block = xlat_start_block( start );
|
nkeynes@359 | 99 | xlat_output = (uint8_t *)block->code;
|
nkeynes@571 | 100 | xlat_recovery_posn = 0;
|
nkeynes@359 | 101 | uint8_t *eob = xlat_output + block->size;
|
nkeynes@408 | 102 | sh4_translate_begin_block(pc);
|
nkeynes@359 | 103 |
|
nkeynes@408 | 104 | do {
|
nkeynes@359 | 105 | if( eob - xlat_output < MAX_INSTRUCTION_SIZE ) {
|
nkeynes@359 | 106 | uint8_t *oldstart = block->code;
|
nkeynes@410 | 107 | block = xlat_extend_block( xlat_output - oldstart + MAX_INSTRUCTION_SIZE );
|
nkeynes@359 | 108 | xlat_output = block->code + (xlat_output - oldstart);
|
nkeynes@359 | 109 | eob = block->code + block->size;
|
nkeynes@359 | 110 | }
|
nkeynes@527 | 111 | done = sh4_translate_instruction( pc );
|
nkeynes@410 | 112 | assert( xlat_output <= eob );
|
nkeynes@359 | 113 | pc += 2;
|
nkeynes@410 | 114 | if ( pc >= lastpc ) {
|
nkeynes@410 | 115 | done = 2;
|
nkeynes@410 | 116 | }
|
nkeynes@408 | 117 | } while( !done );
|
nkeynes@408 | 118 | pc += (done - 2);
|
nkeynes@410 | 119 | if( eob - xlat_output < EPILOGUE_SIZE ) {
|
nkeynes@410 | 120 | uint8_t *oldstart = block->code;
|
nkeynes@410 | 121 | block = xlat_extend_block( xlat_output - oldstart + EPILOGUE_SIZE );
|
nkeynes@410 | 122 | xlat_output = block->code + (xlat_output - oldstart);
|
nkeynes@410 | 123 | }
|
nkeynes@368 | 124 | sh4_translate_end_block(pc);
|
nkeynes@571 | 125 |
|
nkeynes@571 | 126 | /* Write the recovery records onto the end of the code block */
|
nkeynes@571 | 127 | uint32_t recovery_size = sizeof(struct xlat_recovery_record)*xlat_recovery_posn;
|
nkeynes@571 | 128 | uint32_t finalsize = xlat_output - block->code + recovery_size;
|
nkeynes@571 | 129 | if( finalsize > block->size ) {
|
nkeynes@571 | 130 | uint8_t *oldstart = block->code;
|
nkeynes@571 | 131 | block = xlat_extend_block( finalsize );
|
nkeynes@571 | 132 | xlat_output = block->code + (xlat_output - oldstart);
|
nkeynes@571 | 133 | }
|
nkeynes@571 | 134 | memcpy( xlat_output, xlat_recovery, recovery_size);
|
nkeynes@571 | 135 | block->recover_table = (xlat_recovery_record_t)xlat_output;
|
nkeynes@571 | 136 | block->recover_table_size = xlat_recovery_posn;
|
nkeynes@571 | 137 | xlat_commit_block( finalsize, pc-start );
|
nkeynes@359 | 138 | return block->code;
|
nkeynes@359 | 139 | }
|
nkeynes@359 | 140 |
|
nkeynes@398 | 141 | /**
|
nkeynes@398 | 142 | * Translate a linear basic block to a temporary buffer, execute it, and return
|
nkeynes@398 | 143 | * the result of the execution. The translation is discarded.
|
nkeynes@398 | 144 | */
|
nkeynes@408 | 145 | void *sh4_translate_and_run( sh4addr_t start )
|
nkeynes@398 | 146 | {
|
nkeynes@430 | 147 | unsigned char buf[65536];
|
nkeynes@359 | 148 |
|
nkeynes@430 | 149 | sh4addr_t pc = start;
|
nkeynes@398 | 150 | int done;
|
nkeynes@398 | 151 | xlat_output = buf;
|
nkeynes@398 | 152 | uint8_t *eob = xlat_output + sizeof(buf);
|
nkeynes@398 | 153 |
|
nkeynes@408 | 154 | sh4_translate_begin_block(pc);
|
nkeynes@398 | 155 |
|
nkeynes@527 | 156 | while( (done = sh4_translate_instruction( pc )) == 0 ) {
|
nkeynes@398 | 157 | assert( (eob - xlat_output) >= MAX_INSTRUCTION_SIZE );
|
nkeynes@398 | 158 | pc += 2;
|
nkeynes@398 | 159 | }
|
nkeynes@398 | 160 | pc+=2;
|
nkeynes@398 | 161 | sh4_translate_end_block(pc);
|
nkeynes@398 | 162 |
|
nkeynes@408 | 163 | void * (*code)() = (void *)buf;
|
nkeynes@398 | 164 | return code();
|
nkeynes@398 | 165 | }
|
nkeynes@571 | 166 |
|
nkeynes@571 | 167 | /**
|
nkeynes@571 | 168 | * "Execute" the supplied recovery record. Currently this only updates
|
nkeynes@571 | 169 | * sh4r.pc and sh4r.slice_cycle according to the currently executing
|
nkeynes@571 | 170 | * instruction. In future this may be more sophisticated (ie will
|
nkeynes@571 | 171 | * call into generated code).
|
nkeynes@571 | 172 | */
|
nkeynes@571 | 173 | void sh4_translate_run_recovery( xlat_recovery_record_t recovery )
|
nkeynes@571 | 174 | {
|
nkeynes@571 | 175 | sh4r.slice_cycle += (recovery->sh4_icount * sh4_cpu_period);
|
nkeynes@571 | 176 | sh4r.pc += (recovery->sh4_icount<<1);
|
nkeynes@571 | 177 | }
|
nkeynes@571 | 178 |
|
nkeynes@571 | 179 | void sh4_translate_unwind_stack( gboolean abort_after, unwind_thunk_t thunk )
|
nkeynes@571 | 180 | {
|
nkeynes@571 | 181 | void *pc = xlat_get_native_pc();
|
nkeynes@571 | 182 | if( pc == NULL ) {
|
nkeynes@571 | 183 | // This should never happen - indicative of a bug somewhere.
|
nkeynes@571 | 184 | FATAL("Attempted to unwind stack, but translator is not running or stack is corrupt");
|
nkeynes@571 | 185 | }
|
nkeynes@571 | 186 | void *code = xlat_get_code( sh4r.pc );
|
nkeynes@571 | 187 | xlat_recovery_record_t recover = xlat_get_recovery(code, pc, TRUE);
|
nkeynes@571 | 188 | if( recover != NULL ) {
|
nkeynes@571 | 189 | // Can be null if there is no recovery necessary
|
nkeynes@571 | 190 | sh4_translate_run_recovery(recover);
|
nkeynes@571 | 191 | }
|
nkeynes@571 | 192 | if( thunk != NULL ) {
|
nkeynes@571 | 193 | thunk();
|
nkeynes@571 | 194 | }
|
nkeynes@571 | 195 | // finally longjmp back into sh4_xlat_run_slice
|
nkeynes@571 | 196 | longjmp(xlat_jmp_buf, 1);
|
nkeynes@571 | 197 | }
|
nkeynes@571 | 198 |
|
nkeynes@571 | 199 | /**
|
nkeynes@571 | 200 | * Exit the current block at the end of the current instruction, flush the
|
nkeynes@571 | 201 | * translation cache (completely) and return control to sh4_xlat_run_slice.
|
nkeynes@571 | 202 | *
|
nkeynes@571 | 203 | * As a special case, if the current instruction is actually the last
|
nkeynes@571 | 204 | * instruction in the block (ie it's in a delay slot), this function
|
nkeynes@571 | 205 | * returns to allow normal completion of the translation block. Otherwise
|
nkeynes@571 | 206 | * this function never returns.
|
nkeynes@571 | 207 | *
|
nkeynes@571 | 208 | * Must only be invoked (indirectly) from within translated code.
|
nkeynes@571 | 209 | */
|
nkeynes@571 | 210 | void sh4_translate_flush_cache()
|
nkeynes@571 | 211 | {
|
nkeynes@571 | 212 | void *pc = xlat_get_native_pc();
|
nkeynes@571 | 213 | if( pc == NULL ) {
|
nkeynes@571 | 214 | // This should never happen - indicative of a bug somewhere.
|
nkeynes@571 | 215 | FATAL("Attempted to unwind stack, but translator is not running or stack is corrupt");
|
nkeynes@571 | 216 | }
|
nkeynes@571 | 217 | void *code = xlat_get_code( sh4r.pc );
|
nkeynes@571 | 218 | xlat_recovery_record_t recover = xlat_get_recovery(code, pc, TRUE);
|
nkeynes@571 | 219 | if( recover != NULL ) {
|
nkeynes@571 | 220 | // Can be null if there is no recovery necessary
|
nkeynes@571 | 221 | sh4_translate_run_recovery(recover);
|
nkeynes@571 | 222 | xlat_flush_cache();
|
nkeynes@571 | 223 | longjmp(xlat_jmp_buf, 1);
|
nkeynes@571 | 224 | } else {
|
nkeynes@571 | 225 | xlat_flush_cache();
|
nkeynes@571 | 226 | return;
|
nkeynes@571 | 227 | }
|
nkeynes@571 | 228 | }
|
nkeynes@571 | 229 |
|
nkeynes@571 | 230 | void *xlat_get_code_by_vma( sh4vma_t vma )
|
nkeynes@571 | 231 | {
|
nkeynes@571 | 232 | void *result = NULL;
|
nkeynes@571 | 233 |
|
nkeynes@571 | 234 | if( !IS_IN_ICACHE(vma) ) {
|
nkeynes@571 | 235 | if( !mmu_update_icache(sh4r.pc) ) {
|
nkeynes@571 | 236 | // fault - off to the fault handler
|
nkeynes@571 | 237 | if( !mmu_update_icache(sh4r.pc) ) {
|
nkeynes@571 | 238 | // double fault - halt
|
nkeynes@571 | 239 | dreamcast_stop();
|
nkeynes@571 | 240 | ERROR( "Double fault - halting" );
|
nkeynes@571 | 241 | return NULL;
|
nkeynes@571 | 242 | }
|
nkeynes@571 | 243 | }
|
nkeynes@571 | 244 | }
|
nkeynes@571 | 245 | if( sh4_icache.page_vma != -1 ) {
|
nkeynes@571 | 246 | result = xlat_get_code( GET_ICACHE_PHYS(vma) );
|
nkeynes@571 | 247 | }
|
nkeynes@571 | 248 |
|
nkeynes@571 | 249 | return result;
|
nkeynes@571 | 250 | }
|
nkeynes@571 | 251 |
|