nkeynes@1128 | 1 | /**
|
nkeynes@1128 | 2 | * $Id$
|
nkeynes@1128 | 3 | *
|
nkeynes@1128 | 4 | * SH4 shadow execution core - runs xlat + emu together and checks that the
|
nkeynes@1128 | 5 | * results are the same.
|
nkeynes@1128 | 6 | *
|
nkeynes@1128 | 7 | * Copyright (c) 2010 Nathan Keynes.
|
nkeynes@1128 | 8 | *
|
nkeynes@1128 | 9 | * This program is free software; you can redistribute it and/or modify
|
nkeynes@1128 | 10 | * it under the terms of the GNU General Public License as published by
|
nkeynes@1128 | 11 | * the Free Software Foundation; either version 2 of the License, or
|
nkeynes@1128 | 12 | * (at your option) any later version.
|
nkeynes@1128 | 13 | *
|
nkeynes@1128 | 14 | * This program is distributed in the hope that it will be useful,
|
nkeynes@1128 | 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nkeynes@1128 | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nkeynes@1128 | 17 | * GNU General Public License for more details.
|
nkeynes@1128 | 18 | */
|
nkeynes@1128 | 19 |
|
nkeynes@1128 | 20 | #include <stdlib.h>
|
nkeynes@1128 | 21 | #include <string.h>
|
nkeynes@1128 | 22 | #include <assert.h>
|
nkeynes@1128 | 23 |
|
nkeynes@1128 | 24 | #include "clock.h"
|
nkeynes@1128 | 25 | #include "mem.h"
|
nkeynes@1128 | 26 | #include "sh4/sh4.h"
|
nkeynes@1194 | 27 | #include "sh4/sh4core.h"
|
nkeynes@1128 | 28 | #include "sh4/sh4trans.h"
|
nkeynes@1128 | 29 | #include "sh4/mmu.h"
|
nkeynes@1128 | 30 |
|
nkeynes@1194 | 31 | #ifdef HAVE_FRAME_ADDRESS
|
nkeynes@1194 | 32 | static FASTCALL __attribute__((noinline)) void *__first_arg(void *a, void *b) { return a; }
|
nkeynes@1194 | 33 | #define INIT_EXCEPTIONS(label) goto *__first_arg(&&fnstart,&&label); fnstart:
|
nkeynes@1194 | 34 | #define EXCEPTION_EXIT(exc) do{ *(((void **)__builtin_frame_address(0))+1) = exc; } while(0)
|
nkeynes@1194 | 35 | #else
|
nkeynes@1194 | 36 | #define INIT_EXCEPTIONS(label)
|
nkeynes@1194 | 37 | #define EXCEPTION_EXIT() sh4_core_exit(CORE_EXIT_EXCEPTION)
|
nkeynes@1194 | 38 | #endif
|
nkeynes@1194 | 39 |
|
nkeynes@1128 | 40 | typedef enum {
|
nkeynes@1128 | 41 | READ_LONG,
|
nkeynes@1128 | 42 | WRITE_LONG,
|
nkeynes@1128 | 43 | READ_WORD,
|
nkeynes@1128 | 44 | WRITE_WORD,
|
nkeynes@1128 | 45 | READ_BYTE,
|
nkeynes@1128 | 46 | WRITE_BYTE,
|
nkeynes@1128 | 47 | PREFETCH,
|
nkeynes@1128 | 48 | READ_BYTE_FOR_WRITE
|
nkeynes@1128 | 49 | } MemOp;
|
nkeynes@1128 | 50 |
|
nkeynes@1128 | 51 | char *memOpNames[] = { "read_long", "write_long", "read_word", "write_word",
|
nkeynes@1128 | 52 | "read_byte", "write_byte", "prefetch", "read_byte_for_write" };
|
nkeynes@1128 | 53 |
|
nkeynes@1128 | 54 | struct mem_log_entry {
|
nkeynes@1128 | 55 | MemOp op;
|
nkeynes@1128 | 56 | sh4addr_t addr;
|
nkeynes@1128 | 57 | uint32_t value;
|
nkeynes@1194 | 58 | sh4addr_t exception_pc;
|
nkeynes@1128 | 59 | };
|
nkeynes@1128 | 60 |
|
nkeynes@1128 | 61 | static struct sh4_registers shadow_sh4r;
|
nkeynes@1128 | 62 | static struct mem_region_fn **log_address_space;
|
nkeynes@1128 | 63 | static struct mem_region_fn **check_address_space;
|
nkeynes@1128 | 64 | static struct mem_region_fn **real_address_space;
|
nkeynes@1128 | 65 |
|
nkeynes@1128 | 66 | #define MEM_LOG_SIZE 4096
|
nkeynes@1128 | 67 | static struct mem_log_entry *mem_log;
|
nkeynes@1128 | 68 | static uint32_t mem_log_posn, mem_log_size;
|
nkeynes@1128 | 69 | static uint32_t mem_check_posn;
|
nkeynes@1128 | 70 |
|
nkeynes@1128 | 71 | #define IS_STORE_QUEUE(X) (((X)&0xFC000000) == 0xE0000000)
|
nkeynes@1128 | 72 |
|
nkeynes@1194 | 73 | static void log_mem_op( MemOp op, sh4addr_t addr, uint32_t value, int exception )
|
nkeynes@1128 | 74 | {
|
nkeynes@1128 | 75 | if( mem_log_posn == mem_log_size ) {
|
nkeynes@1128 | 76 | struct mem_log_entry *tmp = realloc(mem_log, mem_log_size * sizeof(struct mem_log_entry) * 2);
|
nkeynes@1128 | 77 | assert( tmp != NULL );
|
nkeynes@1128 | 78 | mem_log_size *= 2;
|
nkeynes@1128 | 79 | mem_log = tmp;
|
nkeynes@1128 | 80 | }
|
nkeynes@1128 | 81 | mem_log[mem_log_posn].op = op;
|
nkeynes@1128 | 82 | mem_log[mem_log_posn].addr = addr;
|
nkeynes@1128 | 83 | mem_log[mem_log_posn].value = value;
|
nkeynes@1194 | 84 | if( exception ) {
|
nkeynes@1194 | 85 | mem_log[mem_log_posn].exception_pc = sh4r.pc;
|
nkeynes@1194 | 86 | } else {
|
nkeynes@1194 | 87 | mem_log[mem_log_posn].exception_pc = -1;
|
nkeynes@1194 | 88 | }
|
nkeynes@1128 | 89 | mem_log_posn++;
|
nkeynes@1128 | 90 | }
|
nkeynes@1128 | 91 |
|
nkeynes@1128 | 92 | static void print_mem_op( FILE *f, MemOp op, sh4addr_t addr, uint32_t value )
|
nkeynes@1128 | 93 | {
|
nkeynes@1128 | 94 | if( op == WRITE_LONG || op == WRITE_WORD || op == WRITE_BYTE ) {
|
nkeynes@1128 | 95 | fprintf( f, "%s( %08X, %08X )\n", memOpNames[op], addr, value );
|
nkeynes@1128 | 96 | } else {
|
nkeynes@1128 | 97 | fprintf( f, "%s( %08X )\n", memOpNames[op], addr );
|
nkeynes@1128 | 98 | }
|
nkeynes@1128 | 99 | }
|
nkeynes@1128 | 100 |
|
nkeynes@1128 | 101 | static void dump_mem_ops()
|
nkeynes@1128 | 102 | {
|
nkeynes@1128 | 103 | for( unsigned i=0; i<mem_log_posn; i++ ) {
|
nkeynes@1128 | 104 | print_mem_op( stderr, mem_log[i].op, mem_log[i].addr, mem_log[i].value );
|
nkeynes@1128 | 105 | }
|
nkeynes@1128 | 106 | }
|
nkeynes@1128 | 107 |
|
nkeynes@1194 | 108 | static int32_t check_mem_op( MemOp op, sh4addr_t addr, uint32_t value, int *exception )
|
nkeynes@1128 | 109 | {
|
nkeynes@1128 | 110 | if( mem_check_posn >= mem_log_posn ) {
|
nkeynes@1128 | 111 | fprintf( stderr, "Unexpected interpreter memory operation: " );
|
nkeynes@1128 | 112 | print_mem_op(stderr, op, addr, value );
|
nkeynes@1128 | 113 | abort();
|
nkeynes@1128 | 114 | }
|
nkeynes@1128 | 115 | if( mem_log[mem_check_posn].op != op ||
|
nkeynes@1128 | 116 | mem_log[mem_check_posn].addr != addr ||
|
nkeynes@1128 | 117 | (( op == WRITE_LONG || op == WRITE_WORD || op == WRITE_BYTE ) &&
|
nkeynes@1128 | 118 | mem_log[mem_check_posn].value != value ) ) {
|
nkeynes@1128 | 119 | fprintf(stderr, "Memory operation mismatch. Translator: " );
|
nkeynes@1128 | 120 | print_mem_op(stderr, mem_log[mem_check_posn].op,
|
nkeynes@1128 | 121 | mem_log[mem_check_posn].addr, mem_log[mem_check_posn].value );
|
nkeynes@1128 | 122 | fprintf(stderr, "Emulator: ");
|
nkeynes@1128 | 123 | print_mem_op(stderr, op, addr, value );
|
nkeynes@1128 | 124 | abort();
|
nkeynes@1128 | 125 | }
|
nkeynes@1194 | 126 |
|
nkeynes@1194 | 127 | if( mem_log[mem_check_posn].exception_pc != -1 ) {
|
nkeynes@1194 | 128 | sh4_reraise_exception(mem_log[mem_check_posn].exception_pc);
|
nkeynes@1194 | 129 | *exception = 1;
|
nkeynes@1194 | 130 | } else {
|
nkeynes@1194 | 131 | *exception = 0;
|
nkeynes@1194 | 132 | }
|
nkeynes@1194 | 133 |
|
nkeynes@1128 | 134 | return mem_log[mem_check_posn++].value;
|
nkeynes@1128 | 135 | }
|
nkeynes@1128 | 136 |
|
nkeynes@1128 | 137 | #define CHECK_REG(sym, name) if( xsh4r->sym != esh4r->sym ) { \
|
nkeynes@1128 | 138 | isgood = FALSE; fprintf( stderr, name " Xlt = %08X, Emu = %08X\n", xsh4r->sym, esh4r->sym ); }
|
nkeynes@1128 | 139 |
|
nkeynes@1128 | 140 | static gboolean check_registers( struct sh4_registers *xsh4r, struct sh4_registers *esh4r )
|
nkeynes@1128 | 141 | {
|
nkeynes@1128 | 142 | gboolean isgood = TRUE;
|
nkeynes@1128 | 143 | for( unsigned i=0; i<16; i++ ) {
|
nkeynes@1128 | 144 | if( xsh4r->r[i] != esh4r->r[i] ) {
|
nkeynes@1128 | 145 | isgood = FALSE;
|
nkeynes@1128 | 146 | fprintf( stderr, "R%d Xlt = %08X, Emu = %08X\n", i, xsh4r->r[i], esh4r->r[i] );
|
nkeynes@1128 | 147 | }
|
nkeynes@1128 | 148 | }
|
nkeynes@1128 | 149 | for( unsigned i=0; i<8; i++ ) {
|
nkeynes@1128 | 150 | if( xsh4r->r_bank[i] != esh4r->r_bank[i] ) {
|
nkeynes@1128 | 151 | isgood = FALSE;
|
nkeynes@1128 | 152 | fprintf( stderr, "R_BANK%d Xlt = %08X, Emu = %08X\n", i, xsh4r->r_bank[i], esh4r->r_bank[i] );
|
nkeynes@1128 | 153 | }
|
nkeynes@1128 | 154 | }
|
nkeynes@1128 | 155 | for( unsigned i=0; i<16; i++ ) {
|
nkeynes@1191 | 156 | if( *((uint32_t *)&xsh4r->fr[0][i]) != *((uint32_t *)&esh4r->fr[0][i]) ) {
|
nkeynes@1128 | 157 | isgood = FALSE;
|
nkeynes@1191 | 158 | fprintf( stderr, "FR%d Xlt = %f (0x%08X), Emu = %f (0x%08X)\n", i, xsh4r->fr[0][i],
|
nkeynes@1191 | 159 | *((uint32_t *)&xsh4r->fr[0][i]),
|
nkeynes@1191 | 160 | esh4r->fr[0][i],
|
nkeynes@1191 | 161 | *((uint32_t *)&esh4r->fr[0][i])
|
nkeynes@1191 | 162 | );
|
nkeynes@1128 | 163 | }
|
nkeynes@1128 | 164 | }
|
nkeynes@1128 | 165 | for( unsigned i=0; i<16; i++ ) {
|
nkeynes@1191 | 166 | if( *((uint32_t *)&xsh4r->fr[1][i]) != *((uint32_t *)&esh4r->fr[1][i]) ) {
|
nkeynes@1128 | 167 | isgood = FALSE;
|
nkeynes@1191 | 168 | fprintf( stderr, "XF%d Xlt = %f (0x%08X), Emu = %f (0x%08X)\n", i, xsh4r->fr[1][i],
|
nkeynes@1191 | 169 | *((uint32_t *)&xsh4r->fr[1][i]),
|
nkeynes@1191 | 170 | esh4r->fr[1][i],
|
nkeynes@1191 | 171 | *((uint32_t *)&esh4r->fr[1][i])
|
nkeynes@1191 | 172 | );
|
nkeynes@1128 | 173 | }
|
nkeynes@1128 | 174 | }
|
nkeynes@1128 | 175 |
|
nkeynes@1128 | 176 | CHECK_REG(t, "T");
|
nkeynes@1128 | 177 | CHECK_REG(m, "M");
|
nkeynes@1128 | 178 | CHECK_REG(s, "S");
|
nkeynes@1128 | 179 | CHECK_REG(q, "Q");
|
nkeynes@1128 | 180 | CHECK_REG(pc, "PC");
|
nkeynes@1128 | 181 | CHECK_REG(pr, "PR");
|
nkeynes@1128 | 182 | CHECK_REG(sr, "SR");
|
nkeynes@1128 | 183 | CHECK_REG(fpscr, "FPSCR");
|
nkeynes@1128 | 184 | CHECK_REG(fpul.i, "FPUL");
|
nkeynes@1128 | 185 | if( xsh4r->mac != esh4r->mac ) {
|
nkeynes@1128 | 186 | isgood = FALSE;
|
nkeynes@1128 | 187 | fprintf( stderr, "MAC Xlt = %016llX, Emu = %016llX\n", xsh4r->mac, esh4r->mac );
|
nkeynes@1128 | 188 | }
|
nkeynes@1128 | 189 | CHECK_REG(gbr, "GBR");
|
nkeynes@1128 | 190 | CHECK_REG(ssr, "SSR");
|
nkeynes@1128 | 191 | CHECK_REG(spc, "SPC");
|
nkeynes@1128 | 192 | CHECK_REG(sgr, "SGR");
|
nkeynes@1128 | 193 | CHECK_REG(dbr, "DBR");
|
nkeynes@1128 | 194 | CHECK_REG(vbr, "VBR");
|
nkeynes@1128 | 195 | CHECK_REG(sh4_state, "STATE");
|
nkeynes@1128 | 196 | if( memcmp( xsh4r->store_queue, esh4r->store_queue, sizeof(xsh4r->store_queue) ) != 0 ) {
|
nkeynes@1128 | 197 | isgood = FALSE;
|
nkeynes@1128 | 198 | fprintf( stderr, "Store queue Xlt =\n" );
|
nkeynes@1194 | 199 | fwrite_dump( (unsigned char *)xsh4r->store_queue, sizeof(xsh4r->store_queue), stderr );
|
nkeynes@1128 | 200 | fprintf( stderr, " Emu =\n" );
|
nkeynes@1194 | 201 | fwrite_dump( (unsigned char *)esh4r->store_queue, sizeof(esh4r->store_queue), stderr );
|
nkeynes@1128 | 202 | }
|
nkeynes@1128 | 203 | return isgood;
|
nkeynes@1128 | 204 | }
|
nkeynes@1128 | 205 |
|
nkeynes@1194 | 206 | static FASTCALL int32_t log_read_long( sh4addr_t addr, void *exc )
|
nkeynes@1128 | 207 | {
|
nkeynes@1194 | 208 | INIT_EXCEPTIONS(except);
|
nkeynes@1194 | 209 | int32_t rv = ((mem_read_exc_fn_t)real_address_space[addr>>12]->read_long)(addr, &&except);
|
nkeynes@1194 | 210 | log_mem_op( READ_LONG, addr, rv, 0 );
|
nkeynes@1128 | 211 | return rv;
|
nkeynes@1194 | 212 | except:
|
nkeynes@1194 | 213 | log_mem_op( READ_LONG, addr, rv, 1 );
|
nkeynes@1194 | 214 | EXCEPTION_EXIT(exc);
|
nkeynes@1128 | 215 | }
|
nkeynes@1128 | 216 |
|
nkeynes@1194 | 217 | static FASTCALL int32_t log_read_word( sh4addr_t addr, void *exc )
|
nkeynes@1128 | 218 | {
|
nkeynes@1194 | 219 | INIT_EXCEPTIONS(except);
|
nkeynes@1194 | 220 | int32_t rv = ((mem_read_exc_fn_t)real_address_space[addr>>12]->read_word)(addr, &&except);
|
nkeynes@1194 | 221 | log_mem_op( READ_WORD, addr, rv, 0 );
|
nkeynes@1128 | 222 | return rv;
|
nkeynes@1194 | 223 | except:
|
nkeynes@1194 | 224 | log_mem_op( READ_WORD, addr, rv, 1 );
|
nkeynes@1194 | 225 | EXCEPTION_EXIT(exc);
|
nkeynes@1128 | 226 | }
|
nkeynes@1128 | 227 |
|
nkeynes@1194 | 228 | static FASTCALL int32_t log_read_byte( sh4addr_t addr, void *exc )
|
nkeynes@1128 | 229 | {
|
nkeynes@1194 | 230 | INIT_EXCEPTIONS(except);
|
nkeynes@1194 | 231 | int32_t rv = ((mem_read_exc_fn_t)real_address_space[addr>>12]->read_byte)(addr, &&except);
|
nkeynes@1194 | 232 | log_mem_op( READ_BYTE, addr, rv, 0 );
|
nkeynes@1128 | 233 | return rv;
|
nkeynes@1194 | 234 | except:
|
nkeynes@1194 | 235 | log_mem_op( READ_BYTE, addr, rv, 1 );
|
nkeynes@1194 | 236 | EXCEPTION_EXIT(exc);
|
nkeynes@1128 | 237 | }
|
nkeynes@1128 | 238 |
|
nkeynes@1194 | 239 | static FASTCALL int32_t log_read_byte_for_write( sh4addr_t addr, void *exc )
|
nkeynes@1128 | 240 | {
|
nkeynes@1194 | 241 | INIT_EXCEPTIONS(except);
|
nkeynes@1194 | 242 | int32_t rv = ((mem_read_exc_fn_t)real_address_space[addr>>12]->read_byte_for_write)(addr, &&except);
|
nkeynes@1194 | 243 | log_mem_op( READ_BYTE_FOR_WRITE, addr, rv, 0 );
|
nkeynes@1128 | 244 | return rv;
|
nkeynes@1194 | 245 | except:
|
nkeynes@1194 | 246 | log_mem_op( READ_BYTE_FOR_WRITE, addr, rv, 1 );
|
nkeynes@1194 | 247 | EXCEPTION_EXIT(exc);
|
nkeynes@1128 | 248 | }
|
nkeynes@1128 | 249 |
|
nkeynes@1194 | 250 | static FASTCALL void log_write_long( sh4addr_t addr, uint32_t val, void *exc )
|
nkeynes@1128 | 251 | {
|
nkeynes@1194 | 252 | INIT_EXCEPTIONS(except);
|
nkeynes@1194 | 253 | ((mem_write_exc_fn_t)real_address_space[addr>>12]->write_long)(addr, val, &&except);
|
nkeynes@1128 | 254 | if( !IS_STORE_QUEUE(addr) )
|
nkeynes@1194 | 255 | log_mem_op( WRITE_LONG, addr, val, 0 );
|
nkeynes@1194 | 256 | return;
|
nkeynes@1194 | 257 | except:
|
nkeynes@1194 | 258 | if( !IS_STORE_QUEUE(addr) )
|
nkeynes@1194 | 259 | log_mem_op( WRITE_LONG, addr, val, 1 );
|
nkeynes@1194 | 260 | EXCEPTION_EXIT(exc);
|
nkeynes@1128 | 261 | }
|
nkeynes@1128 | 262 |
|
nkeynes@1194 | 263 | static FASTCALL void log_write_word( sh4addr_t addr, uint32_t val, void *exc )
|
nkeynes@1128 | 264 | {
|
nkeynes@1194 | 265 | INIT_EXCEPTIONS(except);
|
nkeynes@1194 | 266 | ((mem_write_exc_fn_t)real_address_space[addr>>12]->write_word)(addr, val, &&except);
|
nkeynes@1128 | 267 | if( !IS_STORE_QUEUE(addr) )
|
nkeynes@1194 | 268 | log_mem_op( WRITE_WORD, addr, val, 0 );
|
nkeynes@1194 | 269 | return;
|
nkeynes@1194 | 270 | except:
|
nkeynes@1194 | 271 | if( !IS_STORE_QUEUE(addr) )
|
nkeynes@1194 | 272 | log_mem_op( WRITE_WORD, addr, val, 1 );
|
nkeynes@1194 | 273 | EXCEPTION_EXIT(exc);
|
nkeynes@1128 | 274 | }
|
nkeynes@1128 | 275 |
|
nkeynes@1194 | 276 | static FASTCALL void log_write_byte( sh4addr_t addr, uint32_t val, void *exc )
|
nkeynes@1128 | 277 | {
|
nkeynes@1194 | 278 | INIT_EXCEPTIONS(except);
|
nkeynes@1194 | 279 | ((mem_write_exc_fn_t)real_address_space[addr>>12]->write_byte)(addr, val, &&except);
|
nkeynes@1128 | 280 | if( !IS_STORE_QUEUE(addr) )
|
nkeynes@1194 | 281 | log_mem_op( WRITE_BYTE, addr, val, 0 );
|
nkeynes@1194 | 282 | return;
|
nkeynes@1194 | 283 | except:
|
nkeynes@1194 | 284 | if( !IS_STORE_QUEUE(addr) )
|
nkeynes@1194 | 285 | log_mem_op( WRITE_BYTE, addr, val, 1 );
|
nkeynes@1194 | 286 | EXCEPTION_EXIT(exc);
|
nkeynes@1128 | 287 | }
|
nkeynes@1128 | 288 |
|
nkeynes@1194 | 289 | static FASTCALL void log_prefetch( sh4addr_t addr, void *exc )
|
nkeynes@1128 | 290 | {
|
nkeynes@1194 | 291 | INIT_EXCEPTIONS(except);
|
nkeynes@1194 | 292 | ((mem_prefetch_exc_fn_t)real_address_space[addr>>12]->prefetch)(addr, &&except);
|
nkeynes@1194 | 293 | log_mem_op( PREFETCH, addr, 0, 0 );
|
nkeynes@1194 | 294 | return;
|
nkeynes@1194 | 295 | except:
|
nkeynes@1194 | 296 | log_mem_op( PREFETCH, addr, 0, 1 );
|
nkeynes@1194 | 297 | EXCEPTION_EXIT(exc);
|
nkeynes@1128 | 298 | }
|
nkeynes@1128 | 299 |
|
nkeynes@1194 | 300 | static FASTCALL int32_t check_read_long( sh4addr_t addr, void *exc )
|
nkeynes@1128 | 301 | {
|
nkeynes@1194 | 302 | int except;
|
nkeynes@1194 | 303 | int32_t value = check_mem_op( READ_LONG, addr, 0, &except );
|
nkeynes@1194 | 304 | if( except ) {
|
nkeynes@1194 | 305 | EXCEPTION_EXIT(exc);
|
nkeynes@1194 | 306 | }
|
nkeynes@1194 | 307 | return value;
|
nkeynes@1128 | 308 | }
|
nkeynes@1128 | 309 |
|
nkeynes@1194 | 310 | static FASTCALL int32_t check_read_word( sh4addr_t addr, void *exc )
|
nkeynes@1128 | 311 | {
|
nkeynes@1194 | 312 | int except;
|
nkeynes@1194 | 313 | int32_t value = check_mem_op( READ_WORD, addr, 0, &except );
|
nkeynes@1194 | 314 | if( except ) {
|
nkeynes@1194 | 315 | EXCEPTION_EXIT(exc);
|
nkeynes@1194 | 316 | }
|
nkeynes@1194 | 317 | return value;
|
nkeynes@1128 | 318 | }
|
nkeynes@1128 | 319 |
|
nkeynes@1194 | 320 | static FASTCALL int32_t check_read_byte( sh4addr_t addr, void *exc )
|
nkeynes@1128 | 321 | {
|
nkeynes@1194 | 322 | int except;
|
nkeynes@1194 | 323 | int32_t value = check_mem_op( READ_BYTE, addr, 0, &except );
|
nkeynes@1194 | 324 | if( except ) {
|
nkeynes@1194 | 325 | EXCEPTION_EXIT(exc);
|
nkeynes@1194 | 326 | }
|
nkeynes@1194 | 327 | return value;
|
nkeynes@1128 | 328 | }
|
nkeynes@1128 | 329 |
|
nkeynes@1194 | 330 | static FASTCALL int32_t check_read_byte_for_write( sh4addr_t addr, void *exc )
|
nkeynes@1128 | 331 | {
|
nkeynes@1194 | 332 | int except;
|
nkeynes@1194 | 333 | int32_t value = check_mem_op( READ_BYTE_FOR_WRITE, addr, 0, &except );
|
nkeynes@1194 | 334 | if( except ) {
|
nkeynes@1194 | 335 | EXCEPTION_EXIT(exc);
|
nkeynes@1194 | 336 | }
|
nkeynes@1194 | 337 | return value;
|
nkeynes@1128 | 338 | }
|
nkeynes@1128 | 339 |
|
nkeynes@1194 | 340 | static FASTCALL void check_write_long( sh4addr_t addr, uint32_t value, void *exc )
|
nkeynes@1128 | 341 | {
|
nkeynes@1194 | 342 | if( !IS_STORE_QUEUE(addr) ) {
|
nkeynes@1194 | 343 | int except;
|
nkeynes@1194 | 344 | check_mem_op( WRITE_LONG, addr, value, &except );
|
nkeynes@1194 | 345 | if( except ) {
|
nkeynes@1194 | 346 | EXCEPTION_EXIT(exc);
|
nkeynes@1194 | 347 | }
|
nkeynes@1194 | 348 | } else {
|
nkeynes@1194 | 349 | real_address_space[addr>>12]->write_long(addr, value);
|
nkeynes@1194 | 350 | }
|
nkeynes@1128 | 351 | }
|
nkeynes@1128 | 352 |
|
nkeynes@1194 | 353 | static FASTCALL void check_write_word( sh4addr_t addr, uint32_t value, void *exc )
|
nkeynes@1128 | 354 | {
|
nkeynes@1194 | 355 | if( !IS_STORE_QUEUE(addr) ) {
|
nkeynes@1194 | 356 | int except;
|
nkeynes@1194 | 357 | check_mem_op( WRITE_WORD, addr, value, &except );
|
nkeynes@1194 | 358 | if( except ) {
|
nkeynes@1194 | 359 | EXCEPTION_EXIT(exc);
|
nkeynes@1194 | 360 | }
|
nkeynes@1194 | 361 | } else {
|
nkeynes@1194 | 362 | real_address_space[addr>>12]->write_word(addr, value);
|
nkeynes@1194 | 363 | }
|
nkeynes@1128 | 364 | }
|
nkeynes@1128 | 365 |
|
nkeynes@1194 | 366 | static FASTCALL void check_write_byte( sh4addr_t addr, uint32_t value, void *exc )
|
nkeynes@1128 | 367 | {
|
nkeynes@1194 | 368 | if( !IS_STORE_QUEUE(addr) ){
|
nkeynes@1194 | 369 | int except;
|
nkeynes@1194 | 370 | check_mem_op( WRITE_BYTE, addr, value, &except );
|
nkeynes@1194 | 371 | if( except ) {
|
nkeynes@1194 | 372 | EXCEPTION_EXIT(exc);
|
nkeynes@1194 | 373 | }
|
nkeynes@1194 | 374 | } else {
|
nkeynes@1194 | 375 | real_address_space[addr>>12]->write_byte(addr, value);
|
nkeynes@1194 | 376 | }
|
nkeynes@1128 | 377 | }
|
nkeynes@1128 | 378 |
|
nkeynes@1194 | 379 | static FASTCALL void check_prefetch( sh4addr_t addr, void *exc )
|
nkeynes@1128 | 380 | {
|
nkeynes@1194 | 381 | int except;
|
nkeynes@1194 | 382 | check_mem_op( PREFETCH, addr, 0, &except );
|
nkeynes@1194 | 383 | if( except ) {
|
nkeynes@1194 | 384 | EXCEPTION_EXIT(exc);
|
nkeynes@1194 | 385 | }
|
nkeynes@1128 | 386 | }
|
nkeynes@1128 | 387 |
|
nkeynes@1194 | 388 | struct mem_region_fn log_fns = {
|
nkeynes@1194 | 389 | (mem_read_fn_t)log_read_long, (mem_write_fn_t)log_write_long,
|
nkeynes@1194 | 390 | (mem_read_fn_t)log_read_word, (mem_write_fn_t)log_write_word,
|
nkeynes@1194 | 391 | (mem_read_fn_t)log_read_byte, (mem_write_fn_t)log_write_byte,
|
nkeynes@1194 | 392 | NULL, NULL, (mem_prefetch_fn_t)log_prefetch, (mem_read_fn_t)log_read_byte_for_write };
|
nkeynes@1128 | 393 |
|
nkeynes@1194 | 394 | struct mem_region_fn check_fns = {
|
nkeynes@1194 | 395 | (mem_read_fn_t)check_read_long, (mem_write_fn_t)check_write_long,
|
nkeynes@1194 | 396 | (mem_read_fn_t)check_read_word, (mem_write_fn_t)check_write_word,
|
nkeynes@1194 | 397 | (mem_read_fn_t)check_read_byte, (mem_write_fn_t)check_write_byte,
|
nkeynes@1194 | 398 | NULL, NULL, (mem_prefetch_fn_t)check_prefetch, (mem_read_fn_t)check_read_byte_for_write };
|
nkeynes@1128 | 399 |
|
nkeynes@1128 | 400 |
|
nkeynes@1128 | 401 |
|
nkeynes@1128 | 402 |
|
nkeynes@1128 | 403 | void sh4_shadow_block_begin()
|
nkeynes@1128 | 404 | {
|
nkeynes@1128 | 405 | memcpy( &shadow_sh4r, &sh4r, sizeof(struct sh4_registers) );
|
nkeynes@1128 | 406 | mem_log_posn = 0;
|
nkeynes@1128 | 407 | }
|
nkeynes@1128 | 408 |
|
nkeynes@1128 | 409 | void sh4_shadow_block_end()
|
nkeynes@1128 | 410 | {
|
nkeynes@1128 | 411 | struct sh4_registers temp_sh4r;
|
nkeynes@1128 | 412 |
|
nkeynes@1128 | 413 | /* Save the end registers, and restore the state back to the start */
|
nkeynes@1128 | 414 | memcpy( &temp_sh4r, &sh4r, sizeof(struct sh4_registers) );
|
nkeynes@1128 | 415 | memcpy( &sh4r, &shadow_sh4r, sizeof(struct sh4_registers) );
|
nkeynes@1128 | 416 |
|
nkeynes@1128 | 417 | sh4_address_space = check_address_space;
|
nkeynes@1128 | 418 | mem_check_posn = 0;
|
nkeynes@1128 | 419 | sh4r.new_pc = sh4r.pc + 2;
|
nkeynes@1128 | 420 | while( sh4r.slice_cycle < temp_sh4r.slice_cycle ) {
|
nkeynes@1128 | 421 | sh4_execute_instruction();
|
nkeynes@1128 | 422 | sh4r.slice_cycle += sh4_cpu_period;
|
nkeynes@1128 | 423 | }
|
nkeynes@1128 | 424 |
|
nkeynes@1128 | 425 | if( !check_registers( &temp_sh4r, &sh4r ) ) {
|
nkeynes@1128 | 426 | fprintf( stderr, "After executing block at %08X\n", shadow_sh4r.pc );
|
nkeynes@1191 | 427 | fprintf( stderr, "Translated block was:\n" );
|
nkeynes@1191 | 428 | sh4_translate_dump_block(shadow_sh4r.pc);
|
nkeynes@1128 | 429 | abort();
|
nkeynes@1128 | 430 | }
|
nkeynes@1128 | 431 | if( mem_check_posn < mem_log_posn ) {
|
nkeynes@1128 | 432 | fprintf( stderr, "Additional translator memory operations:\n" );
|
nkeynes@1128 | 433 | while( mem_check_posn < mem_log_posn ) {
|
nkeynes@1128 | 434 | print_mem_op( stderr, mem_log[mem_check_posn].op, mem_log[mem_check_posn].addr, mem_log[mem_check_posn].value );
|
nkeynes@1128 | 435 | mem_check_posn++;
|
nkeynes@1128 | 436 | }
|
nkeynes@1128 | 437 | abort();
|
nkeynes@1128 | 438 | }
|
nkeynes@1128 | 439 | sh4_address_space = real_address_space;
|
nkeynes@1128 | 440 | }
|
nkeynes@1128 | 441 |
|
nkeynes@1128 | 442 |
|
nkeynes@1128 | 443 | void sh4_shadow_init()
|
nkeynes@1128 | 444 | {
|
nkeynes@1128 | 445 | real_address_space = sh4_address_space;
|
nkeynes@1128 | 446 | log_address_space = mem_alloc_pages( sizeof(mem_region_fn_t) * 256 );
|
nkeynes@1128 | 447 | check_address_space = mem_alloc_pages( sizeof(mem_region_fn_t) * 256 );
|
nkeynes@1128 | 448 | for( unsigned i=0; i < (256 * 4096); i++ ) {
|
nkeynes@1128 | 449 | log_address_space[i] = &log_fns;
|
nkeynes@1128 | 450 | check_address_space[i] = &check_fns;
|
nkeynes@1128 | 451 | }
|
nkeynes@1128 | 452 |
|
nkeynes@1128 | 453 | mem_log_size = MEM_LOG_SIZE;
|
nkeynes@1128 | 454 | mem_log = malloc( mem_log_size * sizeof(struct mem_log_entry) );
|
nkeynes@1128 | 455 | assert( mem_log != NULL );
|
nkeynes@1128 | 456 |
|
nkeynes@1128 | 457 | sh4_translate_set_callbacks( sh4_shadow_block_begin, sh4_shadow_block_end );
|
nkeynes@1128 | 458 | sh4_translate_set_fastmem( FALSE );
|
nkeynes@1128 | 459 | sh4_translate_set_address_space( log_address_space, log_address_space );
|
nkeynes@1128 | 460 | }
|
nkeynes@1128 | 461 |
|