Search
lxdream.org :: lxdream/src/sh4/sh4trans.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/sh4trans.c
changeset 1300:d18488c8668b
prev1298:d0eb2307b847
next1301:b76840ccf94b
author nkeynes
date Wed May 27 08:46:29 2015 +1000 (5 years ago)
permissions -rw-r--r--
last change Add support for extracting the ELF symbol table and printing symbol names
alongside the SH4 disassembly
view annotate diff log raw
     1 /**
     2  * $Id$
     3  * 
     4  * SH4 translation core module. This part handles the non-target-specific
     5  * section of the translation.
     6  *
     7  * Copyright (c) 2005 Nathan Keynes.
     8  *
     9  * This program is free software; you can redistribute it and/or modify
    10  * it under the terms of the GNU General Public License as published by
    11  * the Free Software Foundation; either version 2 of the License, or
    12  * (at your option) any later version.
    13  *
    14  * This program is distributed in the hope that it will be useful,
    15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    17  * GNU General Public License for more details.
    18  */
    19 #include <assert.h>
    20 #include "eventq.h"
    21 #include "syscall.h"
    22 #include "clock.h"
    23 #include "dreamcast.h"
    24 #include "sh4/sh4core.h"
    25 #include "sh4/sh4trans.h"
    26 #include "sh4/sh4mmio.h"
    27 #include "sh4/sh4dasm.h"
    28 #include "sh4/mmu.h"
    29 #include "xlat/xltcache.h"
    30 #include "xlat/xlatdasm.h"
    32 //#define SINGLESTEP 1
    34 /**
    35  * Execute a timeslice using translated code only (ie translate/execute loop)
    36  */
    37 uint32_t sh4_translate_run_slice( uint32_t nanosecs ) 
    38 {
    39     event_schedule( EVENT_ENDTIMESLICE, nanosecs );
    40     for(;;) {
    41         if( sh4r.event_pending <= sh4r.slice_cycle ) {
    42             sh4_handle_pending_events();
    43             if( sh4r.slice_cycle >= nanosecs )
    44                 return nanosecs;
    45         }
    47         if( IS_SYSCALL(sh4r.pc) ) {
    48             uint32_t pc = sh4r.pc;
    49             sh4r.pc = sh4r.pr;
    50             sh4r.in_delay_slot = 0;
    51             syscall_invoke( pc );
    52         }
    54         void * (*code)() = xlat_get_code_by_vma( sh4r.pc );
    55         if( code != NULL ) {
    56             while( sh4r.xlat_sh4_mode != XLAT_BLOCK_MODE(code) ) {
    57                 code = XLAT_BLOCK_CHAIN(code);
    58                 if( code == NULL ) {
    59                     code = sh4_translate_basic_block( sh4r.pc );
    60                     break;
    61                 }
    62             }
    63         } else {
    64             code = sh4_translate_basic_block( sh4r.pc );
    65         }
    66         sh4_translate_enter(code);
    67     }
    68 }
    70 uint8_t *xlat_output;
    71 xlat_cache_block_t xlat_current_block;
    72 struct xlat_recovery_record xlat_recovery[MAX_RECOVERY_SIZE];
    73 uint32_t xlat_recovery_posn;
    75 void sh4_translate_add_recovery( uint32_t icount )
    76 {
    77     xlat_recovery[xlat_recovery_posn].xlat_offset = 
    78         ((uintptr_t)xlat_output) - ((uintptr_t)xlat_current_block->code);
    79     xlat_recovery[xlat_recovery_posn].sh4_icount = icount;
    80     xlat_recovery_posn++;
    81 }
    83 /**
    84  * Translate a linear basic block, ie all instructions from the start address
    85  * (inclusive) until the next branch/jump instruction or the end of the page
    86  * is reached.
    87  * @param start VMA of the block start (which must already be in the icache)
    88  * @return the address of the translated block
    89  * eg due to lack of buffer space.
    90  */
    91 void * sh4_translate_basic_block( sh4addr_t start )
    92 {
    93     sh4addr_t pc = start;
    94     sh4addr_t lastpc = (pc&0xFFFFF000)+0x1000;
    95     int done;
    96     xlat_current_block = xlat_start_block( GET_ICACHE_PHYS(start) );
    97     xlat_output = (uint8_t *)xlat_current_block->code;
    98     xlat_recovery_posn = 0;
    99     uint8_t *eob = xlat_output + xlat_current_block->size;
   101     if( GET_ICACHE_END() < lastpc ) {
   102         lastpc = GET_ICACHE_END();
   103     }
   105     sh4_translate_begin_block(pc);
   107     do {
   108         if( eob - xlat_output < MAX_INSTRUCTION_SIZE ) {
   109             uint8_t *oldstart = xlat_current_block->code;
   110             xlat_current_block = xlat_extend_block( xlat_output - oldstart + MAX_INSTRUCTION_SIZE );
   111             xlat_output = xlat_current_block->code + (xlat_output - oldstart);
   112             eob = xlat_current_block->code + xlat_current_block->size;
   113         }
   114         done = sh4_translate_instruction( pc ); 
   115         assert( xlat_output <= eob );
   116         pc += 2;
   117         if ( pc >= lastpc && done == 0 ) {
   118             done = 2;
   119         }
   120 #ifdef SINGLESTEP
   121         if( !done ) done = 2;
   122 #endif
   123     } while( !done );
   124     pc += (done - 2);
   126     // Add end-of-block recovery for post-instruction checks
   127     sh4_translate_add_recovery( (pc - start)>>1 ); 
   129     int epilogue_size = sh4_translate_end_block_size();
   130     uint32_t recovery_size = sizeof(struct xlat_recovery_record)*xlat_recovery_posn;
   131     uint32_t finalsize = (xlat_output - xlat_current_block->code) + epilogue_size + recovery_size;
   132     if( xlat_current_block->size < finalsize ) {
   133         uint8_t *oldstart = xlat_current_block->code;
   134         xlat_current_block = xlat_extend_block( finalsize );
   135         xlat_output = xlat_current_block->code + (xlat_output - oldstart);
   136     }	
   137     sh4_translate_end_block(pc);
   138     assert( xlat_output <= (xlat_current_block->code + xlat_current_block->size - recovery_size) );
   140     /* Write the recovery records onto the end of the code block */
   141     memcpy( xlat_output, xlat_recovery, recovery_size);
   142     xlat_current_block->recover_table_offset = xlat_output - (uint8_t *)xlat_current_block->code;
   143     xlat_current_block->recover_table_size = xlat_recovery_posn;
   144     xlat_current_block->xlat_sh4_mode = sh4r.xlat_sh4_mode;
   145     xlat_commit_block( finalsize, start, pc );
   146     return xlat_current_block->code;
   147 }
   149 /**
   150  * "Execute" the supplied recovery record. Currently this only updates
   151  * sh4r.pc and sh4r.slice_cycle according to the currently executing
   152  * instruction. In future this may be more sophisticated (ie will
   153  * call into generated code).
   154  */
   155 void sh4_translate_run_recovery( xlat_recovery_record_t recovery )
   156 {
   157     sh4r.slice_cycle += (recovery->sh4_icount * sh4_cpu_period);
   158     sh4r.pc += (recovery->sh4_icount<<1);
   159 }
   161 /**
   162  * Same as sh4_translate_run_recovery, but is used to recover from a taken
   163  * exception - that is, it fixes sh4r.spc rather than sh4r.pc
   164  */
   165 void sh4_translate_run_exception_recovery( xlat_recovery_record_t recovery )
   166 {
   167     sh4r.slice_cycle += (recovery->sh4_icount * sh4_cpu_period);
   168     sh4r.spc += (recovery->sh4_icount<<1);
   169 }    
   171 void sh4_translate_exit_recover( )
   172 {
   173     void *code = xlat_get_code_by_vma( sh4r.pc );
   174     if( code != NULL ) {
   175         uint32_t size = xlat_get_code_size( code );
   176         void *pc = xlat_get_native_pc( code, size );
   177         if( pc != NULL ) {
   178             // could be null if we're not actually running inside the translator
   179             xlat_recovery_record_t recover = xlat_get_pre_recovery(code, pc);
   180             if( recover != NULL ) {
   181                 // Can be null if there is no recovery necessary
   182                 sh4_translate_run_recovery(recover);
   183             }
   184         }
   185     }
   186 }
   188 void sh4_translate_exception_exit_recover( )
   189 {
   190     void *code = xlat_get_code_by_vma( sh4r.spc );
   191     if( code != NULL ) {
   192         uint32_t size = xlat_get_code_size( code );
   193         void *pc = xlat_get_native_pc( code, size );
   194         if( pc != NULL ) {
   195             // could be null if we're not actually running inside the translator
   196             xlat_recovery_record_t recover = xlat_get_pre_recovery(code, pc);
   197             if( recover != NULL ) {
   198                 // Can be null if there is no recovery necessary
   199                 sh4_translate_run_exception_recovery(recover);
   200             }
   201         }
   202     }
   204 }
   206 void FASTCALL sh4_translate_breakpoint_hit(uint32_t pc)
   207 {
   208     if( sh4_starting && sh4r.slice_cycle == 0 && pc == sh4r.pc ) {
   209         return;
   210     }
   211     sh4_core_exit( CORE_EXIT_BREAKPOINT );
   212 }
   214 void * FASTCALL xlat_get_code_by_vma( sh4vma_t vma )
   215 {
   216     void *result = NULL;
   218     if( IS_IN_ICACHE(vma) ) {
   219         return xlat_get_code( GET_ICACHE_PHYS(vma) );
   220     }
   222     if( IS_SYSCALL(vma) ) {
   223         // lxdream hook
   224         return NULL;
   225     }
   227     if( !mmu_update_icache(vma) ) {
   228         // fault - off to the fault handler
   229         if( !mmu_update_icache(sh4r.pc) ) {
   230             // double fault - halt
   231             ERROR( "Double fault - halting" );
   232             sh4_core_exit(CORE_EXIT_HALT);
   233             return NULL;
   234         }
   235     }
   237     assert( IS_IN_ICACHE(sh4r.pc) );
   238     result = xlat_get_code( GET_ICACHE_PHYS(sh4r.pc) );
   239     return result;
   240 }
   242 /**
   243  * Crashdump translation information.
   244  *
   245  * Print out the currently executing block (if any), in source and target
   246  * assembly.
   247  *
   248  * Note: we want to be _really_ careful not to cause a second-level crash
   249  * at this point (e.g. if the lookup tables are corrupted...)
   250  */
   251 void sh4_translate_crashdump()
   252 {
   253     if( !IS_IN_ICACHE(sh4r.pc) ) {
   254         /** If we're crashing due to an icache lookup failure, we'll probably
   255          * hit this case - just complain and return.
   256          */
   257         fprintf( stderr, "** SH4 PC not in current instruction region **\n" );
   258         return;
   259     }
   260     uint32_t pma = GET_ICACHE_PHYS(sh4r.pc);
   261     void *code = xlat_get_code( pma );
   262     if( code == NULL ) {
   263         fprintf( stderr, "** No translated block for current SH4 PC **\n" );
   264         return;
   265     }
   267     /* Sanity check on the code pointer */
   268     if( !xlat_is_code_pointer(code) ) {
   269         fprintf( stderr, "** Possibly corrupt translation cache **\n" );
   270         return;
   271     }
   273     void *native_pc = xlat_get_native_pc( code, xlat_get_code_size(code) );
   274     sh4_translate_disasm_block( stderr, code, sh4r.pc, native_pc );
   275 }
   277 /**
   278  * Dual-dump the translated block and original SH4 code for the basic block
   279  * starting at sh4_pc. If there is no translated block, this prints an error
   280  * and returns.
   281  */
   282 void sh4_translate_dump_block( uint32_t sh4_pc )
   283 {
   284     if( !IS_IN_ICACHE(sh4_pc) ) {
   285         fprintf( stderr, "** Address %08x not in current instruction region **\n", sh4_pc );
   286         return;
   287     }
   288     uint32_t pma = GET_ICACHE_PHYS(sh4_pc);
   289     void *code = xlat_get_code( pma );
   290     if( code == NULL ) {
   291         fprintf( stderr, "** No translated block for address %08x **\n", sh4_pc );
   292         return;
   293     }
   294     sh4_translate_disasm_block( stderr, code, sh4_pc, NULL );
   295 }
   297 void sh4_translate_dump_block_phys( uint32_t sh4_pma )
   298 {
   299     void *code = xlat_get_code( sh4_pma );
   300     if( code == NULL ) {
   301         fprintf( stderr, "** No translated block for address %08x **\n", sh4_pma );
   302         return;
   303     }
   304     sh4_translate_disasm_block( stderr, code, sh4_pma, NULL );
   306 }
   308 static struct xlat_symbol xlat_symbol_table[] = {
   309     { "sh4r+128", ((char *)&sh4r)+128 },
   310     { "sh4_cpu_period", &sh4_cpu_period },
   311     { "sh4_address_space", NULL },
   312     { "sh4_user_address_space", NULL },
   313     { "sh4_translate_breakpoint_hit", sh4_translate_breakpoint_hit },
   314     { "sh4_translate_link_block", sh4_translate_link_block },
   315     { "sh4_write_fpscr", sh4_write_fpscr },
   316     { "sh4_write_sr", sh4_write_sr },
   317     { "sh4_read_sr", sh4_read_sr },
   318     { "sh4_raise_exception", sh4_raise_exception },
   319     { "sh4_sleep", sh4_sleep },
   320     { "sh4_fsca", sh4_fsca },
   321     { "sh4_ftrv", sh4_ftrv },
   322     { "sh4_switch_fr_banks", sh4_switch_fr_banks },
   323     { "sh4_execute_instruction", sh4_execute_instruction },
   324     { "signsat48", signsat48 },
   325     { "xlat_get_code_by_vma", xlat_get_code_by_vma },
   326     { "xlat_get_code", xlat_get_code }
   327 };
   329 /**
   330  * Disassemble the given translated code block, and it's source code block
   331  * side-by-side. The current native pc will be marked if non-null.
   332  */
   333 void sh4_translate_disasm_block( FILE *out, void *code, sh4addr_t source_start, void *native_pc )
   334 {
   335     char buf[256];
   336     char op[256];
   338     xlat_symbol_table[2].ptr = sh4_address_space;
   339     xlat_symbol_table[3].ptr = sh4_user_address_space;
   340     xlat_disasm_init( xlat_symbol_table, sizeof(xlat_symbol_table)/sizeof(struct xlat_symbol) );
   342     uintptr_t target_start = (uintptr_t)code, target_pc;
   343     uintptr_t target_end = target_start + xlat_get_code_size(code);
   344     uint32_t source_pc = source_start;
   345     uint32_t source_end = source_pc;
   346     xlat_recovery_record_t source_recov_table = XLAT_RECOVERY_TABLE(code);
   347     xlat_recovery_record_t source_recov_end = source_recov_table + XLAT_BLOCK_FOR_CODE(code)->recover_table_size - 1;
   349     for( target_pc = target_start; target_pc < target_end;  ) {
   350         uintptr_t pc2 = xlat_disasm_instruction( target_pc, buf, sizeof(buf), op );
   352         if( source_recov_table < source_recov_end &&
   353             target_pc >= (target_start + source_recov_table->xlat_offset) ) {
   354             source_recov_table++;
   355             if( source_end < (source_start + (source_recov_table->sh4_icount)*2) )
   356                 source_end = source_start + (source_recov_table->sh4_icount)*2;
   357         }
   359         if( source_pc < source_end ) {
   360         	const char *sym = sh4_disasm_get_symbol(source_pc);
   361         	if( sym != 0 ) {
   362         		fprintf( out, "%s:\n", sym );
   363         	}
   364         }
   365 #if SIZEOF_VOID_P == 8
   366         fprintf( out, "%c%016lx: %-30s %-40s", (target_pc == (uintptr_t)native_pc ? '*' : ' '),
   367                       target_pc, op, buf );
   368 #else
   369         fprintf( out, "%c%08lx: %-30s %-40s", (target_pc == (uintptr_t)native_pc ? '*' : ' '),
   370                       target_pc, op, buf );
   371 #endif
   373         if( source_pc < source_end ) {
   374             uint32_t source_pc2 = sh4_disasm_instruction( source_pc, buf, sizeof(buf), op );
   375             fprintf( out, " %08X: %s  %s\n", source_pc, op, buf );
   376             source_pc = source_pc2;
   377         } else {
   378             fprintf( out, "\n" );
   379         }
   381         target_pc = pc2;
   382     }
   384     while( source_pc < source_end ) {
   385         uint32_t source_pc2 = sh4_disasm_instruction( source_pc, buf, sizeof(buf), op );
   386         fprintf( out, "%*c %08X: %s  %s\n", 72,' ', source_pc, op, buf );
   387         source_pc = source_pc2;
   388     }
   389 }
   392 void sh4_translate_dump_cache_by_activity( unsigned int topN )
   393 {
   394     struct xlat_block_ref blocks[topN];
   395     topN = xlat_get_cache_blocks_by_activity(blocks, topN);
   396     unsigned int i;
   397     for( i=0; i<topN; i++ ) {
   398         fprintf( stderr, "0x%08X (%p): %d \n", blocks[i].pc, blocks[i].block->code, blocks[i].block->active);
   399         sh4_translate_disasm_block( stderr, blocks[i].block->code, blocks[i].pc, NULL );
   400         fprintf( stderr, "\n" );
   401     }
   402 }
.