From: Jackie Smith Cashion Date: Fri, 1 Dec 1995 16:42:44 +0000 (+0000) Subject: * gencode.c: Tidied instruction decoding, and added FP instruction X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=8bae0a0c484e4d9b04bc169196587d32028a90a8;p=binutils-gdb.git * 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. --- diff --git a/sim/mips/ChangeLog b/sim/mips/ChangeLog new file mode 100644 index 00000000000..d5e8806272d --- /dev/null +++ b/sim/mips/ChangeLog @@ -0,0 +1,12 @@ +Fri Dec 1 16:37:19 1995 James G. Smith + + * gencode.c: Tidied instruction decoding, and added FP instruction + support. + + * interp.c: Added dineroIII, and BSD profiling support. Also + run-time FP handling. + +Sun Oct 22 00:57:18 1995 James G. Smith + + * Changelog, Makefile.in, README.Cygnus, configure, configure.in, + gencode.c, interp.c, support.h: created. diff --git a/sim/mips/gencode.c b/sim/mips/gencode.c index 330fb8b99fd..41a8292db6a 100644 --- a/sim/mips/gencode.c +++ b/sim/mips/gencode.c @@ -101,7 +101,6 @@ #define FEATURE_HASFPU (1 << 1) /* 0 = no FPU; 1 = include FPU */ #define FEATURE_FPSINGLE (1 << 1) /* 0 = double; 1 = single (only used if FEATURE_HASFPU defined) */ #define FEATURE_GP64 (1 << 2) /* 0 = GPRLEN 32; 1 = GPRLEN 64 */ -#define FEATURE_FP64 (1 << 3) /* 0 = FPRLEN 32; 1 = FPRLEN 64 (only used if FEATURE_HASFPU defined */ #define FEATURE_FAST (1 << 17) /* 0 = normal; 1 = disable features that slow performance */ #define FEATURE_WARN_STALL (1 << 24) /* 0 = nothing; 1 = generate warnings when pipeline would stall */ #define FEATURE_WARN_LOHI (1 << 25) /* 0 = nothing; 1 = generate warnings when LO/HI corrupted */ @@ -110,7 +109,11 @@ #define FEATURE_WARN_R31 (1 << 28) /* 0 = nothing; 1 = generate warnings if r31 used dangerously */ #define FEATURE_WARN_RESULT (1 << 29) /* 0 = nothing; 1 = generate warnings when undefined results may occur */ +#if 1 +#define FEATURE_WARNINGS (FEATURE_WARN_STALL | FEATURE_WARN_LOHI | FEATURE_WARN_ZERO | FEATURE_WARN_R31) +#else #define FEATURE_WARNINGS (FEATURE_WARN_STALL | FEATURE_WARN_LOHI | FEATURE_WARN_ZERO | FEATURE_WARN_R31 | FEATURE_WARN_RESULT) +#endif /* FEATURE_WARN_STALL */ /* If MIPS I we want to raise a warning if an attempt is made to @@ -151,29 +154,42 @@ #include #include #include -#include "opcode/mips.h" /* From Cygnus progressive tree */ +#include "opcode/mips.h" -/* The following manifests do not appear in "progressive/include/opcode/mips.h" */ +/* The following manifests do not appear in "include/opcode/mips.h" */ #define OP_SH_LOCC (8) /* FP condition code */ #define OP_SH_HICC (18) /* FP condition code */ #define OP_MASK_CC (0x07) -#define OP_SH_CC (8) /* FP condition code */ -#define OP_MASK_CC (0x07) + +#define OP_SH_COP1NORM (25) /* Normal COP1 encoding */ +#define OP_MASK_COP1NORM (0x01) /* a single bit */ +#define OP_SH_COP1SPEC (21) /* Special COP1 encodings (within format field) */ +#define OP_MASK_COP1SPEC (0x0F) /* Bits we are interested in */ +#define OP_MASK_COP1SCLR (0x04) /* Bits to be explicitly cleared */ +#define OP_MASK_COP1CMP (0x3) /* Unfortunately another conditional field needed to decode the FP instructions */ +#define OP_SH_COP1CMP (4) + #define OP_SH_FORMAT (21) /* FP short format field */ #define OP_MASK_FORMAT (0x07) + #define OP_SH_TRUE (16) #define OP_MASK_TRUE (0x01) + #define OP_SH_GE (17) #define OP_MASK_GE (0x01) + #define OP_SH_UNSIGNED (16) #define OP_MASK_UNSIGNED (0x01) + #define OP_SH_HINT (16) #define OP_MASK_HINT (0x1F) +#if 0 #ifndef TRUE #define TRUE (1 == 1) #define FALSE (1 == 0) #endif +#endif /*---------------------------------------------------------------------------*/ @@ -198,42 +214,41 @@ typedef struct operand_encoding { /* Values for the "flags" field: */ #define OP_NONE (0 << 0) /* To keep the source tidy */ #define OP_GPR (1 << 0) /* Get operand from integer register bank */ -#define OP_FGR (1 << 1) /* Get operand from FP register bank */ -#define OP_SIGNX (1 << 2) /* Sign-extend the operand */ -#define OP_SHIFT2 (1 << 3) /* Shift field left by 2 */ -#define OP_BITS5 (1 << 4) /* Only take the lo 5-bits of the operand */ +#define OP_SIGNX (1 << 1) /* Sign-extend the operand */ +#define OP_SHIFT2 (1 << 2) /* Shift field left by 2 */ +#define OP_BITS5 (1 << 3) /* Only take the lo 5-bits of the operand */ struct operand_encoding opfields[] = { - {'0',-1,-1,"" ,"" ,(OP_NONE)}, /* special case for explicit zero */ - {'1',-1,-1,"" ,"" ,(OP_NONE)}, /* special case for explicit one */ - {'?',-1,-1,"" ,"" ,(OP_NONE)}, /* undefined (do not care at this level) */ + {'0',-1,-1,"", "", (OP_NONE)}, /* special case for explicit zero */ + {'1',-1,-1,"", "", (OP_NONE)}, /* special case for explicit one */ + {'?',-1,-1,"", "", (OP_NONE)}, /* undefined (do not care at this level) */ /* The rest are the explicit operand fields: */ - {'a', 6, 5,"int" ,"op1" ,(OP_NONE)}, /* shift amount (or hint) */ - {'b',21, 5,"t_fpreg","base" ,(OP_FGR)}, /* FP base/fr register */ - {'c',16, 1,"int" ,"boolean" ,(OP_NONE)}, /* TRUE or FALSE boolean */ - {'d',11, 5,"int" ,"destreg" ,(OP_NONE)}, /* integer destination/rd register */ - {'e', 0,16,"t_reg" ,"offset" ,(OP_SIGNX)}, /* signed offset (lo-3bits must be zero) */ - {'f',25, 1,"int" ,"add" ,(OP_NONE)}, /* TRUE if ADD instruction, FALSE if move or branch */ - {'g',16, 5,"t_reg" ,"op2" ,(OP_GPR)}, /* integer source rt register */ - {'h', 0,16,"t_reg" ,"offset" ,(OP_SIGNX)}, /* signed offset (lo-1bit must be zero) */ - {'i', 0,16,"t_reg" ,"op2" ,(OP_SIGNX)}, /* signed immediate (op2) */ - {'j', 0,26,"ut_reg" ,"op1" ,(OP_SHIFT2)},/* shifted left 2 bits and combined with hi-order bits of address in the delay slot */ - {'k',16, 5,"t_fpreg","index" ,(OP_FGR)}, /* FP index register */ - {'l', 0,16,"t_reg" ,"offset" ,(OP_SIGNX | OP_SHIFT2)}, /* signed offset shifted left 2 to make 18bit signed offset */ - {'m',21, 3,"int" ,"format" ,(OP_NONE)}, /* FP format field */ - {'n',16, 5,"int" ,"hint" ,(OP_NONE)}, /* hint */ - {'o',21, 5,"t_reg" ,"op1" ,(OP_GPR | OP_BITS5)}, /* integer source/rs register (but never treated as 32bit word) */ - {'p', 8, 3,"int" ,"condition_code",(OP_NONE)}, /* FP condition code field */ - {'q',18, 3,"int" ,"condition_code",(OP_NONE)}, /* FP condition code field */ - {'r', 6, 5,"int" ,"destreg" ,(OP_NONE)}, /* FP fd register */ - {'s',21, 5,"t_reg" ,"op1" ,(OP_GPR)}, /* integer source/rs register */ - {'t',16, 5,"int" ,"destreg" ,(OP_NONE)}, /* integer target rt (destination) register */ - {'u', 0, 4,"int" ,"cmpflags" ,(OP_NONE)}, /* FP comparison control flags */ - {'v',11, 5,"t_fpreg","op1" ,(OP_FGR)}, /* FP fs register or hint */ - {'w', 0,16,"t_reg" ,"offset" ,(OP_SIGNX)}, /* signed offset (lo-2bits must be zero) */ - {'x',24, 1,"int" ,"branch" ,(OP_NONE)}, /* TRUE if branch instruction - FALSE if move or add */ - {'y', 0,16,"t_reg" ,"offset" ,(OP_SIGNX)}, /* signed offset */ - {'z', 0,16,"ut_reg" ,"op2" ,(OP_NONE)}, /* unsigned immediate (zero extended) */ + {'a', 6, 5,"int", "op1", (OP_NONE)}, /* shift amount (or hint) */ + {'b',21, 5,"int", "fr", (OP_NONE)}, /* fr register */ + {'c',16, 1,"int", "boolean", (OP_NONE)}, /* TRUE or FALSE boolean */ + {'d',11, 5,"int", "destreg", (OP_NONE)}, /* integer destination/rd register */ + {'e', 0,16,"t_reg", "offset", (OP_SIGNX)}, /* signed offset (lo-3bits must be zero) */ + {'f',17, 1,"int", "likely", (OP_NONE)}, /* set if branch LIKELY */ + {'g',16, 5,"t_reg", "op2", (OP_GPR)}, /* integer source rt register */ + {'h', 0,16,"t_reg", "offset", (OP_SIGNX)}, /* signed offset (lo-1bit must be zero) */ + {'i', 0,16,"t_reg", "op2", (OP_SIGNX)}, /* signed immediate (op2) */ + {'j', 0,26,"ut_reg","op1", (OP_SHIFT2)},/* shifted left 2 bits and combined with hi-order bits of address in the delay slot */ + {'k',16, 5,"int", "ft", (OP_NONE)}, + {'l', 0,16,"t_reg", "offset", (OP_SIGNX | OP_SHIFT2)}, /* signed offset shifted left 2 to make 18bit signed offset */ + {'m',21, 3,"int", "format", (OP_NONE)}, /* FP format field */ + {'n',16, 5,"int", "hint", (OP_NONE)}, /* hint */ + {'o',21, 5,"t_reg", "op1", (OP_GPR | OP_BITS5)}, /* integer source/rs register (but never treated as 32bit word) */ + {'p', 8, 3,"int", "condition_code",(OP_NONE)}, /* FP condition code field */ + {'q',18, 3,"int", "condition_code",(OP_NONE)}, /* FP condition code field */ + {'r', 6, 5,"int", "destreg", (OP_NONE)}, /* FP fd register */ + {'s',21, 5,"t_reg", "op1", (OP_GPR)}, /* integer source/rs register */ + {'t',16, 5,"int", "destreg", (OP_NONE)}, /* integer target rt (destination) register */ + {'u', 0, 4,"int", "cmpflags", (OP_NONE)}, /* FP comparison control flags */ + {'v',11, 5,"int", "fs", (OP_NONE)}, /* FP fs register (or PREFX hint) */ + {'w', 0,16,"t_reg", "offset", (OP_SIGNX)}, /* signed offset (lo-2bits must be zero) */ + {'x',23, 1,"int", "to", (OP_NONE)}, /* TRUE if move To; FALSE if move From */ + {'y', 0,16,"t_reg", "offset", (OP_SIGNX)}, /* signed offset */ + {'z', 0,16,"ut_reg","op2", (OP_NONE)}, /* unsigned immediate (zero extended) */ }; /* Main instruction encoding types: */ @@ -243,6 +258,7 @@ typedef enum { REGIMM, COP1, COP1X, + COP1S, /* These instructions live in the reserved FP format values: 0..15,18-19,22-31 */ UNKNOWN } inst_type; @@ -271,6 +287,7 @@ typedef enum { CACHE, /* co-processor 0 CACHE instruction */ MADD16, /* VR4100 specific multiply-add extensions */ FPMOVE, + FPMOVEC, FPFLOOR, FPCEIL, FPTRUNC, @@ -281,10 +298,7 @@ typedef enum { FPMUL, FPSUB, FPADD, - FPMAB, /* Special encoding for MOVE, ADD and BRANCH instructions */ FPPREFX, - FPSTORE, - FPLOAD, FPRECIP, FPSQRT, FPCONVERT, @@ -295,17 +309,18 @@ typedef enum { /* Flags field: */ #define NONE (0 << 0) /* Zero value (used to keep source tidy) */ #define SIM_SH_SIZE (0) -#define SIM_MASK_SIZE (0x3) -#define BYTE (0) -#define HALFWORD (1) -#define WORD (2) -#define DOUBLEWORD (3) +#define SIM_MASK_SIZE (0x7) +#define BYTE (0) /* 8bit */ +#define HALFWORD (1) /* 16bit */ +#define WORD (2) /* 32bit */ +#define DOUBLEWORD (3) /* 64bit */ +#define SINGLE (4) /* single precision FP */ +#define DOUBLE (5) /* double precision FP */ /* Shorthand to get the size field from the flags value: */ #define GETDATASIZE() ((MIPS_DECODE[loop].flags >> SIM_SH_SIZE) & SIM_MASK_SIZE) /* The rest are single bit flags: */ -#define COPROC (1 << 2) #define MULTIPLY (1 << 3) /* actually FP multiply ADD/SUB modifier */ #define EQ (1 << 4) #define GT (1 << 5) @@ -329,10 +344,15 @@ typedef enum { #define WORD32 (1 << 23) #define FP (1 << 24) /* Floating Point operation */ #define FIXED (1 << 25) /* fixed point arithmetic */ -#define SINGLE (1 << 26) /* single precision FP */ -#define DOUBLE (1 << 27) /* double precision FP */ -/**** Bits 28, 29 and 30 available ****/ +#define COPROC (1 << 26) +#define INTEGER (1 << 27) +#define CONDITIONAL (1 << 28) +#define RECIP (1 << 29) +#define CONTROL (1 << 30) #define NOARG (1 << 31) /* Instruction has no (defined) operands */ +/* NOTE: We can overload the use of certain of these flags, since not + all options are applicable to all instruction types. This will free + up more space for new flags. */ typedef struct instruction { char *name; /* ASCII mnemonic name */ @@ -369,12 +389,13 @@ struct instruction MIPS_DECODE[] = { /* The instructions are alphabetical, and not in instruction bit-order: */ {"ABS", 1,"01000110mmm00000dddddaaaaa000101",COP1, FPABS, (FP)}, {"ADD", 1,"000000sssssgggggddddd00000100000",SPECIAL,ADD, (WORD | WORD32 | OVERFLOW)}, /* rd = rs + rt */ - {"%s", 1,"010001fxmmmnnnnndddddaaaaa000000",COP1, FPMAB, (FP)}, + {"ADD", 1,"01000110mmmkkkkkvvvvvrrrrr000000",COP1, FPADD, (FP)}, {"ADDI", 1,"001000ssssstttttiiiiiiiiiiiiiiii",NORMAL, ADD, (WORD | WORD32 | OVERFLOW)}, {"ADDU", 1,"000000sssssgggggddddd00000100001",SPECIAL,ADD, (WORD | WORD32)}, /* rd = rs + rt */ {"ADDIU", 1,"001001ssssstttttiiiiiiiiiiiiiiii",NORMAL, ADD, (WORD | WORD32)}, {"AND", 1,"000000sssssgggggddddd00000100100",SPECIAL,AND, (NONE)}, /* rd = rs AND rt */ {"ANDI", 1,"001100ssssstttttzzzzzzzzzzzzzzzz",NORMAL, AND, (NONE)}, + {"BC1", 1,"01000101000qqqfcllllllllllllllll",COP1S, BRANCH, (FP)}, {"BEQ", 1,"000100sssssgggggllllllllllllllll",NORMAL, BRANCH, (EQ)}, {"BEQL", 2,"010100sssssgggggllllllllllllllll",NORMAL, BRANCH, (EQ | LIKELY)}, {"BGEZ", 1,"000001sssss00001llllllllllllllll",REGIMM, BRANCH, (GT | EQ)}, @@ -401,6 +422,7 @@ struct instruction MIPS_DECODE[] = { {"CVT.S", 1,"01000110mmm00000vvvvvrrrrr100000",COP1, FPCONVERT,(FP | SINGLE)}, {"CVT.W", 1,"01000110mmm00000vvvvvrrrrr100100",COP1, FPCONVERT,(FP | FIXED | WORD)}, {"C.%s", 1,"01000110mmmkkkkkvvvvvppp0011uuuu",COP1, FPCOMPARE,(FP)}, + {"CxC1", 1,"01000100x10kkkkkvvvvv00000000000",COP1S, FPMOVEC, (FP | WORD | CONTROL)}, {"DADD", 3,"000000sssssgggggddddd00000101100",SPECIAL,ADD, (DOUBLEWORD | OVERFLOW)}, {"DADDI", 3,"011000ssssstttttiiiiiiiiiiiiiiii",NORMAL, ADD, (DOUBLEWORD | OVERFLOW)}, {"DADDU", 3,"000000sssssgggggddddd00000101101",SPECIAL,ADD, (DOUBLEWORD | UNSIGNED)}, @@ -413,6 +435,7 @@ struct instruction MIPS_DECODE[] = { {"DMADD16", (ARCH_VR4100 | 3),"000000sssssggggg0000000000101001",SPECIAL,MADD16, (DOUBLEWORD | HI | LO)}, {"DMULT", 3,"000000sssssggggg0000000000011100",SPECIAL,MUL, (DOUBLEWORD | HI | LO)}, {"DMULTU", 3,"000000sssssggggg0000000000011101",SPECIAL,MUL, (DOUBLEWORD | UNSIGNED | HI | LO)}, + {"DMxC1", 3,"01000100x01kkkkkvvvvv00000000000",COP1S, FPMOVEC, (FP | DOUBLEWORD)}, {"DSLL", 3,"00000000000gggggdddddaaaaa111000",SPECIAL,SHIFT, (DOUBLEWORD | LEFT | LOGICAL)}, {"DSLLV", 3,"000000sssssgggggddddd00000010100",SPECIAL,SHIFT, (DOUBLEWORD | LEFT | LOGICAL)}, {"DSLL32", 3,"00000000000gggggdddddaaaaa111100",SPECIAL,SHIFT, (DOUBLEWORD | LEFT | LOGICAL | HI32)}, /* rd = rt << (sa + 32) */ @@ -437,7 +460,7 @@ struct instruction MIPS_DECODE[] = { {"LDC2", 2,"110110sssssttttteeeeeeeeeeeeeeee",NORMAL, LOAD, (DOUBLEWORD | COPROC)}, {"LDL", 3,"011010ssssstttttyyyyyyyyyyyyyyyy",NORMAL, LOAD, (DOUBLEWORD | LEFT)}, /* NOTE: See "LB" comment */ {"LDR", 3,"011011ssssstttttyyyyyyyyyyyyyyyy",NORMAL, LOAD, (DOUBLEWORD | RIGHT)}, /* NOTE: See "LB" comment */ - {"LDXC1", 4,"010011bbbbbkkkkk00000rrrrr000001",COP1X, FPLOAD, (FP | DOUBLEWORD)}, + {"LDXC1", 4,"010011sssssggggg00000rrrrr000001",COP1X, LOAD, (FP | DOUBLEWORD | COPROC | REG)}, {"LH", 1,"100001sssssttttthhhhhhhhhhhhhhhh",NORMAL, LOAD, (HALFWORD | SIGNEXTEND)}, {"LHU", 1,"100101sssssttttthhhhhhhhhhhhhhhh",NORMAL, LOAD, (HALFWORD)}, {"LL", 2,"110000ssssstttttwwwwwwwwwwwwwwww",NORMAL, LOAD, (WORD | ATOMIC | SIGNEXTEND)}, @@ -449,7 +472,7 @@ struct instruction MIPS_DECODE[] = { {"LWL", 1,"100010ssssstttttyyyyyyyyyyyyyyyy",NORMAL, LOAD, (WORD | LEFT)}, {"LWR", 1,"100110ssssstttttyyyyyyyyyyyyyyyy",NORMAL, LOAD, (WORD | RIGHT)}, {"LWU", 3,"100111ssssstttttwwwwwwwwwwwwwwww",NORMAL, LOAD, (WORD)}, - {"LWXC1", 4,"010011bbbbbkkkkk00000rrrrr000000",COP1X, FPLOAD, (FP | WORD)}, + {"LWXC1", 4,"010011sssssggggg00000rrrrr000000",COP1X, LOAD, (FP | WORD | COPROC | REG)}, {"MADD16", (ARCH_VR4100 | 3),"000000sssssggggg0000000000101000",SPECIAL,MADD16, (WORD | HI | LO)}, {"MADD.D", 4,"010011bbbbbkkkkkvvvvvrrrrr100001",COP1X, FPADD, (FP | MULTIPLY | DOUBLE)}, {"MADD.S", 4,"010011bbbbbkkkkkvvvvvrrrrr100000",COP1X, FPADD, (FP | MULTIPLY | SINGLE)}, @@ -459,17 +482,18 @@ struct instruction MIPS_DECODE[] = { {"MTLO", 1,"000000sssss000000000000000010011",SPECIAL,MOVE, (LO)}, {"MOV", 1,"01000110mmm00000vvvvvrrrrr000110",COP1, FPMOVE, (FP)}, {"MOVN", 4,"000000sssssgggggddddd00000001011",SPECIAL,MOVE, (NOT | EQ)}, - {"MOVN", 4,"01000110mmmkkkkkvvvvvrrrrr010011",COP1, FPMOVE, (FP | NOT)}, - {"MOV%c", 4,"000000sssssqqq0cddddd00000000001",SPECIAL,FPMOVE, (FP)}, /* Test FP condition code, and conditionally move integer register */ - {"MOV%c", 4,"01000110mmmqqq0cvvvvvrrrrr010001",COP1, FPMOVE, (FP)}, /* to test an FP condition and conditionally move an FP value */ + {"MOVN", 4,"01000110mmmgggggvvvvvrrrrr010011",COP1, FPMOVE, (FP | NOT | EQ)}, + {"MOV%c", 4,"000000sssssqqq0cddddd00000000001",SPECIAL,FPMOVE, (FP | CONDITIONAL | INTEGER)}, + {"MOV%c", 4,"01000110mmmqqq0cvvvvvrrrrr010001",COP1, FPMOVE, (FP | CONDITIONAL)}, {"MOVZ", 4,"000000sssssgggggddddd00000001010",SPECIAL,MOVE, (EQ)}, - {"MOVZ", 4,"01000110mmmkkkkkvvvvvrrrrr010010",COP1, FPMOVE, (FP)}, + {"MOVZ", 4,"01000110mmmgggggvvvvvrrrrr010010",COP1, FPMOVE, (FP | EQ)}, {"MSUB.D", 4,"010011bbbbbkkkkkvvvvvrrrrr101001",COP1X, FPSUB, (FP | MULTIPLY | DOUBLE)}, {"MSUB.S", 4,"010011bbbbbkkkkkvvvvvrrrrr101000",COP1X, FPSUB, (FP | MULTIPLY | SINGLE)}, {"MUL", 1,"01000110mmmkkkkkvvvvvrrrrr000010",COP1, FPMUL, (FP | HI | LO)}, {"MULT", 1,"000000sssssggggg0000000000011000",SPECIAL,MUL, (WORD | WORD32 | HI | LO)}, {"MULTU", 1,"000000sssssggggg0000000000011001",SPECIAL,MUL, (WORD | WORD32 | HI | LO)}, - {"NEG", 1,"01000110mmm00000vvvvvrrrrr000111",COP1, FPNEG, (FP | FIXED | DOUBLEWORD)}, + {"MxC1", 1,"01000100x00kkkkkvvvvv00000000000",COP1S, FPMOVEC, (FP | WORD)}, + {"NEG", 1,"01000110mmm00000vvvvvrrrrr000111",COP1, FPNEG, (FP)}, {"NMADD.D", 4,"010011bbbbbkkkkkvvvvvrrrrr110001",COP1X, FPADD, (FP | NOT | MULTIPLY | DOUBLE)}, {"NMADD.S", 4,"010011bbbbbkkkkkvvvvvrrrrr110000",COP1X, FPADD, (FP | NOT | MULTIPLY | SINGLE)}, {"NMSUB.D", 4,"010011bbbbbkkkkkvvvvvrrrrr111001",COP1X, FPSUB, (FP | NOT | MULTIPLY | DOUBLE)}, @@ -478,11 +502,11 @@ struct instruction MIPS_DECODE[] = { {"OR", 1,"000000sssssgggggddddd00000100101",SPECIAL,OR, (NONE)}, {"ORI", 1,"001101ssssstttttzzzzzzzzzzzzzzzz",NORMAL, OR, (NONE)}, {"PREF", 4,"110011sssssnnnnnyyyyyyyyyyyyyyyy",NORMAL, PREFETCH, (NONE)}, - {"PREFX", 4,"010011bbbbbkkkkkvvvvv00000001111",COP1X, FPPREFX, (FP)}, + {"PREFX", 4,"010011sssssgggggvvvvv00000001111",COP1X, FPPREFX, (FP)}, {"RECIP", 4,"01000110mmm00000vvvvvrrrrr010101",COP1, FPRECIP, (FP)}, {"ROUND.L", 3,"01000110mmm00000vvvvvrrrrr001000",COP1, FPROUND, (FP | FIXED | DOUBLEWORD)}, {"ROUND.W", 2,"01000110mmm00000vvvvvrrrrr001100",COP1, FPROUND, (FP | FIXED | WORD)}, - {"RSQRT", 4,"01000110mmm00000vvvvvrrrrr010110",COP1, FPSQRT, (FP)}, + {"RSQRT", 4,"01000110mmm00000vvvvvrrrrr010110",COP1, FPSQRT, (FP | RECIP)}, {"SB", 1,"101000sssssgggggyyyyyyyyyyyyyyyy",NORMAL, STORE, (BYTE)}, {"SC", 2,"111000sssssgggggwwwwwwwwwwwwwwww",NORMAL, STORE, (WORD | ATOMIC)}, {"SCD", 3,"111100sssssgggggeeeeeeeeeeeeeeee",NORMAL, STORE, (DOUBLEWORD | ATOMIC)}, @@ -491,7 +515,7 @@ struct instruction MIPS_DECODE[] = { {"SDC2", 2,"111110sssssttttteeeeeeeeeeeeeeee",NORMAL, STORE, (DOUBLEWORD | COPROC)}, {"SDL", 3,"101100sssssgggggyyyyyyyyyyyyyyyy",NORMAL, STORE, (DOUBLEWORD | LEFT)}, {"SDR", 3,"101101sssssgggggyyyyyyyyyyyyyyyy",NORMAL, STORE, (DOUBLEWORD | RIGHT)}, - {"SDXC1", 4,"010011bbbbbkkkkkvvvvv00000001001",COP1X, FPSTORE, (FP | DOUBLEWORD)}, + {"SDXC1", 4,"010011sssssgggggvvvvv00000001001",COP1X, STORE, (FP | DOUBLEWORD | COPROC | REG)}, {"SH", 1,"101001sssssggggghhhhhhhhhhhhhhhh",NORMAL, STORE, (HALFWORD)}, {"SLL", 1,"00000000000gggggdddddaaaaa000000",SPECIAL,SHIFT, (WORD | LEFT | LOGICAL)}, /* rd = rt << sa */ {"SLLV", 1,"000000ooooogggggddddd00000000100",SPECIAL,SHIFT, (WORD | LEFT | LOGICAL)}, /* rd = rt << rs - with "SLL" depends on "s" and "a" field values */ @@ -500,10 +524,10 @@ struct instruction MIPS_DECODE[] = { {"SLTU", 1,"000000sssssgggggddddd00000101011",SPECIAL,SET, (LT | UNSIGNED)}, {"SLTIU", 1,"001011ssssstttttiiiiiiiiiiiiiiii",NORMAL, SET, (LT | UNSIGNED)}, {"SQRT", 2,"01000110mmm00000vvvvvrrrrr000100",COP1, FPSQRT, (FP)}, - {"SRA", 1,"00000000000gggggdddddaaaaa000011",SPECIAL,SHIFT, (WORD | WORD32 | WORD | RIGHT | ARITHMETIC)}, - {"SRAV", 1,"000000ooooogggggddddd00000000111",SPECIAL,SHIFT, (WORD | WORD32 | WORD | RIGHT | ARITHMETIC)}, - {"SRL", 1,"00000000000gggggdddddaaaaa000010",SPECIAL,SHIFT, (WORD | WORD32 | WORD | RIGHT | LOGICAL)}, - {"SRLV", 1,"000000ooooogggggddddd00000000110",SPECIAL,SHIFT, (WORD | WORD32 | WORD | RIGHT | LOGICAL)}, + {"SRA", 1,"00000000000gggggdddddaaaaa000011",SPECIAL,SHIFT, (WORD | WORD32 | RIGHT | ARITHMETIC)}, + {"SRAV", 1,"000000ooooogggggddddd00000000111",SPECIAL,SHIFT, (WORD | WORD32 | RIGHT | ARITHMETIC)}, + {"SRL", 1,"00000000000gggggdddddaaaaa000010",SPECIAL,SHIFT, (WORD | WORD32 | RIGHT | LOGICAL)}, + {"SRLV", 1,"000000ooooogggggddddd00000000110",SPECIAL,SHIFT, (WORD | WORD32 | RIGHT | LOGICAL)}, {"SUB", 1,"000000sssssgggggddddd00000100010",SPECIAL,SUB, (WORD | WORD32 | OVERFLOW)}, {"SUB", 1,"01000110mmmkkkkkvvvvvrrrrr000001",COP1, FPSUB, (FP)}, {"SUBU", 1,"000000sssssgggggddddd00000100011",SPECIAL,SUB, (WORD | WORD32)}, @@ -512,7 +536,7 @@ struct instruction MIPS_DECODE[] = { {"SWC2", 1,"111010ssssstttttwwwwwwwwwwwwwwww",NORMAL, STORE, (WORD | COPROC)}, {"SWL", 1,"101010sssssgggggyyyyyyyyyyyyyyyy",NORMAL, STORE, (WORD | LEFT)}, {"SWR", 1,"101110sssssgggggyyyyyyyyyyyyyyyy",NORMAL, STORE, (WORD | RIGHT)}, - {"SWXC1", 4,"010011bbbbbkkkkkvvvvv00000001000",COP1X, FPSTORE, (FP)}, + {"SWXC1", 4,"010011sssssgggggvvvvv00000001000",COP1X, STORE, (FP | WORD | COPROC | REG)}, {"SYNC", 2,"000000000000000000000aaaaa001111",SPECIAL,SYNC, (NONE)}, /* z = 5bit stype field */ {"SYSCALL", 1,"000000????????????????????001100",SPECIAL,SYSCALL, (NOARG)}, {"TEQ", 2,"000000sssssggggg??????????110100",SPECIAL,TRAP, (EQ)}, @@ -567,7 +591,7 @@ convert_bitmap(bitmap,onemask,zeromask,dontmask) if (oefield < (sizeof(opfields) / sizeof(struct operand_encoding))) { if ((lastoe != -1) && (lastoe != oefield)) if ((loop - lastsp) != (opfields[lastoe].flen)) { - fprintf(stderr,"Invalid field length %d for bitmap field '%c' (0x%02X) (should be %d)\n",(loop - lastsp),(((bitmap[31 - loop] < 0x20) || (bitmap[31 - loop] >= 0x7F)) ? '.' : bitmap[31 - loop]),bitmap[31 - loop],opfields[lastoe].flen); + fprintf(stderr,"Invalid field length %d for bitmap field '%c' (0x%02X) (should be %d) : bitmap = \"%s\"\n",(loop - lastsp),(((bitmap[31 - loop] < 0x20) || (bitmap[31 - loop] >= 0x7F)) ? '.' : bitmap[31 - loop]),bitmap[31 - loop],opfields[lastoe].flen,bitmap); exit(4); } @@ -635,8 +659,8 @@ build_operands(flags) if (opfields[loop].flags & OP_SIGNX) printf("SIGNEXTEND((%s)",opfields[loop].type); - if (opfields[loop].flags & (OP_GPR | OP_FGR)) - printf("%s[",((opfields[loop].flags & OP_GPR) ? "GPR" : "FGR")); + if (opfields[loop].flags & OP_GPR) + printf("GPR["); if (opfields[loop].flags & OP_SHIFT2) printf("("); @@ -646,7 +670,7 @@ build_operands(flags) if (opfields[loop].flags & OP_SHIFT2) printf(" << 2)"); - if (opfields[loop].flags & (OP_GPR | OP_FGR)) + if (opfields[loop].flags & OP_GPR) printf("]"); if (opfields[loop].flags & OP_BITS5) @@ -676,10 +700,10 @@ process_instructions(doarch,features) int doisa = (doarch & MASK_ISA); int limit = (sizeof(MIPS_DECODE) / sizeof(instruction)); int gprlen=((features & FEATURE_GP64) ? 64 : 32); - int fprlen=((features & FEATURE_FP64) ? 64 : 32); int proc64 = ((features & FEATURE_PROC32) ? 0 : -1); int dofp = (features & FEATURE_HASFPU); int fpsingle = (features & FEATURE_FPSINGLE); + int maxisa; int loop; if (limit < 1) { @@ -692,21 +716,26 @@ process_instructions(doarch,features) exit(3); } - fprlen = ((features & FEATURE_FP64) ? 64 : 32); - if (fprlen != gprlen) - fprintf(stderr,"Warning: FPR (%d) and GPR (%d) are different sizes\n",fprlen,gprlen); - /* NOTE: "proc64" also differentiates between 32- and 64-bit wide memory */ + maxisa = 0; + for (loop = 0; (loop < limit); loop++) + if ((MIPS_DECODE[loop].isa & MASK_ISA) > maxisa) + maxisa = (MIPS_DECODE[loop].isa & MASK_ISA); + + if (doisa == 0) + doisa = maxisa; + printf("#if defined(SIM_MANIFESTS)\n"); + printf("#define MIPSISA (%d)\n",doisa); if (proc64) printf("#define PROCESSOR_64BIT (1 == 1)\n"); printf("#define LOADDRMASK (0x%08X)\n",(proc64 ? 0x7 : 0x3)); + /* The FP registers are the same width as the CPU registers: */ printf("#define GPRLEN (%d)\n",gprlen); - printf("#define FPRLEN (%d)\n",fprlen); printf("typedef %s t_reg;\n",((gprlen == 64) ? "word64" : "int")); printf("typedef %s ut_reg;\n",((gprlen == 64) ? "uword64" : "unsigned int")); - printf("typedef %s t_fpreg;\n",((fprlen == 64) ? "word64" : "int")); + printf("typedef %s t_fpreg;\n",((gprlen == 64) ? "word64" : "int")); if (dofp) printf("#define HASFPU (1 == 1)\n"); if (features & FEATURE_FAST) @@ -740,12 +769,33 @@ process_instructions(doarch,features) printf("/* Actual instruction decoding block */\n"); printf("{\n"); - printf("int num = ((instruction >> %d) & 0x%08X);\n",OP_SH_OP,OP_MASK_OP); - printf("if (num == 0x00) num = (%d + ((instruction >> %d) & 0x%08X));\n",(OP_MASK_OP + 1),OP_SH_SPEC,OP_MASK_SPEC); - printf("else if (num == 0x01) num = (%d + ((instruction >> %d) & 0x%08X));\n",((OP_MASK_OP + 1) + (OP_MASK_SPEC + 1)),OP_SH_RT,OP_MASK_RT); - printf("else if (num == 0x11) num = (%d + ((instruction >> %d) & 0x%08X));\n",((OP_MASK_OP + 1) + (OP_MASK_SPEC + 1) + (OP_MASK_RT + 1)),OP_SH_SPEC,OP_MASK_SPEC); - printf("else if (num == 0x13) num = (%d + ((instruction >> %d) & 0x%08X));\n",((OP_MASK_OP + 1) + (OP_MASK_SPEC + 1) + (OP_MASK_RT + 1) + (OP_MASK_SPEC + 1)),OP_SH_SPEC,OP_MASK_SPEC); - printf("/* Total possible switch entries: %d */\n",((OP_MASK_OP + 1) + (OP_MASK_SPEC + 1) + (OP_MASK_RT + 1) + (OP_MASK_SPEC + 1) + (OP_MASK_SPEC + 1))) ; + { + int limit; + printf("int num = ((instruction >> %d) & 0x%08X);\n",OP_SH_OP,OP_MASK_OP); + limit = (OP_MASK_OP + 1); + printf("if (num == 0x00) num = (%d + ((instruction >> %d) & 0x%08X));\n",limit,OP_SH_SPEC,OP_MASK_SPEC); + limit += (OP_MASK_SPEC + 1); + printf("else if (num == 0x01) num = (%d + ((instruction >> %d) & 0x%08X));\n",limit,OP_SH_RT,OP_MASK_RT); + limit += (OP_MASK_RT + 1); + printf("else if (num == 0x11) {\n"); + printf(" if ((instruction & (0x%08X << %d)) == 0x%08X)\n",OP_MASK_COP1NORM,OP_SH_COP1NORM,(OP_MASK_COP1NORM << OP_SH_COP1NORM)); + printf(" if ((instruction & (0x%08X << %d)) == 0x%08X)\n",OP_MASK_COP1CMP,OP_SH_COP1CMP,(OP_MASK_COP1CMP << OP_SH_COP1CMP)); + printf(" num = (%d + ((instruction >> %d) & 0x%08X));\n",limit,OP_SH_SPEC,(OP_MASK_SPEC & (OP_MASK_COP1CMP << OP_SH_COP1CMP))); + printf(" else\n"); + printf(" num = (%d + ((instruction >> %d) & 0x%08X));\n",limit,OP_SH_SPEC,OP_MASK_SPEC); + limit += (OP_MASK_SPEC + 1); + printf(" else\n"); + /* To keep this code quick, we just clear out the "to" bit + here. The proper (though slower) code would be to have another + conditional, checking whether this instruction is a branch or + not, before limiting the range to the bottom two bits of the + move operation. */ + printf(" num = (%d + (((instruction >> %d) & 0x%08X) & ~0x%08X));\n",limit,OP_SH_COP1SPEC,OP_MASK_COP1SPEC,OP_MASK_COP1SCLR); + limit += (OP_MASK_COP1SPEC + 1); + printf("} else if (num == 0x13) num = (%d + ((instruction >> %d) & 0x%08X));\n",limit,OP_SH_SPEC,OP_MASK_SPEC); + limit += (OP_MASK_SPEC + 1); + printf("/* Total possible switch entries: %d */\n",limit) ; + } printf("switch (num)\n") ; printf("{\n"); @@ -754,7 +804,7 @@ process_instructions(doarch,features) valid, before checking if the instruction matches any of the architecture specific flags. NOTE: We allow a selected ISA of zero to be used to match all standard instructions. */ - if (((((MIPS_DECODE[loop].isa & MASK_ISA) <= doisa) || (doisa == 0)) && (((MIPS_DECODE[loop].isa & ~MASK_ISA) == 0) || ((MIPS_DECODE[loop].isa & ~MASK_ISA) & doarch) != 0)) && (!(MIPS_DECODE[loop].flags & FP) || ((MIPS_DECODE[loop].flags & FP) && dofp))) { + if ((((MIPS_DECODE[loop].isa & MASK_ISA) <= doisa) && (((MIPS_DECODE[loop].isa & ~MASK_ISA) == 0) || ((MIPS_DECODE[loop].isa & ~MASK_ISA) & doarch) != 0)) && (!(MIPS_DECODE[loop].flags & FP) || ((MIPS_DECODE[loop].flags & FP) && dofp))) { unsigned int onemask; unsigned int zeromask; unsigned int dontmask; @@ -795,9 +845,14 @@ process_instructions(doarch,features) number = (((OP_MASK_OP + 1) + (OP_MASK_SPEC + 1) + (OP_MASK_RT + 1)) + ((onemask >> OP_SH_SPEC) & OP_MASK_SPEC)) ; break ; + case COP1S : + mask = ((OP_MASK_OP << OP_SH_OP) | (OP_MASK_COP1SPEC << OP_SH_COP1SPEC)) ; + number = (((OP_MASK_OP + 1) + (OP_MASK_SPEC + 1) + (OP_MASK_RT + 1) + (OP_MASK_SPEC + 1)) + ((onemask >> OP_SH_COP1SPEC) & OP_MASK_COP1SPEC)) ; + break; + case COP1X : mask = ((OP_MASK_OP << OP_SH_OP) | (OP_MASK_SPEC << OP_SH_SPEC)) ; - number = (((OP_MASK_OP + 1) + (OP_MASK_SPEC + 1) + (OP_MASK_RT + 1) + (OP_MASK_SPEC + 1)) + ((onemask >> OP_SH_SPEC) & OP_MASK_SPEC)) ; + number = (((OP_MASK_OP + 1) + (OP_MASK_SPEC + 1) + (OP_MASK_RT + 1) + (OP_MASK_COP1SPEC + 1) + (OP_MASK_SPEC + 1)) + ((onemask >> OP_SH_SPEC) & OP_MASK_SPEC)) ; break ; default : @@ -861,7 +916,7 @@ process_instructions(doarch,features) just raise a warning. NOTE: This is a different check to the later decoding, which checks for the final address being valid. */ - if ((flags & (fieldval('e') | fieldval('w') | fieldval('h'))) && ((doisa == 4) || (doisa == 0))) { + if ((flags & (fieldval('e') | fieldval('w') | fieldval('h'))) && (doisa >= 4)) { printf(" if (instruction & 0x%1X)\n",((flags & fieldval('e')) ? 0x7 : ((flags & fieldval('w')) ? 0x3 : 0x1))); printf(" {\n"); /* NOTE: If we change this to a SignalException(), we must @@ -874,7 +929,7 @@ process_instructions(doarch,features) #endif /* The extended condition codes only appeared in ISA IV */ - if ((flags & fieldval('p')) && ((doisa < 4) && (doisa != 0))) { + if ((flags & fieldval('p')) && (doisa < 4)) { printf(" if (condition_code != 0)\n"); printf(" {\n"); printf(" SignalException(ReservedInstruction,instruction);\n"); @@ -887,23 +942,38 @@ process_instructions(doarch,features) exit(1); } +#if 1 + /* The R4000 book differs slightly from the MIPS IV ISA + manual. An example is the sign-extension of a 64-bit processor + SUBU operation, and what is meant by an Undefined Result. This + is now provided purely as a warning. After examining a HW + implementation, this is now purely a warning... and the actual + operation is performed, with possibly undefined results. */ + if (((MIPS_DECODE[loop].flags & WORD32) && proc64) && (features & FEATURE_WARN_RESULT)) { + /* The compiler should optimise out an OR with zero */ + printf(" if (%s | %s)\n",((flags & fieldval('s')) ? "NOTWORDVALUE(op1)" : "0"),((flags & fieldval('g')) ? "NOTWORDVALUE(op2)" : "0")); + printf(" UndefinedResult();\n") ; + } +#else /* Check that the source is a 32bit value */ if ((MIPS_DECODE[loop].flags & WORD32) && proc64) { - /* This relies on the compiler optimising out an OR with zero */ + /* The compiler should optimise out an OR with zero */ printf(" if (%s | %s)\n",((flags & fieldval('s')) ? "NOTWORDVALUE(op1)" : "0"),((flags & fieldval('g')) ? "NOTWORDVALUE(op2)" : "0")); printf(" UndefinedResult();\n") ; printf(" else\n") ; } +#endif printf(" {\n") ; switch (MIPS_DECODE[loop].type) { /* TODO: To make these easier to edit and maintain, they should actually be provided as source macros (or inline functions) - OUTSIDE this main switch statement. */ + OUTSIDE this main switch statement. The PPC simulator has a + neater scheme for describing the instruction sequences. */ case ADD: - case SUB : + case SUB: { char *basetype = "unknown"; switch (GETDATASIZE()) { @@ -914,16 +984,18 @@ process_instructions(doarch,features) basetype = "long long"; break; default : - fprintf(stderr,"Opcode table error: size of SUB operands not known (%d)\n",GETDATASIZE()); + fprintf(stderr,"Opcode table error: size of ADD/SUB operands not known (%d)\n",GETDATASIZE()); exit(1); } if ((MIPS_DECODE[loop].type) == ADD) { printf(" unsigned %s temp = (unsigned %s)(op1 + op2);\n",basetype,basetype); printf(" signed %s tempS = (signed %s)temp;\n",basetype,basetype); - printf(" if (((op1 < 0) == (op2 < 0)) && ((tempS < 0) != (op1 < 0)))\n"); - printf(" SignalException(IntegerOverflow);\n"); - printf(" else\n"); + if (MIPS_DECODE[loop].flags & OVERFLOW) { + printf(" if (((op1 < 0) == (op2 < 0)) && ((tempS < 0) != (op1 < 0)))\n"); + printf(" SignalException(IntegerOverflow);\n"); + printf(" else\n"); + } if (!proc64 || (MIPS_DECODE[loop].flags & UNSIGNED) || (GETDATASIZE() == DOUBLEWORD)) printf(" GPR[destreg] = (%s)temp;\n",regtype); else /* only sign-extend when placing 32bit result in 64bit processor */ @@ -948,7 +1020,7 @@ process_instructions(doarch,features) } break ; - case MUL : + case MUL: if (features & FEATURE_WARN_LOHI) { printf(" CHECKHILO(\"Multiplication\");\n"); } @@ -976,7 +1048,7 @@ process_instructions(doarch,features) printf(" }\n"); break ; - case DIV : + case DIV: { int boolU = (MIPS_DECODE[loop].flags & UNSIGNED); @@ -995,7 +1067,7 @@ process_instructions(doarch,features) } break ; - case SHIFT : + case SHIFT: { int datalen = GETDATASIZE(); int bits = ((datalen == WORD) ? 32 : 64); @@ -1035,10 +1107,13 @@ process_instructions(doarch,features) if (MIPS_DECODE[loop].flags & HI32) printf(" op1 |= (1 << 6);\n"); - /* TODO: We should not need to perform the pre-masking with - 0xFFFFFFFF when shifting left. The sign-extension code (if - required) will replace any remaining hi-bits. */ - printf(" GPR[destreg] = ((unsigned long long)(op2%s) %s op1);\n",((bits == 32) ? " & 0xFFFFFFFF" : ""),((MIPS_DECODE[loop].flags & LEFT) ? "<<" : ">>")); + /* We do not need to perform pre-masking with 0xFFFFFFFF when + dealing with 32bit shift lefts, since the sign-extension + code will replace any remaining hi-bits: */ + if (MIPS_DECODE[loop].flags & LEFT) + printf(" GPR[destreg] = ((unsigned long long)op2 << op1);\n"); + else + printf(" GPR[destreg] = ((unsigned long long)(op2%s) >> op1);\n",((bits == 32) ? " & 0xFFFFFFFF" : "")); /* For ARITHMETIC shifts, we must duplicate the sign-bit */ if (MIPS_DECODE[loop].flags & ARITHMETIC) @@ -1050,7 +1125,7 @@ process_instructions(doarch,features) } break ; - case MOVE : + case MOVE: if (MIPS_DECODE[loop].flags & (HI | LO)) { char *regname = ((MIPS_DECODE[loop].flags & LO) ? "LO" : "HI"); if (flags & fieldval('d')) @@ -1078,19 +1153,19 @@ process_instructions(doarch,features) } break ; - case SYNC : + case SYNC: printf(" SyncOperation(op1);\n"); break ; - case SYSCALL : + case SYSCALL: printf(" SignalException(SystemCall);\n"); break ; - case BREAK : + case BREAK: printf(" SignalException(BreakPoint);\n"); break ; - case TRAP : + case TRAP: { int boolNOT = (MIPS_DECODE[loop].flags & NOT); int boolEQ = (MIPS_DECODE[loop].flags & EQ); @@ -1115,7 +1190,7 @@ process_instructions(doarch,features) } break ; - case SET : + case SET: { int boolU = (MIPS_DECODE[loop].flags & UNSIGNED); @@ -1131,23 +1206,23 @@ process_instructions(doarch,features) } break ; - case AND : + case AND: printf(" GPR[destreg] = (op1 & op2);\n"); break ; - case OR : + case OR: printf(" GPR[destreg] = (%sop1 | op2);\n",((MIPS_DECODE[loop].flags & NOT) ? "~" : "")); break ; - case XOR : + case XOR: printf(" GPR[destreg] = (op1 ^ op2);\n"); break ; - case DECODE : + case DECODE: printf(" decode_coproc(instruction);\n"); break ; - case CACHE : + case CACHE: /* 16-bit offset is sign-extended and added to the base register to make a virtual address */ /* The virtual address is translated to a physical address using the TLB */ /* The hint specifies a cache operation for that address */ @@ -1159,7 +1234,7 @@ process_instructions(doarch,features) printf(" CacheOp(hint,vaddr,paddr,instruction);\n"); break; - case MADD16 : /* VR4100 specific multiply-add instructions */ + case MADD16: /* VR4100 specific multiply-add instructions */ /* Some of this code is shared with the standard multiply routines, so an effort should be made to merge where possible. */ @@ -1183,8 +1258,8 @@ process_instructions(doarch,features) printf(" }\n"); break; - case RSVD : /* "Reserved Instruction" on MIPS IV, or if co-proc 3 absent. Otherwise "CoProcessorUnusable" */ - if ((doisa < 4) && (doisa != 0)) { + case RSVD: /* "Reserved Instruction" on MIPS IV, or if co-proc 3 absent. Otherwise "CoProcessorUnusable" */ + if (doisa < 4) { printf(" if (CoProcPresent(3))\n"); printf(" SignalException(CoProcessorUnusable);\n"); printf(" else\n"); @@ -1192,7 +1267,7 @@ process_instructions(doarch,features) printf(" SignalException(ReservedInstruction,instruction);\n"); break ; - case JUMP : + case JUMP: if (MIPS_DECODE[loop].flags & LINK) { if (!(MIPS_DECODE[loop].flags & REG)) printf(" int destreg = 31;\n"); @@ -1204,25 +1279,34 @@ process_instructions(doarch,features) printf(" DELAYSLOT();\n"); break ; - case BRANCH : /* execute delay slot instruction before branch unless (LIKELY && branch_not_taken) */ - if ((MIPS_DECODE[loop].flags & NOT) && !(MIPS_DECODE[loop].flags & EQ)) { - fprintf(stderr,"NOT specified when not EQ in \"%s\"\n",MIPS_DECODE[loop].name); - exit(7); + case BRANCH: /* execute delay slot instruction before branch unless (LIKELY && branch_not_taken) */ + if (MIPS_DECODE[loop].flags & FP) { + if (doisa < 4) { + printf(" if (condition_code != 0)\n"); + printf(" SignalException(ReservedInstruction,instruction);\n"); + printf(" else {\n"); + } + /* "PREVCOC1()" should be the COC1 value at the start of the preceding instruction */ + printf(" int condition = (%s == boolean);\n",((doisa < 4) ? "PREVCOC1()" : "GETFCC(condition_code)")); + } else { + if ((MIPS_DECODE[loop].flags & NOT) && !(MIPS_DECODE[loop].flags & EQ)) { + fprintf(stderr,"NOT specified when not EQ in \"%s\"\n",MIPS_DECODE[loop].name); + exit(7); + } + if ((MIPS_DECODE[loop].flags & NOT) && (MIPS_DECODE[loop].flags & (GT | LT))) { + fprintf(stderr,"NOT specified with GT or LT in \"%s\"\n",MIPS_DECODE[loop].name); + exit(7); + } + /* GT LT */ + if (MIPS_DECODE[loop].flags & GT) + printf(" int condition = (op1 >%s 0);\n",((MIPS_DECODE[loop].flags & EQ) ? "=" : "")); + else + if (MIPS_DECODE[loop].flags & LT) + printf(" int condition = (op1 <%s 0);\n",((MIPS_DECODE[loop].flags & EQ) ? "=" : "")); + else + if (MIPS_DECODE[loop].flags & EQ) + printf(" int condition = (op1 %c= op2);\n",((MIPS_DECODE[loop].flags & NOT) ? '!' : '=')); } - if ((MIPS_DECODE[loop].flags & NOT) && (MIPS_DECODE[loop].flags & (GT | LT))) { - fprintf(stderr,"NOT specified with GT or LT in \"%s\"\n",MIPS_DECODE[loop].name); - exit(7); - } - - /* GT LT */ - if (MIPS_DECODE[loop].flags & GT) - printf(" int condition = (op1 >%s 0);\n",((MIPS_DECODE[loop].flags & EQ) ? "=" : "")); - else - if (MIPS_DECODE[loop].flags & LT) - printf(" int condition = (op1 <%s 0);\n",((MIPS_DECODE[loop].flags & EQ) ? "=" : "")); - else - if (MIPS_DECODE[loop].flags & EQ) - printf(" int condition = (op1 %c= op2);\n",((MIPS_DECODE[loop].flags & NOT) ? '!' : '=')); if (MIPS_DECODE[loop].flags & LINK) { if (features & FEATURE_WARN_R31) { @@ -1237,19 +1321,21 @@ process_instructions(doarch,features) printf(" DSPC = (PC + offset);\n"); printf(" DELAYSLOT();\n"); printf(" }\n"); - if (MIPS_DECODE[loop].flags & LIKELY) { + if ((MIPS_DECODE[loop].flags & FP) && (doisa != 1)) { + printf(" else if (likely) {\n"); + printf(" NULLIFY();\n"); + printf(" }\n"); + } else if (MIPS_DECODE[loop].flags & LIKELY) { printf(" else\n"); printf(" NULLIFY();\n"); } + if ((MIPS_DECODE[loop].flags & FP) && (doisa < 4)) + printf(" }\n"); break ; - /* TODO: After writing the main pass through this memory - transfer block, quite a few bugs were discovered, and fixed - in situ. This code should be gone through and tidied and - optimised. */ - case PREFETCH : /* The beginning is shared with normal load operations */ - case LOAD : - case STORE : + case PREFETCH: /* The beginning is shared with normal load operations */ + case LOAD: + case STORE: { int isload = ((MIPS_DECODE[loop].type == LOAD) || (MIPS_DECODE[loop].type == PREFETCH)); int datalen; @@ -1277,7 +1363,10 @@ process_instructions(doarch,features) break ; } - printf(" uword64 vaddr = ((unsigned long long)op1 + offset);\n"); + if (MIPS_DECODE[loop].flags & REG) + printf(" uword64 vaddr = ((unsigned long long)op1 + op2);\n"); + else + printf(" uword64 vaddr = ((unsigned long long)op1 + offset);\n"); printf(" uword64 paddr;\n"); printf(" int uncached;\n"); @@ -1297,421 +1386,601 @@ process_instructions(doarch,features) printf(" {\n"); printf(" %s memval;\n",(proc64 ? "uword64" : "unsigned int")); - if (MIPS_DECODE[loop].flags & COPROC) { - if ((datalen != 4) && (datalen != 8)) { - fprintf(stderr,"Co-processor operation not WORD or DOUBLEWORD in length \"%s\"\n",MIPS_DECODE[loop].name); - exit(6); - } + if ((MIPS_DECODE[loop].flags & COPROC) && ((datalen != 4) && (datalen != 8))) { + fprintf(stderr,"Co-processor transfer operation not WORD or DOUBLEWORD in length \"%s\"\n",MIPS_DECODE[loop].name); + exit(6); + } - if (isload) { - printf(" memval = LoadMemory(uncached,%s,paddr,vaddr,isDATA,isREAL);\n",accesslength); - /* NOTE: The R4000 user manual has the COP_LW occuring - in the same cycle as the rest of the instruction, - yet the MIPS IV shows the operation happening on the - next cycle. To keep the simulator simple, this code - follows the R4000 manual. Experimentation with a - silicon implementation will be needed to ascertain - the correct operation. */ - printf(" COP_L%c(((instruction >> 26) & 0x3),destreg,memval);\n",((datalen == 8) ? 'D' : 'W')); - } else { - printf(" memval = COP_S%c(((instruction >> 26) & 0x3),destreg);\n",((datalen == 8) ? 'D' : 'W')); - printf(" StoreMemory(uncached,%s,memval,paddr,vaddr,isREAL);\n",accesslength); + if (MIPS_DECODE[loop].flags & (LEFT | RIGHT)) { + if ((MIPS_DECODE[loop].flags & LEFT) && (MIPS_DECODE[loop].flags & RIGHT)) { + fprintf(stderr,"Memory transfer with both LEFT and RIGHT specified \"%s\"\n",MIPS_DECODE[loop].name); + exit(4); } - } else - if (MIPS_DECODE[loop].flags & (LEFT | RIGHT)) { - if ((MIPS_DECODE[loop].flags & LEFT) && (MIPS_DECODE[loop].flags & RIGHT)) { - fprintf(stderr,"Memory transfer with both LEFT and RIGHT specified \"%s\"\n",MIPS_DECODE[loop].name); - exit(4); - } - switch (datalen) { - case 8 : - if (!proc64) { - fprintf(stderr,"DOUBLEWORD shifted memory transfers only valid for 64-bit processors \"%s\"\n",MIPS_DECODE[loop].name); - exit(4); + switch (datalen) { + case 8: + if (!proc64) { + fprintf(stderr,"DOUBLEWORD shifted memory transfers only valid for 64-bit processors \"%s\"\n",MIPS_DECODE[loop].name); + exit(4); + } + /* fall through to... */ + case 4: + { + char *maskstr = ((datalen == 8) ? "((uword64)-1)" : "0xFFFFFFFF"); + + printf(" uword64 mask = %d;\n",((datalen == 8) ? 0x7 : 0x3)); + printf(" unsigned int reverse = (ReverseEndian ? mask : 0);\n"); + printf(" unsigned int bigend = (BigEndianCPU ? mask : 0);\n"); + printf(" int byte;\n"); + printf(" paddr = ((paddr & ~mask) | ((paddr & mask) ^ reverse));\n"); + printf(" byte = ((vaddr & mask) ^ bigend);\n"); + printf(" if (%sBigEndianMem)\n",((MIPS_DECODE[loop].flags & LEFT) ? "!" : "")); + printf(" paddr &= ~mask;\n"); + + if (isload) { + if (MIPS_DECODE[loop].flags & LEFT) + printf(" memval = LoadMemory(uncached,byte,paddr,vaddr,isDATA,isREAL);\n"); + else + printf(" memval = LoadMemory(uncached,(%d - byte),paddr,vaddr,isDATA,isREAL);\n",(datalen - 1)); } - /* fall through to... */ - case 4 : - { - char *maskstr = ((datalen == 8) ? "((uword64)-1)" : "0xFFFFFFFF"); - - printf(" uword64 mask = %d;\n",((datalen == 8) ? 0x7 : 0x3)); - printf(" unsigned int reverse = (ReverseEndian ? mask : 0);\n"); - printf(" unsigned int bigend = (BigEndianCPU ? mask : 0);\n"); - printf(" int byte;\n"); - printf(" paddr = ((paddr & ~mask) | ((paddr & mask) ^ reverse));\n"); - printf(" byte = ((vaddr & mask) ^ bigend);\n"); - printf(" if (%sBigEndianMem)\n",((MIPS_DECODE[loop].flags & LEFT) ? "!" : "")); - printf(" paddr &= ~mask;\n"); + if (MIPS_DECODE[loop].flags & LEFT) { if (isload) { - if (MIPS_DECODE[loop].flags & LEFT) - printf(" memval = LoadMemory(uncached,byte,paddr,vaddr,isDATA,isREAL);\n"); - else - printf(" memval = LoadMemory(uncached,(%d - byte),paddr,vaddr,isDATA,isREAL);\n",(datalen - 1)); - } - - if (MIPS_DECODE[loop].flags & LEFT) { - if (isload) { - /* For WORD transfers work out if the value - will be in the top or bottom of the - DOUBLEWORD returned: */ - if (proc64 && (datalen == 4)) { - printf(" if ((vaddr & (1 << 2)) ^ (BigEndianCPU << 2)) {\n"); - printf(" memval >>= 32;\n"); - printf(" }\n"); - } - printf(" GPR[destreg] = ((memval << ((%d - byte) * 8)) | (GPR[destreg] & (((uword64)1 << ((%d - byte) * 8)) - 1)));\n",(datalen - 1),(datalen - 1)); - if (proc64 && (datalen == 4)) - printf(" GPR[destreg] = SIGNEXTEND(GPR[destreg],32);\n"); - } else { /* store */ - printf(" memval = (op2 >> (8 * (%d - byte)));\n",(datalen - 1)); - /* TODO: This is duplicated in the LOAD code - above - and the RIGHT LOAD and STORE code - below. It should be merged if possible. */ - if (proc64 && (datalen == 4)) { - printf(" if ((vaddr & (1 << 2)) ^ (BigEndianCPU << 2)) {\n"); - printf(" memval <<= 32;\n"); - printf(" }\n"); - } - printf(" StoreMemory(uncached,byte,memval,paddr,vaddr,isREAL);\n"); + /* For WORD transfers work out if the value will + be in the top or bottom of the DOUBLEWORD + returned: */ + if (proc64 && (datalen == 4)) { + printf(" if ((vaddr & (1 << 2)) ^ (BigEndianCPU << 2)) {\n"); + printf(" memval >>= 32;\n"); + printf(" }\n"); + } + printf(" GPR[destreg] = ((memval << ((%d - byte) * 8)) | (GPR[destreg] & (((uword64)1 << ((%d - byte) * 8)) - 1)));\n",(datalen - 1),(datalen - 1)); + if (proc64 && (datalen == 4)) + printf(" GPR[destreg] = SIGNEXTEND(GPR[destreg],32);\n"); + } else { /* store */ + printf(" memval = (op2 >> (8 * (%d - byte)));\n",(datalen - 1)); + /* TODO: This is duplicated in the LOAD code + above - and the RIGHT LOAD and STORE code + below. It should be merged if possible. */ + if (proc64 && (datalen == 4)) { + printf(" if ((vaddr & (1 << 2)) ^ (BigEndianCPU << 2)) {\n"); + printf(" memval <<= 32;\n"); + printf(" }\n"); } - } else { /* RIGHT */ - if (isload) { - if (proc64 && (datalen == 4)) { - printf(" if ((vaddr & (1 << 2)) ^ (BigEndianCPU << 2)) {\n"); - printf(" memval >>= 32;\n"); - printf(" }\n"); - } - printf(" {\n"); - printf(" uword64 srcmask;\n"); - /* All of this extra code is just a bodge - required because some hosts don't allow ((v) << 64). - The SPARC just leaves the (v) value un-touched. */ - printf(" if (byte == 0)\n"); - printf(" srcmask = 0;\n"); - printf(" else\n"); - printf(" srcmask = ((uword64)-1 << (8 * (%d - byte)));\n",datalen); - printf(" GPR[destreg] = ((GPR[destreg] & srcmask) | (memval >> (8 * byte)));\n",datalen); - printf(" }\n"); - if (proc64 && (datalen == 4)) - printf(" GPR[destreg] = SIGNEXTEND(GPR[destreg],32);\n"); - } else { /* store */ - printf(" memval = (op2 << (byte * 8));\n"); - printf(" StoreMemory(uncached,(%s - byte),memval,paddr,vaddr,isREAL);\n",accesslength); + printf(" StoreMemory(uncached,byte,memval,paddr,vaddr,isREAL);\n"); + } + } else { /* RIGHT */ + if (isload) { + if (proc64 && (datalen == 4)) { + printf(" if ((vaddr & (1 << 2)) ^ (BigEndianCPU << 2)) {\n"); + printf(" memval >>= 32;\n"); + printf(" }\n"); } + printf(" {\n"); + printf(" uword64 srcmask;\n"); + /* All of this extra code is just a bodge + required because some hosts don't allow + ((v) << 64). The SPARC just leaves the (v) + value un-touched. */ + printf(" if (byte == 0)\n"); + printf(" srcmask = 0;\n"); + printf(" else\n"); + printf(" srcmask = ((uword64)-1 << (8 * (%d - byte)));\n",datalen); + printf(" GPR[destreg] = ((GPR[destreg] & srcmask) | (memval >> (8 * byte)));\n",datalen); + printf(" }\n"); + if (proc64 && (datalen == 4)) + printf(" GPR[destreg] = SIGNEXTEND(GPR[destreg],32);\n"); + } else { /* store */ + printf(" memval = (op2 << (byte * 8));\n"); + printf(" StoreMemory(uncached,(%s - byte),memval,paddr,vaddr,isREAL);\n",accesslength); } } - break; - - default : - fprintf(stderr,"Shifted memory transfer not WORD or DOUBLEWORD in length \"%s\"\n",MIPS_DECODE[loop].name); - exit(6); } - } else { /* normal memory transfer */ - if (((datalen == 8) || ((datalen == 4) & MIPS_DECODE[loop].flags & UNSIGNED)) && !proc64) { - fprintf(stderr,"Operation not available with 32bit wide memory access \"%s\"\n",MIPS_DECODE[loop].name); - exit(4); - /* TODO: The R4000 documentation suggests the LWU - executed when in 32bit processor mode should cause - a ReservedInstruction exception. This will mean - adding a run-time check into the code sequence. */ - } - - if (isload) { - printf(" uword64 mask = %d;\n",(proc64 ? 0x7 : 0x3)); - printf(" unsigned int shift = %d;\n",(datalen >> 1)); - printf(" unsigned int reverse = (ReverseEndian ? (mask >> shift) : 0);\n"); - printf(" unsigned int bigend = (BigEndianCPU ? (mask >> shift) : 0);\n"); - printf(" unsigned int byte;\n"); + break; - if (datalen != 8) /* not for DOUBLEWORD */ - printf(" paddr = ((paddr & ~mask) | ((paddr & mask) ^ (reverse << shift)));\n"); + default: + fprintf(stderr,"Shifted memory transfer not WORD or DOUBLEWORD in length \"%s\"\n",MIPS_DECODE[loop].name); + exit(6); + } + } else { /* normal memory transfer */ + if (((datalen == 8) || ((datalen == 4) & (MIPS_DECODE[loop].flags & UNSIGNED))) && !proc64) { + fprintf(stderr,"Operation not available with 32bit wide memory access \"%s\"\n",MIPS_DECODE[loop].name); + exit(4); + /* TODO: The R4000 documentation states that a LWU + instruction executed when in a 32bit processor mode + should cause a ReservedInstruction exception. This + will mean adding a run-time check into the code + sequence. */ + } - printf(" memval = LoadMemory(uncached,%s,paddr,vaddr,isDATA,isREAL);\n",accesslength); + if (isload) { + printf(" uword64 mask = %d;\n",(proc64 ? 0x7 : 0x3)); + printf(" unsigned int shift = %d;\n",(datalen >> 1)); + printf(" unsigned int reverse = (ReverseEndian ? (mask >> shift) : 0);\n"); + printf(" unsigned int bigend = (BigEndianCPU ? (mask >> shift) : 0);\n"); + printf(" unsigned int byte;\n"); - /* The following will only make sense if the "LoadMemory" above returns a DOUBLEWORD entity */ - if (datalen != 8) { /* not for DOUBLEWORD */ - int valmask; - switch (datalen) { - case 1 : - valmask = 0xFF; - break; + if (datalen != 8) /* not for DOUBLEWORD */ + printf(" paddr = ((paddr & ~mask) | ((paddr & mask) ^ (reverse << shift)));\n"); - case 2 : - valmask = 0xFFFF; - break; + printf(" memval = LoadMemory(uncached,%s,paddr,vaddr,isDATA,isREAL);\n",accesslength); - case 4 : - valmask = 0xFFFFFFFF; - break; + /* The following will only make sense if the + "LoadMemory" above returns a DOUBLEWORD entity */ + if (datalen != 8) { /* not for DOUBLEWORD */ + int valmask; + switch (datalen) { + case 1: + valmask = 0xFF; + break; + + case 2: + valmask = 0xFFFF; + break; + + case 4: + valmask = 0xFFFFFFFF; + break; + + default: + fprintf(stderr,"Unrecognised datalen (%d) when processing \"%s\"\n",datalen,MIPS_DECODE[loop].name); + exit(4); + } + printf(" byte = ((vaddr & mask) ^ (bigend << shift));\n"); + /* NOTE: The R4000 user manual has the COP_LW + occuring in the same cycle as the rest of the + instruction, yet the MIPS IV shows the operation + happening on the next cycle. To keep the simulator + simple, this code follows the R4000 + manual. Experimentation with a silicon + implementation will be needed to ascertain the + correct operation. */ + if (MIPS_DECODE[loop].flags & COPROC) + printf(" COP_LW(((instruction >> 26) & 0x3),destreg,(unsigned int)"); + else + printf(" GPR[destreg] = ("); + + if (MIPS_DECODE[loop].flags & SIGNEXTEND) + printf("SIGNEXTEND("); + printf("((memval >> (8 * byte)) & 0x%08X)",valmask); + if (MIPS_DECODE[loop].flags & SIGNEXTEND) + printf(",%d)",(datalen * 8)); + printf(");\n"); + } else { + if (MIPS_DECODE[loop].flags & COPROC) + printf(" COP_LD(((instruction >> 26) & 0x3),destreg,memval);;\n"); + else + printf(" GPR[destreg] = memval;\n"); + } + } else { /* store operation */ + if ((datalen == 1) || (datalen == 2)) { + /* SH and SB */ + printf(" uword64 mask = %d;\n",(proc64 ? 0x7 : 0x3)); + printf(" unsigned int shift = %d;\n",(datalen >> 1)); + printf(" unsigned int reverse = (ReverseEndian ? (mask >> shift) : 0);\n"); + printf(" unsigned int bigend = (BigEndianCPU ? (mask >> shift) : 0);\n"); + printf(" unsigned int byte;\n"); - default : - fprintf(stderr,"Unrecognised datalen (%d) when processing \"%s\"\n",datalen,MIPS_DECODE[loop].name); - exit(4); - } - printf(" byte = ((vaddr & mask) ^ (bigend << shift));\n"); - printf(" GPR[destreg] = "); - if (MIPS_DECODE[loop].flags & SIGNEXTEND) - printf("SIGNEXTEND("); - printf("((memval >> (8 * byte)) & 0x%08X)",valmask); - if (MIPS_DECODE[loop].flags & SIGNEXTEND) - printf(",%d)",(datalen * 8)); - printf(";\n"); - } else - printf(" GPR[destreg] = memval;\n"); - } else { /* store operation */ - if ((datalen == 1) || (datalen == 2)) { - /* SH and SB */ - printf(" uword64 mask = %d;\n",(proc64 ? 0x7 : 0x3)); - printf(" unsigned int shift = %d;\n",(datalen >> 1)); - printf(" unsigned int reverse = (ReverseEndian ? (mask >> shift) : 0);\n"); - printf(" unsigned int bigend = (BigEndianCPU ? (mask >> shift) : 0);\n"); + printf(" paddr = ((paddr & ~mask) | ((paddr & mask) ^ (reverse << shift)));\n"); + printf(" byte = ((vaddr & mask) ^ (bigend << shift));\n"); + printf(" memval = (op2 << (8 * byte));\n"); + } else + if (proc64 && (datalen == 4)) { /* proc64 SC and SW */ printf(" unsigned int byte;\n"); - - printf(" paddr = ((paddr & ~mask) | ((paddr & mask) ^ (reverse << shift)));\n"); - printf(" byte = ((vaddr & mask) ^ (bigend << shift));\n"); - printf(" memval = (op2 << (8 * byte));\n"); - } else - if (proc64 && (datalen == 4)) { /* proc64 SC and SW */ - printf(" unsigned int byte;\n"); - printf(" paddr = ((paddr & ~0x7) | ((paddr & 0x7) ^ (ReverseEndian << 2)));\n"); - printf(" byte = ((vaddr & 0x7) ^ (BigEndianCPU << 2));\n"); + printf(" paddr = ((paddr & ~0x7) | ((paddr & 0x7) ^ (ReverseEndian << 2)));\n"); + printf(" byte = ((vaddr & 0x7) ^ (BigEndianCPU << 2));\n"); + if (MIPS_DECODE[loop].flags & COPROC) + printf(" memval = (((unsigned long long)COP_SW(((instruction >> 26) & 0x3),%s)) << (8 * byte));\n",((MIPS_DECODE[loop].flags & FP) ? "fs" : "destreg")); + else printf(" memval = (op2 << (8 * byte));\n"); - } else /* !proc64 SC and SW, plus proc64 SD and SCD */ - printf(" memval = op2;\n"); - - if (MIPS_DECODE[loop].flags & ATOMIC) - printf(" if (LLBIT)\n"); - - printf(" {\n"); - printf(" StoreMemory(uncached,%s,memval,paddr,vaddr,isREAL);\n",accesslength); - printf(" }\n"); - } + } else { /* !proc64 SC and SW, plus proc64 SD and SCD */ + if (MIPS_DECODE[loop].flags & COPROC) + printf(" memval = (unsigned long long)COP_S%c(((instruction >> 26) & 0x3),%s);\n",((datalen == 8) ? 'D' : 'W'),((MIPS_DECODE[loop].flags & FP) ? "fs" : "destreg")); + else + printf(" memval = op2;\n"); + } + + if (MIPS_DECODE[loop].flags & ATOMIC) + printf(" if (LLBIT)\n"); + + printf(" {\n"); + printf(" StoreMemory(uncached,%s,memval,paddr,vaddr,isREAL);\n",accesslength); + printf(" }\n"); + } - if (MIPS_DECODE[loop].flags & ATOMIC) { - if ((datalen != 4) && (datalen != 8)) { - fprintf(stderr,"ATOMIC can only be applied to WORD and DOUBLEWORD instructions \"%s\"\n",MIPS_DECODE[loop].name); - exit(4); - } else - if (isload) - printf(" LLBIT = 1;\n"); - else { - /* The documentation states that: - - SC *WILL* fail if coherent store into the same - block occurs, or if an exception occurs between - the LL and SC instructions. - - SC *MAY* fail if a load, store or prefetch is - executed on the processor (VR4300 doesn't seem - to), or if the instructions between the LL and - SC are not in a 2048byte contiguous VM range. - - SC *MUST* have been preceded by an LL - (i.e. LLBIT will be set), and it must use the - same Vaddr, Paddr and cache-coherence algorithm - as the LL (which means we should store this - information from the load-conditional). - */ - printf(" GPR[(instruction >> %d) & 0x%08X] = LLBIT;\n",OP_SH_RT,OP_MASK_RT); - } - } - } + if (MIPS_DECODE[loop].flags & ATOMIC) { + if ((datalen != 4) && (datalen != 8)) { + fprintf(stderr,"ATOMIC can only be applied to WORD and DOUBLEWORD instructions \"%s\"\n",MIPS_DECODE[loop].name); + exit(4); + } else + if (isload) + printf(" LLBIT = 1;\n"); + else { + /* The documentation states that: + + SC *WILL* fail if coherent store into the same + block occurs, or if an exception occurs between + the LL and SC instructions. + + SC *MAY* fail if a load, store or prefetch is + executed on the processor (VR4300 doesn't seem + to), or if the instructions between the LL and + SC are not in a 2048byte contiguous VM range. + + SC *MUST* have been preceded by an LL + (i.e. LLBIT will be set), and it must use the + same Vaddr, Paddr and cache-coherence algorithm + as the LL (which means we should store this + information from the load-conditional). + */ + printf(" GPR[(instruction >> %d) & 0x%08X] = LLBIT;\n",OP_SH_RT,OP_MASK_RT); + } + } + } printf(" }\n"); } printf(" }\n"); } break ; -/* Following are FP instructions. It may be simpler to provide these - as explicit function calls, rather than coding explicit simulations - at this point. This will help keep this generator simpler, at the - cost of some simulation performance. */ - - case FPMOVE : - printf(" printf(\"TODO: FPMOVE operation %s */\\n\");\n",MIPS_DECODE[loop].name) ; -/* - MOV - MOV.fmt - MOVN - MOVN.fmt - MOV%c - integer <-> integer MOVF and MOVT - MOV%c - FP <-> FP MOVF.fmt and MOVT.fmt -*/ + case FPPREFX: + /* This code could be merged with the PREFIX generation above: */ + printf(" uword64 vaddr = ((unsigned long long)op1 + (unsigned long long)op2);\n"); + printf(" uword64 paddr;\n"); + printf(" int uncached;\n"); + printf(" if (AddressTranslation(vaddr,isDATA,isLOAD,&paddr,&uncached,isTARGET,isREAL))\n"); + printf(" Prefetch(uncached,paddr,vaddr,isDATA,fs);\n"); break ; - case FPMAB : - printf("/* FPMAB */\n"); - printf("if (add) {\n"); - printf(" word64 fpop1 = ValueFPR(hint,format);\n"); - printf(" word64 fpop2 = ValueFPR(destreg,format);\n"); - printf(" StoreFPR(op1,format,(fpop1 + fpop2));\n"); - printf("} else if (branch) {\n"); - printf(" printf(\"TODO: FP Branch support\\n\");\n"); - printf("} else { /* MOVE */\n"); - /* TODO: Since the following is so large - maybe it should be punted to a specific routine */ - printf(" if ((op1 != 0x0) || (format & (1 << 1)))\n"); - printf(" SignalException(ReservedInstruction,instruction);\n"); - printf(" else\n"); - printf(" {\n"); - printf(" int dprec = (format & (1 << 0));\n"); - printf(" int to = (format & (1 << 2));\n"); - printf(" if (dprec)\n"); - printf(" {\n"); - printf(" if (to)\n"); - printf(" {\n"); - if ((doisa < 4) && (doisa != 0)) - { - printf(" if (SizeFGR() == 64)\n"); - printf(" FGR[destreg] = GPR[hint];\n"); - printf(" else\n"); - printf(" if ((destreg & 0x1) == 0)\n"); - printf(" {\n"); - printf(" FGR[destreg + 1] = (GPR[hint] >> 32);\n"); - printf(" FGR[destreg] = (GPR[hint] & 0xFFFFFFFF);\n"); - printf(" }\n"); - printf(" else\n"); - printf(" UndefinedResult();\n"); - } - else - { - printf("printf(\"TODO .. pending operation for next cycle\\n\");\n"); - } - printf(" }\n"); - printf(" else\n"); - printf(" {\n"); - if ((doisa < 4) && (doisa != 0)) - { - printf(" if (SizeFGR() == 64)\n"); - printf(" GPR[hint] = FGR[destreg];\n"); - printf(" else\n"); - printf(" if ((destreg & 0x1) == 0)\n"); - printf(" GPR[hint] = ((FGR[destreg + 1] << 32) | FGR[destreg]);\n"); - printf(" else\n"); - printf(" UndefinedResult();\n"); - } - else - { - printf(" pending_reg = hint;\n"); - printf(" if (SizeFGR() == 64)\n"); - printf(" pending_data = FGR[destreg];\n"); - printf(" else\n"); - printf(" if ((destreg & 0x1) == 0)\n"); - printf(" pending_data = ((FGR[destreg + 1] << 32) | FGR[destreg]);\n"); - printf(" else\n"); - printf(" UndefinedResult();\n"); - } - printf(" }\n"); - printf(" }\n"); - printf(" else /* single precision */\n"); - printf(" {\n"); - printf(" if (to)\n"); - printf(" {\n"); - if ((doisa < 4) && (doisa != 0)) - { - printf(" if (SizeFGR() == 64)\n"); - printf(" FGR[destreg] = (0xDEADC0DE | (GPR[hint] & 0xFFFFFFFF));\n"); - printf(" else\n"); - printf(" FGR[destreg] = (GPR[hint] & 0xFFFFFFFF);\n"); - } - else - { - printf(" pending_reg = (FPREG | destreg);\n"); - printf(" if (SizeFGR() == 64)\n"); - printf(" pending_data = (0xDEADC0DE | (GPR[hint] & 0xFFFFFFFF));\n"); - printf(" else\n"); - printf(" pending_data = (GPR[hint] & 0xFFFFFFFF);\n"); - } - printf(" }\n"); - printf(" else\n"); - if ((doisa ==4) || (doisa == 0)) /* in this cycle */ - { - printf(" GPR[hint] = SIGNEXTEND(FGR[destreg],32);\n"); - } - else /* write-back occurs in next cycle */ - { - printf(" pending_reg = hint;\n"); - printf(" pending_data = SIGNEXTEND(FGR[destreg],32);\n"); - } - printf(" }\n"); - printf(" }\n"); - printf("}\n"); - break; - - case FPFLOOR : - printf(" printf(\"TODO: FPFLOOR operation %s\\n\");\n",MIPS_DECODE[loop].name) ; - /* Lots of checks on paramater validity must be performed */ - break ; - - case FPCEIL : - printf(" printf(\"TODO: FPCEIL operation %s\\n\");\n",MIPS_DECODE[loop].name) ; + case FPMOVEC: + if (MIPS_DECODE[loop].flags & CONTROL) { + /* The following "magic" of interpreting the FP + control-register number would not be needed if we were not + trying to match our internal register numbers with those + used by GDB. */ + printf(" if (to) {\n"); + if (doisa < 4) { + printf(" if (fs == 0) {\n"); + printf(" PENDING_FILL((fs + FCR0IDX),(GPR[ft]&0xFFFFFFFF));\n"); + printf(" } else if (fs == 31) {\n"); + printf(" PENDING_FILL((fs + FCR31IDX),(GPR[ft]&0xFFFFFFFF));\n"); + printf(" } /* else NOP */\n"); + printf(" PENDING_FILL(COCIDX,0); /* special case */\n"); + } else { + printf(" if (fs == 0) {\n"); + printf(" FCR0 = (GPR[ft] & 0xFFFFFFFF);\n"); + printf(" } else if (fs == 31) {\n"); + printf(" FCR31 = (GPR[ft] & 0xFFFFFFFF);\n"); + printf(" } /* else NOP */\n"); + printf(" SETFCC(0,((FCR31 & (1 << 23)) ? 1 : 0)); /* COC[1] */\n"); + } + printf(" } else { /* control from */\n"); + if (doisa < 4) { + printf(" if (fs == 0) {\n"); + printf(" PENDING_FILL(ft,SIGNEXTEND(FCR0,32));\n"); + printf(" } else if (fs == 31) {\n"); + printf(" PENDING_FILL(ft,SIGNEXTEND(FCR31,32));\n"); + printf(" } /* else NOP */\n"); + } else { + printf(" if (fs == 0) {\n"); + printf(" GPR[ft] = SIGNEXTEND(FCR0,32);\n"); + printf(" } else if (fs == 31) {\n"); + printf(" GPR[ft] = SIGNEXTEND(FCR31,32);\n"); + printf(" } /* else NOP */\n"); + } + printf(" }\n"); + } else { + printf(" if (to) {\n"); + if (GETDATASIZE() == WORD) { + if (doisa < 4) { + printf(" if (SizeFGR() == 64) {\n"); + printf(" PENDING_FILL((fs + FGRIDX),((unsigned long long)((unsigned long long)0xDEADC0DE << 32) | (GPR[ft]&0xFFFFFFFF)));\n"); + printf(" } else { \n"); + printf(" PENDING_FILL((fs + FGRIDX),(GPR[ft]&0xFFFFFFFF));\n"); + printf(" }\n"); + } else { + printf(" if (SizeFGR() == 64)\n"); + printf(" FGR[fs] = ((unsigned long long)((unsigned long long)0xDEADC0DE << 32) | (GPR[ft] & 0xFFFFFFFF));\n"); + printf(" else\n"); + printf(" FGR[fs] = (GPR[ft] & 0xFFFFFFFF);\n"); + printf(" fpr_state[fs] = fmt_uninterpreted;\n"); + } + } else if (GETDATASIZE() == DOUBLEWORD) { + if (doisa < 4) { + printf(" if (SizeFGR() == 64) {\n"); + printf(" PENDING_FILL((fs + FGRIDX),GPR[ft]);\n"); + printf(" } else\n"); + printf(" if ((fs & 0x1) == 0)\n"); + printf(" {\n"); + printf(" PENDING_FILL(((fs + 1) + FGRIDX),(GPR[ft]>>32));\n"); + printf(" PENDING_FILL((fs + FGRIDX),(GPR[ft]&0xFFFFFFFF));\n"); + printf(" }\n"); + if (features & FEATURE_WARN_RESULT) { + printf(" else\n"); + printf(" UndefinedResult();\n"); + } + } else { + printf(" if (SizeFGR() == 64) {\n"); + printf(" FGR[fs] = GPR[ft];\n"); + printf(" fpr_state[fs] = fmt_uninterpreted;\n"); + printf(" } else\n"); + printf(" if ((fs & 0x1) == 0)\n"); + printf(" {\n"); + printf(" FGR[fs + 1] = (GPR[ft] >> 32);\n"); + printf(" FGR[fs] = (GPR[ft] & 0xFFFFFFFF);\n"); + printf(" fpr_state[fs + 1] = fmt_uninterpreted;\n"); + printf(" fpr_state[fs] = fmt_uninterpreted;\n"); + printf(" }\n"); + if (features & FEATURE_WARN_RESULT) { + printf(" else\n"); + printf(" UndefinedResult();\n"); + } + } + } else { + fprintf(stderr,"Invalid data width specified in FPU Move operation\n"); + exit(1); + } + printf(" } else {\n"); + if (GETDATASIZE() == WORD) { + if (doisa < 4) /* write-back occurs in next cycle */ + printf(" PENDING_FILL(ft,SIGNEXTEND(FGR[fs],32));\n"); + else /* in this cycle */ + printf(" GPR[ft] = SIGNEXTEND(FGR[fs],32);\n"); + } else if (GETDATASIZE() == DOUBLEWORD) { + if (doisa < 4) { + printf(" if (SizeFGR() == 64) {\n"); + printf(" PENDING_FILL(ft,FGR[fs]);\n"); + printf(" } else\n"); + printf(" if ((fs & 0x1) == 0) {\n"); + printf(" PENDING_FILL(ft,((FGR[fs+1]<<32)|FGR[fs]));\n"); + printf(" } else {\n"); + printf(" PENDING_FILL(ft,((unsigned long long)0xDEADC0DE << 32) | 0xBAD0BAD0);\n"); + if (features & FEATURE_WARN_RESULT) + printf(" UndefinedResult();\n"); + printf(" }\n"); + } else { + printf(" if (SizeFGR() == 64)\n"); + printf(" GPR[ft] = FGR[fs];\n"); + printf(" else\n"); + printf(" if ((fs & 0x1) == 0)\n"); + printf(" GPR[ft] = ((FGR[fs + 1] << 32) | FGR[fs]);\n"); + printf(" else {\n"); + printf(" GPR[ft] = (((unsigned long long)0xDEADC0DE << 32) | 0xBAD0BAD0);\n"); + if (features & FEATURE_WARN_RESULT) + printf(" UndefinedResult();\n"); + printf(" }\n"); + } + } else { + fprintf(stderr,"Invalid data width specified in FPU Move operation\n"); + exit(1); + } + printf(" }\n"); + } break ; - case FPTRUNC : - printf(" printf(\"TODO: FPTRUNC operation %s\\n\");\n",MIPS_DECODE[loop].name) ; + case FPMOVE: + if (MIPS_DECODE[loop].flags & CONDITIONAL) { + if (MIPS_DECODE[loop].flags & INTEGER) { /* moving GPR - testing FGR */ + printf(" if (GETFCC(condition_code) == boolean)\n"); + printf(" GPR[destreg] = op1;\n"); + } else { + if (MIPS_DECODE[loop].flags & EQ) /* moving FGR - testing GPR */ + printf(" if (op2 %c= 0)\n",((MIPS_DECODE[loop].flags & NOT) ? '!' : '=')); + else + printf(" if (GETFCC(condition_code) == boolean)\n"); + printf(" StoreFPR(destreg,format,ValueFPR(fs,format));\n"); + printf(" else\n"); + printf(" StoreFPR(destreg,format,ValueFPR(destreg,format));\n"); + } + } else { /* simple MOVE */ + printf(" StoreFPR(destreg,format,ValueFPR(fs,format));\n"); + } break ; - case FPROUND : - printf(" printf(\"TODO: FPROUND operation %s\\n\");\n",MIPS_DECODE[loop].name) ; + case FPNEG: + printf(" if ((format != fmt_single) && (format != fmt_double))\n"); + printf(" SignalException(ReservedInstruction,instruction);\n"); + printf(" else\n"); + printf(" StoreFPR(destreg,format,Negate(ValueFPR(fs,format),format));\n"); break ; - case FPNEG : - printf(" printf(\"TODO: FPNEG operation %s\\n\");\n",MIPS_DECODE[loop].name) ; + case FPABS: + printf(" if ((format != fmt_single) && (format != fmt_double))\n"); + printf(" SignalException(ReservedInstruction,instruction);\n"); + printf(" else\n"); + printf(" StoreFPR(destreg,format,AbsoluteValue(ValueFPR(fs,format),format));\n"); break ; - case FPABS : - printf(" printf(\"TODO: FPABS operation %s\\n\");\n",MIPS_DECODE[loop].name) ; + case FPDIV: + printf(" if ((format != fmt_single) && (format != fmt_double))\n"); + printf(" SignalException(ReservedInstruction,instruction);\n"); + printf(" else\n"); + printf(" StoreFPR(destreg,format,Divide(ValueFPR(fs,format),ValueFPR(ft,format),format));\n"); break ; - case FPDIV : - printf(" printf(\"TODO: FPDIV operation %s\\n\");\n",MIPS_DECODE[loop].name) ; + case FPMUL: + printf(" if ((format != fmt_single) && (format != fmt_double))\n"); + printf(" SignalException(ReservedInstruction,instruction);\n"); + printf(" else\n"); + printf(" StoreFPR(destreg,format,Multiply(ValueFPR(fs,format),ValueFPR(ft,format),format));\n"); break ; - case FPMUL : - printf(" printf(\"TODO: FPMUL operation %s\\n\");\n",MIPS_DECODE[loop].name) ; + case FPRECIP: + printf(" if ((format != fmt_single) && (format != fmt_double))\n"); + printf(" SignalException(ReservedInstruction,instruction);\n"); + printf(" else\n"); + printf(" StoreFPR(destreg,format,Recip(ValueFPR(fs,format),format));\n"); break ; - case FPSUB : - printf(" printf(\"TODO: FPSUB operation %s\\n\");\n",MIPS_DECODE[loop].name) ; + case FPSQRT: + printf(" if ((format != fmt_single) && (format != fmt_double))\n"); + printf(" SignalException(ReservedInstruction,instruction);\n"); + printf(" else\n"); + printf(" StoreFPR(destreg,format,%s(SquareRoot(ValueFPR(fs,format),format)));\n",((MIPS_DECODE[loop].flags & RECIP) ? "Recip" : "")); break ; - case FPADD : - printf(" printf(\"TODO: FPADD operation %s\\n\");\n",MIPS_DECODE[loop].name) ; - break ; + case FPCEIL: + case FPFLOOR: + case FPTRUNC: + case FPROUND: + { + char *op = ""; + char *type = ""; - case FPPREFX : - printf(" printf(\"TODO: FPPREFX operation %s\\n\");\n",MIPS_DECODE[loop].name) ; - break ; + switch (MIPS_DECODE[loop].type) { + case FPCEIL: + op = "FP_RM_TOPINF"; + break; + case FPFLOOR: + op = "FP_RM_TOMINF"; + break; + case FPTRUNC: + op = "FP_RM_TOZERO"; + break; + case FPROUND: + op = "FP_RM_NEAREST"; + break; + default: + fprintf(stderr,"Error: Handled missing for FP reason code %d\n",MIPS_DECODE[loop].type); + exit(1); + } - case FPSTORE : - printf(" printf(\"TODO: FPSTORE operation %s\\n\");\n",MIPS_DECODE[loop].name) ; + switch (GETDATASIZE()) { + case WORD : + type = "fmt_word"; + break; + case DOUBLEWORD : + type = "fmt_long"; + break; + default: + fprintf(stderr,"Error in instruction encoding table for FP %s operation (not WORD or DOUBLEWORD)\n",op); + exit(1); + } + printf(" if ((format != fmt_single) && (format != fmt_double))\n"); + printf(" SignalException(ReservedInstruction,instruction);\n"); + printf(" else\n"); + printf(" StoreFPR(destreg,%s,Convert(%s,ValueFPR(fs,format),format,%s));\n",type,op,type); + } break ; - case FPLOAD : - printf(" printf(\"TODO: FPLOAD operation %s\\n\");\n",MIPS_DECODE[loop].name) ; - break ; + case FPCONVERT: + { + char *type = ""; + switch (GETDATASIZE()) { + case SINGLE: + type = "fmt_single"; + break; + case DOUBLE: + type = "fmt_double"; + break; + case WORD: + type = "fmt_word"; + break; + case DOUBLEWORD: + type = "fmt_long"; + break; + default : + fprintf(stderr,"Error: Unknown data size %d in FPCONVERT instruction\n",GETDATASIZE()); + exit(1); + } - case FPRECIP : - printf(" printf(\"TODO: FPRECIP operation %s\\n\");\n",MIPS_DECODE[loop].name) ; + /* Not all combinations of conversion are valid at the + moment: When converting to a fixed-point format, only + floating-point sources are allowed. */ + printf(" if ((format == %s) | %s)\n",type,((MIPS_DECODE[loop].flags & FIXED) ? "((format == fmt_long) || (format == fmt_word))": "0")); + printf(" SignalException(ReservedInstruction,instruction);\n"); + printf(" else\n"); + printf(" StoreFPR(destreg,%s,Convert(GETRM(),ValueFPR(fs,format),format,%s));\n",type,type); + } break ; - case FPSQRT : - printf(" printf(\"TODO: FPSQRT operation %s\\n\");\n",MIPS_DECODE[loop].name) ; + case FPSUB: + if (MIPS_DECODE[loop].flags & MULTIPLY) { + char *type = ""; + switch (GETDATASIZE()) { + case SINGLE: + type = "fmt_single"; + break; + case DOUBLE: + type = "fmt_double"; + break; + default: + fprintf(stderr,"Error: Invalid data size %d for FPSUB operation\n",GETDATASIZE()); + exit(1); + } + printf(" StoreFPR(destreg,%s,%s(Sub(Multiply(ValueFPR(fs,%s),ValueFPR(ft,%s),%s),ValueFPR(fr,%s),%s),%s));\n",type,((MIPS_DECODE[loop].flags & NOT) ? "Negate" : ""),type,type,type,type,type,type); + } else { + printf(" if ((format != fmt_single) && (format != fmt_double))\n"); + printf(" SignalException(ReservedInstruction,instruction);\n"); + printf(" else\n"); + printf(" StoreFPR(destreg,format,Sub(ValueFPR(fs,format),ValueFPR(ft,format),format));\n"); + } break ; - case FPCONVERT : - printf(" printf(\"TODO: FPCONVERT operation %s\\n\");\n",MIPS_DECODE[loop].name) ; + case FPADD: + if (MIPS_DECODE[loop].flags & MULTIPLY) { + char *type = ""; + switch (GETDATASIZE()) { + case SINGLE: + type = "fmt_single"; + break; + case DOUBLE: + type = "fmt_double"; + break; + default: + fprintf(stderr,"Error: Invalid data size %d for FPADD operation in instruction table\n",GETDATASIZE()); + exit(1); + } + printf(" StoreFPR(destreg,%s,%s(Add(Multiply(ValueFPR(fs,%s),ValueFPR(ft,%s),%s),ValueFPR(fr,%s),%s),%s));\n",type,((MIPS_DECODE[loop].flags & NOT) ? "Negate" : ""),type,type,type,type,type,type); + } else { + printf(" if ((format != fmt_single) && (format != fmt_double))\n"); + printf(" SignalException(ReservedInstruction,instruction);\n"); + printf(" else\n"); + printf(" StoreFPR(destreg,format,Add(ValueFPR(fs,format),ValueFPR(ft,format),format));\n"); + } break ; - case FPCOMPARE : - printf(" printf(\"TODO: FPCOMPARE operation %s\\n\");\n",MIPS_DECODE[loop].name) ; - /* "cmpflags" encodes the comparison */ - /* if "cmpflags & (1 << 3)" then MIPS IV only */ + case FPCOMPARE: /* For the MIPS I,II or III there *MUST* be at least one instruction between the compare that sets a condition code - and the branch that tests it. */ + and the branch that tests it. NOTE: However the hardware + does not detect this condition. */ + /* Explicitly limit the operation to S and D formats: */ + printf(" if ((format != fmt_single) && (format != fmt_double))\n"); + printf(" SignalException(ReservedInstruction,instruction);\n") ; + printf(" else {\n"); + if (doisa < 4) { + printf(" if ((cmpflags & (1 << 3)) || (condition_code != 0))\n"); + printf(" SignalException(ReservedInstruction,instruction);\n") ; + printf(" else\n"); + } + printf(" {\n"); + printf(" int ignore = 0;\n"); + printf(" int less = 0;\n"); + printf(" int equal = 0;\n"); + printf(" int unordered = 1;\n"); + printf(" unsigned long long ofs = ValueFPR(fs,format);\n"); + printf(" unsigned long long oft = ValueFPR(ft,format);\n"); + printf(" if (NaN(ofs,format) || NaN(oft,format)) {\n"); + printf(" if (FCSR & FP_ENABLE(IO)) {\n"); + printf(" FCSR |= FP_CAUSE(IO);\n"); + printf(" SignalException(FPE);\n"); + printf(" ignore = 1;\n"); + printf(" }\n"); + printf(" } else {\n"); + printf(" less = Less(ofs,oft,format);\n"); + printf(" equal = Equal(ofs,oft,format);\n"); + printf(" unordered = 0;\n"); + printf(" }\n"); + printf(" if (!ignore) {\n"); + printf(" int condition = (((cmpflags & (1 << 2)) && less) || ((cmpflags & (1 << 1)) && equal) || ((cmpflags & (1 << 0)) && unordered));\n"); + printf(" SETFCC(condition_code,condition);\n"); + printf(" }\n"); + printf(" }\n"); + printf(" }\n"); break ; - default : + default: fprintf(stderr,"Unrecognised opcode type %d\n",MIPS_DECODE[loop].type) ; exit(6) ; } @@ -1753,8 +2022,6 @@ struct { {"gp64", T_NONE, FEATURE_GP64,FEATURE_GP64,"\t\t\tSelect 64bit GP registers"}, {"gp32", T_NONE, FEATURE_GP64,0,"\t\t\tSelect 32bit GP registers"}, {"no-fp", T_NONE, FEATURE_HASFPU,0,"\t\tDisable FP simulation"}, - {"fp64", T_NONE, (FEATURE_FP64 | FEATURE_HASFPU),(FEATURE_FP64 | FEATURE_HASFPU),"\t\t\tSelect 64bit FP registers"}, - {"fp32", T_NONE, (FEATURE_FP64 | FEATURE_HASFPU),FEATURE_HASFPU,"\t\t\tSelect 32bit FP registers"}, {"single-float",T_NONE, (FEATURE_FPSINGLE | FEATURE_HASFPU),(FEATURE_FPSINGLE | FEATURE_HASFPU),"\t\tSelect single precision only FPU"}, {"double-float",T_NONE, (FEATURE_FPSINGLE | FEATURE_HASFPU),FEATURE_HASFPU,"\t\tSelect double precision FPU"}, {0, T_NONE, 0,0} @@ -1845,9 +2112,9 @@ main(argc,argv) features |= FEATURE_FPSINGLE; if (features & FEATURE_PROC32) - features &= ~(FEATURE_FP64 | FEATURE_GP64); + features &= ~FEATURE_GP64; else - features |= (FEATURE_FP64 | FEATURE_GP64); + features |= FEATURE_GP64; while (1) { int this_option_optind = (optind ? optind : 1); @@ -1933,14 +2200,14 @@ main(argc,argv) doarch = ((doarch & ~MASK_ISA) | num); if ((num == 0) || (num > 2)) { - if ((features & FEATURE_PROC32) || !(features & FEATURE_GP64) || !(features & FEATURE_FP64)) - fprintf(stderr,"%s: Warning: -mips%d forcing -mfp64, -mgp64\n",progname,num); - features |= (FEATURE_GP64 | FEATURE_FP64); + if ((features & FEATURE_PROC32) || !(features & FEATURE_GP64)) + fprintf(stderr,"%s: Warning: -mips%d forcing -mgp64\n",progname,num); + features |= FEATURE_GP64; features &= ~FEATURE_PROC32; } else { - if (!(features & FEATURE_PROC32) || (features & FEATURE_GP64) || (features & FEATURE_FP64)) - fprintf(stderr,"%s: Warning: -mips%d forcing -mfp32, -mgp32\n",progname,num); - features &= ~(FEATURE_GP64 | FEATURE_FP64); + if (!(features & FEATURE_PROC32) || (features & FEATURE_GP64)) + fprintf(stderr,"%s: Warning: -mips%d forcing -mgp32\n",progname,num); + features &= ~FEATURE_GP64; features |= FEATURE_PROC32; } } else { diff --git a/sim/mips/interp.c b/sim/mips/interp.c new file mode 100644 index 00000000000..0cffa94d4bb --- /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 +#include +#include +#include +#include +#include +#include + +#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=] [--size=]"); +#if defined(TRACE) + callback->printf_filtered(callback," [-t [--tracefile=]]"); +#endif /* TRACE */ +#if defined(PROFILE) + callback->printf_filtered(callback," [-p [--frequency=] [--samples=]]"); +#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"," : 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) ? "" : (((v) == fmt_uninterpreted) ? "" : "")))))) +#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 <*/