Search
lxdream.org :: lxdream/src/sh4/shadow.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/shadow.c
changeset 1191:12fdf3aafcd4
prev1128:180abbb2a1f3
next1194:ee6ce5804608
author nkeynes
date Fri Dec 02 18:18:04 2011 +1000 (8 years ago)
permissions -rw-r--r--
last change SH4 shadow-mode tweaks
- Fix exceptions generated by the translator to account for the excepting
instruction(s) in the cycle counts.
- Compare floating point regs bitwise rather than with FP comparisons
(otherwise can fail due to nan != nan)
- Dump the translated block when we abort with an inconsistency
file annotate diff log raw
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@1128
    27
#include "sh4/sh4trans.h"
nkeynes@1128
    28
#include "sh4/mmu.h"
nkeynes@1128
    29
nkeynes@1128
    30
typedef enum {
nkeynes@1128
    31
    READ_LONG,
nkeynes@1128
    32
    WRITE_LONG,
nkeynes@1128
    33
    READ_WORD,
nkeynes@1128
    34
    WRITE_WORD,
nkeynes@1128
    35
    READ_BYTE,
nkeynes@1128
    36
    WRITE_BYTE,
nkeynes@1128
    37
    PREFETCH,
nkeynes@1128
    38
    READ_BYTE_FOR_WRITE
nkeynes@1128
    39
} MemOp;
nkeynes@1128
    40
nkeynes@1128
    41
char *memOpNames[] = { "read_long", "write_long", "read_word", "write_word",
nkeynes@1128
    42
        "read_byte", "write_byte", "prefetch", "read_byte_for_write" };
nkeynes@1128
    43
nkeynes@1128
    44
struct mem_log_entry {
nkeynes@1128
    45
    MemOp op;
nkeynes@1128
    46
    sh4addr_t addr;
nkeynes@1128
    47
    uint32_t value;
nkeynes@1128
    48
};
nkeynes@1128
    49
nkeynes@1128
    50
static struct sh4_registers shadow_sh4r;
nkeynes@1128
    51
static struct mem_region_fn **log_address_space;
nkeynes@1128
    52
static struct mem_region_fn **check_address_space;
nkeynes@1128
    53
static struct mem_region_fn **real_address_space;
nkeynes@1128
    54
nkeynes@1128
    55
#define MEM_LOG_SIZE 4096
nkeynes@1128
    56
static struct mem_log_entry *mem_log;
nkeynes@1128
    57
static uint32_t mem_log_posn, mem_log_size;
nkeynes@1128
    58
static uint32_t mem_check_posn;
nkeynes@1128
    59
nkeynes@1128
    60
#define IS_STORE_QUEUE(X) (((X)&0xFC000000) == 0xE0000000)
nkeynes@1128
    61
nkeynes@1128
    62
static void log_mem_op( MemOp op, sh4addr_t addr, uint32_t value )
nkeynes@1128
    63
{
nkeynes@1128
    64
    if( mem_log_posn == mem_log_size ) {
nkeynes@1128
    65
        struct mem_log_entry *tmp = realloc(mem_log, mem_log_size * sizeof(struct mem_log_entry) * 2);
nkeynes@1128
    66
        assert( tmp != NULL );
nkeynes@1128
    67
        mem_log_size *= 2;
nkeynes@1128
    68
        mem_log = tmp;
nkeynes@1128
    69
    }
nkeynes@1128
    70
    mem_log[mem_log_posn].op = op;
nkeynes@1128
    71
    mem_log[mem_log_posn].addr = addr;
nkeynes@1128
    72
    mem_log[mem_log_posn].value = value;
nkeynes@1128
    73
    mem_log_posn++;
nkeynes@1128
    74
}
nkeynes@1128
    75
nkeynes@1128
    76
