Search
lxdream.org :: lxdream/src/sh4/sh4trans.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/sh4trans.c
changeset 914:72abecf5a315
prev906:268ea359f884
next936:f394309c399a
next953:f4a156508ad1
author nkeynes
date Thu Dec 11 23:26:03 2008 +0000 (15 years ago)
permissions -rw-r--r--
last change Disable the generational translation cache - I've got no evidence that it
actually helps performance, and it simplifies things to get rid of it (in
particular, translated code doesn't have to worry about being moved now).
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
.