diff options
author | Jackie Smith Cashion <jsmith@redhat.com> | 1995-12-01 16:42:44 +0000 |
---|---|---|
committer | Jackie Smith Cashion <jsmith@redhat.com> | 1995-12-01 16:42:44 +0000 |
commit | 8bae0a0c484e4d9b04bc169196587d32028a90a8 (patch) | |
tree | d59a17d3711437e22108768807bcb91a1293b0f5 /sim/mips/interp.c | |
parent | d4c8a45ee7c4779480dfe1e3e395367c90cb6513 (diff) | |
download | gdb-8bae0a0c484e4d9b04bc169196587d32028a90a8.zip gdb-8bae0a0c484e4d9b04bc169196587d32028a90a8.tar.gz gdb-8bae0a0c484e4d9b04bc169196587d32028a90a8.tar.bz2 |
* gencode.c: Tidied instruction decoding, and added FP instruction
support.
* interp.c: Added dineroIII, and BSD profiling support. Also
run-time FP handling.
At the moment the options are still mostly build-time controlled,
rather than run-time. Also work still needs to be done to remove (long
long) usage (However this is trivial, just time-consuming).
The out-standing instruction work to be done is in supporting round
and trunc for FP operations, and providing better exception support.
Diffstat (limited to 'sim/mips/interp.c')
-rw-r--r-- | sim/mips/interp.c | 3465 |
1 files changed, 3465 insertions, 0 deletions
diff --git a/sim/mips/interp.c b/sim/mips/interp.c new file mode 100644 index 0000000..0cffa94 --- /dev/null +++ b/sim/mips/interp.c @@ -0,0 +1,3465 @@ +/*> interp.c <*/ +/* Simulator for the MIPS architecture. + + This file is part of the MIPS sim + + THIS SOFTWARE IS NOT COPYRIGHTED + + Cygnus offers the following for use in the public domain. Cygnus + makes no warranty with regard to the software or it's performance + and the user accepts the software "AS IS" with all faults. + + CYGNUS DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD TO + THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + + $Revision$ + $Author$ + $Date$ + +NOTEs: + +We only need to take account of the target endianness when moving data +between the simulator and the host. We do not need to worry about the +endianness of the host, since this sim code and GDB are executing in +the same process. + +The IDT monitor (found on the VR4300 board), seems to lie about +register contents. It seems to treat the registers as sign-extended +32-bit values. This cause *REAL* problems when single-stepping 64-bit +code on the hardware. + +*/ + +/* The TRACE and PROFILE manifests enable the provision of extra + features. If they are not defined then a simpler (quicker) + simulator is constructed without the required run-time checks, + etc. */ +#if 1 /* 0 to allow user build selection, 1 to force inclusion */ +#define TRACE (1) +#define PROFILE (1) +#endif + +#include <stdio.h> +#include <stdarg.h> +#include <ansidecl.h> +#include <signal.h> +#include <ctype.h> +#include <limits.h> +#include <math.h> + +#include "getopt.h" +#include "libiberty.h" + +#include "remote-sim.h" /* GDB simulator interface */ +#include "callback.h" /* GDB simulator callback interface */ + +#include "support.h" /* internal support manifests */ + +/* Get the simulator engine description, without including the code: */ +#define SIM_MANIFESTS +#include "engine.c" +#undef SIM_MANIFESTS + +/* The following reserved instruction value is used when a simulator + trap is required. NOTE: Care must be taken, since this value may be + used in later revisions of the MIPS ISA. */ +#define RSVD_INSTRUCTION (0x7C000000) +#define RSVD_INSTRUCTION_AMASK (0x03FFFFFF) + +/* NOTE: These numbers depend on the processor architecture being + simulated: */ +#define Interrupt (0) +#define TLBModification (1) +#define TLBLoad (2) +#define TLBStore (3) +#define AddressLoad (4) +#define AddressStore (5) +#define InstructionFetch (6) +#define DataReference (7) +#define SystemCall (8) +#define BreakPoint (9) +#define ReservedInstruction (10) +#define CoProcessorUnusable (11) +#define IntegerOverflow (12) /* Arithmetic overflow (IDT monitor raises SIGFPE) */ +#define Trap (13) +#define FPE (15) +#define Watch (23) + +/* The following exception code is actually private to the simulator + world. It is *NOT* a processor feature, and is used to signal + run-time errors in the simulator. */ +#define SimulatorFault (0xFFFFFFFF) + +/* The following are generic to all versions of the MIPS architecture + to date: */ +/* Memory Access Types (for CCA): */ +#define Uncached (0) +#define CachedNoncoherent (1) +#define CachedCoherent (2) +#define Cached (3) + +#define isINSTRUCTION (1 == 0) /* FALSE */ +#define isDATA (1 == 1) /* TRUE */ + +#define isLOAD (1 == 0) /* FALSE */ +#define isSTORE (1 == 1) /* TRUE */ + +#define isREAL (1 == 0) /* FALSE */ +#define isRAW (1 == 1) /* TRUE */ + +#define isTARGET (1 == 0) /* FALSE */ +#define isHOST (1 == 1) /* TRUE */ + +/* The "AccessLength" specifications for Loads and Stores. NOTE: This + is the number of bytes minus 1. */ +#define AccessLength_BYTE (0) +#define AccessLength_HALFWORD (1) +#define AccessLength_TRIPLEBYTE (2) +#define AccessLength_WORD (3) +#define AccessLength_QUINTIBYTE (4) +#define AccessLength_SEXTIBYTE (5) +#define AccessLength_SEPTIBYTE (6) +#define AccessLength_DOUBLEWORD (7) + +#if defined(HASFPU) +/* FPU registers must be one of the following types. All other values + are reserved (and undefined). */ +typedef enum { + fmt_single = 0, + fmt_double = 1, + fmt_word = 4, + fmt_long = 5, + /* The following are well outside the normal acceptable format + range, and are used in the register status vector. */ + fmt_unknown = 0x10000000, + fmt_uninterpreted = 0x20000000, +} FP_formats; +#endif /* HASFPU */ + +/* NOTE: We cannot avoid globals, since the GDB "sim_" interface does + not allow a private variable to be passed around. This means that + simulators under GDB can only be single-threaded. However, it would + be possible for the simulators to be multi-threaded if GDB allowed + for a private pointer to be maintained. i.e. a general "void **ptr" + variable that GDB passed around in the argument list to all of + sim_xxx() routines. It could be initialised to NULL by GDB, and + then updated by sim_open() and used by the other sim_xxx() support + functions. This would allow new features in the simulator world, + like storing a context - continuing execution to gather a result, + and then going back to the point where the context was saved and + changing some state before continuing. i.e. the ability to perform + UNDOs on simulations. It would also allow the simulation of + shared-memory multi-processor systems. */ + +static host_callback *callback = NULL; /* handle onto the current callback structure */ + +/* The warning system should be improved, to allow more information to + be passed about the cause: */ +#define WARNING(m) { callback->printf_filtered(callback,"SIM Warning: %s\n",(m)); } + +/* This is nasty, since we have to rely on matching the register + numbers used by GDB. Unfortunately, depending on the MIPS target + GDB uses different register numbers. We cannot just include the + relevant "gdb/tm.h" link, since GDB may not be configured before + the sim world, and also the GDB header file requires too much other + state. */ +/* TODO: Sort out a scheme for *KNOWING* the mapping between real + registers, and the numbers that GDB uses. At the moment due to the + order that the tools are built, we cannot rely on a configured GDB + world whilst constructing the simulator. This means we have to + assume the GDB register number mapping. */ +#define LAST_EMBED_REGNUM (89) + +/* To keep this default simulator simple, and fast, we use a direct + vector of registers. The internal simulator engine then uses + manifests to access the correct slot. */ +ut_reg registers[LAST_EMBED_REGNUM + 1]; +int register_widths[LAST_EMBED_REGNUM + 1]; + +#define GPR (®isters[0]) +#if defined(HASFPU) +#define FGRIDX (38) +#define FGR (®isters[FGRIDX]) +#endif /* HASFPU */ +#define LO (registers[33]) +#define HI (registers[34]) +#define PC (registers[37]) +#define CAUSE (registers[36]) +#define SRIDX (32) +#define SR (registers[SRIDX]) /* CPU status register */ +#define FCR0IDX (71) +#define FCR0 (registers[FCR0IDX]) /* really a 32bit register */ +#define FCR31IDX (70) +#define FCR31 (registers[FCR31IDX]) /* really a 32bit register */ +#define FCSR (FCR31) +#define COCIDX (LAST_EMBED_REGNUM + 2) /* special case : outside the normal range */ + +/* The following are pseudonyms for standard registers */ +#define ZERO (registers[0]) +#define V0 (registers[2]) +#define A0 (registers[4]) +#define A1 (registers[5]) +#define A2 (registers[6]) +#define A3 (registers[7]) +#define SP (registers[29]) +#define RA (registers[31]) + +ut_reg EPC = 0; /* Exception PC */ + +#if defined(HASFPU) +/* Keep the current format state for each register: */ +FP_formats fpr_state[32]; +#endif /* HASFPU */ + +/* VR4300 CP0 configuration register: */ +unsigned int CONFIG = 0; + +/* The following are internal simulator state variables: */ +ut_reg IPC = 0; /* internal Instruction PC */ +ut_reg DSPC = 0; /* delay-slot PC */ + + +/* TODO : these should be the bitmasks for these bits within the + status register. At the moment the following are VR4300 + bit-positions: */ +#define status_KSU_mask (0x3) /* mask for KSU bits */ +#define status_KSU_shift (3) /* shift for field */ +#define ksu_kernel (0x0) +#define ksu_supervisor (0x1) +#define ksu_user (0x2) +#define ksu_unknown (0x3) + +#define status_RE (1 << 25) /* Reverse Endian in user mode */ +#define status_FR (1 << 26) /* enables MIPS III additional FP registers */ +#define status_SR (1 << 20) /* soft reset or NMI */ +#define status_BEV (1 << 22) /* Location of general exception vectors */ +#define status_TS (1 << 21) /* TLB shutdown has occurred */ +#define status_ERL (1 << 2) /* Error level */ +#define status_RP (1 << 27) /* Reduced Power mode */ + +#define config_EP_mask (0xF) +#define config_EP_shift (27) +#define config_EP_D (0x0) +#define config_EP_DxxDxx (0x6) + +#define config_BE (1 << 15) + +#define cause_BD ((unsigned)1 << 31) /* Exception in branch delay slot */ + +#if defined(HASFPU) +/* Macro to update FPSR condition-code field. This is complicated by + the fact that there is a hole in the index range of the bits within + the FCSR register. Also, the number of bits visible depends on the + MIPS ISA version being supported. */ +#define SETFCC(cc,v) {\ + int bit = ((cc == 0) ? 23 : (24 + (cc)));\ + FCSR = ((FCSR & ~(1 << bit)) | ((v) << bit));\ + } +#define GETFCC(cc) (((((cc) == 0) ? (FCSR & (1 << 23)) : (FCSR & (1 << (24 + (cc))))) != 0) ? 1 : 0) + +/* This should be the COC1 value at the start of the preceding + instruction: */ +#define PREVCOC1() ((state & simPCOC1) ? 1 : 0) +#endif /* HASFPU */ + +/* Standard FCRS bits: */ +#define IR (0) /* Inexact Result */ +#define UF (1) /* UnderFlow */ +#define OF (2) /* OverFlow */ +#define DZ (3) /* Division by Zero */ +#define IO (4) /* Invalid Operation */ +#define UO (5) /* Unimplemented Operation */ + +/* Get masks for individual flags: */ +#if 1 /* SAFE version */ +#define FP_FLAGS(b) (((unsigned)(b) < 5) ? (1 << ((b) + 2)) : 0) +#define FP_ENABLE(b) (((unsigned)(b) < 5) ? (1 << ((b) + 7)) : 0) +#define FP_CAUSE(b) (((unsigned)(b) < 6) ? (1 << ((b) + 12)) : 0) +#else +#define FP_FLAGS(b) (1 << ((b) + 2)) +#define FP_ENABLE(b) (1 << ((b) + 7)) +#define FP_CAUSE(b) (1 << ((b) + 12)) +#endif + +#define FP_FS (1 << 24) /* MIPS III onwards : Flush to Zero */ + +#define FP_MASK_RM (0x3) +#define FP_SH_RM (0) +#define FP_RM_NEAREST (0) /* Round to nearest (Round) */ +#define FP_RM_TOZERO (1) /* Round to zero (Trunc) */ +#define FP_RM_TOPINF (2) /* Round to Plus infinity (Ceil) */ +#define FP_RM_TOMINF (3) /* Round to Minus infinity (Floor) */ +#define GETRM() (int)((FCSR >> FP_SH_RM) & FP_MASK_RM) + +/* Slots for delayed register updates. For the moment we just have a + fixed number of slots (rather than a more generic, dynamic + system). This keeps the simulator fast. However, we only allow for + the register update to be delayed for a single instruction + cycle. */ +#define PSLOTS (5) /* Maximum number of instruction cycles */ +int pending_in; +int pending_out; +int pending_total; +int pending_slot_count[PSLOTS]; +int pending_slot_reg[PSLOTS]; +ut_reg pending_slot_value[PSLOTS]; + +/* The following are not used for MIPS IV onwards: */ +#define PENDING_FILL(r,v) {\ +printf("DBG: FILL BEFORE pending_in = %d, pending_out = %d, pending_total = %d\n",pending_in,pending_out,pending_total);\ + if (pending_slot_reg[pending_in] != (LAST_EMBED_REGNUM + 1))\ + callback->printf_filtered(callback,"SIM Warning: Attempt to over-write pending value\n");\ + pending_slot_count[pending_in] = 2;\ + pending_slot_reg[pending_in] = (r);\ + pending_slot_value[pending_in] = (v);\ +printf("DBG: FILL reg %d value = 0x%08X%08X\n",(r),(unsigned int)(((unsigned long long)(v))>>32),(unsigned int)((v)&0xFFFFFFFF));\ + pending_total++;\ + pending_in++;\ + if (pending_in == PSLOTS)\ + pending_in = 0;\ +printf("DBG: FILL AFTER pending_in = %d, pending_out = %d, pending_total = %d\n",pending_in,pending_out,pending_total);\ + } + +int LLBIT = 0; +/* LLBIT = Load-Linked bit. A bit of "virtual" state used by atomic + read-write instructions. It is set when a linked load occurs. It is + tested and cleared by the conditional store. It is cleared (during + other CPU operations) when a store to the location would no longer + be atomic. In particular, it is cleared by exception return + instructions. */ + +int HIACCESS = 0; +int LOACCESS = 0; +/* The HIACCESS and LOACCESS counts are used to ensure that + corruptions caused by using the HI or LO register to close to a + following operation are spotted. */ + +/* If either of the preceding two instructions have accessed the HI or + LO registers, then the values they see should be + undefined. However, to keep the simulator world simple, we just let + them use the value read and raise a warning to notify the user: */ +#define CHECKHILO(s) {\ + if ((HIACCESS != 0) || (LOACCESS != 0))\ + callback->printf_filtered(callback,"SIM Warning: %s over-writing HI and LO registers values\n",(s));\ + /* Set the access counts, since we are about\ + to update the HI and LO registers: */\ + HIACCESS = LOACCESS = 3; /* 3rd instruction will be safe */\ + } + +/* NOTE: We keep the following status flags as bit values (1 for true, + 0 for false). This allows them to be used in binary boolean + operations without worrying about what exactly the non-zero true + value is. */ + +/* UserMode */ +#define UserMode ((((SR & status_KSU_mask) >> status_KSU_shift) == ksu_user) ? 1 : 0) + +/* BigEndianMem */ +/* Hardware configuration. Affects endianness of LoadMemory and + StoreMemory and the endianness of Kernel and Supervisor mode + execution. The value is 0 for little-endian; 1 for big-endian. */ +#define BigEndianMem ((CONFIG & config_BE) ? 1 : 0) +/* NOTE: Problems will occur if the simulator memory model does not + match the host program expectation. i.e. if the host is writing + big-endian values to a little-endian memory model. */ + +/* ReverseEndian */ +/* This mode is selected if in User mode with the RE bit being set in + SR (Status Register). It reverses the endianness of load and store + instructions. */ +#define ReverseEndian (((SR & status_RE) && UserMode) ? 1 : 0) + +/* BigEndianCPU */ +/* The endianness for load and store instructions (0=little;1=big). In + User mode this endianness may be switched by setting the state_RE + bit in the SR register. Thus, BigEndianCPU may be computed as + (BigEndienMem EOR ReverseEndian). */ +#define BigEndianCPU (BigEndianMem ^ ReverseEndian) /* Already bits */ + +#if !defined(FASTSIM) || defined(PROFILE) +/* At the moment these values will be the same, since we do not have + access to the pipeline cycle count information from the simulator + engine. */ +unsigned int instruction_fetches = 0; +unsigned int pipeline_ticks = 0; +#endif + +/* Flags in the "state" variable: */ +#define simSTOP (1 << 0) /* 0 = execute; 1 = stop simulation */ +#define simSTEP (1 << 1) /* 0 = run; 1 = single-step */ +#define simHALTEX (1 << 2) /* 0 = run; 1 = halt on exception */ +#define simHALTIN (1 << 3) /* 0 = run; 1 = halt on interrupt */ +#define simTRACE (1 << 8) /* 0 = do nothing; 1 = trace address activity */ +#define simPROFILE (1 << 9) /* 0 = do nothing; 1 = gather profiling samples */ +#define simHOSTBE (1 << 10) /* 0 = little-endian; 1 = big-endian (host endianness) */ +/* Whilst simSTOP is not set, the simulator control loop should just + keep simulating instructions. The simSTEP flag is used to force + single-step execution. */ +#define simBE (1 << 16) /* 0 = little-endian; 1 = big-endian (target endianness) */ +#define simPCOC0 (1 << 17) /* COC[1] from current */ +#define simPCOC1 (1 << 18) /* COC[1] from previous */ +#define simDELAYSLOT (1 << 24) /* 0 = do nothing; 1 = delay slot entry exists */ +#define simSKIPNEXT (1 << 25) /* 0 = do nothing; 1 = skip instruction */ +#define simEXCEPTION (1 << 26) /* 0 = no exception; 1 = exception has occurred */ +#define simEXIT (1 << 27) /* 0 = do nothing; 1 = run-time exit() processing */ + +unsigned int state = (0 | simBE); /* big-endian simulator by default */ +unsigned int rcexit = 0; /* _exit() reason code holder */ + +#define DELAYSLOT() {\ + if (state & simDELAYSLOT) callback->printf_filtered(callback,"SIM Warning: Delay slot already activated (branch in delay slot?)\n");\ + state |= simDELAYSLOT;\ + } + +#define NULLIFY() {\ + state &= ~simDELAYSLOT;\ + state |= simSKIPNEXT;\ + } + +/* Very simple memory model to start with: */ +unsigned char *membank = NULL; +ut_reg membank_base = 0xA0000000; +unsigned membank_size = (1 << 20); /* (16 << 20); */ /* power-of-2 */ + +/* Simple run-time monitor support */ +unsigned char *monitor = NULL; +ut_reg monitor_base = 0xBFC00000; +unsigned monitor_size = (1 << 9); /* power-of-2 */ + +#if defined(TRACE) +char *tracefile = "trace.din"; /* default filename for trace log */ +FILE *tracefh = NULL; +#endif /* TRACE */ + +#if defined(PROFILE) +unsigned profile_frequency = 256; +unsigned profile_nsamples = (128 << 10); +unsigned short *profile_hist = NULL; +ut_reg profile_minpc; +ut_reg profile_maxpc; +int profile_shift = 0; /* address shift amount */ +#endif /* PROFILE */ + +/*---------------------------------------------------------------------------*/ +/*-- GDB simulator interface ------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +static void dotrace PARAMS((FILE *tracefh,int type,unsigned int address,int width,char *comment,...)); +extern void sim_error PARAMS((char *fmt,...)); +static void ColdReset PARAMS((void)); +static int AddressTranslation PARAMS((unsigned long long vAddr,int IorD,int LorS,unsigned long long *pAddr,int *CCA,int host,int raw)); +static void StoreMemory PARAMS((int CCA,int AccessLength,unsigned long long MemElem,unsigned long long pAddr,unsigned long long vAddr,int raw)); +static unsigned long long LoadMemory PARAMS((int CCA,int AccessLength,unsigned long long pAddr,unsigned long long vAddr,int IorD,int raw)); +static void SignalException PARAMS((int exception,...)); +static void simulate PARAMS((void)); +static long getnum(char *value); +extern void sim_size(unsigned int newsize); +extern void sim_set_profile(int frequency); +static unsigned int power2(unsigned int value); + +void +sim_open (args) + char *args; +{ + if (callback == NULL) { + fprintf(stderr,"SIM Error: sim_open() called without callbacks attached\n"); + return; + } + + /* The following ensures that the standard file handles for stdin, + stdout and stderr are initialised: */ + callback->init(callback); + + state = 0; + CHECKSIM(); + if (state & simEXCEPTION) { + fprintf(stderr,"This simulator is not suitable for this host configuration\n"); + exit(1); + } + + { + int data = 0x12; + if (*((char *)&data) != 0x12) + state |= simHOSTBE; /* big-endian host */ + } + +#if defined(HASFPU) + /* Check that the host FPU conforms to IEEE 754-1985 for the SINGLE + and DOUBLE binary formats. This is a bit nasty, requiring that we + trust the explicit manifests held in the source: */ + { + unsigned int s[2]; + s[0] = 0x40805A5A; + s[1] = 0x00000000; + if (((float)4.01102924346923828125 != *(float *)s) || ((double)523.2939453125 != *(double *)s)) { + fprintf(stderr,"The host executing the simulator does not seem to have IEEE 754-1985 std FP\n"); + exit(1); + } + } +#endif /* HASFPU */ + + /* This is NASTY, in that we are assuming the size of specific + registers: */ + { + int rn; + for (rn = 0; (rn < (LAST_EMBED_REGNUM + 1)); rn++) { + if (rn < 32) + register_widths[rn] = GPRLEN; + else if ((rn >= FGRIDX) && (rn < (FGRIDX + 32))) + register_widths[rn] = GPRLEN; + else if ((rn >= 33) && (rn <= 37)) + register_widths[rn] = GPRLEN; + else if ((rn == SRIDX) || (rn == FCR0IDX) || (rn == FCR31IDX) || ((rn >= 72) && (rn <= 89))) + register_widths[rn] = 32; + else + register_widths[rn] = 0; + } + } + + /* It would be good if we could select particular named MIPS + architecture simulators. However, having a pre-built, fixed + engine would mean including multiple engines. If the simulator is + changed to a run-time conditional version, then the ability to + select a particular architecture would be straightforward. */ + if (args != NULL) { + int c; + char *cline; + char **argv; + int argc; + static struct option cmdline[] = { + {"help", 0,0,'h'}, + {"name", 1,0,'n'}, + {"profile", 0,0,'p'}, + {"size", 1,0,'s'}, + {"trace", 0,0,'t'}, + {"tracefile",1,0,'z'}, + {"frequency",1,0,'y'}, + {"samples", 1,0,'x'}, + {0, 0,0,0} + }; + + /* Unfortunately, getopt_long() is designed to be used with + vectors, where the first option is normally program name (and + ignored). We cheat by creating a dummy first argument, so that + we can use the standard argument processing. */ +#define DUMMYARG "simulator " + cline = (char *)malloc(strlen(args) + strlen(DUMMYARG) + 1); + if (cline == NULL) { + fprintf(stderr,"Failed to allocate memory for command line buffer\n"); + exit(1); + } + sprintf(cline,"%s%s",DUMMYARG,args); + argv = buildargv(cline); + for (argc = 0; argv[argc]; argc++); + + /* Unfortunately, getopt_long() assumes that it is ignoring the + first argument (normally the program name). This means it + ignores the first option on our "args" line. */ + optind = 0; /* Force reset of argument processing */ + while (1) { + int option_index = 0; + + c = getopt_long(argc,argv,"hn:s:tp",cmdline,&option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + callback->printf_filtered(callback,"Usage:\n\t\ +target sim [-h] [--name=<model>] [--size=<amount>]"); +#if defined(TRACE) + callback->printf_filtered(callback," [-t [--tracefile=<name>]]"); +#endif /* TRACE */ +#if defined(PROFILE) + callback->printf_filtered(callback," [-p [--frequency=<count>] [--samples=<count>]]"); +#endif /* PROFILE */ + callback->printf_filtered(callback,"\n"); + break; + + case 'n': + callback->printf_filtered(callback,"Explicit model selection not yet available (Ignoring \"%s\")\n",optarg); + break; + + case 's': + membank_size = (unsigned)getnum(optarg); + break; + + case 't': +#if defined(TRACE) + /* Eventually the simTRACE flag could be treated as a toggle, to + allow external control of the program points being traced + (i.e. only from main onwards, excluding the run-time setup, + etc.). */ + state |= simTRACE; +#else /* !TRACE */ + fprintf(stderr,"\ +Simulator constructed without tracing support (for performance).\n\ +Re-compile simulator with \"-DTRACE\" to enable this option.\n"); +#endif /* !TRACE */ + break; + + case 'z': +#if defined(TRACE) + if (optarg != NULL) { + char *tmp; + tmp = (char *)malloc(strlen(optarg) + 1); + if (tmp == NULL) + callback->printf_filtered(callback,"Failed to allocate buffer for tracefile name \"%s\"\n",optarg); + else { + strcpy(tmp,optarg); + tracefile = tmp; + callback->printf_filtered(callback,"Placing trace information into file \"%s\"\n",tracefile); + } + } +#endif /* TRACE */ + break; + + case 'p': +#if defined(PROFILE) + state |= simPROFILE; +#else /* !PROFILE */ + fprintf(stderr,"\ +Simulator constructed without profiling support (for performance).\n\ +Re-compile simulator with \"-DPROFILE\" to enable this option.\n"); +#endif /* !PROFILE */ + break; + + case 'x': +#if defined(PROFILE) + profile_nsamples = (unsigned)getnum(optarg); +#endif /* PROFILE */ + break; + + case 'y': +#if defined(PROFILE) + sim_set_profile((int)getnum(optarg)); +#endif /* PROFILE */ + break; + + default: + callback->printf_filtered(callback,"Warning: Simulator getopt returned unrecognised code 0x%08X\n",c); + case '?': + break; + } + } + + if (optind < argc) { + callback->printf_filtered(callback,"Warning: Ignoring spurious non-option arguments "); + while (optind < argc) + callback->printf_filtered(callback,"\"%s\" ",argv[optind++]); + callback->printf_filtered(callback,"\n"); + } + + freeargv(argv); + } + + /* If the host has "mmap" available we could use it to provide a + very large virtual address space for the simulator, since memory + would only be allocated within the "mmap" space as it is + accessed. This can also be linked to the architecture specific + support, required to simulate the MMU. */ + sim_size(membank_size); + /* NOTE: The above will also have enabled any profiling state */ + + ColdReset(); + /* If we were providing a more complete I/O, co-processor or memory + simulation, we should perform any "device" initialisation at this + point. This can include pre-loading memory areas with particular + patterns (e.g. simulating ROM monitors). */ + + /* We can start writing to the memory, now that the processor has + been reset: */ + monitor = (unsigned char *)calloc(1,monitor_size); + if (!monitor) { + fprintf(stderr,"Not enough VM for monitor simulation (%d bytes)\n",monitor_size); + } else { + int loop; + /* Entry into the IDT monitor is via fixed address vectors, and + not using machine instructions. To avoid clashing with use of + the MIPS TRAP system, we place our own (simulator specific) + "undefined" instructions into the relevant vector slots. */ + for (loop = 0; (loop < monitor_size); loop += 4) { + unsigned long long vaddr = (monitor_base + loop); + unsigned long long paddr; + int cca; + if (AddressTranslation(vaddr,isDATA,isSTORE,&paddr,&cca,isTARGET,isRAW)) + StoreMemory(cca,AccessLength_WORD,(RSVD_INSTRUCTION | (loop >> 2)),paddr,vaddr,isRAW); + } + } + +#if defined(TRACE) + if (state & simTRACE) { + tracefh = fopen(tracefile,"wb+"); + if (tracefh == NULL) { + callback->printf_filtered(callback,"Failed to create file \"%s\", writing trace information to stderr.\n",tracefile); + tracefh = stderr; + } + } +#endif /* TRACE */ + + return; +} + +/* For the profile writing, we write the data in the host + endianness. This unfortunately means we are assuming that the + profile file we create is processed on the same host executing the + simulator. The gmon.out file format should either have an explicit + endianness, or a method of encoding the endianness in the file + header. */ +static int +writeout32(fh,val) + FILE *fh; + unsigned int val; +{ + char buff[4]; + int res = 1; + + if (state & simHOSTBE) { + buff[3] = ((val >> 0) & 0xFF); + buff[2] = ((val >> 8) & 0xFF); + buff[1] = ((val >> 16) & 0xFF); + buff[0] = ((val >> 24) & 0xFF); + } else { + buff[0] = ((val >> 0) & 0xFF); + buff[1] = ((val >> 8) & 0xFF); + buff[2] = ((val >> 16) & 0xFF); + buff[3] = ((val >> 24) & 0xFF); + } + if (fwrite(buff,4,1,fh) != 1) { + callback->printf_filtered(callback,"Failed to write 4bytes to the profile file\n"); + res = 0; + } + return(res); +} + +static int +writeout16(fh,val) + FILE *fh; + unsigned short val; +{ + char buff[2]; + int res = 1; + if (state & simHOSTBE) { + buff[1] = ((val >> 0) & 0xFF); + buff[0] = ((val >> 8) & 0xFF); + } else { + buff[0] = ((val >> 0) & 0xFF); + buff[1] = ((val >> 8) & 0xFF); + } + if (fwrite(buff,2,1,fh) != 1) { + callback->printf_filtered(callback,"Failed to write 2bytes to the profile file\n"); + res = 0; + } + return(res); +} + +void +sim_close (quitting) + int quitting; +{ +#ifdef DEBUG + printf("DBG: sim_close: entered (quitting = %d)\n",quitting); +#endif + + /* Cannot assume sim_kill() has been called */ + /* "quitting" is non-zero if we cannot hang on errors */ + + /* Ensure that any resources allocated through the callback + mechanism are released: */ + callback->shutdown(callback); + +#if defined(PROFILE) + if ((state & simPROFILE) && (profile_hist != NULL)) { + unsigned short *p = profile_hist; + FILE *pf = fopen("gmon.out","wb"); + int loop; + + if (pf == NULL) + callback->printf_filtered(callback,"Failed to open \"gmon.out\" profile file\n"); + else { + int ok; +#ifdef DEBUG + printf("DBG: minpc = 0x%08X\n",(unsigned int)profile_minpc); + printf("DBG: maxpc = 0x%08X\n",(unsigned int)profile_maxpc); +#endif /* DEBUG */ + ok = writeout32(pf,(unsigned int)profile_minpc); + if (ok) + ok = writeout32(pf,(unsigned int)profile_maxpc); + if (ok) + ok = writeout32(pf,(profile_nsamples * 2) + 12); /* size of sample buffer (+ header) */ +#ifdef DEBUG + printf("DBG: nsamples = %d (size = 0x%08X)\n",profile_nsamples,((profile_nsamples * 2) + 12)); +#endif /* DEBUG */ + for (loop = 0; (ok && (loop < profile_nsamples)); loop++) { + ok = writeout16(pf,profile_hist[loop]); + if (!ok) + break; + } + + fclose(pf); + } + + free(profile_hist); + profile_hist = NULL; + state &= ~simPROFILE; + } +#endif /* PROFILE */ + +#if defined(TRACE) + if (tracefh != stderr) + fclose(tracefh); + state &= ~simTRACE; +#endif /* TRACE */ + + if (membank) + cfree(membank); + membank = NULL; + + return; +} + +void +sim_resume (step,signal) + int step, signal; +{ +#ifdef DEBUG + printf("DBG: sim_resume entered: step = %d, signal = %d (membank = 0x%08X)\n",step,signal,membank); +#endif /* DEBUG */ + + if (step) + state |= simSTEP; /* execute only a single instruction */ + else + state &= ~(simSTOP | simSTEP); /* execute until event */ + + state |= (simHALTEX | simHALTIN); /* treat interrupt event as exception */ + + /* Start executing instructions from the current state (set + explicitly by register updates, or by sim_create_inferior): */ + + simulate(); + return; +} + +int +sim_write (addr,buffer,size) + SIM_ADDR addr; + unsigned char *buffer; + int size; +{ + int index = size; + unsigned long long vaddr = (unsigned long long)addr; + + /* Return the number of bytes written, or zero if error. */ +#ifdef DEBUG + callback->printf_filtered(callback,"sim_write(0x%08X%08X,buffer,%d);\n",(unsigned int)((unsigned long long)(addr)>>32),(unsigned int)(addr&0xFFFFFFFF),size); +#endif + + /* We provide raw read and write routines, since we do not want to + count the GDB memory accesses in our statistics gathering. */ + + /* There is a lot of code duplication in the individual blocks + below, but the variables are declared locally to a block to give + the optimiser the best chance of improving the code. We have to + perform slow byte reads from the host memory, to ensure that we + get the data into the correct endianness for the (simulated) + target memory world. */ + + /* Mask count to get odd byte, odd halfword, and odd word out of the + way. We can then perform doubleword transfers to and from the + simulator memory for optimum performance. */ + if (index && (index & 1)) { + unsigned long long paddr; + int cca; + if (AddressTranslation(vaddr,isDATA,isSTORE,&paddr,&cca,isTARGET,isRAW)) { + unsigned long long value = ((unsigned long long)(*buffer++)); + StoreMemory(cca,AccessLength_BYTE,value,paddr,vaddr,isRAW); + } + vaddr++; + index &= ~1; /* logical operations usually quicker than arithmetic on RISC systems */ + } + if (index && (index & 2)) { + unsigned long long paddr; + int cca; + if (AddressTranslation(vaddr,isDATA,isSTORE,&paddr,&cca,isTARGET,isRAW)) { + unsigned long long value; + /* We need to perform the following magic to ensure that that + bytes are written into same byte positions in the target memory + world, regardless of the endianness of the host. */ + if (BigEndianMem) { + value = ((unsigned long long)(*buffer++) << 8); + value |= ((unsigned long long)(*buffer++) << 0); + } else { + value = ((unsigned long long)(*buffer++) << 0); + value |= ((unsigned long long)(*buffer++) << 8); + } + StoreMemory(cca,AccessLength_HALFWORD,value,paddr,vaddr,isRAW); + } + vaddr += 2; + index &= ~2; + } + if (index && (index & 4)) { + unsigned long long paddr; + int cca; + if (AddressTranslation(vaddr,isDATA,isSTORE,&paddr,&cca,isTARGET,isRAW)) { + unsigned long long value; + if (BigEndianMem) { + value = ((unsigned long long)(*buffer++) << 24); + value |= ((unsigned long long)(*buffer++) << 16); + value |= ((unsigned long long)(*buffer++) << 8); + value |= ((unsigned long long)(*buffer++) << 0); + } else { + value = ((unsigned long long)(*buffer++) << 0); + value |= ((unsigned long long)(*buffer++) << 8); + value |= ((unsigned long long)(*buffer++) << 16); + value |= ((unsigned long long)(*buffer++) << 24); + } + StoreMemory(cca,AccessLength_WORD,value,paddr,vaddr,isRAW); + } + vaddr += 4; + index &= ~4; + } + for (;index; index -= 8) { + unsigned long long paddr; + int cca; + if (AddressTranslation(vaddr,isDATA,isSTORE,&paddr,&cca,isTARGET,isRAW)) { + unsigned long long value; + if (BigEndianMem) { + value = ((unsigned long long)(*buffer++) << 56); + value |= ((unsigned long long)(*buffer++) << 48); + value |= ((unsigned long long)(*buffer++) << 40); + value |= ((unsigned long long)(*buffer++) << 32); + value |= ((unsigned long long)(*buffer++) << 24); + value |= ((unsigned long long)(*buffer++) << 16); + value |= ((unsigned long long)(*buffer++) << 8); + value |= ((unsigned long long)(*buffer++) << 0); + } else { + value = ((unsigned long long)(*buffer++) << 0); + value |= ((unsigned long long)(*buffer++) << 8); + value |= ((unsigned long long)(*buffer++) << 16); + value |= ((unsigned long long)(*buffer++) << 24); + value |= ((unsigned long long)(*buffer++) << 32); + value |= ((unsigned long long)(*buffer++) << 40); + value |= ((unsigned long long)(*buffer++) << 48); + value |= ((unsigned long long)(*buffer++) << 56); + } + StoreMemory(cca,AccessLength_DOUBLEWORD,value,paddr,vaddr,isRAW); + } + vaddr += 8; + } + + return(size); +} + +int +sim_read (addr,buffer,size) + SIM_ADDR addr; + unsigned char *buffer; + int size; +{ + int index; + + /* Return the number of bytes read, or zero if error. */ +#ifdef DEBUG + callback->printf_filtered(callback,"sim_read(0x%08X%08X,buffer,%d);\n",(unsigned int)((unsigned long long)(addr)>>32),(unsigned int)(addr&0xFFFFFFFF),size); +#endif /* DEBUG */ + + /* TODO: Perform same optimisation as the sim_write() code + above. NOTE: This will require a bit more work since we will need + to ensure that the source physical address is doubleword aligned + before, and then deal with trailing bytes. */ + for (index = 0; (index < size); index++) { + unsigned long long vaddr,paddr,value; + int cca; + vaddr = (unsigned long long)addr + index; + if (AddressTranslation(vaddr,isDATA,isLOAD,&paddr,&cca,isTARGET,isRAW)) { + value = LoadMemory(cca,AccessLength_BYTE,paddr,vaddr,isDATA,isRAW); + buffer[index] = (unsigned char)(value&0xFF); + } else + break; + } + + return(index); +} + +void +sim_store_register (rn,memory) + int rn; + unsigned char *memory; +{ +#ifdef DEBUG + callback->printf_filtered(callback,"sim_store_register(%d,*memory=0x%08X%08X);\n",rn,*((unsigned int *)memory),*((unsigned int *)(memory + 4))); +#endif /* DEBUG */ + + /* Unfortunately this suffers from the same problem as the register + numbering one. We need to know what the width of each logical + register number is for the architecture being simulated. */ + if (register_widths[rn] == 0) + callback->printf_filtered(callback,"Warning: Invalid register width for %d (register store ignored)\n",rn); + else { + if (register_widths[rn] == 32) + registers[rn] = *((unsigned int *)memory); + else + registers[rn] = *((unsigned long long *)memory); + } + + return; +} + +void +sim_fetch_register (rn,memory) + int rn; + unsigned char *memory; +{ +#ifdef DEBUG + callback->printf_filtered(callback,"sim_fetch_register(%d=0x%08X%08X,mem) : place simulator registers into memory\n",rn,(unsigned int)(registers[rn]>>32),(unsigned int)(registers[rn]&0xFFFFFFFF)); +#endif /* DEBUG */ + + if (register_widths[rn] == 0) + callback->printf_filtered(callback,"Warning: Invalid register width for %d (register fetch ignored)\n",rn); + else { + if (register_widths[rn] == 32) + *((unsigned int *)memory) = (registers[rn] & 0xFFFFFFFF); + else /* 64bit register */ + *((unsigned long long *)memory) = registers[rn]; + } + return; +} + +void +sim_stop_reason (reason,sigrc) + enum sim_stop *reason; + int *sigrc; +{ +/* We can have "*reason = {sim_exited, sim_stopped, sim_signalled}", so + sim_exited *sigrc = argument to exit() + sim_stopped *sigrc = exception number + sim_signalled *sigrc = signal number +*/ + if (state & simEXCEPTION) { + /* If "sim_signalled" is used, GDB expects normal SIGNAL numbers, + and not the MIPS specific exception codes. */ +#if 1 + /* For some reason, sending GDB a sim_signalled reason cause it to + terminate out. */ + *reason = sim_stopped; +#else + *reason = sim_signalled; +#endif + switch ((CAUSE >> 2) & 0x1F) { + case Interrupt: + *sigrc = SIGINT; /* wrong type of interrupt, but it will do for the moment */ + break; + + case TLBModification: + case TLBLoad: + case TLBStore: + case AddressLoad: + case AddressStore: + case InstructionFetch: + case DataReference: + *sigrc = SIGBUS; + break; + + case ReservedInstruction: + case CoProcessorUnusable: + *sigrc = SIGILL; + break; + + case IntegerOverflow: + case FPE: + *sigrc = SIGFPE; + break; + + case Trap: + case Watch: + case SystemCall: + case BreakPoint: + *sigrc = SIGTRAP; + break; + + default : /* Unknown internal exception */ + *sigrc = SIGQUIT; + break; + } + } else if (state & simEXIT) { + printf("DBG: simEXIT (%d)\n",rcexit); + *reason = sim_exited; + *sigrc = rcexit; + } else { /* assume single-stepping */ + *reason = sim_stopped; + *sigrc = SIGTRAP; + } + state &= ~(simEXCEPTION | simEXIT); + return; +} + +void +sim_info (verbose) + int verbose; +{ + /* Accessed from the GDB "info files" command: */ + + callback->printf_filtered(callback,"MIPS %d-bit simulator\n",(PROCESSOR_64BIT ? 64 : 32)); + + callback->printf_filtered(callback,"%s endian memory model\n",(BigEndianMem ? "Big" : "Little")); + + callback->printf_filtered(callback,"0x%08X bytes of memory at 0x%08X%08X\n",(unsigned int)membank_size,(unsigned int)(membank_base>>32),(unsigned int)(membank_base&0xFFFFFFFF)); + +#if !defined(FASTSIM) + callback->printf_filtered(callback,"Instruction fetches = %d\n",instruction_fetches); + callback->printf_filtered(callback,"Pipeline ticks = %d\n",pipeline_ticks); + /* It would be a useful feature, if when performing multi-cycle + simulations (rather than single-stepping) we keep the start and + end times of the execution, so that we can give a performance + figure for the simulator. */ +#endif /* !FASTSIM */ + + /* print information pertaining to MIPS ISA and architecture being simulated */ + /* things that may be interesting */ + /* instructions executed - if available */ + /* cycles executed - if available */ + /* pipeline stalls - if available */ + /* virtual time taken */ + /* profiling size */ + /* profiling frequency */ + /* profile minpc */ + /* profile maxpc */ + + return; +} + +int +sim_load (prog,from_tty) + char *prog; + int from_tty; +{ + /* Return non-zero if the caller should handle the load. Zero if + we have loaded the image. */ + return(-1); +} + +void +sim_create_inferior (start_address,argv,env) + SIM_ADDR start_address; + char **argv; + char **env; +{ +#ifdef DEBUG + printf("DBG: sim_create_inferior entered: start_address = 0x%08X\n",start_address); +#endif /* DEBUG */ + + /* Prepare to execute the program to be simulated */ + /* argv and env are NULL terminated lists of pointers */ + +#if 1 + PC = (unsigned long long)start_address; +#else + /* TODO: Sort this properly. SIM_ADDR may already be a 64bit value: */ + PC = SIGNEXTEND(start_address,32); +#endif + /* NOTE: GDB normally sets the PC explicitly. However, this call is + used by other clients of the simulator. */ + + if (argv || env) { + callback->printf_filtered(callback,"sim_create_inferior() : passed arguments ignored\n"); +#if 1 /* def DEBUG */ + { + char **cptr; + for (cptr = argv; (cptr && *cptr); cptr++) + printf("DBG: arg \"%s\"\n",*cptr); + } +#endif /* DEBUG */ + /* We should really place the argv slot values into the argument + registers, and onto the stack as required. However, this + assumes that we have a stack defined, which is not necessarily + true at the moment. */ + } + + return; +} + +void +sim_kill () +{ +#if 1 + /* This routine should be for terminating any existing simulation + thread. Since we are single-threaded only at the moment, this is + not an issue. It should *NOT* be used to terminate the + simulator. */ +#else /* do *NOT* call sim_close */ + sim_close(1); /* Do not hang on errors */ + /* This would also be the point where any memory mapped areas used + by the simulator should be released. */ +#endif + return; +} + +int +sim_get_quit_code () +{ + /* The standard MIPS PCS (Procedure Calling Standard) uses V0(r2) as + the function return value. However, it may be more correct for + this to return the argument to the exit() function (if + called). */ + return(V0); +} + +void +sim_set_callbacks (p) + host_callback *p; +{ + callback = p; + return; +} + +typedef enum {e_terminate,e_help,e_setmemsize,e_reset} e_cmds; + +static struct t_sim_command { + e_cmds id; + const char *name; + const char *help; +} sim_commands[] = { + {e_help, "help", ": Show MIPS simulator private commands"}, + {e_setmemsize,"set-memory-size","<n> : Specify amount of memory simulated"}, + {e_reset, "reset-system", ": Reset the simulated processor"}, + {e_terminate, NULL} +}; + +void +sim_do_command (cmd) + char *cmd; +{ + struct t_sim_command *cptr; + + if (!(cmd && *cmd != '\0')) + cmd = "help"; + + /* NOTE: Accessed from the GDB "sim" commmand: */ + for (cptr = sim_commands; cptr && cptr->name; cptr++) + if (strncmp(cmd,cptr->name,strlen(cptr->name)) == 0) { + cmd += strlen(cptr->name); + switch (cptr->id) { + case e_help: /* no arguments */ + { /* no arguments */ + struct t_sim_command *lptr; + callback->printf_filtered(callback,"List of MIPS simulator commands:\n"); + for (lptr = sim_commands; lptr->name; lptr++) + callback->printf_filtered(callback,"%s %s\n",lptr->name,lptr->help); + } + break; + + case e_setmemsize: /* memory size argument */ + { + unsigned int newsize = (unsigned int)getnum(cmd); + sim_size(newsize); + } + break; + + case e_reset: /* no arguments */ + ColdReset(); + /* NOTE: See the comments in sim_open() relating to device + initialisation. */ + break; + + default: + callback->printf_filtered(callback,"FATAL: Matched \"%s\", but failed to match command id %d.\n",cmd,cptr->id); + break; + } + break; + } + + if (!(cptr->name)) + callback->printf_filtered(callback,"Error: \"%s\" is not a valid MIPS simulator command.\n",cmd); + + return; +} + +/*---------------------------------------------------------------------------*/ +/* NOTE: The following routines do not seem to be used by GDB at the + moment. However, they may be useful to the standalone simulator + world. */ + + +/* The profiling format is described in the "gmon_out.h" header file */ +void +sim_set_profile (n) + int n; +{ +#if defined(PROFILE) + profile_frequency = n; + state |= simPROFILE; +#endif /* PROFILE */ + return; +} + +void +sim_set_profile_size (n) + int n; +{ +#if defined(PROFILE) + if (state & simPROFILE) { + int bsize; + + /* Since we KNOW that the memory banks are a power-of-2 in size: */ + profile_nsamples = power2(n); + profile_minpc = membank_base; + profile_maxpc = (membank_base + membank_size); + + /* Just in-case we are sampling every address: NOTE: The shift + right of 2 is because we only have word-aligned PC addresses. */ + if (profile_nsamples > (membank_size >> 2)) + profile_nsamples = (membank_size >> 2); + + /* Since we are dealing with power-of-2 values: */ + profile_shift = (((membank_size >> 2) / profile_nsamples) - 1); + + bsize = (profile_nsamples * sizeof(unsigned short)); + if (profile_hist == NULL) + profile_hist = (unsigned short *)calloc(64,(bsize / 64)); + else + profile_hist = (unsigned short *)realloc(profile_hist,bsize); + if (profile_hist == NULL) { + callback->printf_filtered(callback,"Failed to allocate VM for profiling buffer (0x%08X bytes)\n",bsize); + state &= ~simPROFILE; + } + } +#endif /* PROFILE */ + + return; +} + +void +sim_size(newsize) + unsigned int newsize; +{ + char *new; + /* Used by "run", and internally, to set the simulated memory size */ + newsize = power2(newsize); + if (membank == NULL) + new = (char *)calloc(64,(membank_size / 64)); + else + new = (char *)realloc(membank,newsize); + if (new == NULL) { + if (membank == NULL) + callback->printf_filtered(callback,"Not enough VM for simulation memory of 0x%08X bytes\n",membank_size); + else + callback->printf_filtered(callback,"Failed to resize memory (still 0x%08X bytes)\n",membank_size); + } else { + membank_size = (unsigned)newsize; + membank = new; + callback->printf_filtered(callback,"Memory size now 0x%08X bytes\n",membank_size); +#if defined(PROFILE) + /* Ensure that we sample across the new memory range */ + sim_set_profile_size(profile_nsamples); +#endif /* PROFILE */ + } + + return; +} + +int +sim_trace() +{ + /* This routine is called by the "run" program, when detailed + execution information is required. Rather than executing a single + instruction, and looping around externally... we just start + simulating, returning TRUE when the simulator stops (for whatever + reason). */ + +#if defined(TRACE) + /* Ensure tracing is enabled, if available */ + if (tracefh != NULL) + state |= simTRACE; +#endif /* TRACE */ + + state &= ~(simSTOP | simSTEP); /* execute until event */ + state |= (simHALTEX | simHALTIN); /* treat interrupt event as exception */ + /* Start executing instructions from the current state (set + explicitly by register updates, or by sim_create_inferior): */ + simulate(); + + return(1); +} + +/*---------------------------------------------------------------------------*/ +/*-- Private simulator support interface ------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +static void +sim_monitor(reason) + unsigned int reason; +{ + /* The IDT monitor actually allows two instructions per vector + slot. However, the simulator currently causes a trap on each + individual instruction. We cheat, and lose the bottom bit. */ + reason >>= 1; + + /* The following callback functions are available, however the + monitor we are simulating does not make use of them: get_errno, + isatty, lseek, rename, system, time and unlink */ + switch (reason) { + case 6: /* int open(char *path,int flags) */ + { + const char *ptr; + unsigned long long paddr; + int cca; + if (AddressTranslation(A0,isDATA,isLOAD,&paddr,&cca,isHOST,isREAL)) + V0 = callback->open(callback,(char *)((int)paddr),(int)A1); + else + callback->printf_filtered(callback,"WARNING: Attempt to pass pointer that does not reference simulated memory\n"); + } + break; + + case 7: /* int read(int file,char *ptr,int len) */ + { + const char *ptr; + unsigned long long paddr; + int cca; + if (AddressTranslation(A1,isDATA,isLOAD,&paddr,&cca,isHOST,isREAL)) + V0 = callback->read(callback,(int)A0,(char *)((int)paddr),(int)A2); + else + callback->printf_filtered(callback,"WARNING: Attempt to pass pointer that does not reference simulated memory\n"); + } + break; + + case 8: /* int write(int file,char *ptr,int len) */ + { + const char *ptr; + unsigned long long paddr; + int cca; + if (AddressTranslation(A1,isDATA,isLOAD,&paddr,&cca,isHOST,isREAL)) + V0 = callback->write(callback,(int)A0,(const char *)((int)paddr),(int)A2); + else + callback->printf_filtered(callback,"WARNING: Attempt to pass pointer that does not reference simulated memory\n"); + } + break; + + case 10: /* int close(int file) */ + V0 = callback->close(callback,(int)A0); + break; + + case 11: /* char inbyte(void) */ + { + char tmp; + if (callback->read_stdin(callback,&tmp,sizeof(char)) != sizeof(char)) { + callback->printf_filtered(callback,"WARNING: Invalid return from character read\n"); + V0 = -1; + } + else + V0 = tmp; + } + break; + + case 12: /* void outbyte(char chr) : write a byte to "stdout" */ + { + char tmp = (char)(A0 & 0xFF); + callback->write_stdout(callback,&tmp,sizeof(char)); + } + break; + + case 17: /* void _exit() */ + callback->printf_filtered(callback,"sim_monitor(17): _exit(int reason) to be coded\n"); + state |= (simSTOP | simEXIT); /* stop executing code */ + rcexit = (unsigned int)(A0 & 0xFFFFFFFF); + break; + + case 55: /* void get_mem_info(unsigned int *ptr) */ + /* in: A0 = pointer to three word memory location */ + /* out: [A0 + 0] = size */ + /* [A0 + 4] = instruction cache size */ + /* [A0 + 8] = data cache size */ + { + unsigned long long vaddr = A0; + unsigned long long paddr, value; + int cca; + int failed = 0; + + /* NOTE: We use RAW memory writes here, but since we are not + gathering statistics for the monitor calls we are simulating, + it is not an issue. */ + + /* Memory size */ + if (AddressTranslation(vaddr,isDATA,isSTORE,&paddr,&cca,isTARGET,isREAL)) { + value = (unsigned long long)membank_size; + StoreMemory(cca,AccessLength_WORD,value,paddr,vaddr,isRAW); + /* We re-do the address translations, in-case the block + overlaps a memory boundary: */ + value = 0; + vaddr += (AccessLength_WORD + 1); + if (AddressTranslation(vaddr,isDATA,isSTORE,&paddr,&cca,isTARGET,isREAL)) { + StoreMemory(cca,AccessLength_WORD,value,paddr,vaddr,isRAW); + vaddr += (AccessLength_WORD + 1); + if (AddressTranslation(vaddr,isDATA,isSTORE,&paddr,&cca,isTARGET,isREAL)) + StoreMemory(cca,AccessLength_WORD,value,paddr,vaddr,isRAW); + else + failed = -1; + } else + failed = -1; + } else + failed = -1; + + if (failed) + callback->printf_filtered(callback,"WARNING: Invalid pointer passed into monitor call\n"); + } + break; + + default: + callback->printf_filtered(callback,"TODO: sim_monitor(%d) : PC = 0x%08X%08X\n",reason,(unsigned int)(IPC>>32),(unsigned int)(IPC&0xFFFFFFFF)); + callback->printf_filtered(callback,"(Arguments : A0 = 0x%08X%08X : A1 = 0x%08X%08X : A2 = 0x%08X%08X : A3 = 0x%08X%08X)\n",(unsigned int)(A0>>32),(unsigned int)(A0&0xFFFFFFFF),(unsigned int)(A1>>32),(unsigned int)(A1&0xFFFFFFFF),(unsigned int)(A2>>32),(unsigned int)(A2&0xFFFFFFFF),(unsigned int)(A3>>32),(unsigned int)(A3&0xFFFFFFFF)); + break; + } + return; +} + +void +sim_error(fmt) + char *fmt; +{ + va_list ap; + va_start(ap,fmt); + callback->printf_filtered(callback,"SIM Error: "); + callback->printf_filtered(callback,fmt,ap); + va_end(ap); + SignalException(SimulatorFault,""); + return; +} + +static unsigned int +power2(value) + unsigned int value; +{ + int loop,tmp; + + /* Round *UP* to the nearest power-of-2 if not already one */ + if (value != (value & ~(value - 1))) { + for (tmp = value, loop = 0; (tmp != 0); loop++) + tmp >>= 1; + value = (1 << loop); + } + + return(value); +} + +static long +getnum(value) + char *value; +{ + long num; + char *end; + + num = strtol(value,&end,10); + if (end == value) + callback->printf_filtered(callback,"Warning: Invalid number \"%s\" ignored, using zero\n",value); + else { + if (*end && ((tolower(*end) == 'k') || (tolower(*end) == 'm'))) { + if (tolower(*end) == 'k') + num *= (1 << 10); + else + num *= (1 << 20); + end++; + } + if (*end) + callback->printf_filtered(callback,"Warning: Spurious characters \"%s\" at end of number ignored\n",end); + } + + return(num); +} + +/*-- trace support ----------------------------------------------------------*/ + +/* The TRACE support is provided (if required) in the memory accessing + routines. Since we are also providing the architecture specific + features, the architecture simulation code can also deal with + notifying the TRACE world of cache flushes, etc. Similarly we do + not need to provide profiling support in the simulator engine, + since we can sample in the instruction fetch control loop. By + defining the TRACE manifest, we add tracing as a run-time + option. */ + +#if defined(TRACE) +/* Tracing by default produces "din" format (as required by + dineroIII). Each line of such a trace file *MUST* have a din label + and address field. The rest of the line is ignored, so comments can + be included if desired. The first field is the label which must be + one of the following values: + + 0 read data + 1 write data + 2 instruction fetch + 3 escape record (treated as unknown access type) + 4 escape record (causes cache flush) + + The address field is a 32bit (lower-case) hexadecimal address + value. The address should *NOT* be preceded by "0x". + + The size of the memory transfer is not important when dealing with + cache lines (as long as no more than a cache line can be + transferred in a single operation :-), however more information + could be given following the dineroIII requirement to allow more + complete memory and cache simulators to provide better + results. i.e. the University of Pisa has a cache simulator that can + also take bus size and speed as (variable) inputs to calculate + complete system performance (a much more useful ability when trying + to construct an end product, rather than a processor). They + currently have an ARM version of their tool called ChARM. */ + +static +void dotrace(tracefh,type,address,width,comment) + FILE *tracefh; + int type; + unsigned int address; + int width; + char *comment; +{ + if (state & simTRACE) { + va_list ap; + fprintf(tracefh,"%d %08x ; width %d ; ",type,address,width); + va_start(ap,comment); + fprintf(tracefh,comment,ap); + va_end(ap); + fprintf(tracefh,"\n"); + } + /* NOTE: Since the "din" format will only accept 32bit addresses, and + we may be generating 64bit ones, we should put the hi-32bits of the + address into the comment field. */ + + /* TODO: Provide a buffer for the trace lines. We can then avoid + performing writes until the buffer is filled, or the file is + being closed. */ + + /* NOTE: We could consider adding a comment field to the "din" file + produced using type 3 markers (unknown access). This would then + allow information about the program that the "din" is for, and + the MIPs world that was being simulated, to be placed into the + trace file. */ + + return; +} +#endif /* TRACE */ + +/*---------------------------------------------------------------------------*/ +/*-- simulator engine -------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +static void +ColdReset() +{ + /* RESET: Fixed PC address: */ + PC = (((unsigned long long)0xFFFFFFFF<<32) | 0xBFC00000); + /* The reset vector address is in the unmapped, uncached memory space. */ + + SR &= ~(status_SR | status_TS | status_RP); + SR |= (status_ERL | status_BEV); + /* VR4300 starts in Big-Endian mode */ + CONFIG &= ~(config_EP_mask << config_EP_shift); + CONFIG |= ((config_EP_D << config_EP_shift) | config_BE); + /* TODO: The VR4300 CONFIG register is not modelled fully at the moment */ + +#if defined(HASFPU) && (GPRLEN == (64)) + /* Cheat and allow access to the complete register set immediately: */ + SR |= status_FR; /* 64bit registers */ +#endif /* HASFPU and 64bit FP registers */ + + /* Ensure that any instructions with pending register updates are + cleared: */ + { + int loop; + for (loop = 0; (loop < PSLOTS); loop++) + pending_slot_reg[loop] = (LAST_EMBED_REGNUM + 1); + pending_in = pending_out = pending_total = 0; + } + +#if defined(HASFPU) + /* Initialise the FPU registers to the unknown state */ + { + int rn; + for (rn = 0; (rn < 32); rn++) + fpr_state[rn] = fmt_uninterpreted; + } +#endif /* HASFPU */ + + return; +} + +/* Description from page A-22 of the "MIPS IV Instruction Set" manual (revision 3.1) */ +/* Translate a virtual address to a physical address and cache + coherence algorithm describing the mechanism used to resolve the + memory reference. Given the virtual address vAddr, and whether the + reference is to Instructions ot Data (IorD), find the corresponding + physical address (pAddr) and the cache coherence algorithm (CCA) + used to resolve the reference. If the virtual address is in one of + the unmapped address spaces the physical address and the CCA are + determined directly by the virtual address. If the virtual address + is in one of the mapped address spaces then the TLB is used to + determine the physical address and access type; if the required + translation is not present in the TLB or the desired access is not + permitted the function fails and an exception is taken. + + NOTE: This function is extended to return an exception state. This, + along with the exception generation is used to notify whether a + valid address translation occured */ + +static int +AddressTranslation(vAddr,IorD,LorS,pAddr,CCA,host,raw) + unsigned long long vAddr; + int IorD; + int LorS; + unsigned long long *pAddr; + int *CCA; + int host; + int raw; +{ + int res = -1; /* TRUE : Assume good return */ + +#ifdef DEBUG + callback->printf_filtered(callback,"AddressTranslation(0x%08X%08X,%s,%s,...);\n",(unsigned int)(vAddr >> 32),(unsigned int)(vAddr & 0xFFFFFFFF),(IorD ? "isDATA" : "isINSTRUCTION"),(LorS ? "iSTORE" : "isLOAD")); +#endif + + /* Check that the address is valid for this memory model */ + + /* For a simple (flat) memory model, we simply pass virtual + addressess through (mostly) unchanged. */ + vAddr &= 0xFFFFFFFF; + *pAddr = vAddr; /* default for isTARGET */ + *CCA = Uncached; /* not used for isHOST */ + + /* NOTE: This is a duplicate of the code that appears in the + LoadMemory and StoreMemory functions. They should be merged into + a single function (that can be in-lined if required). */ + if ((vAddr >= membank_base) && (vAddr < (membank_base + membank_size))) { + if (host) + *pAddr = (int)&membank[((unsigned int)(vAddr - membank_base) & (membank_size - 1))]; + } else if ((vAddr >= monitor_base) && (vAddr < (monitor_base + monitor_size))) { + if (host) + *pAddr = (int)&monitor[((unsigned int)(vAddr - monitor_base) & (monitor_size - 1))]; + } else { +#if 1 /* def DEBUG */ + callback->printf_filtered(callback,"Failed: AddressTranslation(0x%08X%08X,%s,%s,...);\n",(unsigned int)(vAddr >> 32),(unsigned int)(vAddr & 0xFFFFFFFF),(IorD ? "isDATA" : "isINSTRUCTION"),(LorS ? "isSTORE" : "isLOAD")); +#endif /* DEBUG */ + res = 0; /* AddressTranslation has failed */ + *pAddr = -1; + if (!raw) /* only generate exceptions on real memory transfers */ + SignalException((LorS == isSTORE) ? AddressStore : AddressLoad); + else + callback->printf_filtered(callback,"AddressTranslation for %s %s from 0x%08X%08X failed\n",(IorD ? "data" : "instruction"),(LorS ? "store" : "load"),(unsigned int)(vAddr>>32),(unsigned int)(vAddr&0xFFFFFFFF)); + } + + return(res); +} + +/* Description from page A-23 of the "MIPS IV Instruction Set" manual (revision 3.1) */ +/* Prefetch data from memory. Prefetch is an advisory instruction for + which an implementation specific action is taken. The action taken + may increase performance, but must not change the meaning of the + program, or alter architecturally-visible state. */ +static void +Prefetch(CCA,pAddr,vAddr,DATA,hint) + int CCA; + unsigned long long pAddr; + unsigned long long vAddr; + int DATA; + int hint; +{ +#ifdef DEBUG + callback->printf_filtered(callback,"Prefetch(%d,0x%08X%08X,0x%08X%08X,%d,%d);\n",CCA,(unsigned int)(pAddr >> 32),(unsigned int)(pAddr & 0xFFFFFFFF),(unsigned int)(vAddr >> 32),(unsigned int)(vAddr & 0xFFFFFFFF),DATA,hint); +#endif /* DEBUG */ + + /* For our simple memory model we do nothing */ + return; +} + +/* Description from page A-22 of the "MIPS IV Instruction Set" manual (revision 3.1) */ +/* Load a value from memory. Use the cache and main memory as + specified in the Cache Coherence Algorithm (CCA) and the sort of + access (IorD) to find the contents of AccessLength memory bytes + starting at physical location pAddr. The data is returned in the + fixed width naturally-aligned memory element (MemElem). The + low-order two (or three) bits of the address and the AccessLength + indicate which of the bytes within MemElem needs to be given to the + processor. If the memory access type of the reference is uncached + then only the referenced bytes are read from memory and valid + within the memory element. If the access type is cached, and the + data is not present in cache, an implementation specific size and + alignment block of memory is read and loaded into the cache to + satisfy a load reference. At a minimum, the block is the entire + memory element. */ +static unsigned long long +LoadMemory(CCA,AccessLength,pAddr,vAddr,IorD,raw) + int CCA; + int AccessLength; + unsigned long long pAddr; + unsigned long long vAddr; + int IorD; + int raw; +{ + unsigned long long value; + +#ifdef DEBUG + if (membank == NULL) + callback->printf_filtered(callback,"DBG: LoadMemory(%d,%d,0x%08X%08X,0x%08X%08X,%s,%s)\n",CCA,AccessLength,(unsigned int)(pAddr >> 32),(unsigned int)(pAddr & 0xFFFFFFFF),(unsigned int)(vAddr >> 32),(unsigned int)(vAddr & 0xFFFFFFFF),(IorD ? "isDATA" : "isINSTRUCTION"),(raw ? "isRAW" : "isREAL")); +#endif /* DEBUG */ + +#if defined(WARN_MEM) + if (CCA != uncached) + callback->printf_filtered(callback,"SIM Warning: LoadMemory CCA (%d) is not uncached (currently all accesses treated as cached)\n",CCA); + + if (((pAddr & LOADDRMASK) + AccessLength) > LOADDRMASK) { + /* In reality this should be a Bus Error */ + sim_error("AccessLength of %d would extend over 64bit aligned boundary for physical address 0x%08X%08X\n",AccessLength,(unsigned int)(pAddr>>32),(unsigned int)(pAddr&0xFFFFFFFF)); + } +#endif /* WARN_MEM */ + + /* Decide which physical memory locations are being dealt with. At + this point we should be able to split the pAddr bits into the + relevant address map being simulated. If the "raw" variable is + set, the memory read being performed should *NOT* update any I/O + state or affect the CPU state. This also includes avoiding + affecting statistics gathering. */ + + /* If instruction fetch then we need to check that the two lo-order + bits are zero, otherwise raise a InstructionFetch exception: */ + if ((IorD == isINSTRUCTION) && ((pAddr & 0x3) != 0)) + SignalException(InstructionFetch); + else { + unsigned int index; + unsigned char *mem = NULL; + + /* TODO: #assert (isRAW) - check that our boolean is the correct way around */ + +#if defined(TRACE) + if (!raw) + dotrace(tracefh,((IorD == isDATA) ? 0 : 2),(unsigned int)(pAddr&0xFFFFFFFF),(AccessLength + 1),"load%s",((IorD == isDATA) ? "" : " instruction")); +#endif /* TRACE */ + + /* NOTE: Quicker methods of decoding the address space can be used + when a real memory map is being simulated (i.e. using hi-order + address bits to select device). */ + if ((pAddr >= membank_base) && (pAddr < (membank_base + membank_size))) { + index = ((unsigned int)(pAddr - membank_base) & (membank_size - 1)); + mem = membank; + } else if ((pAddr >= monitor_base) && (pAddr < (monitor_base + monitor_size))) { + index = ((unsigned int)(pAddr - monitor_base) & (monitor_size - 1)); + mem = monitor; + } + if (mem == NULL) + sim_error("Simulator memory not found for physical address 0x%08X%08X\n",(unsigned int)(pAddr>>32),(unsigned int)(pAddr&0xFFFFFFFF)); + else { + /* If we obtained the endianness of the host, and it is the same + as the target memory system we can optimise the memory + accesses. However, without that information we must perform + slow transfer, and hope that the compiler optimisation will + merge successive loads. */ + value = 0; /* no data loaded yet */ + + /* In reality we should always be loading a doubleword value (or + word value in 32bit memory worlds). The external code then + extracts the required bytes. However, to keep performance + high we only load the required bytes into the relevant + slots. */ + if (BigEndianMem) + switch (AccessLength) { /* big-endian memory */ + case AccessLength_DOUBLEWORD : + value |= ((unsigned long long)mem[index++] << 56); + case AccessLength_SEPTIBYTE : + value |= ((unsigned long long)mem[index++] << 48); + case AccessLength_SEXTIBYTE : + value |= ((unsigned long long)mem[index++] << 40); + case AccessLength_QUINTIBYTE : + value |= ((unsigned long long)mem[index++] << 32); + case AccessLength_WORD : + value |= ((unsigned int)mem[index++] << 24); + case AccessLength_TRIPLEBYTE : + value |= ((unsigned int)mem[index++] << 16); + case AccessLength_HALFWORD : + value |= ((unsigned int)mem[index++] << 8); + case AccessLength_BYTE : + value |= mem[index]; + break; + } + else { + index += (AccessLength + 1); + switch (AccessLength) { /* little-endian memory */ + case AccessLength_DOUBLEWORD : + value |= ((unsigned long long)mem[--index] << 56); + case AccessLength_SEPTIBYTE : + value |= ((unsigned long long)mem[--index] << 48); + case AccessLength_SEXTIBYTE : + value |= ((unsigned long long)mem[--index] << 40); + case AccessLength_QUINTIBYTE : + value |= ((unsigned long long)mem[--index] << 32); + case AccessLength_WORD : + value |= ((unsigned long long)mem[--index] << 24); + case AccessLength_TRIPLEBYTE : + value |= ((unsigned long long)mem[--index] << 16); + case AccessLength_HALFWORD : + value |= ((unsigned long long)mem[--index] << 8); + case AccessLength_BYTE : + value |= ((unsigned long long)mem[--index] << 0); + break; + } + } + +#ifdef DEBUG + printf("DBG: LoadMemory() : (offset %d) : value = 0x%08X%08X\n",(int)(pAddr & LOADDRMASK),(unsigned int)(value>>32),(unsigned int)(value&0xFFFFFFFF)); +#endif /* DEBUG */ + + /* TODO: We could try and avoid the shifts when dealing with raw + memory accesses. This would mean updating the LoadMemory and + StoreMemory routines to avoid shifting the data before + returning or using it. */ + if (!raw) { /* do nothing for raw accessess */ + if (BigEndianMem) + value <<= (((7 - (pAddr & LOADDRMASK)) - AccessLength) * 8); + else /* little-endian only needs to be shifted up to the correct byte offset */ + value <<= ((pAddr & LOADDRMASK) * 8); + } + +#ifdef DEBUG + printf("DBG: LoadMemory() : shifted value = 0x%08X%08X\n",(unsigned int)(value>>32),(unsigned int)(value&0xFFFFFFFF)); +#endif DEBUG + } + } + + return(value); +} + +/* Description from page A-23 of the "MIPS IV Instruction Set" manual (revision 3.1) */ +/* Store a value to memory. The specified data is stored into the + physical location pAddr using the memory hierarchy (data caches and + main memory) as specified by the Cache Coherence Algorithm + (CCA). The MemElem contains the data for an aligned, fixed-width + memory element (word for 32-bit processors, doubleword for 64-bit + processors), though only the bytes that will actually be stored to + memory need to be valid. The low-order two (or three) bits of pAddr + and the AccessLength field indicates which of the bytes within the + MemElem data should actually be stored; only these bytes in memory + will be changed. */ +static void +StoreMemory(CCA,AccessLength,MemElem,pAddr,vAddr,raw) + int CCA; + int AccessLength; + unsigned long long MemElem; + unsigned long long pAddr; + unsigned long long vAddr; + int raw; +{ +#ifdef DEBUG + callback->printf_filtered(callback,"DBG: StoreMemory(%d,%d,0x%08X%08X,0x%08X%08X,0x%08X%08X,%s)\n",CCA,AccessLength,(unsigned int)(MemElem >> 32),(unsigned int)(MemElem & 0xFFFFFFFF),(unsigned int)(pAddr >> 32),(unsigned int)(pAddr & 0xFFFFFFFF),(unsigned int)(vAddr >> 32),(unsigned int)(vAddr & 0xFFFFFFFF),(raw ? "isRAW" : "isREAL")); +#endif /* DEBUG */ + +#if defined(WARN_MEM) + if (CCA != uncached) + callback->printf_filtered(callback,"SIM Warning: StoreMemory CCA (%d) is not uncached (currently all accesses treated as cached)\n",CCA); + + if (((pAddr & LOADDRMASK) + AccessLength) > LOADDRMASK) + sim_error("AccessLength of %d would extend over 64bit aligned boundary for physical address 0x%08X%08X\n",AccessLength,(unsigned int)(pAddr>>32),(unsigned int)(pAddr&0xFFFFFFFF)); +#endif /* WARN_MEM */ + +#if defined(TRACE) + if (!raw) + dotrace(tracefh,1,(unsigned int)(pAddr&0xFFFFFFFF),(AccessLength + 1),"store"); +#endif /* TRACE */ + + /* See the comments in the LoadMemory routine about optimising + memory accesses. Also if we wanted to make the simulator smaller, + we could merge a lot of this code with the LoadMemory + routine. However, this would slow the simulator down with + run-time conditionals. */ + { + unsigned int index; + unsigned char *mem = NULL; + + if ((pAddr >= membank_base) && (pAddr < (membank_base + membank_size))) { + index = ((unsigned int)(pAddr - membank_base) & (membank_size - 1)); + mem = membank; + } else if ((pAddr >= monitor_base) && (pAddr < (monitor_base + monitor_size))) { + index = ((unsigned int)(pAddr - monitor_base) & (monitor_size - 1)); + mem = monitor; + } + + if (mem == NULL) + sim_error("Simulator memory not found for physical address 0x%08X%08X\n",(unsigned int)(pAddr>>32),(unsigned int)(pAddr&0xFFFFFFFF)); + else { + int shift = 0; + +#ifdef DEBUG + printf("DBG: StoreMemory: offset = %d MemElem = 0x%08X%08X\n",(unsigned int)(pAddr & LOADDRMASK),(unsigned int)(MemElem>>32),(unsigned int)(MemElem&0xFFFFFFFF)); +#endif /* DEBUG */ + + if (BigEndianMem) { + if (raw) + shift = ((7 - AccessLength) * 8); + else /* real memory access */ + shift = ((pAddr & LOADDRMASK) * 8); + MemElem <<= shift; + } else { + /* no need to shift raw little-endian data */ + if (!raw) + MemElem >>= ((pAddr & LOADDRMASK) * 8); + } + +#ifdef DEBUG + printf("DBG: StoreMemory: shift = %d MemElem = 0x%08X%08X\n",shift,(unsigned int)(MemElem>>32),(unsigned int)(MemElem&0xFFFFFFFF)); +#endif /* DEBUG */ + + if (BigEndianMem) { + switch (AccessLength) { /* big-endian memory */ + case AccessLength_DOUBLEWORD : + mem[index++] = (unsigned char)(MemElem >> 56); + MemElem <<= 8; + case AccessLength_SEPTIBYTE : + mem[index++] = (unsigned char)(MemElem >> 56); + MemElem <<= 8; + case AccessLength_SEXTIBYTE : + mem[index++] = (unsigned char)(MemElem >> 56); + MemElem <<= 8; + case AccessLength_QUINTIBYTE : + mem[index++] = (unsigned char)(MemElem >> 56); + MemElem <<= 8; + case AccessLength_WORD : + mem[index++] = (unsigned char)(MemElem >> 56); + MemElem <<= 8; + case AccessLength_TRIPLEBYTE : + mem[index++] = (unsigned char)(MemElem >> 56); + MemElem <<= 8; + case AccessLength_HALFWORD : + mem[index++] = (unsigned char)(MemElem >> 56); + MemElem <<= 8; + case AccessLength_BYTE : + mem[index++] = (unsigned char)(MemElem >> 56); + break; + } + } else { + index += (AccessLength + 1); + switch (AccessLength) { /* little-endian memory */ + case AccessLength_DOUBLEWORD : + mem[--index] = (unsigned char)(MemElem >> 56); + case AccessLength_SEPTIBYTE : + mem[--index] = (unsigned char)(MemElem >> 48); + case AccessLength_SEXTIBYTE : + mem[--index] = (unsigned char)(MemElem >> 40); + case AccessLength_QUINTIBYTE : + mem[--index] = (unsigned char)(MemElem >> 32); + case AccessLength_WORD : + mem[--index] = (unsigned char)(MemElem >> 24); + case AccessLength_TRIPLEBYTE : + mem[--index] = (unsigned char)(MemElem >> 16); + case AccessLength_HALFWORD : + mem[--index] = (unsigned char)(MemElem >> 8); + case AccessLength_BYTE : + mem[--index] = (unsigned char)(MemElem >> 0); + break; + } + } + } + } + + return; +} + +/* Description from page A-26 of the "MIPS IV Instruction Set" manual (revision 3.1) */ +/* Order loads and stores to synchronise shared memory. Perform the + action necessary to make the effects of groups of synchronizable + loads and stores indicated by stype occur in the same order for all + processors. */ +static void +SyncOperation(stype) + int stype; +{ +#ifdef DEBUG + callback->printf_filtered(callback,"SyncOperation(%d) : TODO\n",stype); +#endif /* DEBUG */ + return; +} + +/* Description from page A-26 of the "MIPS IV Instruction Set" manual (revision 3.1) */ +/* Signal an exception condition. This will result in an exception + that aborts the instruction. The instruction operation pseudocode + will never see a return from this function call. */ +static void +SignalException(exception) + int exception; +{ + /* Ensure that any active atomic read/modify/write operation will fail: */ + LLBIT = 0; + + switch (exception) { + /* TODO: For testing purposes I have been ignoring TRAPs. In + reality we should either simulate them, or allow the user to + ignore them at run-time. */ + case Trap : + callback->printf_filtered(callback,"Ignoring instruction TRAP (PC 0x%08X%08X)\n",(unsigned int)(IPC>>32),(unsigned int)(IPC&0xFFFFFFFF)); + break; + + case ReservedInstruction : + { + va_list ap; + unsigned int instruction; + va_start(ap,exception); + instruction = va_arg(ap,unsigned int); + va_end(ap); + /* Provide simple monitor support using ReservedInstruction + exceptions. The following code simulates the fixed vector + entry points into the IDT monitor by causing a simulator + trap, performing the monitor operation, and returning to + the address held in the $ra register (standard PCS return + address). This means we only need to pre-load the vector + space with suitable instruction values. For systems were + actual trap instructions are used, we would not need to + perform this magic. */ + if ((instruction & ~RSVD_INSTRUCTION_AMASK) == RSVD_INSTRUCTION) { + sim_monitor(instruction & RSVD_INSTRUCTION_AMASK); + PC = RA; /* simulate the return from the vector entry */ + /* NOTE: This assumes that a branch-and-link style + instruction was used to enter the vector (which is the + case with the current IDT monitor). */ + break; /* out of the switch statement */ + } /* else fall through to normal exception processing */ + callback->printf_filtered(callback,"DBG: ReservedInstruction 0x%08X at IPC = 0x%08X%08X\n",instruction,(unsigned int)(IPC>>32),(unsigned int)(IPC&0xFFFFFFFF)); + } + + default: +#if 1 /* def DEBUG */ + callback->printf_filtered(callback,"DBG: SignalException(%d) IPC = 0x%08X%08X\n",exception,(unsigned int)(IPC>>32),(unsigned int)(IPC&0xFFFFFFFF)); +#endif /* DEBUG */ + /* Store exception code into current exception id variable (used + by exit code): */ + + /* TODO: If not simulating exceptions then stop the simulator + execution. At the moment we always stop the simulation. */ + state |= (simSTOP | simEXCEPTION); + CAUSE = (exception << 2); + if (state & simDELAYSLOT) { + CAUSE |= cause_BD; + EPC = (IPC - 4); /* reference the branch instruction */ + } else + EPC = IPC; + /* The following is so that the simulator will continue from the + exception address on breakpoint operations. */ + PC = EPC; + break; + + case SimulatorFault: + { + va_list ap; + char *msg; + va_start(ap,exception); + msg = va_arg(ap,char *); + fprintf(stderr,"FATAL: Simulator error \"%s\"\n",msg); + va_end(ap); + } + exit(1); + } + + return; +} + +#if defined(WARN_RESULT) +/* Description from page A-26 of the "MIPS IV Instruction Set" manual (revision 3.1) */ +/* This function indicates that the result of the operation is + undefined. However, this should not affect the instruction + stream. All that is meant to happen is that the destination + register is set to an undefined result. To keep the simulator + simple, we just don't bother updating the destination register, so + the overall result will be undefined. If desired we can stop the + simulator by raising a pseudo-exception. */ +static void +UndefinedResult() +{ + callback->printf_filtered(callback,"UndefinedResult: IPC = 0x%08X%08X\n",(unsigned int)(IPC>>32),(unsigned int)(IPC&0xFFFFFFFF)); +#if 0 /* Disabled for the moment, since it actually happens a lot at the moment. */ + state |= simSTOP; +#endif + return; +} +#endif /* WARN_RESULT */ + +static void +CacheOp(op,pAddr,vAddr,instruction) + int op; + unsigned long long pAddr; + unsigned long long vAddr; + unsigned int instruction; +{ + /* If CP0 is not useable (User or Supervisor mode) and the CP0 + enable bit in the Status Register is clear - a coprocessor + unusable exception is taken. */ + callback->printf_filtered(callback,"TODO: Cache availability checking (PC = 0x%08X%08X)\n",(unsigned int)(IPC>>32),(unsigned int)(IPC&0xFFFFFFFF)); + + switch (op & 0x3) { + case 0: /* instruction cache */ + switch (op >> 2) { + case 0: /* Index Invalidate */ + case 1: /* Index Load Tag */ + case 2: /* Index Store Tag */ + case 4: /* Hit Invalidate */ + case 5: /* Fill */ + case 6: /* Hit Writeback */ + callback->printf_filtered(callback,"SIM Warning: Instruction CACHE operation %d to be coded\n",(op >> 2)); + break; + + default: + SignalException(ReservedInstruction,instruction); + break; + } + break; + + case 1: /* data cache */ + switch (op >> 2) { + case 0: /* Index Writeback Invalidate */ + case 1: /* Index Load Tag */ + case 2: /* Index Store Tag */ + case 3: /* Create Dirty */ + case 4: /* Hit Invalidate */ + case 5: /* Hit Writeback Invalidate */ + case 6: /* Hit Writeback */ + callback->printf_filtered(callback,"SIM Warning: Data CACHE operation %d to be coded\n",(op >> 2)); + break; + + default: + SignalException(ReservedInstruction,instruction); + break; + } + break; + + default: /* unrecognised cache ID */ + SignalException(ReservedInstruction,instruction); + break; + } + + return; +} + +/*-- FPU support routines ---------------------------------------------------*/ + +#if defined(HASFPU) /* Only needed when building FPU aware simulators */ + +#if 1 +#define SizeFGR() (GPRLEN) +#else +/* They depend on the CPU being simulated */ +#define SizeFGR() ((PROCESSOR_64BIT && ((SR & status_FR) == 1)) ? 64 : 32) +#endif + +/* Numbers are held in normalized form. The SINGLE and DOUBLE binary + formats conform to ANSI/IEEE Std 754-1985. */ +/* SINGLE precision floating: + * seeeeeeeefffffffffffffffffffffff + * s = 1bit = sign + * e = 8bits = exponent + * f = 23bits = fraction + */ +/* SINGLE precision fixed: + * siiiiiiiiiiiiiiiiiiiiiiiiiiiiiii + * s = 1bit = sign + * i = 31bits = integer + */ +/* DOUBLE precision floating: + * seeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffff + * s = 1bit = sign + * e = 11bits = exponent + * f = 52bits = fraction + */ +/* DOUBLE precision fixed: + * siiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii + * s = 1bit = sign + * i = 63bits = integer + */ + +/* Extract sign-bit: */ +#define FP_S_s(v) (((v) & ((unsigned)1 << 31)) ? 1 : 0) +#define FP_D_s(v) (((v) & ((unsigned long long)1 << 63)) ? 1 : 0) +/* Extract biased exponent: */ +#define FP_S_be(v) (((v) >> 23) & 0xFF) +#define FP_D_be(v) (((v) >> 52) & 0x7FF) +/* Extract unbiased Exponent: */ +#define FP_S_e(v) (FP_S_be(v) - 0x7F) +#define FP_D_e(v) (FP_D_be(v) - 0x3FF) +/* Extract complete fraction field: */ +#define FP_S_f(v) ((v) & ~((unsigned)0x1FF << 23)) +#define FP_D_f(v) ((v) & ~((unsigned long long)0xFFF << 52)) +/* Extract numbered fraction bit: */ +#define FP_S_fb(b,v) (((v) & (1 << (23 - (b)))) ? 1 : 0) +#define FP_D_fb(b,v) (((v) & (1 << (52 - (b)))) ? 1 : 0) + +/* Explicit QNaN values used when value required: */ +#define FPQNaN_SINGLE (0x7FBFFFFF) +#define FPQNaN_WORD (0x7FFFFFFF) +#define FPQNaN_DOUBLE (((unsigned long long)0x7FF7FFFF << 32) | 0xFFFFFFFF) +#define FPQNaN_LONG (((unsigned long long)0x7FFFFFFF << 32) | 0xFFFFFFFF) + +/* Explicit Infinity values used when required: */ +#define FPINF_SINGLE (0x7F800000) +#define FPINF_DOUBLE (((unsigned long long)0x7FF00000 << 32) | 0x00000000) + +#if 1 /* def DEBUG */ +#define RMMODE(v) (((v) == FP_RM_NEAREST) ? "Round" : (((v) == FP_RM_TOZERO) ? "Trunc" : (((v) == FP_RM_TOPINF) ? "Ceil" : "Floor"))) +#define DOFMT(v) (((v) == fmt_single) ? "single" : (((v) == fmt_double) ? "double" : (((v) == fmt_word) ? "word" : (((v) == fmt_long) ? "long" : (((v) == fmt_unknown) ? "<unknown>" : (((v) == fmt_uninterpreted) ? "<uninterpreted>" : "<format error>")))))) +#endif /* DEBUG */ + +static unsigned long long +ValueFPR(fpr,fmt) + int fpr; + FP_formats fmt; +{ + unsigned long long value; + int err = 0; + + /* Treat unused register values, as fixed-point 64bit values: */ + if ((fmt == fmt_uninterpreted) || (fmt == fmt_unknown)) +#if 1 + /* If request to read data as "uninterpreted", then use the current + encoding: */ + fmt = fpr_state[fpr]; +#else + fmt = fmt_long; +#endif + + /* For values not yet accessed, set to the desired format: */ + if (fpr_state[fpr] == fmt_uninterpreted) { + fpr_state[fpr] = fmt; +#ifdef DEBUG + printf("DBG: Register %d was fmt_uninterpreted. Now %s\n",fpr,DOFMT(fmt)); +#endif /* DEBUG */ + } + if (fmt != fpr_state[fpr]) { + callback->printf_filtered(callback,"Warning: FPR %d (format %s) being accessed with format %s - setting to unknown (PC = 0x%08X%08X)\n",fpr,DOFMT(fpr_state[fpr]),DOFMT(fmt),(unsigned int)(IPC>>32),(unsigned int)(IPC&0xFFFFFFFF)); + fpr_state[fpr] = fmt_unknown; + } + + if (fpr_state[fpr] == fmt_unknown) { + /* Set QNaN value: */ + switch (fmt) { + case fmt_single: + value = FPQNaN_SINGLE; + break; + + case fmt_double: + value = FPQNaN_DOUBLE; + break; + + case fmt_word: + value = FPQNaN_WORD; + break; + + case fmt_long: + value = FPQNaN_LONG; + break; + + default: + err = -1; + break; + } + } else if (SizeFGR() == 64) { + switch (fmt) { + case fmt_single: + case fmt_word: + value = (FGR[fpr] & 0xFFFFFFFF); + break; + + case fmt_uninterpreted: + case fmt_double: + case fmt_long: + value = FGR[fpr]; + break; + + default : + err = -1; + break; + } + } else if ((fpr & 1) == 0) { /* even registers only */ + switch (fmt) { + case fmt_single: + case fmt_word: + value = (FGR[fpr] & 0xFFFFFFFF); + break; + + case fmt_uninterpreted: + case fmt_double: + case fmt_long: + value = ((FGR[fpr+1] << 32) | (FGR[fpr] & 0xFFFFFFFF)); + break; + + default : + err = -1; + break; + } + } + + if (err) + SignalException(SimulatorFault,"Unrecognised FP format in ValueFPR()"); + +#ifdef DEBUG + printf("DBG: ValueFPR: fpr = %d, fmt = %s, value = 0x%08X%08X : PC = 0x%08X%08X : SizeFGR() = %d\n",fpr,DOFMT(fmt),(unsigned int)(value>>32),(unsigned int)(value&0xFFFFFFFF),(unsigned int)(IPC>>32),(unsigned int)(IPC&0xFFFFFFFF),SizeFGR()); +#endif /* DEBUG */ + + return(value); +} + +static void +StoreFPR(fpr,fmt,value) + int fpr; + FP_formats fmt; + unsigned long long value; +{ + int err = 0; + +#ifdef DEBUG + printf("DBG: StoreFPR: fpr = %d, fmt = %s, value = 0x%08X%08X : PC = 0x%08X%08X : SizeFGR() = %d\n",fpr,DOFMT(fmt),(unsigned int)(value>>32),(unsigned int)(value&0xFFFFFFFF),(unsigned int)(IPC>>32),(unsigned int)(IPC&0xFFFFFFFF),SizeFGR()); +#endif /* DEBUG */ + + if (SizeFGR() == 64) { + switch (fmt) { + case fmt_single : + case fmt_word : + FGR[fpr] = (((unsigned long long)0xDEADC0DE << 32) | (value & 0xFFFFFFFF)); + fpr_state[fpr] = fmt; + break; + + case fmt_uninterpreted: + case fmt_double : + case fmt_long : + FGR[fpr] = value; + fpr_state[fpr] = fmt; + break; + + default : + fpr_state[fpr] = fmt_unknown; + err = -1; + break; + } + } else if ((fpr & 1) == 0) { /* even register number only */ + switch (fmt) { + case fmt_single : + case fmt_word : + FGR[fpr+1] = 0xDEADC0DE; + FGR[fpr] = (value & 0xFFFFFFFF); + fpr_state[fpr + 1] = fmt; + fpr_state[fpr] = fmt; + break; + + case fmt_uninterpreted: + case fmt_double : + case fmt_long : + FGR[fpr+1] = (value >> 32); + FGR[fpr] = (value & 0xFFFFFFFF); + fpr_state[fpr + 1] = fmt; + fpr_state[fpr] = fmt; + break; + + default : + fpr_state[fpr] = fmt_unknown; + err = -1; + break; + } + } else + UndefinedResult(); + + if (err) + SignalException(SimulatorFault,"Unrecognised FP format in StoreFPR()"); + +#ifdef DEBUG + printf("DBG: StoreFPR: fpr[%d] = 0x%08X%08X (format %s)\n",fpr,(unsigned int)(FGR[fpr]>>32),(unsigned int)(FGR[fpr]&0xFFFFFFFF),DOFMT(fmt)); +#endif /* DEBUG */ + + return; +} + +static int +NaN(op,fmt) + unsigned long long op; + FP_formats fmt; +{ + int boolean = 0; + + /* Check if (((E - bias) == (E_max + 1)) && (fraction != 0)). We + know that the exponent field is biased... we we cheat and avoid + removing the bias value. */ + switch (fmt) { + case fmt_single: + boolean = ((FP_S_be(op) == 0xFF) && (FP_S_f(op) != 0)); + /* We could use "FP_S_fb(1,op)" to ascertain whether we are + dealing with a SNaN or QNaN */ + break; + case fmt_double: + boolean = ((FP_D_be(op) == 0x7FF) && (FP_D_f(op) != 0)); + /* We could use "FP_S_fb(1,op)" to ascertain whether we are + dealing with a SNaN or QNaN */ + break; + case fmt_word: + boolean = (op == FPQNaN_WORD); + break; + case fmt_long: + boolean = (op == FPQNaN_LONG); + break; + } + +#ifdef DEBUG +printf("DBG: NaN: returning %d for 0x%08X%08X (format = %s)\n",boolean,(unsigned int)(op>>32),(unsigned int)(op&0xFFFFFFFF),DOFMT(fmt)); +#endif /* DEBUG */ + + return(boolean); +} + +static int +Infinity(op,fmt) + unsigned long long op; + FP_formats fmt; +{ + int boolean = 0; + +#ifdef DEBUG + printf("DBG: Infinity: format %s 0x%08X%08X (PC = 0x%08X%08X)\n",DOFMT(fmt),(unsigned int)(op>>32),(unsigned int)(op&0xFFFFFFFF),(unsigned int)(IPC>>32),(unsigned int)(IPC&0xFFFFFFFF)); +#endif /* DEBUG */ + + /* Check if (((E - bias) == (E_max + 1)) && (fraction == 0)). We + know that the exponent field is biased... we we cheat and avoid + removing the bias value. */ + switch (fmt) { + case fmt_single: + boolean = ((FP_S_be(op) == 0xFF) && (FP_S_f(op) == 0)); + break; + case fmt_double: + boolean = ((FP_D_be(op) == 0x7FF) && (FP_D_f(op) == 0)); + break; + default: + printf("DBG: TODO: unrecognised format (%s) for Infinity check\n",DOFMT(fmt)); + break; + } + +#ifdef DEBUG + printf("DBG: Infinity: returning %d for 0x%08X%08X (format = %s)\n",boolean,(unsigned int)(op>>32),(unsigned int)(op&0xFFFFFFFF),DOFMT(fmt)); +#endif /* DEBUG */ + + return(boolean); +} + +static int +Less(op1,op2,fmt) + unsigned long long op1; + unsigned long long op2; + FP_formats fmt; +{ + int boolean = 0; + +#ifdef DEBUG + printf("DBG: Less: %s: op1 = 0x%08X%08X : op2 = 0x%08X%08X\n",DOFMT(fmt),(unsigned int)(op1>>32),(unsigned int)(op1&0xFFFFFFFF),(unsigned int)(op2>>32),(unsigned int)(op2&0xFFFFFFFF)); +#endif /* DEBUG */ + + /* TODO: Perform argument error checking */ + + /* The format type should already have been checked: */ + switch (fmt) { + case fmt_single: + { + unsigned int wop1 = (unsigned int)op1; + unsigned int wop2 = (unsigned int)op2; + boolean = (*(float *)&wop1 < *(float *)&wop2); + } + break; + case fmt_double: + boolean = (*(double *)&op1 < *(double *)&op2); + break; + } + +#ifdef DEBUG + printf("DBG: Less: returning %d (format = %s)\n",boolean,DOFMT(fmt)); +#endif /* DEBUG */ + + return(boolean); +} + +static int +Equal(op1,op2,fmt) + unsigned long long op1; + unsigned long long op2; + FP_formats fmt; +{ + int boolean = 0; + +#ifdef DEBUG + printf("DBG: Equal: %s: op1 = 0x%08X%08X : op2 = 0x%08X%08X\n",DOFMT(fmt),(unsigned int)(op1>>32),(unsigned int)(op1&0xFFFFFFFF),(unsigned int)(op2>>32),(unsigned int)(op2&0xFFFFFFFF)); +#endif /* DEBUG */ + + /* TODO: Perform argument error checking */ + + /* The format type should already have been checked: */ + switch (fmt) { + case fmt_single: + boolean = ((op1 & 0xFFFFFFFF) == (op2 & 0xFFFFFFFF)); + break; + case fmt_double: + boolean = (op1 == op2); + break; + } + +#ifdef DEBUG + printf("DBG: Equal: returning %d (format = %s)\n",boolean,DOFMT(fmt)); +#endif /* DEBUG */ + + return(boolean); +} + +static unsigned long long +Negate(op,fmt) + unsigned long long op; + FP_formats fmt; +{ + unsigned long long result; + +#ifdef DEBUG + printf("DBG: Negate: %s: op = 0x%08X%08X\n",DOFMT(fmt),(unsigned int)(op>>32),(unsigned int)(op&0xFFFFFFFF)); +#endif /* DEBUG */ + + /* The format type should already have been checked: */ + switch (fmt) { + case fmt_single: + { + unsigned int wop = (unsigned int)op; + float tmp = ((float)0.0 - *(float *)&wop); + result = (unsigned long long)*(unsigned int *)&tmp; + } + break; + case fmt_double: + { + double tmp = ((double)0.0 - *(double *)&op); + result = *(unsigned long long *)&tmp; + } + break; + } + + return(result); +} + +static unsigned long long +Add(op1,op2,fmt) + unsigned long long op1; + unsigned long long op2; + FP_formats fmt; +{ + unsigned long long result; + +#ifdef DEBUG + printf("DBG: Add: %s: op1 = 0x%08X%08X : op2 = 0x%08X%08X\n",DOFMT(fmt),(unsigned int)(op1>>32),(unsigned int)(op1&0xFFFFFFFF),(unsigned int)(op2>>32),(unsigned int)(op2&0xFFFFFFFF)); +#endif /* DEBUG */ + + /* TODO: Perform argument error checking */ + + /* The format type should already have been checked: */ + switch (fmt) { + case fmt_single: + { + unsigned int wop1 = (unsigned int)op1; + unsigned int wop2 = (unsigned int)op2; + float tmp = (*(float *)&wop1 + *(float *)&wop2); + result = (unsigned long long)*(unsigned int *)&tmp; + } + break; + case fmt_double: + { + double tmp = (*(double *)&op1 + *(double *)&op2); + result = *(unsigned long long *)&tmp; + } + break; + } + +#ifdef DEBUG + printf("DBG: Add: returning 0x%08X%08X (format = %s)\n",(unsigned int)(result>>32),(unsigned int)(result&0xFFFFFFFF),DOFMT(fmt)); +#endif /* DEBUG */ + + return(result); +} + +static unsigned long long +Sub(op1,op2,fmt) + unsigned long long op1; + unsigned long long op2; + FP_formats fmt; +{ + unsigned long long result; + +#ifdef DEBUG + printf("DBG: Sub: %s: op1 = 0x%08X%08X : op2 = 0x%08X%08X\n",DOFMT(fmt),(unsigned int)(op1>>32),(unsigned int)(op1&0xFFFFFFFF),(unsigned int)(op2>>32),(unsigned int)(op2&0xFFFFFFFF)); +#endif /* DEBUG */ + + /* TODO: Perform argument error checking */ + + /* The format type should already have been checked: */ + switch (fmt) { + case fmt_single: + { + unsigned int wop1 = (unsigned int)op1; + unsigned int wop2 = (unsigned int)op2; + float tmp = (*(float *)&wop1 - *(float *)&wop2); + result = (unsigned long long)*(unsigned int *)&tmp; + } + break; + case fmt_double: + { + double tmp = (*(double *)&op1 - *(double *)&op2); + result = *(unsigned long long *)&tmp; + } + break; + } + +#ifdef DEBUG + printf("DBG: Sub: returning 0x%08X%08X (format = %s)\n",(unsigned int)(result>>32),(unsigned int)(result&0xFFFFFFFF),DOFMT(fmt)); +#endif /* DEBUG */ + + return(result); +} + +static unsigned long long +Multiply(op1,op2,fmt) + unsigned long long op1; + unsigned long long op2; + FP_formats fmt; +{ + unsigned long long result; + +#ifdef DEBUG + printf("DBG: Multiply: %s: op1 = 0x%08X%08X : op2 = 0x%08X%08X\n",DOFMT(fmt),(unsigned int)(op1>>32),(unsigned int)(op1&0xFFFFFFFF),(unsigned int)(op2>>32),(unsigned int)(op2&0xFFFFFFFF)); +#endif /* DEBUG */ + + /* TODO: Perform argument error checking */ + + /* The format type should already have been checked: */ + switch (fmt) { + case fmt_single: + { + unsigned int wop1 = (unsigned int)op1; + unsigned int wop2 = (unsigned int)op2; + float tmp = (*(float *)&wop1 * *(float *)&wop2); + result = (unsigned long long)*(unsigned int *)&tmp; + } + break; + case fmt_double: + { + double tmp = (*(double *)&op1 * *(double *)&op2); + result = *(unsigned long long *)&tmp; + } + break; + } + +#ifdef DEBUG + printf("DBG: Multiply: returning 0x%08X%08X (format = %s)\n",(unsigned int)(result>>32),(unsigned int)(result&0xFFFFFFFF),DOFMT(fmt)); +#endif /* DEBUG */ + + return(result); +} + +static unsigned long long +Divide(op1,op2,fmt) + unsigned long long op1; + unsigned long long op2; + FP_formats fmt; +{ + unsigned long long result; + +#ifdef DEBUG + printf("DBG: Divide: %s: op1 = 0x%08X%08X : op2 = 0x%08X%08X\n",DOFMT(fmt),(unsigned int)(op1>>32),(unsigned int)(op1&0xFFFFFFFF),(unsigned int)(op2>>32),(unsigned int)(op2&0xFFFFFFFF)); +#endif /* DEBUG */ + + /* TODO: Perform argument error checking */ + + /* The format type should already have been checked: */ + switch (fmt) { + case fmt_single: + { + unsigned int wop1 = (unsigned int)op1; + unsigned int wop2 = (unsigned int)op2; + float tmp = (*(float *)&wop1 / *(float *)&wop2); + result = (unsigned long long)*(unsigned int *)&tmp; + } + break; + case fmt_double: + { + double tmp = (*(double *)&op1 / *(double *)&op2); + result = *(unsigned long long *)&tmp; + } + break; + } + +#ifdef DEBUG + printf("DBG: Divide: returning 0x%08X%08X (format = %s)\n",(unsigned int)(result>>32),(unsigned int)(result&0xFFFFFFFF),DOFMT(fmt)); +#endif /* DEBUG */ + + return(result); +} + +static unsigned long long +Recip(op,fmt) + unsigned long long op; + FP_formats fmt; +{ + unsigned long long result; + +#ifdef DEBUG + printf("DBG: Recip: %s: op = 0x%08X%08X\n",DOFMT(fmt),(unsigned int)(op>>32),(unsigned int)(op&0xFFFFFFFF)); +#endif /* DEBUG */ + + /* TODO: Perform argument error checking */ + + /* The format type should already have been checked: */ + switch (fmt) { + case fmt_single: + { + unsigned int wop = (unsigned int)op; + float tmp = ((float)1.0 / *(float *)&wop); + result = (unsigned long long)*(unsigned int *)&tmp; + } + break; + case fmt_double: + { + double tmp = ((double)1.0 / *(double *)&op); + result = *(unsigned long long *)&tmp; + } + break; + } + +#ifdef DEBUG + printf("DBG: Recip: returning 0x%08X%08X (format = %s)\n",(unsigned int)(result>>32),(unsigned int)(result&0xFFFFFFFF),DOFMT(fmt)); +#endif /* DEBUG */ + + return(result); +} + +static unsigned long long +SquareRoot(op,fmt) + unsigned long long op; + FP_formats fmt; +{ + unsigned long long result; + +#ifdef DEBUG + printf("DBG: SquareRoot: %s: op = 0x%08X%08X\n",DOFMT(fmt),(unsigned int)(op>>32),(unsigned int)(op&0xFFFFFFFF)); +#endif /* DEBUG */ + + /* TODO: Perform argument error checking */ + + /* The format type should already have been checked: */ + switch (fmt) { + case fmt_single: + { + unsigned int wop = (unsigned int)op; + float tmp = ((float)sqrt((double)*(float *)&wop)); + result = (unsigned long long)*(unsigned int *)&tmp; + } + break; + case fmt_double: + { + double tmp = (sqrt(*(double *)&op)); + result = *(unsigned long long *)&tmp; + } + break; + } + +#ifdef DEBUG + printf("DBG: SquareRoot: returning 0x%08X%08X (format = %s)\n",(unsigned int)(result>>32),(unsigned int)(result&0xFFFFFFFF),DOFMT(fmt)); +#endif /* DEBUG */ + + return(result); +} + +static unsigned long long +Convert(rm,op,from,to) + int rm; + unsigned long long op; + FP_formats from; + FP_formats to; +{ + unsigned long long result; + +#ifdef DEBUG + printf("DBG: Convert: mode %s : op 0x%08X%08X : from %s : to %s : (PC = 0x%08X%08X)\n",RMMODE(rm),(unsigned int)(op>>32),(unsigned int)(op&0xFFFFFFFF),DOFMT(from),DOFMT(to),(unsigned int)(IPC>>32),(unsigned int)(IPC&0xFFFFFFFF)); +#endif /* DEBUG */ + + /* The value "op" is converted to the destination format, rounding + using mode "rm". When the destination is a fixed-point format, + then a source value of Infinity, NaN or one which would round to + an integer outside the fixed point range then an IEEE Invalid + Operation condition is raised. */ + switch (to) { + case fmt_single: + { + float tmp; + switch (from) { + case fmt_double: + tmp = (float)(*(double *)&op); + break; + + case fmt_word: + tmp = (float)((int)(op & 0xFFFFFFFF)); + break; + + case fmt_long: + tmp = (float)((int)op); + break; + } + + switch (rm) { + case FP_RM_NEAREST: + printf("TODO: FPConvert: round(float)\n"); + break; + + case FP_RM_TOZERO: + printf("TODO: FPConvert: trunc(float)\n"); + break; + + case FP_RM_TOPINF: + printf("TODO: FPConvert: ceil(float)\n"); + break; + + case FP_RM_TOMINF: + printf("TODO: FPConvert: floor(float)\n"); + break; + } + result = (unsigned long long)*(unsigned int *)&tmp; + } + break; + + case fmt_double: + { + double tmp; + + switch (from) { + case fmt_single: + { + unsigned int wop = (unsigned int)op; + tmp = (double)(*(float *)&wop); + } + break; + + case fmt_word: + tmp = (double)((long long)SIGNEXTEND((op & 0xFFFFFFFF),32)); + break; + + case fmt_long: + tmp = (double)((long long)op); + break; + } + switch (rm) { + case FP_RM_NEAREST: + printf("TODO: FPConvert: round(double)\n"); + break; + + case FP_RM_TOZERO: + printf("TODO: FPConvert: trunc(double)\n"); + break; + + case FP_RM_TOPINF: + tmp = ceil(*(double *)&tmp); + break; + + case FP_RM_TOMINF: + tmp = floor(*(double *)&tmp); + break; + } + result = *(unsigned long long *)&tmp; + } + break; + + case fmt_word: + case fmt_long: + if (Infinity(op,from) || NaN(op,from) || (1 == 0/*TODO: check range */)) { + printf("DBG: TODO: update FCSR\n"); + SignalException(FPE); + } else { + if (to == fmt_word) { + unsigned int tmp; + switch (from) { + case fmt_single: + { + unsigned int wop = (unsigned int)op; + tmp = (unsigned int)*((float *)&wop); + } + break; + case fmt_double: + tmp = (unsigned int)*((double *)&op); +#ifdef DEBUG + printf("DBG: from double %.30f (0x%08X%08X) to word: 0x%08X\n",*((double *)&op),(unsigned int)(op>>32),(unsigned int)(op&0xFFFFFFFF),tmp); +#endif /* DEBUG */ + break; + } + result = (unsigned long long)tmp; + } else { /* fmt_long */ + switch (from) { + case fmt_single: + { + unsigned int wop = (unsigned int)op; + result = (unsigned long long)*((float *)&wop); + } + break; + case fmt_double: + result = (unsigned long long)*((double *)&op); + break; + } + } + } + break; + } + +#ifdef DEBUG + printf("DBG: Convert: returning 0x%08X%08X (to format = %s)\n",(unsigned int)(result>>32),(unsigned int)(result&0xFFFFFFFF),DOFMT(to)); +#endif /* DEBUG */ + + return(result); +} +#endif /* HASFPU */ + +/*-- co-processor support routines ------------------------------------------*/ + +static int +CoProcPresent(coproc_number) + unsigned int coproc_number; +{ + /* Return TRUE if simulator provides a model for the given co-processor number */ + return(0); +} + +static void +COP_LW(coproc_num,coproc_reg,memword) + int coproc_num, coproc_reg; + unsigned int memword; +{ + switch (coproc_num) { +#if defined(HASFPU) + case 1: +#ifdef DEBUG + printf("DBG: COP_LW: memword = 0x%08X (unsigned long long)memword = 0x%08X%08X\n",memword,(unsigned int)(((unsigned long long)memword)>>32),(unsigned int)(((unsigned long long)memword)&0xFFFFFFFF)); +#endif + StoreFPR(coproc_reg,fmt_uninterpreted,(unsigned long long)memword); + break; +#endif /* HASFPU */ + + default: + callback->printf_filtered(callback,"COP_LW(%d,%d,0x%08X) at IPC = 0x%08X%08X : TODO (architecture specific)\n",coproc_num,coproc_reg,memword,(unsigned int)(IPC>>32),(unsigned int)(IPC&0xFFFFFFFF)); + break; + } + + return; +} + +static void +COP_LD(coproc_num,coproc_reg,memword) + int coproc_num, coproc_reg; + unsigned long long memword; +{ + switch (coproc_num) { +#if defined(HASFPU) + case 1: + StoreFPR(coproc_reg,fmt_uninterpreted,memword); + break; +#endif /* HASFPU */ + + default: + callback->printf_filtered(callback,"COP_LD(%d,%d,0x%08X%08X) at IPC = 0x%08X%08X : TODO (architecture specific)\n",coproc_num,coproc_reg,(unsigned int)(memword >> 32),(unsigned int)(memword & 0xFFFFFFFF),(unsigned int)(IPC>>32),(unsigned int)(IPC&0xFFFFFFFF)); + break; + } + + return; +} + +static unsigned int +COP_SW(coproc_num,coproc_reg) + int coproc_num, coproc_reg; +{ + unsigned int value = 0; + switch (coproc_num) { +#if defined(HASFPU) + case 1: +#if 1 + value = (unsigned int)ValueFPR(coproc_reg,fmt_uninterpreted); +#else +#if 1 + value = (unsigned int)ValueFPR(coproc_reg,fpr_state[coproc_reg]); +#else +#ifdef DEBUG + printf("DBG: COP_SW: reg in format %s (will be accessing as single)\n",DOFMT(fpr_state[coproc_reg])); +#endif /* DEBUG */ + value = (unsigned int)ValueFPR(coproc_reg,fmt_single); +#endif +#endif + break; +#endif /* HASFPU */ + + default: + callback->printf_filtered(callback,"COP_SW(%d,%d) at IPC = 0x%08X%08X : TODO (architecture specific)\n",coproc_num,coproc_reg,(unsigned int)(IPC>>32),(unsigned int)(IPC&0xFFFFFFFF)); + break; + } + + return(value); +} + +static unsigned long long +COP_SD(coproc_num,coproc_reg) + int coproc_num, coproc_reg; +{ + unsigned long long value = 0; + switch (coproc_num) { +#if defined(HASFPU) + case 1: +#if 1 + value = ValueFPR(coproc_reg,fmt_uninterpreted); +#else +#if 1 + value = ValueFPR(coproc_reg,fpr_state[coproc_reg]); +#else +#ifdef DEBUG + printf("DBG: COP_SD: reg in format %s (will be accessing as double)\n",DOFMT(fpr_state[coproc_reg])); +#endif /* DEBUG */ + value = ValueFPR(coproc_reg,fmt_double); +#endif +#endif + break; +#endif /* HASFPU */ + + default: + callback->printf_filtered(callback,"COP_SD(%d,%d) at IPC = 0x%08X%08X : TODO (architecture specific)\n",coproc_num,coproc_reg,(unsigned int)(IPC>>32),(unsigned int)(IPC&0xFFFFFFFF)); + break; + } + + return(value); +} + +static void +decode_coproc(instruction) + unsigned int instruction; +{ + int coprocnum = ((instruction >> 26) & 3); + + switch (coprocnum) { + case 0: /* standard CPU control and cache registers */ + { + /* NOTEs: + Standard CP0 registers + 0 = Index R4000 VR4100 VR4300 + 1 = Random R4000 VR4100 VR4300 + 2 = EntryLo0 R4000 VR4100 VR4300 + 3 = EntryLo1 R4000 VR4100 VR4300 + 4 = Context R4000 VR4100 VR4300 + 5 = PageMask R4000 VR4100 VR4300 + 6 = Wired R4000 VR4100 VR4300 + 8 = BadVAddr R4000 VR4100 VR4300 + 9 = Count R4000 VR4100 VR4300 + 10 = EntryHi R4000 VR4100 VR4300 + 11 = Compare R4000 VR4100 VR4300 + 12 = SR R4000 VR4100 VR4300 + 13 = Cause R4000 VR4100 VR4300 + 14 = EPC R4000 VR4100 VR4300 + 15 = PRId R4000 VR4100 VR4300 + 16 = Config R4000 VR4100 VR4300 + 17 = LLAddr R4000 VR4100 VR4300 + 18 = WatchLo R4000 VR4100 VR4300 + 19 = WatchHi R4000 VR4100 VR4300 + 20 = XContext R4000 VR4100 VR4300 + 26 = PErr or ECC R4000 VR4100 VR4300 + 27 = CacheErr R4000 VR4100 + 28 = TagLo R4000 VR4100 VR4300 + 29 = TagHi R4000 VR4100 VR4300 + 30 = ErrorEPC R4000 VR4100 VR4300 + */ + int code = ((instruction >> 21) & 0x1F); + /* R4000 Users Manual (second edition) lists the following CP0 + instructions: + DMFC0 Doubleword Move From CP0 (VR4100 = 01000000001tttttddddd00000000000) + DMTC0 Doubleword Move To CP0 (VR4100 = 01000000101tttttddddd00000000000) + MFC0 word Move From CP0 (VR4100 = 01000000000tttttddddd00000000000) + MTC0 word Move To CP0 (VR4100 = 01000000100tttttddddd00000000000) + TLBR Read Indexed TLB Entry (VR4100 = 01000010000000000000000000000001) + TLBWI Write Indexed TLB Entry (VR4100 = 01000010000000000000000000000010) + TLBWR Write Random TLB Entry (VR4100 = 01000010000000000000000000000110) + TLBP Probe TLB for Matching Entry (VR4100 = 01000010000000000000000000001000) + CACHE Cache operation (VR4100 = 101111bbbbbpppppiiiiiiiiiiiiiiii) + ERET Exception return (VR4100 = 01000010000000000000000000011000) + */ + if (((code == 0x00) || (code == 0x04)) && ((instruction & 0x7FF) == 0)) { + int rt = ((instruction >> 16) & 0x1F); + int rd = ((instruction >> 11) & 0x1F); + if (code == 0x00) { /* MF : move from */ + callback->printf_filtered(callback,"Warning: MFC0 %d,%d not handled yet (architecture specific)\n",rt,rd); + GPR[rt] = 0xDEADC0DE; /* CPR[0,rd] */ + } else { /* MT : move to */ + /* CPR[0,rd] = GPR[rt]; */ + callback->printf_filtered(callback,"Warning: MTC0 %d,%d not handled yet (architecture specific)\n",rt,rd); + } + } else + callback->printf_filtered(callback,"Warning: Unrecognised COP0 instruction 0x%08X at IPC = 0x%08X%08X : No handler present\n",instruction,(unsigned int)(IPC>>32),(unsigned int)(IPC&0xFFFFFFFF)); + /* TODO: When executed an ERET or RFE instruction we should + clear LLBIT, to ensure that any out-standing atomic + read/modify/write sequence fails. */ + } + break; + + case 2: /* undefined co-processor */ + callback->printf_filtered(callback,"Warning: COP2 instruction 0x%08X at IPC = 0x%08X%08X : No handler present\n",instruction,(unsigned int)(IPC>>32),(unsigned int)(IPC&0xFFFFFFFF)); + break; + + case 1: /* should not occur (FPU co-processor) */ + case 3: /* should not occur (FPU co-processor) */ + SignalException(ReservedInstruction,instruction); + break; + } + + return; +} + +/*-- instruction simulation -------------------------------------------------*/ + +static void +simulate () +{ + unsigned int pipeline_count = 1; + +#ifdef DEBUG + if (membank == NULL) { + printf("DBG: simulate() entered with no memory\n"); + exit(1); + } +#endif /* DEBUG */ + +#if 0 /* Disabled to check that everything works OK */ + /* The VR4300 seems to sign-extend the PC on its first + access. However, this may just be because it is currently + configured in 32bit mode. However... */ + PC = SIGNEXTEND(PC,32); +#endif + + /* main controlling loop */ + do { + /* Fetch the next instruction from the simulator memory: */ + unsigned long long vaddr = (unsigned long long)PC; + unsigned long long paddr; + int cca; + unsigned int instruction; + int dsstate = (state & simDELAYSLOT); + +#ifdef DEBUG + { + printf("DBG: state = 0x%08X :",state); + if (state & simSTOP) printf(" simSTOP"); + if (state & simSTEP) printf(" simSTEP"); + if (state & simHALTEX) printf(" simHALTEX"); + if (state & simHALTIN) printf(" simHALTIN"); + if (state & simBE) printf(" simBE"); + } +#endif /* DEBUG */ + +#ifdef DEBUG + if (dsstate) + callback->printf_filtered(callback,"DBG: DSPC = 0x%08X%08X\n",(unsigned int)(DSPC>>32),(unsigned int)(DSPC&0xFFFFFFFF)); +#endif /* DEBUG */ + + if (AddressTranslation(PC,isINSTRUCTION,isLOAD,&paddr,&cca,isTARGET,isREAL)) { /* Copy the action of the LW instruction */ + unsigned int reverse = (ReverseEndian ? 1 : 0); + unsigned int bigend = (BigEndianCPU ? 1 : 0); + unsigned long long value; + unsigned int byte; + paddr = ((paddr & ~0x7) | ((paddr & 0x7) ^ (reverse << 2))); + value = LoadMemory(cca,AccessLength_WORD,paddr,vaddr,isINSTRUCTION,isREAL); + byte = ((vaddr & 0x7) ^ (bigend << 2)); + instruction = ((value >> (8 * byte)) & 0xFFFFFFFF); + } else { + fprintf(stderr,"Cannot translate address for PC = 0x%08X%08X failed\n",(unsigned int)(PC>>32),(unsigned int)(PC&0xFFFFFFFF)); + exit(1); + } + +#ifdef DEBUG + callback->printf_filtered(callback,"DBG: fetched 0x%08X from PC = 0x%08X%08X\n",instruction,(unsigned int)(PC>>32),(unsigned int)(PC&0xFFFFFFFF)); +#endif /* DEBUG */ + +#if !defined(FASTSIM) || defined(PROFILE) + instruction_fetches++; +#if defined(PROFILE) + if ((state & simPROFILE) && ((instruction_fetches % profile_frequency) == 0) && profile_hist) { + int n = ((unsigned int)(PC - profile_minpc) >> (profile_shift + 2)); + if (n < profile_nsamples) { + /* NOTE: The counts for the profiling bins are only 16bits wide */ + if (profile_hist[n] != USHRT_MAX) + (profile_hist[n])++; + } + } +#endif /* PROFILE */ +#endif /* !FASTSIM && PROFILE */ + + IPC = PC; /* copy PC for this instruction */ + /* This is required by exception processing, to ensure that we can + cope with exceptions in the delay slots of branches that may + already have changed the PC. */ + PC += 4; /* increment ready for the next fetch */ + /* NOTE: If we perform a delay slot change to the PC, this + increment is not requuired. However, it would make the + simulator more complicated to try and avoid this small hit. */ + + /* Currently this code provides a simple model. For more + complicated models we could perform exception status checks at + this point, and set the simSTOP state as required. This could + also include processing any hardware interrupts raised by any + I/O model attached to the simulator context. + + Support for "asynchronous" I/O events within the simulated world + could be providing by managing a counter, and calling a I/O + specific handler when a particular threshold is reached. On most + architectures a decrement and check for zero operation is + usually quicker than an increment and compare. However, the + process of managing a known value decrement to zero, is higher + than the cost of using an explicit value UINT_MAX into the + future. Which system is used will depend on how complicated the + I/O model is, and how much it is likely to affect the simulator + bandwidth. + + If events need to be scheduled further in the future than + UINT_MAX event ticks, then the I/O model should just provide its + own counter, triggered from the event system. */ + + /* MIPS pipeline ticks. To allow for future support where the + pipeline hit of individual instructions is known, this control + loop manages a "pipeline_count" variable. It is initialised to + 1 (one), and will only be changed by the simulator engine when + executing an instruction. If the engine does not have access to + pipeline cycle count information then all instructions will be + treated as using a single cycle. NOTE: A standard system is not + provided by the default simulator because different MIPS + architectures have different cycle counts for the same + instructions. */ + +#if defined(HASFPU) + /* Set previous flag, depending on current: */ + if (state & simPCOC0) + state |= simPCOC1; + else + state &= ~simPCOC1; + /* and update the current value: */ + if (GETFCC(0)) + state |= simPCOC0; + else + state &= ~simPCOC0; +#endif /* HASFPU */ + +/* NOTE: For multi-context simulation environments the "instruction" + variable should be local to this routine. */ + +/* Shorthand accesses for engine. Note: If we wanted to use global + variables (and a single-threaded simulator engine), then we can + create the actual variables with these names. */ + + if (!(state & simSKIPNEXT)) { + /* Include the simulator engine */ +#include "engine.c" +#if ((GPRLEN == 64) && !defined(PROCESSOR_64BIT)) || ((GPRLEN == 32) && defined(PROCESSOR_64BIT)) +#error "Mismatch between run-time simulator code and simulation engine" +#endif + +#if defined(WARN_LOHI) + /* Decrement the HI/LO validity ticks */ + if (HIACCESS > 0) + HIACCESS--; + if (LOACCESS > 0) + LOACCESS--; +#endif /* WARN_LOHI */ + +#if defined(WARN_ZERO) + /* For certain MIPS architectures, GPR[0] is hardwired to zero. We + should check for it being changed. It is better doing it here, + than within the simulator, since it will help keep the simulator + small. */ + if (ZERO != 0) { + callback->printf_filtered(callback,"SIM Warning: The ZERO register has been updated with 0x%08X%08X (PC = 0x%08X%08X)\nSIM Warning: Resetting back to zero\n",(unsigned int)(ZERO>>32),(unsigned int)(ZERO&0xFFFFFFFF),(unsigned int)(IPC>>32),(unsigned int)(IPC&0xFFFFFFFF)); + ZERO = 0; /* reset back to zero before next instruction */ + } +#endif /* WARN_ZERO */ + } else /* simSKIPNEXT check */ + state &= ~simSKIPNEXT; + + /* If the delay slot was active before the instruction is + executed, then update the PC to its new value: */ + if (dsstate) { +#ifdef DEBUG + printf("DBG: dsstate set before instruction execution - updating PC to 0x%08X%08X\n",(unsigned int)(DSPC>>32),(unsigned int)(DSPC&0xFFFFFFFF)); +#endif /* DEBUG */ + PC = DSPC; + state &= ~simDELAYSLOT; + } + + if (MIPSISA < 4) { /* The following is only required on pre MIPS IV processors: */ + /* Deal with pending register updates: */ +#ifdef DEBUG + printf("DBG: EMPTY BEFORE pending_in = %d, pending_out = %d, pending_total = %d\n",pending_in,pending_out,pending_total); +#endif /* DEBUG */ + if (pending_out != pending_in) { + int loop; + int index = pending_out; + int total = pending_total; + if (pending_total == 0) { + fprintf(stderr,"FATAL: Mis-match on pending update pointers\n"); + exit(1); + } + for (loop = 0; (loop < total); loop++) { +#ifdef DEBUG + printf("DBG: BEFORE index = %d, loop = %d\n",index,loop); +#endif /* DEBUG */ + if (pending_slot_reg[index] != (LAST_EMBED_REGNUM + 1)) { +#ifdef DEBUG + printf("pending_slot_count[%d] = %d\n",index,pending_slot_count[index]); +#endif /* DEBUG */ + if (--(pending_slot_count[index]) == 0) { +#ifdef DEBUG + printf("pending_slot_reg[%d] = %d\n",index,pending_slot_reg[index]); + printf("pending_slot_value[%d] = 0x%08X%08X\n",index,(unsigned int)(pending_slot_value[index]>>32),(unsigned int)(pending_slot_value[index]&0xFFFFFFFF)); +#endif /* DEBUG */ + if (pending_slot_reg[index] == COCIDX) { + SETFCC(0,((FCR31 & (1 << 23)) ? 1 : 0)); + } else { + registers[pending_slot_reg[index]] = pending_slot_value[index]; +#if defined(HASFPU) + /* The only time we have PENDING updates to FPU + registers, is when performing binary transfers. This + means we should update the register type field. */ + if ((pending_slot_reg[index] >= FGRIDX) && (pending_slot_reg[index] < (FGRIDX + 32))) + fpr_state[pending_slot_reg[index]] = fmt_uninterpreted; +#endif /* HASFPU */ + } +#ifdef DEBUG + printf("registers[%d] = 0x%08X%08X\n",pending_slot_reg[index],(unsigned int)(registers[pending_slot_reg[index]]>>32),(unsigned int)(registers[pending_slot_reg[index]]&0xFFFFFFFF)); +#endif /* DEBUG */ + pending_slot_reg[index] = (LAST_EMBED_REGNUM + 1); + pending_out++; + if (pending_out == PSLOTS) + pending_out = 0; + pending_total--; + } + } +#ifdef DEBUG + printf("DBG: AFTER index = %d, loop = %d\n",index,loop); +#endif /* DEBUG */ + index++; + if (index == PSLOTS) + index = 0; + } + } +#ifdef DEBUG + printf("DBG: EMPTY AFTER pending_in = %d, pending_out = %d, pending_total = %d\n",pending_in,pending_out,pending_total); +#endif /* DEBUG */ + } + +#if !defined(FASTSIM) + pipeline_ticks += pipeline_count; +#endif /* FASTSIM */ + + if (state & simSTEP) + state |= simSTOP; + } while (!(state & simSTOP)); + +#ifdef DEBUG + if (membank == NULL) { + printf("DBG: simulate() LEAVING with no memory\n"); + exit(1); + } +#endif /* DEBUG */ + + return; +} + +/*---------------------------------------------------------------------------*/ +/*> EOF interp.c <*/ |