static void print_mem_op( FILE *f, MemOp op, sh4addr_t addr, uint32_t value )
nkeynes@1128
    77
{
nkeynes@1128
    78
    if( op == WRITE_LONG || op == WRITE_WORD || op == WRITE_BYTE ) {
nkeynes@1128
    79
        fprintf( f, "%s( %08X, %08X )\n", memOpNames[op], addr, value );
nkeynes@1128
    80
    } else {
nkeynes@1128
    81
        fprintf( f, "%s( %08X )\n", memOpNames[op], addr );
nkeynes@1128
    82
    }
nkeynes@1128
    83
}
nkeynes@1128
    84
nkeynes@1128
    85
static void dump_mem_ops()
nkeynes@1128
    86
{
nkeynes@1128
    87
    for( unsigned i=0; i<mem_log_posn; i++ ) {
nkeynes@1128
    88
        print_mem_op( stderr, mem_log[i].op, mem_log[i].addr, mem_log[i].value );
nkeynes@1128
    89
    }
nkeynes@1128
    90
}
nkeynes@1128
    91
nkeynes@1128
    92
static int32_t check_mem_op( MemOp op, sh4addr_t addr, uint32_t value )
nkeynes@1128
    93
{
nkeynes@1128
    94
    if( mem_check_posn >= mem_log_posn ) {
nkeynes@1128
    95
        fprintf( stderr, "Unexpected interpreter memory operation: " );
nkeynes@1128
    96
        print_mem_op(stderr, op, addr, value );
nkeynes@1128
    97
        abort();
nkeynes@1128
    98
    }
nkeynes@1128
    99
    if( mem_log[mem_check_posn].op != op ||
nkeynes@1128
   100
        mem_log[mem_check_posn].addr != addr ||
nkeynes@1128
   101
        (( op == WRITE_LONG || op == WRITE_WORD || op == WRITE_BYTE ) &&
nkeynes@1128
   102
           mem_log[mem_check_posn].value != value ) ) {
nkeynes@1128
   103
        fprintf(stderr, "Memory operation mismatch. Translator: " );
nkeynes@1128
   104
        print_mem_op(stderr, mem_log[mem_check_posn].op,
nkeynes@1128
   105
                mem_log[mem_check_posn].addr, mem_log[mem_check_posn].value );
nkeynes@1128
   106
        fprintf(stderr, "Emulator: ");
nkeynes@1128
   107
        print_mem_op(stderr, op, addr, value );
nkeynes@1128
   108
        abort();
nkeynes@1128
   109
    }
nkeynes@1128
   110
    return mem_log[mem_check_posn++].value;
nkeynes@1128
   111
}
nkeynes@1128
   112
nkeynes@1128
   113
#define CHECK_REG(sym, name) if( xsh4r->sym != esh4r->sym ) { \
nkeynes@1128
   114
    isgood = FALSE; fprintf( stderr, name "  Xlt = %08X, Emu = %08X\n", xsh4r->sym, esh4r->sym ); }
nkeynes@1128
   115
nkeynes@1128
   116
