nkeynes@1128: /** nkeynes@1128: * $Id$ nkeynes@1128: * nkeynes@1128: * SH4 shadow execution core - runs xlat + emu together and checks that the nkeynes@1128: * results are the same. nkeynes@1128: * nkeynes@1128: * Copyright (c) 2010 Nathan Keynes. nkeynes@1128: * nkeynes@1128: * This program is free software; you can redistribute it and/or modify nkeynes@1128: * it under the terms of the GNU General Public License as published by nkeynes@1128: * the Free Software Foundation; either version 2 of the License, or nkeynes@1128: * (at your option) any later version. nkeynes@1128: * nkeynes@1128: * This program is distributed in the hope that it will be useful, nkeynes@1128: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@1128: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@1128: * GNU General Public License for more details. nkeynes@1128: */ nkeynes@1128: nkeynes@1128: #include nkeynes@1128: #include nkeynes@1128: #include nkeynes@1128: nkeynes@1128: #include "clock.h" nkeynes@1128: #include "mem.h" nkeynes@1128: #include "sh4/sh4.h" nkeynes@1194: #include "sh4/sh4core.h" nkeynes@1128: #include "sh4/sh4trans.h" nkeynes@1128: #include "sh4/mmu.h" nkeynes@1128: nkeynes@1194: #ifdef HAVE_FRAME_ADDRESS nkeynes@1194: static FASTCALL __attribute__((noinline)) void *__first_arg(void *a, void *b) { return a; } nkeynes@1194: #define INIT_EXCEPTIONS(label) goto *__first_arg(&&fnstart,&&label); fnstart: nkeynes@1194: #define EXCEPTION_EXIT(exc) do{ *(((void **)__builtin_frame_address(0))+1) = exc; } while(0) nkeynes@1194: #else nkeynes@1194: #define INIT_EXCEPTIONS(label) nkeynes@1194: #define EXCEPTION_EXIT() sh4_core_exit(CORE_EXIT_EXCEPTION) nkeynes@1194: #endif nkeynes@1194: nkeynes@1128: typedef enum { nkeynes@1128: READ_LONG, nkeynes@1128: WRITE_LONG, nkeynes@1128: READ_WORD, nkeynes@1128: WRITE_WORD, nkeynes@1128: READ_BYTE, nkeynes@1128: WRITE_BYTE, nkeynes@1128: PREFETCH, nkeynes@1128: READ_BYTE_FOR_WRITE nkeynes@1128: } MemOp; nkeynes@1128: nkeynes@1128: char *memOpNames[] = { "read_long", "write_long", "read_word", "write_word", nkeynes@1128: "read_byte", "write_byte", "prefetch", "read_byte_for_write" }; nkeynes@1128: nkeynes@1128: struct mem_log_entry { nkeynes@1128: MemOp op; nkeynes@1128: sh4addr_t addr; nkeynes@1128: uint32_t value; nkeynes@1194: sh4addr_t exception_pc; nkeynes@1128: }; nkeynes@1128: nkeynes@1128: static struct sh4_registers shadow_sh4r; nkeynes@1128: static struct mem_region_fn **log_address_space; nkeynes@1128: static struct mem_region_fn **check_address_space; nkeynes@1128: static struct mem_region_fn **real_address_space; nkeynes@1128: nkeynes@1128: #define MEM_LOG_SIZE 4096 nkeynes@1128: static struct mem_log_entry *mem_log; nkeynes@1128: static uint32_t mem_log_posn, mem_log_size; nkeynes@1128: static uint32_t mem_check_posn; nkeynes@1128: nkeynes@1128: #define IS_STORE_QUEUE(X) (((X)&0xFC000000) == 0xE0000000) nkeynes@1128: nkeynes@1194: static void log_mem_op( MemOp op, sh4addr_t addr, uint32_t value, int exception ) nkeynes@1128: { nkeynes@1128: if( mem_log_posn == mem_log_size ) { nkeynes@1128: struct mem_log_entry *tmp = realloc(mem_log, mem_log_size * sizeof(struct mem_log_entry) * 2); nkeynes@1128: assert( tmp != NULL ); nkeynes@1128: mem_log_size *= 2; nkeynes@1128: mem_log = tmp; nkeynes@1128: } nkeynes@1128: mem_log[mem_log_posn].op = op; nkeynes@1128: mem_log[mem_log_posn].addr = addr; nkeynes@1128: mem_log[mem_log_posn].value = value; nkeynes@1194: if( exception ) { nkeynes@1194: mem_log[mem_log_posn].exception_pc = sh4r.pc; nkeynes@1194: } else { nkeynes@1194: mem_log[mem_log_posn].exception_pc = -1; nkeynes@1194: } nkeynes@1128: mem_log_posn++; nkeynes@1128: } nkeynes@1128: nkeynes@1128: static void print_mem_op( FILE *f, MemOp op, sh4addr_t addr, uint32_t value ) nkeynes@1128: { nkeynes@1128: if( op == WRITE_LONG || op == WRITE_WORD || op == WRITE_BYTE ) { nkeynes@1128: fprintf( f, "%s( %08X, %08X )\n", memOpNames[op], addr, value ); nkeynes@1128: } else { nkeynes@1128: fprintf( f, "%s( %08X )\n", memOpNames[op], addr ); nkeynes@1128: } nkeynes@1128: } nkeynes@1128: nkeynes@1128: static void dump_mem_ops() nkeynes@1128: { nkeynes@1128: for( unsigned i=0; i= mem_log_posn ) { nkeynes@1128: fprintf( stderr, "Unexpected interpreter memory operation: " ); nkeynes@1128: print_mem_op(stderr, op, addr, value ); nkeynes@1128: abort(); nkeynes@1128: } nkeynes@1128: if( mem_log[mem_check_posn].op != op || nkeynes@1128: mem_log[mem_check_posn].addr != addr || nkeynes@1128: (( op == WRITE_LONG || op == WRITE_WORD || op == WRITE_BYTE ) && nkeynes@1128: mem_log[mem_check_posn].value != value ) ) { nkeynes@1128: fprintf(stderr, "Memory operation mismatch. Translator: " ); nkeynes@1128: print_mem_op(stderr, mem_log[mem_check_posn].op, nkeynes@1128: mem_log[mem_check_posn].addr, mem_log[mem_check_posn].value ); nkeynes@1128: fprintf(stderr, "Emulator: "); nkeynes@1128: print_mem_op(stderr, op, addr, value ); nkeynes@1128: abort(); nkeynes@1128: } nkeynes@1194: nkeynes@1194: if( mem_log[mem_check_posn].exception_pc != -1 ) { nkeynes@1194: sh4_reraise_exception(mem_log[mem_check_posn].exception_pc); nkeynes@1194: *exception = 1; nkeynes@1194: } else { nkeynes@1194: *exception = 0; nkeynes@1194: } nkeynes@1194: nkeynes@1128: return mem_log[mem_check_posn++].value; nkeynes@1128: } nkeynes@1128: nkeynes@1128: #define CHECK_REG(sym, name) if( xsh4r->sym != esh4r->sym ) { \ nkeynes@1128: isgood = FALSE; fprintf( stderr, name " Xlt = %08X, Emu = %08X\n", xsh4r->sym, esh4r->sym ); } nkeynes@1128: nkeynes@1128: static gboolean check_registers( struct sh4_registers *xsh4r, struct sh4_registers *esh4r ) nkeynes@1128: { nkeynes@1128: gboolean isgood = TRUE; nkeynes@1128: for( unsigned i=0; i<16; i++ ) { nkeynes@1128: if( xsh4r->r[i] != esh4r->r[i] ) { nkeynes@1128: isgood = FALSE; nkeynes@1128: fprintf( stderr, "R%d Xlt = %08X, Emu = %08X\n", i, xsh4r->r[i], esh4r->r[i] ); nkeynes@1128: } nkeynes@1128: } nkeynes@1128: for( unsigned i=0; i<8; i++ ) { nkeynes@1128: if( xsh4r->r_bank[i] != esh4r->r_bank[i] ) { nkeynes@1128: isgood = FALSE; nkeynes@1128: fprintf( stderr, "R_BANK%d Xlt = %08X, Emu = %08X\n", i, xsh4r->r_bank[i], esh4r->r_bank[i] ); nkeynes@1128: } nkeynes@1128: } nkeynes@1128: for( unsigned i=0; i<16; i++ ) { nkeynes@1191: if( *((uint32_t *)&xsh4r->fr[0][i]) != *((uint32_t *)&esh4r->fr[0][i]) ) { nkeynes@1128: isgood = FALSE; nkeynes@1191: fprintf( stderr, "FR%d Xlt = %f (0x%08X), Emu = %f (0x%08X)\n", i, xsh4r->fr[0][i], nkeynes@1191: *((uint32_t *)&xsh4r->fr[0][i]), nkeynes@1191: esh4r->fr[0][i], nkeynes@1191: *((uint32_t *)&esh4r->fr[0][i]) nkeynes@1191: ); nkeynes@1128: } nkeynes@1128: } nkeynes@1128: for( unsigned i=0; i<16; i++ ) { nkeynes@1191: if( *((uint32_t *)&xsh4r->fr[1][i]) != *((uint32_t *)&esh4r->fr[1][i]) ) { nkeynes@1128: isgood = FALSE; nkeynes@1191: fprintf( stderr, "XF%d Xlt = %f (0x%08X), Emu = %f (0x%08X)\n", i, xsh4r->fr[1][i], nkeynes@1191: *((uint32_t *)&xsh4r->fr[1][i]), nkeynes@1191: esh4r->fr[1][i], nkeynes@1191: *((uint32_t *)&esh4r->fr[1][i]) nkeynes@1191: ); nkeynes@1128: } nkeynes@1128: } nkeynes@1128: nkeynes@1128: CHECK_REG(t, "T"); nkeynes@1128: CHECK_REG(m, "M"); nkeynes@1128: CHECK_REG(s, "S"); nkeynes@1128: CHECK_REG(q, "Q"); nkeynes@1128: CHECK_REG(pc, "PC"); nkeynes@1128: CHECK_REG(pr, "PR"); nkeynes@1128: CHECK_REG(sr, "SR"); nkeynes@1128: CHECK_REG(fpscr, "FPSCR"); nkeynes@1128: CHECK_REG(fpul.i, "FPUL"); nkeynes@1128: if( xsh4r->mac != esh4r->mac ) { nkeynes@1128: isgood = FALSE; nkeynes@1128: fprintf( stderr, "MAC Xlt = %016llX, Emu = %016llX\n", xsh4r->mac, esh4r->mac ); nkeynes@1128: } nkeynes@1128: CHECK_REG(gbr, "GBR"); nkeynes@1128: CHECK_REG(ssr, "SSR"); nkeynes@1128: CHECK_REG(spc, "SPC"); nkeynes@1128: CHECK_REG(sgr, "SGR"); nkeynes@1128: CHECK_REG(dbr, "DBR"); nkeynes@1128: CHECK_REG(vbr, "VBR"); nkeynes@1128: CHECK_REG(sh4_state, "STATE"); nkeynes@1128: if( memcmp( xsh4r->store_queue, esh4r->store_queue, sizeof(xsh4r->store_queue) ) != 0 ) { nkeynes@1128: isgood = FALSE; nkeynes@1128: fprintf( stderr, "Store queue Xlt =\n" ); nkeynes@1194: fwrite_dump( (unsigned char *)xsh4r->store_queue, sizeof(xsh4r->store_queue), stderr ); nkeynes@1128: fprintf( stderr, " Emu =\n" ); nkeynes@1194: fwrite_dump( (unsigned char *)esh4r->store_queue, sizeof(esh4r->store_queue), stderr ); nkeynes@1128: } nkeynes@1128: return isgood; nkeynes@1128: } nkeynes@1128: nkeynes@1194: static FASTCALL int32_t log_read_long( sh4addr_t addr, void *exc ) nkeynes@1128: { nkeynes@1194: INIT_EXCEPTIONS(except); nkeynes@1194: int32_t rv = ((mem_read_exc_fn_t)real_address_space[addr>>12]->read_long)(addr, &&except); nkeynes@1194: log_mem_op( READ_LONG, addr, rv, 0 ); nkeynes@1128: return rv; nkeynes@1194: except: nkeynes@1194: log_mem_op( READ_LONG, addr, rv, 1 ); nkeynes@1194: EXCEPTION_EXIT(exc); nkeynes@1128: } nkeynes@1128: nkeynes@1194: static FASTCALL int32_t log_read_word( sh4addr_t addr, void *exc ) nkeynes@1128: { nkeynes@1194: INIT_EXCEPTIONS(except); nkeynes@1194: int32_t rv = ((mem_read_exc_fn_t)real_address_space[addr>>12]->read_word)(addr, &&except); nkeynes@1194: log_mem_op( READ_WORD, addr, rv, 0 ); nkeynes@1128: return rv; nkeynes@1194: except: nkeynes@1194: log_mem_op( READ_WORD, addr, rv, 1 ); nkeynes@1194: EXCEPTION_EXIT(exc); nkeynes@1128: } nkeynes@1128: nkeynes@1194: static FASTCALL int32_t log_read_byte( sh4addr_t addr, void *exc ) nkeynes@1128: { nkeynes@1194: INIT_EXCEPTIONS(except); nkeynes@1194: int32_t rv = ((mem_read_exc_fn_t)real_address_space[addr>>12]->read_byte)(addr, &&except); nkeynes@1194: log_mem_op( READ_BYTE, addr, rv, 0 ); nkeynes@1128: return rv; nkeynes@1194: except: nkeynes@1194: log_mem_op( READ_BYTE, addr, rv, 1 ); nkeynes@1194: EXCEPTION_EXIT(exc); nkeynes@1128: } nkeynes@1128: nkeynes@1194: static FASTCALL int32_t log_read_byte_for_write( sh4addr_t addr, void *exc ) nkeynes@1128: { nkeynes@1194: INIT_EXCEPTIONS(except); nkeynes@1194: int32_t rv = ((mem_read_exc_fn_t)real_address_space[addr>>12]->read_byte_for_write)(addr, &&except); nkeynes@1194: log_mem_op( READ_BYTE_FOR_WRITE, addr, rv, 0 ); nkeynes@1128: return rv; nkeynes@1194: except: nkeynes@1194: log_mem_op( READ_BYTE_FOR_WRITE, addr, rv, 1 ); nkeynes@1194: EXCEPTION_EXIT(exc); nkeynes@1128: } nkeynes@1128: nkeynes@1194: static FASTCALL void log_write_long( sh4addr_t addr, uint32_t val, void *exc ) nkeynes@1128: { nkeynes@1194: INIT_EXCEPTIONS(except); nkeynes@1194: ((mem_write_exc_fn_t)real_address_space[addr>>12]->write_long)(addr, val, &&except); nkeynes@1128: if( !IS_STORE_QUEUE(addr) ) nkeynes@1194: log_mem_op( WRITE_LONG, addr, val, 0 ); nkeynes@1194: return; nkeynes@1194: except: nkeynes@1194: if( !IS_STORE_QUEUE(addr) ) nkeynes@1194: log_mem_op( WRITE_LONG, addr, val, 1 ); nkeynes@1194: EXCEPTION_EXIT(exc); nkeynes@1128: } nkeynes@1128: nkeynes@1194: static FASTCALL void log_write_word( sh4addr_t addr, uint32_t val, void *exc ) nkeynes@1128: { nkeynes@1194: INIT_EXCEPTIONS(except); nkeynes@1194: ((mem_write_exc_fn_t)real_address_space[addr>>12]->write_word)(addr, val, &&except); nkeynes@1128: if( !IS_STORE_QUEUE(addr) ) nkeynes@1194: log_mem_op( WRITE_WORD, addr, val, 0 ); nkeynes@1194: return; nkeynes@1194: except: nkeynes@1194: if( !IS_STORE_QUEUE(addr) ) nkeynes@1194: log_mem_op( WRITE_WORD, addr, val, 1 ); nkeynes@1194: EXCEPTION_EXIT(exc); nkeynes@1128: } nkeynes@1128: nkeynes@1194: static FASTCALL void log_write_byte( sh4addr_t addr, uint32_t val, void *exc ) nkeynes@1128: { nkeynes@1194: INIT_EXCEPTIONS(except); nkeynes@1194: ((mem_write_exc_fn_t)real_address_space[addr>>12]->write_byte)(addr, val, &&except); nkeynes@1128: if( !IS_STORE_QUEUE(addr) ) nkeynes@1194: log_mem_op( WRITE_BYTE, addr, val, 0 ); nkeynes@1194: return; nkeynes@1194: except: nkeynes@1194: if( !IS_STORE_QUEUE(addr) ) nkeynes@1194: log_mem_op( WRITE_BYTE, addr, val, 1 ); nkeynes@1194: EXCEPTION_EXIT(exc); nkeynes@1128: } nkeynes@1128: nkeynes@1194: static FASTCALL void log_prefetch( sh4addr_t addr, void *exc ) nkeynes@1128: { nkeynes@1194: INIT_EXCEPTIONS(except); nkeynes@1194: ((mem_prefetch_exc_fn_t)real_address_space[addr>>12]->prefetch)(addr, &&except); nkeynes@1194: log_mem_op( PREFETCH, addr, 0, 0 ); nkeynes@1194: return; nkeynes@1194: except: nkeynes@1194: log_mem_op( PREFETCH, addr, 0, 1 ); nkeynes@1194: EXCEPTION_EXIT(exc); nkeynes@1128: } nkeynes@1128: nkeynes@1194: static FASTCALL int32_t check_read_long( sh4addr_t addr, void *exc ) nkeynes@1128: { nkeynes@1194: int except; nkeynes@1194: int32_t value = check_mem_op( READ_LONG, addr, 0, &except ); nkeynes@1194: if( except ) { nkeynes@1194: EXCEPTION_EXIT(exc); nkeynes@1194: } nkeynes@1194: return value; nkeynes@1128: } nkeynes@1128: nkeynes@1194: static FASTCALL int32_t check_read_word( sh4addr_t addr, void *exc ) nkeynes@1128: { nkeynes@1194: int except; nkeynes@1194: int32_t value = check_mem_op( READ_WORD, addr, 0, &except ); nkeynes@1194: if( except ) { nkeynes@1194: EXCEPTION_EXIT(exc); nkeynes@1194: } nkeynes@1194: return value; nkeynes@1128: } nkeynes@1128: nkeynes@1194: static FASTCALL int32_t check_read_byte( sh4addr_t addr, void *exc ) nkeynes@1128: { nkeynes@1194: int except; nkeynes@1194: int32_t value = check_mem_op( READ_BYTE, addr, 0, &except ); nkeynes@1194: if( except ) { nkeynes@1194: EXCEPTION_EXIT(exc); nkeynes@1194: } nkeynes@1194: return value; nkeynes@1128: } nkeynes@1128: nkeynes@1194: static FASTCALL int32_t check_read_byte_for_write( sh4addr_t addr, void *exc ) nkeynes@1128: { nkeynes@1194: int except; nkeynes@1194: int32_t value = check_mem_op( READ_BYTE_FOR_WRITE, addr, 0, &except ); nkeynes@1194: if( except ) { nkeynes@1194: EXCEPTION_EXIT(exc); nkeynes@1194: } nkeynes@1194: return value; nkeynes@1128: } nkeynes@1128: nkeynes@1194: static FASTCALL void check_write_long( sh4addr_t addr, uint32_t value, void *exc ) nkeynes@1128: { nkeynes@1194: if( !IS_STORE_QUEUE(addr) ) { nkeynes@1194: int except; nkeynes@1194: check_mem_op( WRITE_LONG, addr, value, &except ); nkeynes@1194: if( except ) { nkeynes@1194: EXCEPTION_EXIT(exc); nkeynes@1194: } nkeynes@1194: } else { nkeynes@1194: real_address_space[addr>>12]->write_long(addr, value); nkeynes@1194: } nkeynes@1128: } nkeynes@1128: nkeynes@1194: static FASTCALL void check_write_word( sh4addr_t addr, uint32_t value, void *exc ) nkeynes@1128: { nkeynes@1194: if( !IS_STORE_QUEUE(addr) ) { nkeynes@1194: int except; nkeynes@1194: check_mem_op( WRITE_WORD, addr, value, &except ); nkeynes@1194: if( except ) { nkeynes@1194: EXCEPTION_EXIT(exc); nkeynes@1194: } nkeynes@1194: } else { nkeynes@1194: real_address_space[addr>>12]->write_word(addr, value); nkeynes@1194: } nkeynes@1128: } nkeynes@1128: nkeynes@1194: static FASTCALL void check_write_byte( sh4addr_t addr, uint32_t value, void *exc ) nkeynes@1128: { nkeynes@1194: if( !IS_STORE_QUEUE(addr) ){ nkeynes@1194: int except; nkeynes@1194: check_mem_op( WRITE_BYTE, addr, value, &except ); nkeynes@1194: if( except ) { nkeynes@1194: EXCEPTION_EXIT(exc); nkeynes@1194: } nkeynes@1194: } else { nkeynes@1194: real_address_space[addr>>12]->write_byte(addr, value); nkeynes@1194: } nkeynes@1128: } nkeynes@1128: nkeynes@1194: static FASTCALL void check_prefetch( sh4addr_t addr, void *exc ) nkeynes@1128: { nkeynes@1194: int except; nkeynes@1194: check_mem_op( PREFETCH, addr, 0, &except ); nkeynes@1194: if( except ) { nkeynes@1194: EXCEPTION_EXIT(exc); nkeynes@1194: } nkeynes@1128: } nkeynes@1128: nkeynes@1194: struct mem_region_fn log_fns = { nkeynes@1194: (mem_read_fn_t)log_read_long, (mem_write_fn_t)log_write_long, nkeynes@1194: (mem_read_fn_t)log_read_word, (mem_write_fn_t)log_write_word, nkeynes@1194: (mem_read_fn_t)log_read_byte, (mem_write_fn_t)log_write_byte, nkeynes@1194: NULL, NULL, (mem_prefetch_fn_t)log_prefetch, (mem_read_fn_t)log_read_byte_for_write }; nkeynes@1128: nkeynes@1194: struct mem_region_fn check_fns = { nkeynes@1194: (mem_read_fn_t)check_read_long, (mem_write_fn_t)check_write_long, nkeynes@1194: (mem_read_fn_t)check_read_word, (mem_write_fn_t)check_write_word, nkeynes@1194: (mem_read_fn_t)check_read_byte, (mem_write_fn_t)check_write_byte, nkeynes@1194: NULL, NULL, (mem_prefetch_fn_t)check_prefetch, (mem_read_fn_t)check_read_byte_for_write }; nkeynes@1128: nkeynes@1128: nkeynes@1128: nkeynes@1128: nkeynes@1128: void sh4_shadow_block_begin() nkeynes@1128: { nkeynes@1128: memcpy( &shadow_sh4r, &sh4r, sizeof(struct sh4_registers) ); nkeynes@1128: mem_log_posn = 0; nkeynes@1128: } nkeynes@1128: nkeynes@1128: void sh4_shadow_block_end() nkeynes@1128: { nkeynes@1128: struct sh4_registers temp_sh4r; nkeynes@1128: nkeynes@1128: /* Save the end registers, and restore the state back to the start */ nkeynes@1128: memcpy( &temp_sh4r, &sh4r, sizeof(struct sh4_registers) ); nkeynes@1128: memcpy( &sh4r, &shadow_sh4r, sizeof(struct sh4_registers) ); nkeynes@1128: nkeynes@1128: sh4_address_space = check_address_space; nkeynes@1128: mem_check_posn = 0; nkeynes@1128: sh4r.new_pc = sh4r.pc + 2; nkeynes@1128: while( sh4r.slice_cycle < temp_sh4r.slice_cycle ) { nkeynes@1128: sh4_execute_instruction(); nkeynes@1128: sh4r.slice_cycle += sh4_cpu_period; nkeynes@1128: } nkeynes@1128: nkeynes@1128: if( !check_registers( &temp_sh4r, &sh4r ) ) { nkeynes@1128: fprintf( stderr, "After executing block at %08X\n", shadow_sh4r.pc ); nkeynes@1191: fprintf( stderr, "Translated block was:\n" ); nkeynes@1191: sh4_translate_dump_block(shadow_sh4r.pc); nkeynes@1128: abort(); nkeynes@1128: } nkeynes@1128: if( mem_check_posn < mem_log_posn ) { nkeynes@1128: fprintf( stderr, "Additional translator memory operations:\n" ); nkeynes@1128: while( mem_check_posn < mem_log_posn ) { nkeynes@1128: print_mem_op( stderr, mem_log[mem_check_posn].op, mem_log[mem_check_posn].addr, mem_log[mem_check_posn].value ); nkeynes@1128: mem_check_posn++; nkeynes@1128: } nkeynes@1128: abort(); nkeynes@1128: } nkeynes@1128: sh4_address_space = real_address_space; nkeynes@1128: } nkeynes@1128: nkeynes@1128: nkeynes@1128: void sh4_shadow_init() nkeynes@1128: { nkeynes@1128: real_address_space = sh4_address_space; nkeynes@1128: log_address_space = mem_alloc_pages( sizeof(mem_region_fn_t) * 256 ); nkeynes@1128: check_address_space = mem_alloc_pages( sizeof(mem_region_fn_t) * 256 ); nkeynes@1128: for( unsigned i=0; i < (256 * 4096); i++ ) { nkeynes@1128: log_address_space[i] = &log_fns; nkeynes@1128: check_address_space[i] = &check_fns; nkeynes@1128: } nkeynes@1128: nkeynes@1128: mem_log_size = MEM_LOG_SIZE; nkeynes@1128: mem_log = malloc( mem_log_size * sizeof(struct mem_log_entry) ); nkeynes@1128: assert( mem_log != NULL ); nkeynes@1128: nkeynes@1128: sh4_translate_set_callbacks( sh4_shadow_block_begin, sh4_shadow_block_end ); nkeynes@1128: sh4_translate_set_fastmem( FALSE ); nkeynes@1128: sh4_translate_set_address_space( log_address_space, log_address_space ); nkeynes@1128: } nkeynes@1128: