Search
lxdream.org :: lxdream/src/sh4/sh4trans.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/sh4trans.c
changeset 571:9bc09948d0f2
prev569:a1c49e1e8776
next577:a181aeacd6e8
author nkeynes
date Thu Jan 10 08:28:37 2008 +0000 (15 years ago)
branchlxdream-mmu
permissions -rw-r--r--
last change More MMU work in progess. Much better now...
file annotate diff log raw
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
.