static gboolean check_registers( struct sh4_registers *xsh4r, struct sh4_registers *esh4r )
nkeynes@1128
   117
{
nkeynes@1128
   118
    gboolean isgood = TRUE;
nkeynes@1128
   119
    for( unsigned i=0; i<16; i++ ) {
nkeynes@1128
   120
        if( xsh4r->r[i] != esh4r->r[i] ) {
nkeynes@1128
   121
            isgood = FALSE;
nkeynes@1128
   122
            fprintf( stderr, "R%d  Xlt = %08X, Emu = %08X\n", i, xsh4r->r[i], esh4r->r[i] );
nkeynes@1128
   123
        }
nkeynes@1128
   124
    }
nkeynes@1128
   125
    for( unsigned i=0; i<8; i++ ) {
nkeynes@1128
   126
        if( xsh4r->r_bank[i] != esh4r->r_bank[i] ) {
nkeynes@1128
   127
            isgood = FALSE;
nkeynes@1128
   128
            fprintf( stderr, "R_BANK%d  Xlt = %08X, Emu = %08X\n", i, xsh4r->r_bank[i], esh4r->r_bank[i] );
nkeynes@1128
   129
        }
nkeynes@1128
   130
    }
nkeynes@1128
   131
    for( unsigned i=0; i<16; i++ ) {
nkeynes@1191
   132
        if( *((uint32_t *)&xsh4r->fr[0][i]) != *((uint32_t *)&esh4r->fr[0][i]) ) {
nkeynes@1128
   133
            isgood = FALSE;
nkeynes@1191
   134
            fprintf( stderr, "FR%d  Xlt = %f (0x%08X), Emu = %f (0x%08X)\n", i, xsh4r->fr[0][i],
nkeynes@1191
   135
                    *((uint32_t *)&xsh4r->fr[0][i]),
nkeynes@1191
   136
                    esh4r->fr[0][i],
nkeynes@1191
   137
                    *((uint32_t *)&esh4r->fr[0][i])
nkeynes@1191
   138
                    );
nkeynes@1128
   139
        }
nkeynes@1128
   140
    }
nkeynes@1128
   141
    for( unsigned i=0; i<16; i++ ) {
nkeynes@1191
   142
        if( *((uint32_t *)&xsh4r->fr[1][i]) != *((uint32_t *)&esh4r->fr[1][i]) ) {
nkeynes@1128
   143
            isgood = FALSE;
nkeynes@1191
   144
            fprintf( stderr, "XF%d  Xlt = %f (0x%08X), Emu = %f (0x%08X)\n", i, xsh4r->fr[1][i],
nkeynes@1191
   145
                    *((uint32_t *)&xsh4r->fr[1][i]),
nkeynes@1191
   146
                    esh4r->fr[1][i],
nkeynes@1191
   147
                    *((uint32_t *)&esh4r->fr[1][i])
nkeynes@1191
   148
                    );
nkeynes@1128
   149
        }
nkeynes@1128
   150
    }
nkeynes@1128
   151
nkeynes@1128
   152
    CHECK_REG(t, "T");
nkeynes@1128
   153
    CHECK_REG(m, "M");
nkeynes@1128
   154
    CHECK_REG(s, "S");
nkeynes@1128
   155
    CHECK_REG(q, "Q");
nkeynes@1128
   156
    CHECK_REG(pc, "PC");
nkeynes@1128
   157
    CHECK_REG(pr, "PR");
nkeynes@1128
   158
    CHECK_REG(sr, "SR");
nkeynes@1128
   159
    CHECK_REG(fpscr, "FPSCR");
nkeynes@1128
   160
    CHECK_REG(fpul.i, "FPUL");
nkeynes@1128
   161
    if( xsh4r->mac != esh4r->mac ) {
nkeynes@1128
   162
        isgood = FALSE;
nkeynes@1128
   163
        fprintf( stderr, "MAC  Xlt = %016llX, Emu = %016llX\n", xsh4r->mac, esh4r->mac );
nkeynes@1128
   164
    }
nkeynes@1128
   165
    CHECK_REG(gbr, "GBR");
nkeynes@1128
   166
    CHECK_REG(ssr, "SSR");
nkeynes@1128
   167
    CHECK_REG(spc, "SPC");
nkeynes@1128
   168
    CHECK_REG(sgr, "SGR");
nkeynes@1128
   169
    CHECK_REG(dbr, "DBR");
nkeynes@1128
   170
    CHECK_REG(vbr, "VBR");
nkeynes@1128
   171
    CHECK_REG(sh4_state, "STATE");
nkeynes@1128
   172
    if( memcmp( xsh4r->store_queue, esh4r->store_queue, sizeof(xsh4r->store_queue) ) != 0 ) {
nkeynes@1128
   173
        isgood = FALSE;
nkeynes@1128
   174
        fprintf( stderr, "Store queue  Xlt =\n" );
nkeynes@1128
   175
        fwrite_dump( xsh4r->store_queue, sizeof(xsh4r->store_queue), stderr );
nkeynes@1128
   176
        fprintf( stderr, "             Emu =\n" );
nkeynes@1128
   177
        fwrite_dump( esh4r->store_queue, sizeof(esh4r->store_queue), stderr );
nkeynes@1128
   178
    }
nkeynes@1128
   179
    return isgood;
nkeynes@1128
   180
}
nkeynes@1128
   181
