# HG changeset patch # User nkeynes # Date 1323688416 -36000 # Node ID ee6ce5804608990d3be4ed199cb8d20ac28bff37 # Parent dff55bdc4f46f7f9afc135749902327e8fcf95d7 Handle memory exceptions thrown while in shadow mode --- a/src/sh4/sh4.c Mon Dec 12 21:10:04 2011 +1000 +++ b/src/sh4/sh4.c Mon Dec 12 21:13:36 2011 +1000 @@ -550,6 +550,17 @@ sh4r.in_delay_slot = 0; } +void FASTCALL sh4_reraise_exception( sh4addr_t exception_pc ) +{ + sh4r.spc = sh4r.pc; + sh4r.ssr = sh4_read_sr(); + sh4r.sgr = sh4r.r[15]; + sh4r.pc = exception_pc; + sh4r.new_pc = sh4r.pc + 2; + sh4_write_sr( sh4r.ssr |SR_MD|SR_BL|SR_RB ); + sh4r.in_delay_slot = 0; +} + void FASTCALL sh4_accept_interrupt( void ) { uint32_t code = intc_accept_interrupt(); --- a/src/sh4/sh4core.h Mon Dec 12 21:10:04 2011 +1000 +++ b/src/sh4/sh4core.h Mon Dec 12 21:13:36 2011 +1000 @@ -243,6 +243,11 @@ void FASTCALL sh4_accept_interrupt( void ); /** + * Helper method to update the SH4 registers for an exception, without + * touching the MMU registers. Mainly for use in shadow mode. + */ +void FASTCALL sh4_reraise_exception( sh4addr_t exception_pc ); +/** * Complete the current instruction as part of a core exit. Prevents the * system from being left in an inconsistent state when an exit is * triggered during a memory write. --- a/src/sh4/sh4x86.in Mon Dec 12 21:10:04 2011 +1000 +++ b/src/sh4/sh4x86.in Mon Dec 12 21:13:36 2011 +1000 @@ -623,7 +623,7 @@ static void jump_next_block_fixed_pc( sh4addr_t pc ) { if( IS_IN_ICACHE(pc) ) { - if( sh4_x86.sh4_mode != SH4_MODE_UNKNOWN ) { + if( sh4_x86.sh4_mode != SH4_MODE_UNKNOWN && sh4_x86.end_callback == NULL ) { /* Fixed address, in cache, and fixed SH4 mode - generate a call to the * fetch-and-backpatch routine, which will replace the call with a branch */ emit_translate_and_backpatch(); @@ -2683,7 +2683,11 @@ COUNT_INST(I_FTRV); check_fpuen(); if( sh4_x86.double_prec == 0 ) { - if( sh4_x86.sse3_enabled ) { + if( sh4_x86.sse3_enabled && sh4_x86.begin_callback == NULL ) { + /* FIXME: For now, disable this inlining when we're running in shadow mode - + * it gives slightly different results from the emu core. Need to + * fix the precision so both give the right results. + */ MOVAPS_rbpdisp_xmm( REG_OFFSET(fr[1][0]), 1 ); // M1 M0 M3 M2 MOVAPS_rbpdisp_xmm( REG_OFFSET(fr[1][4]), 0 ); // M5 M4 M7 M6 MOVAPS_rbpdisp_xmm( REG_OFFSET(fr[1][8]), 3 ); // M9 M8 M11 M10 --- a/src/sh4/shadow.c Mon Dec 12 21:10:04 2011 +1000 +++ b/src/sh4/shadow.c Mon Dec 12 21:13:36 2011 +1000 @@ -24,9 +24,19 @@ #include "clock.h" #include "mem.h" #include "sh4/sh4.h" +#include "sh4/sh4core.h" #include "sh4/sh4trans.h" #include "sh4/mmu.h" +#ifdef HAVE_FRAME_ADDRESS +static FASTCALL __attribute__((noinline)) void *__first_arg(void *a, void *b) { return a; } +#define INIT_EXCEPTIONS(label) goto *__first_arg(&&fnstart,&&label); fnstart: +#define EXCEPTION_EXIT(exc) do{ *(((void **)__builtin_frame_address(0))+1) = exc; } while(0) +#else +#define INIT_EXCEPTIONS(label) +#define EXCEPTION_EXIT() sh4_core_exit(CORE_EXIT_EXCEPTION) +#endif + typedef enum { READ_LONG, WRITE_LONG, @@ -45,6 +55,7 @@ MemOp op; sh4addr_t addr; uint32_t value; + sh4addr_t exception_pc; }; static struct sh4_registers shadow_sh4r; @@ -59,7 +70,7 @@ #define IS_STORE_QUEUE(X) (((X)&0xFC000000) == 0xE0000000) -static void log_mem_op( MemOp op, sh4addr_t addr, uint32_t value ) +static void log_mem_op( MemOp op, sh4addr_t addr, uint32_t value, int exception ) { if( mem_log_posn == mem_log_size ) { struct mem_log_entry *tmp = realloc(mem_log, mem_log_size * sizeof(struct mem_log_entry) * 2); @@ -70,6 +81,11 @@ mem_log[mem_log_posn].op = op; mem_log[mem_log_posn].addr = addr; mem_log[mem_log_posn].value = value; + if( exception ) { + mem_log[mem_log_posn].exception_pc = sh4r.pc; + } else { + mem_log[mem_log_posn].exception_pc = -1; + } mem_log_posn++; } @@ -89,7 +105,7 @@ } } -static int32_t check_mem_op( MemOp op, sh4addr_t addr, uint32_t value ) +static int32_t check_mem_op( MemOp op, sh4addr_t addr, uint32_t value, int *exception ) { if( mem_check_posn >= mem_log_posn ) { fprintf( stderr, "Unexpected interpreter memory operation: " ); @@ -107,6 +123,14 @@ print_mem_op(stderr, op, addr, value ); abort(); } + + if( mem_log[mem_check_posn].exception_pc != -1 ) { + sh4_reraise_exception(mem_log[mem_check_posn].exception_pc); + *exception = 1; + } else { + *exception = 0; + } + return mem_log[mem_check_posn++].value; } @@ -172,120 +196,206 @@ if( memcmp( xsh4r->store_queue, esh4r->store_queue, sizeof(xsh4r->store_queue) ) != 0 ) { isgood = FALSE; fprintf( stderr, "Store queue Xlt =\n" ); - fwrite_dump( xsh4r->store_queue, sizeof(xsh4r->store_queue), stderr ); + fwrite_dump( (unsigned char *)xsh4r->store_queue, sizeof(xsh4r->store_queue), stderr ); fprintf( stderr, " Emu =\n" ); - fwrite_dump( esh4r->store_queue, sizeof(esh4r->store_queue), stderr ); + fwrite_dump( (unsigned char *)esh4r->store_queue, sizeof(esh4r->store_queue), stderr ); } return isgood; } -static FASTCALL int32_t log_read_long( sh4addr_t addr ) +static FASTCALL int32_t log_read_long( sh4addr_t addr, void *exc ) { - int32_t rv = real_address_space[addr>>12]->read_long(addr); - log_mem_op( READ_LONG, addr, rv ); + INIT_EXCEPTIONS(except); + int32_t rv = ((mem_read_exc_fn_t)real_address_space[addr>>12]->read_long)(addr, &&except); + log_mem_op( READ_LONG, addr, rv, 0 ); return rv; +except: + log_mem_op( READ_LONG, addr, rv, 1 ); + EXCEPTION_EXIT(exc); } -static FASTCALL int32_t log_read_word( sh4addr_t addr ) +static FASTCALL int32_t log_read_word( sh4addr_t addr, void *exc ) { - int32_t rv = real_address_space[addr>>12]->read_word(addr); - log_mem_op( READ_WORD, addr, rv ); + INIT_EXCEPTIONS(except); + int32_t rv = ((mem_read_exc_fn_t)real_address_space[addr>>12]->read_word)(addr, &&except); + log_mem_op( READ_WORD, addr, rv, 0 ); return rv; +except: + log_mem_op( READ_WORD, addr, rv, 1 ); + EXCEPTION_EXIT(exc); } -static FASTCALL int32_t log_read_byte( sh4addr_t addr ) +static FASTCALL int32_t log_read_byte( sh4addr_t addr, void *exc ) { - int32_t rv = real_address_space[addr>>12]->read_byte(addr); - log_mem_op( READ_BYTE, addr, rv ); + INIT_EXCEPTIONS(except); + int32_t rv = ((mem_read_exc_fn_t)real_address_space[addr>>12]->read_byte)(addr, &&except); + log_mem_op( READ_BYTE, addr, rv, 0 ); return rv; +except: + log_mem_op( READ_BYTE, addr, rv, 1 ); + EXCEPTION_EXIT(exc); } -static FASTCALL int32_t log_read_byte_for_write( sh4addr_t addr ) +static FASTCALL int32_t log_read_byte_for_write( sh4addr_t addr, void *exc ) { - int32_t rv = real_address_space[addr>>12]->read_byte_for_write(addr); - log_mem_op( READ_BYTE_FOR_WRITE, addr, rv ); + INIT_EXCEPTIONS(except); + int32_t rv = ((mem_read_exc_fn_t)real_address_space[addr>>12]->read_byte_for_write)(addr, &&except); + log_mem_op( READ_BYTE_FOR_WRITE, addr, rv, 0 ); return rv; +except: + log_mem_op( READ_BYTE_FOR_WRITE, addr, rv, 1 ); + EXCEPTION_EXIT(exc); } -static FASTCALL void log_write_long( sh4addr_t addr, uint32_t val ) +static FASTCALL void log_write_long( sh4addr_t addr, uint32_t val, void *exc ) { + INIT_EXCEPTIONS(except); + ((mem_write_exc_fn_t)real_address_space[addr>>12]->write_long)(addr, val, &&except); if( !IS_STORE_QUEUE(addr) ) - log_mem_op( WRITE_LONG, addr, val ); - real_address_space[addr>>12]->write_long(addr, val); + log_mem_op( WRITE_LONG, addr, val, 0 ); + return; +except: + if( !IS_STORE_QUEUE(addr) ) + log_mem_op( WRITE_LONG, addr, val, 1 ); + EXCEPTION_EXIT(exc); } -static FASTCALL void log_write_word( sh4addr_t addr, uint32_t val ) +static FASTCALL void log_write_word( sh4addr_t addr, uint32_t val, void *exc ) { + INIT_EXCEPTIONS(except); + ((mem_write_exc_fn_t)real_address_space[addr>>12]->write_word)(addr, val, &&except); if( !IS_STORE_QUEUE(addr) ) - log_mem_op( WRITE_WORD, addr, val ); - real_address_space[addr>>12]->write_word(addr, val); + log_mem_op( WRITE_WORD, addr, val, 0 ); + return; +except: + if( !IS_STORE_QUEUE(addr) ) + log_mem_op( WRITE_WORD, addr, val, 1 ); + EXCEPTION_EXIT(exc); } -static FASTCALL void log_write_byte( sh4addr_t addr, uint32_t val ) +static FASTCALL void log_write_byte( sh4addr_t addr, uint32_t val, void *exc ) { + INIT_EXCEPTIONS(except); + ((mem_write_exc_fn_t)real_address_space[addr>>12]->write_byte)(addr, val, &&except); if( !IS_STORE_QUEUE(addr) ) - log_mem_op( WRITE_BYTE, addr, val ); - real_address_space[addr>>12]->write_byte(addr, val); + log_mem_op( WRITE_BYTE, addr, val, 0 ); + return; +except: + if( !IS_STORE_QUEUE(addr) ) + log_mem_op( WRITE_BYTE, addr, val, 1 ); + EXCEPTION_EXIT(exc); } -static FASTCALL void log_prefetch( sh4addr_t addr ) +static FASTCALL void log_prefetch( sh4addr_t addr, void *exc ) { - log_mem_op( PREFETCH, addr, 0 ); - real_address_space[addr>>12]->prefetch(addr); + INIT_EXCEPTIONS(except); + ((mem_prefetch_exc_fn_t)real_address_space[addr>>12]->prefetch)(addr, &&except); + log_mem_op( PREFETCH, addr, 0, 0 ); + return; +except: + log_mem_op( PREFETCH, addr, 0, 1 ); + EXCEPTION_EXIT(exc); } -static FASTCALL int32_t check_read_long( sh4addr_t addr ) +static FASTCALL int32_t check_read_long( sh4addr_t addr, void *exc ) { - return check_mem_op( READ_LONG, addr, 0 ); + int except; + int32_t value = check_mem_op( READ_LONG, addr, 0, &except ); + if( except ) { + EXCEPTION_EXIT(exc); + } + return value; } -static FASTCALL int32_t check_read_word( sh4addr_t addr ) +static FASTCALL int32_t check_read_word( sh4addr_t addr, void *exc ) { - return check_mem_op( READ_WORD, addr, 0 ); + int except; + int32_t value = check_mem_op( READ_WORD, addr, 0, &except ); + if( except ) { + EXCEPTION_EXIT(exc); + } + return value; } -static FASTCALL int32_t check_read_byte( sh4addr_t addr ) +static FASTCALL int32_t check_read_byte( sh4addr_t addr, void *exc ) { - return check_mem_op( READ_BYTE, addr, 0 ); + int except; + int32_t value = check_mem_op( READ_BYTE, addr, 0, &except ); + if( except ) { + EXCEPTION_EXIT(exc); + } + return value; } -static FASTCALL int32_t check_read_byte_for_write( sh4addr_t addr ) +static FASTCALL int32_t check_read_byte_for_write( sh4addr_t addr, void *exc ) { - return check_mem_op( READ_BYTE_FOR_WRITE, addr, 0 ); + int except; + int32_t value = check_mem_op( READ_BYTE_FOR_WRITE, addr, 0, &except ); + if( except ) { + EXCEPTION_EXIT(exc); + } + return value; } -static FASTCALL void check_write_long( sh4addr_t addr, uint32_t value ) +static FASTCALL void check_write_long( sh4addr_t addr, uint32_t value, void *exc ) { - if( !IS_STORE_QUEUE(addr) ) - check_mem_op( WRITE_LONG, addr, value ); + if( !IS_STORE_QUEUE(addr) ) { + int except; + check_mem_op( WRITE_LONG, addr, value, &except ); + if( except ) { + EXCEPTION_EXIT(exc); + } + } else { + real_address_space[addr>>12]->write_long(addr, value); + } } -static FASTCALL void check_write_word( sh4addr_t addr, uint32_t value ) +static FASTCALL void check_write_word( sh4addr_t addr, uint32_t value, void *exc ) { - if( !IS_STORE_QUEUE(addr) ) - check_mem_op( WRITE_WORD, addr, value ); + if( !IS_STORE_QUEUE(addr) ) { + int except; + check_mem_op( WRITE_WORD, addr, value, &except ); + if( except ) { + EXCEPTION_EXIT(exc); + } + } else { + real_address_space[addr>>12]->write_word(addr, value); + } } -static FASTCALL void check_write_byte( sh4addr_t addr, uint32_t value ) +static FASTCALL void check_write_byte( sh4addr_t addr, uint32_t value, void *exc ) { - if( !IS_STORE_QUEUE(addr) ) - check_mem_op( WRITE_BYTE, addr, value ); + if( !IS_STORE_QUEUE(addr) ){ + int except; + check_mem_op( WRITE_BYTE, addr, value, &except ); + if( except ) { + EXCEPTION_EXIT(exc); + } + } else { + real_address_space[addr>>12]->write_byte(addr, value); + } } -static FASTCALL void check_prefetch( sh4addr_t addr ) +static FASTCALL void check_prefetch( sh4addr_t addr, void *exc ) { - check_mem_op( PREFETCH, addr, 0 ); + int except; + check_mem_op( PREFETCH, addr, 0, &except ); + if( except ) { + EXCEPTION_EXIT(exc); + } } -struct mem_region_fn log_fns = { log_read_long, log_write_long, - log_read_word, log_write_word, - log_read_byte, log_write_byte, - NULL, NULL, log_prefetch, log_read_byte_for_write }; +struct mem_region_fn log_fns = { + (mem_read_fn_t)log_read_long, (mem_write_fn_t)log_write_long, + (mem_read_fn_t)log_read_word, (mem_write_fn_t)log_write_word, + (mem_read_fn_t)log_read_byte, (mem_write_fn_t)log_write_byte, + NULL, NULL, (mem_prefetch_fn_t)log_prefetch, (mem_read_fn_t)log_read_byte_for_write }; -struct mem_region_fn check_fns = { check_read_long, check_write_long, - check_read_word, check_write_word, - check_read_byte, check_write_byte, - NULL, NULL, check_prefetch, check_read_byte_for_write }; +struct mem_region_fn check_fns = { + (mem_read_fn_t)check_read_long, (mem_write_fn_t)check_write_long, + (mem_read_fn_t)check_read_word, (mem_write_fn_t)check_write_word, + (mem_read_fn_t)check_read_byte, (mem_write_fn_t)check_write_byte, + NULL, NULL, (mem_prefetch_fn_t)check_prefetch, (mem_read_fn_t)check_read_byte_for_write };