nkeynes@1128
   182
static FASTCALL int32_t log_read_long( sh4addr_t addr )
nkeynes@1128
   183
{
nkeynes@1128
   184
    int32_t rv = real_address_space[addr>>12]->read_long(addr);
nkeynes@1128
   185
    log_mem_op( READ_LONG, addr, rv );
nkeynes@1128
   186
    return rv;
nkeynes@1128
   187
}
nkeynes@1128
   188
nkeynes@1128
   189
static FASTCALL int32_t log_read_word( sh4addr_t addr )
nkeynes@1128
   190
{
nkeynes@1128
   191
    int32_t rv = real_address_space[addr>>12]->read_word(addr);
nkeynes@1128
   192
    log_mem_op( READ_WORD, addr, rv );
nkeynes@1128
   193
    return rv;
nkeynes@1128
   194
}
nkeynes@1128
   195
nkeynes@1128
   196
static FASTCALL int32_t log_read_byte( sh4addr_t addr )
nkeynes@1128
   197
{
nkeynes@1128
   198
    int32_t rv = real_address_space[addr>>12]->read_byte(addr);
nkeynes@1128
   199
    log_mem_op( READ_BYTE, addr, rv );
nkeynes@1128
   200
    return rv;
nkeynes@1128
   201
}
nkeynes@1128
   202
nkeynes@1128
   203
static FASTCALL int32_t log_read_byte_for_write( sh4addr_t addr )
nkeynes@1128
   204
{
nkeynes@1128
   205
    int32_t rv = real_address_space[addr>>12]->read_byte_for_write(addr);
nkeynes@1128
   206
    log_mem_op( READ_BYTE_FOR_WRITE, addr, rv );
nkeynes@1128
   207
    return rv;
nkeynes@1128
   208
}
nkeynes@1128
   209
nkeynes@1128
   210
static FASTCALL void log_write_long( sh4addr_t addr, uint32_t val )
nkeynes@1128
   211
{
nkeynes@1128
   212
    if( !IS_STORE_QUEUE(addr) )
nkeynes@1128
   213
        log_mem_op( WRITE_LONG, addr, val );
nkeynes@1128
   214
    real_address_space[addr>>12]->write_long(addr, val);
nkeynes@1128
   215
}
nkeynes@1128
   216
nkeynes@1128
   217
static FASTCALL void log_write_word( sh4addr_t addr, uint32_t val )
nkeynes@1128
   218
{
nkeynes@1128
   219
    if( !IS_STORE_QUEUE(addr) )
nkeynes@1128
   220
        log_mem_op( WRITE_WORD, addr, val );
nkeynes@1128
   221
    real_address_space[addr>>12]->write_word(addr, val);
nkeynes@1128
   222
}
nkeynes@1128
   223
nkeynes@1128
   224
static FASTCALL void log_write_byte( sh4addr_t addr, uint32_t val )
nkeynes@1128
   225
{
nkeynes@1128
   226
    if( !IS_STORE_QUEUE(addr) )
nkeynes@1128
   227
        log_mem_op( WRITE_BYTE, addr, val );
nkeynes@1128
   228
    real_address_space[addr>>12]->write_byte(addr, val);
nkeynes@1128
   229
}
nkeynes@1128
   230
nkeynes@1128
   231
static FASTCALL void log_prefetch( sh4addr_t addr )
nkeynes@1128
   232
{
nkeynes@1128
   233
    log_mem_op( PREFETCH, addr, 0 );
nkeynes@1128
   234
    real_address_space[addr>>12]->prefetch(addr);
nkeynes@1128
   235
}
nkeynes@1128
   236
nkeynes@1128
   237
static FASTCALL int32_t check_read_long( sh4addr_t addr )
nkeynes@1128
   238
{
nkeynes@1128
   239
    return check_mem_op( READ_LONG, addr, 0 );
nkeynes@1128
   240
}
nkeynes@1128
   241
nkeynes@1128
   242
static FASTCALL int32_t check_read_word( sh4addr_t addr )
nkeynes@1128
   243
{
nkeynes@1128
   244
    return check_mem_op( READ_WORD, addr, 0 );
nkeynes@1128
   245
}
nkeynes@1128
   246
nkeynes@1128
   247
static FASTCALL int32_t check_read_byte( sh4addr_t addr )
nkeynes@1128
   248
{
nkeynes@1128
   249
    return check_mem_op( READ_BYTE, addr, 0 );
nkeynes@1128
   250
}
nkeynes@1128
   251
nkeynes@1128
   252
static FASTCALL int32_t check_read_byte_for_write( sh4addr_t addr )
nkeynes@1128
   253
{
nkeynes@1128
   254
    return check_mem_op( READ_BYTE_FOR_WRITE, addr, 0 );
nkeynes@1128
   255
}
nkeynes@1128
   256
nkeynes@1128
   257
static FASTCALL void check_write_long( sh4addr_t addr, uint32_t value )
nkeynes@1128
   258
{
nkeynes@1128
   259
    if( !IS_STORE_QUEUE(addr) )
nkeynes@1128
   260
        check_mem_op( WRITE_LONG, addr, value );
nkeynes@1128
   261
}
nkeynes@1128
   262
nkeynes@1128
   263
static FASTCALL void check_write_word( sh4addr_t addr, uint32_t value )
nkeynes@1128
   264
{
nkeynes@1128
   265
    if( !IS_STORE_QUEUE(addr) )
nkeynes@1128
   266
        check_mem_op( WRITE_WORD, addr, value );
nkeynes@1128
   267
}
nkeynes@1128
   268
nkeynes@1128
   269
static FASTCALL void check_write_byte( sh4addr_t addr, uint32_t value )
nkeynes@1128
   270
{
nkeynes@1128
   271
    if( !IS_STORE_QUEUE(addr) )
nkeynes@1128
   272
        check_mem_op( WRITE_BYTE, addr, value );
nkeynes@1128
   273
}
nkeynes@1128
   274
nkeynes@1128
   275
static FASTCALL void check_prefetch( sh4addr_t addr )
nkeynes@1128
   276
{
nkeynes@1128
   277
    check_mem_op( PREFETCH, addr, 0 );
nkeynes@1128
   278
}
nkeynes@1128
   279
nkeynes@1128
   280
struct mem_region_fn log_fns = { log_read_long, log_write_long,
nkeynes@1128
   281
        log_read_word, log_write_word,
nkeynes@1128
   282
        log_read_byte, log_write_byte,
nkeynes@1128
   283
        NULL, NULL, log_prefetch, log_read_byte_for_write };
nkeynes@1128
   284
nkeynes@1128
   285
struct mem_region_fn check_fns = { check_read_long, check_write_long,
nkeynes@1128
   286
        check_read_word, check_write_word,
nkeynes@1128
   287
        check_read_byte, check_write_byte,
nkeynes@1128
   288
        NULL, NULL, check_prefetch, check_read_byte_for_write };
nkeynes@1128
   289
nkeynes@1128
   290
nkeynes@1128
   291
nkeynes@1128
   292
nkeynes@1128
   293
void sh4_shadow_block_begin()
nkeynes@1128
   294
{
nkeynes@1128
   295
    memcpy( &shadow_sh4r, &sh4r, sizeof(struct sh4_registers) );
nkeynes@1128
   296
    mem_log_posn = 0;
nkeynes@1128
   297
}
nkeynes@1128
   298
nkeynes@1128
   299
void sh4_shadow_block_end()
nkeynes@1128
   300
{
nkeynes@1128
   301
    struct sh4_registers temp_sh4r;
nkeynes@1128
   302
nkeynes@1128
   303
    /* Save the end registers, and restore the state back to the start */
nkeynes@1128
   304
    memcpy( &temp_sh4r, &sh4r, sizeof(struct sh4_registers) );
nkeynes@1128
   305
    memcpy( &sh4r, &shadow_sh4r, sizeof(struct sh4_registers) );
nkeynes@1128
   306
nkeynes@1128
   307
    sh4_address_space = check_address_space;
nkeynes@1128
   308
    mem_check_posn = 0;
nkeynes@1128
   309
    sh4r.new_pc = sh4r.pc + 2;
nkeynes@1128
   310
    while( sh4r.slice_cycle < temp_sh4r.slice_cycle ) {
nkeynes@1128
   311
        sh4_execute_instruction();
nkeynes@1128
   312
        sh4r.slice_cycle += sh4_cpu_period;
nkeynes@1128
   313
    }
nkeynes@1128
   314
nkeynes@1128
   315
    if( !check_registers( &temp_sh4r, &sh4r ) ) {
nkeynes@1128
   316
        fprintf( stderr, "After executing block at %08X\n", shadow_sh4r.pc );
nkeynes@1191
   317
        fprintf( stderr, "Translated block was:\n" );
nkeynes@1191
   318
        sh4_translate_dump_block(shadow_sh4r.pc);
nkeynes@1128
   319
        abort();
nkeynes@1128
   320
    }
nkeynes@1128
   321
    if( mem_check_posn < mem_log_posn ) {
nkeynes@1128
   322
        fprintf( stderr, "Additional translator memory operations:\n" );
nkeynes@1128
   323
        while( mem_check_posn < mem_log_posn ) {
nkeynes@1128
   324
            print_mem_op( stderr, mem_log[mem_check_posn].op, mem_log[mem_check_posn].addr, mem_log[mem_check_posn].value );
nkeynes@1128
   325
            mem_check_posn++;
nkeynes@1128
   326
        }
nkeynes@1128
   327
        abort();
nkeynes@1128
   328
    }
nkeynes@1128
   329
    sh4_address_space = real_address_space;
nkeynes@1128
   330
}
nkeynes@1128
   331
nkeynes@1128
   332
nkeynes@1128
   333
void sh4_shadow_init()
nkeynes@1128
   334
{
nkeynes@1128
   335
    real_address_space = sh4_address_space;
nkeynes@1128
   336
    log_address_space = mem_alloc_pages( sizeof(mem_region_fn_t) * 256 );
nkeynes@1128
   337
    check_address_space = mem_alloc_pages( sizeof(mem_region_fn_t) * 256 );
nkeynes@1128
   338
    for( unsigned i=0; i < (256 * 4096); i++ ) {
nkeynes@1128
   339
        log_address_space[i] = &log_fns;
nkeynes@1128
   340
        check_address_space[i] = &check_fns;
nkeynes@1128
   341
    }
nkeynes@1128
   342
nkeynes@1128
   343
    mem_log_size = MEM_LOG_SIZE;
nkeynes@1128
   344
    mem_log = malloc( mem_log_size * sizeof(struct mem_log_entry) );
nkeynes@1128
   345
    assert( mem_log != NULL );
nkeynes@1128
   346
nkeynes@1128
   347
    sh4_translate_set_callbacks( sh4_shadow_block_begin, sh4_shadow_block_end );
nkeynes@1128
   348
    sh4_translate_set_fastmem( FALSE );
nkeynes@1128
   349
    sh4_translate_set_address_space( log_address_space, log_address_space );
nkeynes@1128
   350
}
nkeynes@1128
   351
.