| 1 | 33 | Agner | /****************************  emulator.h   **********************************
 | 
      
         | 2 |  |  | * Author:        Agner Fog
 | 
      
         | 3 |  |  | * date created:  2018-02-18
 | 
      
         | 4 |  |  | * Last modified: 2021-04-02
 | 
      
         | 5 |  |  | * Version:       1.11
 | 
      
         | 6 |  |  | * Project:       Binary tools for ForwardCom instruction set
 | 
      
         | 7 |  |  | * Module:        emulator.h
 | 
      
         | 8 |  |  | * Description:
 | 
      
         | 9 |  |  | * Header file for emulator
 | 
      
         | 10 |  |  | *
 | 
      
         | 11 |  |  | * Copyright 2018-2021 GNU General Public License http://www.gnu.org/licenses
 | 
      
         | 12 |  |  | *****************************************************************************/
 | 
      
         | 13 |  |  |  
 | 
      
         | 14 |  |  | // structure for memory map
 | 
      
         | 15 |  |  | struct SMemoryMap {
 | 
      
         | 16 |  |  |     uint64_t startAddress;                       // virtual address boundary (must be divisible by 8)
 | 
      
         | 17 |  |  |     uint64_t access_addend;                      // (access_addend & 7) is access permission: SHF_READ, SHF_WRITE, SHF_EXEC
 | 
      
         | 18 |  |  |                                                  // (access_addend & ~7) is added to the virtual address to get physical address
 | 
      
         | 19 |  |  | };
 | 
      
         | 20 |  |  |  
 | 
      
         | 21 |  |  | // union for an operand value of any type
 | 
      
         | 22 |  |  | union SNum {
 | 
      
         | 23 |  |  |     uint64_t q;                                  // 64 bit unsigned integer
 | 
      
         | 24 |  |  |     int64_t  qs;                                 // 64 bit signed integer
 | 
      
         | 25 |  |  |     uint32_t i;                                  // 32 bit unsigned integer
 | 
      
         | 26 |  |  |     int32_t  is;                                 // 32 bit signed integer
 | 
      
         | 27 |  |  |     uint16_t s;                                  // 16 bit unsigned integer
 | 
      
         | 28 |  |  |     int16_t  ss;                                 // 16 bit signed integer
 | 
      
         | 29 |  |  |     uint8_t  b;                                  // 8 bit  unsigned integer
 | 
      
         | 30 |  |  |     int8_t   bs;                                 // 8 bit  signed integer
 | 
      
         | 31 |  |  |     double d;                                    // double precision float
 | 
      
         | 32 |  |  |     float f;                                     // single precision float
 | 
      
         | 33 |  |  | };
 | 
      
         | 34 |  |  |  
 | 
      
         | 35 |  |  | // Indexes into perfCounters array
 | 
      
         | 36 |  |  | const int perf_cpu_clock_cycles = 1;
 | 
      
         | 37 |  |  | const int perf_instructions = 2;
 | 
      
         | 38 |  |  | const int perf_2size_instructions = 3;
 | 
      
         | 39 |  |  | const int perf_3size_instructions = 4;
 | 
      
         | 40 |  |  | const int perf_gp_instructions = 5;
 | 
      
         | 41 |  |  | const int perf_gp_instructions_mask0 = 6;
 | 
      
         | 42 |  |  | const int perf_vector_instructions = 7;
 | 
      
         | 43 |  |  | const int perf_control_transfer_instructions = 8;
 | 
      
         | 44 |  |  | const int perf_direct_jumps = 9;
 | 
      
         | 45 |  |  | const int perf_indirect_jumps = 10;
 | 
      
         | 46 |  |  | const int perf_cond_jumps = 11;
 | 
      
         | 47 |  |  | const int perf_unknown_instruction = 12;
 | 
      
         | 48 |  |  | const int perf_wrong_operands = 13;
 | 
      
         | 49 |  |  | const int perf_array_overflow = 14;
 | 
      
         | 50 |  |  | const int perf_read_violation = 15;
 | 
      
         | 51 |  |  | const int perf_write_violation = 16;
 | 
      
         | 52 |  |  | const int perf_misaligned = 17;
 | 
      
         | 53 |  |  | const int perf_address_of_first_error = 18;
 | 
      
         | 54 |  |  | const int perf_type_of_first_error = 19;
 | 
      
         | 55 |  |  | const int number_of_perf_counters = 20;          // number of performance counter registers
 | 
      
         | 56 |  |  |  
 | 
      
         | 57 |  |  | // Indexes into capabilities registers array
 | 
      
         | 58 |  |  | const int disable_errors_capability_register = 2;// register for disabling errors
 | 
      
         | 59 |  |  | const int number_of_capability_registers = 16;   // number of capability registers
 | 
      
         | 60 |  |  | class CEmulator;                                 // preliminary declaration
 | 
      
         | 61 |  |  |  
 | 
      
         | 62 |  |  | // Class for a thread or CPU core in the emulator
 | 
      
         | 63 |  |  | class CThread {
 | 
      
         | 64 |  |  | public:
 | 
      
         | 65 |  |  |     CThread();                                   // constructor
 | 
      
         | 66 |  |  |     ~CThread();                                  // destructor
 | 
      
         | 67 |  |  |     void run();                                  // start running
 | 
      
         | 68 |  |  |     void setRegisters(CEmulator * emulator);     // initialize registers etc.
 | 
      
         | 69 |  |  |     uint64_t ip;                                 // instruction pointer
 | 
      
         | 70 |  |  |     uint64_t ip0;                                // address base for code and read-only data
 | 
      
         | 71 |  |  |     uint64_t datap;                              // base pointer for writeable data
 | 
      
         | 72 |  |  |     uint64_t threadp;                            // base pointer for thread-local data
 | 
      
         | 73 |  |  |     uint64_t ninstructions;                      // number of instructions executed
 | 
      
         | 74 |  |  |     uint32_t numContr;                           // numeric control register
 | 
      
         | 75 |  |  |     uint32_t lastMask;                           // shows last status of subnormal support
 | 
      
         | 76 |  |  |     uint32_t options;                            // option bits in instruction
 | 
      
         | 77 |  |  |     uint32_t exception;                          // exception or jump caused by current instruction
 | 
      
         | 78 |  |  |     STemplate const * pInstr;                    // current instruction code
 | 
      
         | 79 |  |  |     SFormat  const * fInstr;                     // format of current instruction
 | 
      
         | 80 |  |  |     SNum     parm[6];                            // parm[0] = value of first operand if 3 operands
 | 
      
         | 81 |  |  |                                                  // parm[1] = value of first operand if 2 operands or second operand if 3 operands
 | 
      
         | 82 |  |  |                                                  // parm[2] = value of last operand
 | 
      
         | 83 |  |  |                                                  // parm[3] = value of mask register or NUMCONTR
 | 
      
         | 84 |  |  |                                                  // parm[4] = value of immediate operand without shift or conversion
 | 
      
         | 85 |  |  |                                                  // parm[5] = high part of double size return value
 | 
      
         | 86 |  |  |     uint8_t  operands[6];                        // instruction operands. 0x00-0x1F = register. 0x20 = immediate, 0x40 = memory
 | 
      
         | 87 |  |  |                                                  // operands[0] is destination register
 | 
      
         | 88 |  |  |                                                  // operands[1] is mask register
 | 
      
         | 89 |  |  |                                                  // operands[2] is fallback register
 | 
      
         | 90 |  |  |                                                  // two-operand instructions use operands[4-5]
 | 
      
         | 91 |  |  |                                                  // three-operand instructions use operands[3-5]
 | 
      
         | 92 |  |  |     uint8_t  op;                                 // operation code
 | 
      
         | 93 |  |  |     uint8_t  operandType;                        // operand type for current instruction
 | 
      
         | 94 |  |  |     uint8_t  nOperands;                          // number of source operands for current instruction
 | 
      
         | 95 |  |  |     uint8_t  vect;                               // instruction uses vector registers
 | 
      
         | 96 |  |  |     uint8_t  running;                            // thread is running. 0 = stop, 1 = save RD, 2 = don't save RD
 | 
      
         | 97 |  |  |     bool     readonly;                           // expect memory address to be in read-only section
 | 
      
         | 98 |  |  |     bool     ignoreMask;                         // call execution function even if mask is zero
 | 
      
         | 99 |  |  |     bool     doubleStep;                         // execution function will process two vector elements at a time
 | 
      
         | 100 |  |  |     bool     noVectorLength;                     // RS is not a vector register, or vector length is determined by execution function
 | 
      
         | 101 |  |  |     bool     dontRead;                           // don't read source operand before execution
 | 
      
         | 102 |  |  |     bool     unchangedRd;                        // store instruction: RD is not destination
 | 
      
         | 103 |  |  |     bool     terminate;                          // stop execution
 | 
      
         | 104 |  |  |     bool     memory_error;                       // memory address error
 | 
      
         | 105 |  |  |     CMemoryBuffer vectors;                       // vector register i is at offset i*MaxVectorLength
 | 
      
         | 106 |  |  |     uint64_t registers[32];                      // value of register r0 - r31
 | 
      
         | 107 |  |  |     uint32_t vectorLength[32];                   // length of vector registers v0 - v31
 | 
      
         | 108 |  |  |     uint32_t vectorLengthM;                      // vector length of memory operand
 | 
      
         | 109 |  |  |     uint32_t vectorLengthR;                      // vector length of result
 | 
      
         | 110 |  |  |     uint32_t vectorOffset;                       // offset to current element within vector
 | 
      
         | 111 |  |  |     uint32_t MaxVectorLength;                    // maximum vector length
 | 
      
         | 112 |  |  |     uint32_t returnType;                         // debug return output. bit 0-3: operand type (8 = half precision). bit 4: register. bit 5: memory. //(bit6: one extra element save_cp)
 | 
      
         | 113 |  |  |                                                  // bit 8: vector. bit 12: jump. bit 13: jump taken
 | 
      
         | 114 |  |  |     int8_t * memory;                             // program memory
 | 
      
         | 115 |  |  |     int8_t * tempBuffer;                         // temporary buffer for vector operand
 | 
      
         | 116 |  |  |     uint64_t memAddress;                         // address of memory operand
 | 
      
         | 117 |  |  |     int64_t  addrOperand;                        // relative address of memory operand or jump target
 | 
      
         | 118 |  |  |     uint64_t readVectorElement(uint32_t v, uint32_t vectorOffset); // read vector element
 | 
      
         | 119 |  |  |     void writeVectorElement(uint32_t v, uint64_t value, uint32_t vectorOffset); // write vector element
 | 
      
         | 120 |  |  |     uint64_t getMemoryAddress();                 // get address of a memory operand
 | 
      
         | 121 |  |  |     uint64_t readMemoryOperand(uint64_t address);// read a memory operand
 | 
      
         | 122 |  |  |     void writeMemoryOperand(uint64_t val, uint64_t address);  // write a memory operand
 | 
      
         | 123 |  |  |     void interrupt(uint32_t n);                  // interrupt or trap
 | 
      
         | 124 |  |  |     uint64_t checkSysMemAccess(uint64_t address, uint64_t size, uint8_t rd, uint8_t rs, uint8_t mode);
 | 
      
         | 125 |  |  |     int fprintfEmulated(FILE * stream, const char * format, uint64_t * argumentList); // emulate fprintf with ForwardCom argument list
 | 
      
         | 126 |  |  |     // check if system function has access to a particular address
 | 
      
         | 127 |  |  |     void systemCall(uint32_t mod, uint32_t funcid, uint8_t rd, uint8_t rs); // entry for system calls
 | 
      
         | 128 |  |  |     uint64_t makeNan(uint32_t code, uint32_t operandType);// make a NAN with exception code and address in payload
 | 
      
         | 129 |  |  |     CDynamicArray<uint64_t> callStack;           // stack of return addresses
 | 
      
         | 130 |  |  |     uint32_t callDept;                           // maximum number of entries observed in callStack
 | 
      
         | 131 |  |  |     uint64_t entry_point;                        // program entry point
 | 
      
         | 132 |  |  |     uint64_t perfCounters[number_of_perf_counters];// performance counters
 | 
      
         | 133 |  |  |     uint64_t capabilyReg[number_of_capability_registers];// capability registers
 | 
      
         | 134 |  |  | protected:
 | 
      
         | 135 |  |  |     uint32_t mapIndex1;                          // last memory map index for code
 | 
      
         | 136 |  |  |     uint32_t mapIndex2;                          // last memory map index for read-only data
 | 
      
         | 137 |  |  |     uint32_t mapIndex3;                          // last memory map index for writeable data
 | 
      
         | 138 |  |  |     CEmulator * emulator;                        // pointer to owner
 | 
      
         | 139 |  |  |     CDynamicArray<SMemoryMap> memoryMap;         // memory map
 | 
      
         | 140 |  |  |     CTextFileBuffer listOut;                     // output debug listing
 | 
      
         | 141 |  |  |     uint32_t listFileName;                       // file name for listOut (index into cmd.fileNameBuffer)
 | 
      
         | 142 |  |  |     uint32_t listLines;                          // line counter
 | 
      
         | 143 |  |  |     void fetch();                                // fetch next instruction
 | 
      
         | 144 |  |  |     void decode();                               // decode current instruction
 | 
      
         | 145 |  |  |     void execute();                              // execute current instruction
 | 
      
         | 146 |  |  |     void listStart();                            // start writing debug list
 | 
      
         | 147 |  |  |     void listInstruction(uint64_t address);      // write current instruction to debug list
 | 
      
         | 148 |  |  | public:
 | 
      
         | 149 |  |  |     void listResult(uint64_t result);            // write result of current instruction to debug list
 | 
      
         | 150 |  |  |     void performanceCounters();                  // update performance counters
 | 
      
         | 151 |  |  |     uint64_t readRegister(uint8_t reg) {         // read register value
 | 
      
         | 152 |  |  |         if (vect) {                              // this function is inlined for performance reasons
 | 
      
         | 153 |  |  |             uint64_t val = vectors.get<uint64_t>(reg*MaxVectorLength);
 | 
      
         | 154 |  |  |             if (vectorLength[reg] < 8) {
 | 
      
         | 155 |  |  |                 // vector is less than 8 bytes. zero-extend to 8 bytes
 | 
      
         | 156 |  |  |                 val &= ((uint64_t)1 << vectorLength[reg]) - 1;
 | 
      
         | 157 |  |  |             }
 | 
      
         | 158 |  |  |             return val;
 | 
      
         | 159 |  |  |         }
 | 
      
         | 160 |  |  |         else {
 | 
      
         | 161 |  |  |             return registers[reg];
 | 
      
         | 162 |  |  |         }
 | 
      
         | 163 |  |  |     }
 | 
      
         | 164 |  |  | };
 | 
      
         | 165 |  |  |  
 | 
      
         | 166 |  |  | // Class for the whole emulator
 | 
      
         | 167 |  |  | class CEmulator : public CELF {
 | 
      
         | 168 |  |  | public:
 | 
      
         | 169 |  |  |     CEmulator();                                 // constructor
 | 
      
         | 170 |  |  |     ~CEmulator();                                // destructor
 | 
      
         | 171 |  |  |     void go();                                   // start
 | 
      
         | 172 |  |  | protected:
 | 
      
         | 173 |  |  |     void load();                                 // load executable file into memory
 | 
      
         | 174 |  |  |     void relocate();                             // relocate any absolute addresses and system function id's
 | 
      
         | 175 |  |  |     void disassemble();                          // make disassembly listing for debug output
 | 
      
         | 176 |  |  |     uint32_t MaxVectorLength;                    // maximum vector length
 | 
      
         | 177 |  |  |     int8_t * memory;                             // program memory
 | 
      
         | 178 |  |  |     uint64_t memsize;                            // total allocated memory size
 | 
      
         | 179 |  |  |     uint32_t maxNumThreads;                      // maximum number of threads
 | 
      
         | 180 |  |  |     uint64_t ip0;                                // address base for code and read-only data
 | 
      
         | 181 |  |  |     uint64_t datap0;                             // address base for writeable data
 | 
      
         | 182 |  |  |     uint64_t threadp0;                           // address base for thread data of main thread
 | 
      
         | 183 |  |  |     uint64_t stackp;                             // pointer to stack
 | 
      
         | 184 |  |  |     uint64_t stackSize;                          // data stack size for main thread
 | 
      
         | 185 |  |  |     uint64_t callStackSize;                      // call stack size for main thread
 | 
      
         | 186 |  |  |     uint64_t heapSize;                           // heap size for main thread
 | 
      
         | 187 |  |  |     uint32_t environmentSize;                    // maximum size of environment and command line data
 | 
      
         | 188 |  |  |     CMetaBuffer<CThread> threads;                // one or more threads
 | 
      
         | 189 |  |  |     CDynamicArray<SMemoryMap> memoryMap;         // main memory map
 | 
      
         | 190 |  |  |     CDynamicArray<SLineRef> lineList;            // Cross reference of code addresses to lines in dissassembler output
 | 
      
         | 191 |  |  |     CTextFileBuffer disassemOut;                 // Output file from disassembler
 | 
      
         | 192 |  |  |     CDisassembler disassembler;                  // disassembler for producing output list
 | 
      
         | 193 |  |  |     friend class CThread;
 | 
      
         | 194 |  |  | };
 | 
      
         | 195 |  |  |  
 | 
      
         | 196 |  |  | // Functions for floating point exception and rounding control
 | 
      
         | 197 |  |  | void setRoundingMode(uint8_t r);
 | 
      
         | 198 |  |  | void clearExceptionFlags();
 | 
      
         | 199 |  |  | uint32_t getExceptionFlags();
 | 
      
         | 200 |  |  | void enableSubnormals(uint32_t e);
 | 
      
         | 201 |  |  |  
 | 
      
         | 202 |  |  | // universal function type for execution function
 | 
      
         | 203 |  |  | // all operands and option bits are accessed via *thread
 | 
      
         | 204 |  |  | typedef uint64_t (*PFunc)(CThread * thread);
 | 
      
         | 205 |  |  |  
 | 
      
         | 206 |  |  | // Tables of execution functions
 | 
      
         | 207 |  |  | extern PFunc funcTab1[64];                       // multiformat instructions
 | 
      
         | 208 |  |  | extern PFunc funcTab2[64];                       // jump instructions
 | 
      
         | 209 |  |  | extern PFunc funcTab3[16];                       // jump instructions with 24 bit offset
 | 
      
         | 210 |  |  | // single format instructions:
 | 
      
         | 211 |  |  | extern PFunc funcTab4[64];                       // format 1.0
 | 
      
         | 212 |  |  | extern PFunc funcTab5[64];                       // format 1.1
 | 
      
         | 213 |  |  | extern PFunc funcTab6[64];                       // format 1.2
 | 
      
         | 214 |  |  | extern PFunc funcTab7[64];                       // format 1.3
 | 
      
         | 215 |  |  | extern PFunc funcTab8[64];                       // format 1.4
 | 
      
         | 216 |  |  | extern PFunc funcTab9[64];                       // format 1.8
 | 
      
         | 217 |  |  | extern PFunc funcTab10[64];                      // format 2.5
 | 
      
         | 218 |  |  | extern PFunc funcTab11[64];                      // format 2.6
 | 
      
         | 219 |  |  | extern PFunc funcTab12[64];                      // format 2.9
 | 
      
         | 220 |  |  | extern PFunc funcTab13[64];                      // format 3.1
 | 
      
         | 221 |  |  |  
 | 
      
         | 222 |  |  | // Table of execution function tables, indexed by fInstr->exeTable
 | 
      
         | 223 |  |  | extern PFunc * metaFunctionTable[];
 | 
      
         | 224 |  |  | // Table of dispatch functions for single format instructions with E template
 | 
      
         | 225 |  |  | extern PFunc EDispatchTable[];
 | 
      
         | 226 |  |  |  
 | 
      
         | 227 |  |  | // Table of number of operands for each instruction
 | 
      
         | 228 |  |  | extern uint8_t numOperands[15][64];
 | 
      
         | 229 |  |  | extern uint8_t numOperands2071[64];
 | 
      
         | 230 |  |  | extern uint8_t numOperands2261[64];
 | 
      
         | 231 |  |  | extern uint8_t numOperands2271[64];
 | 
      
         | 232 |  |  |  
 | 
      
         | 233 |  |  | // Execution functions shared between multiple cpp files
 | 
      
         | 234 |  |  | uint64_t f_nop(CThread * thread);
 | 
      
         | 235 |  |  | uint64_t f_add(CThread * thread);
 | 
      
         | 236 |  |  | uint64_t f_sub(CThread * thread);
 | 
      
         | 237 |  |  | uint64_t f_mul(CThread * thread);
 | 
      
         | 238 |  |  | uint64_t f_div(CThread * thread);
 | 
      
         | 239 |  |  | uint64_t f_mul_add(CThread * thread);
 | 
      
         | 240 |  |  | uint64_t f_add_h(CThread * thread);
 | 
      
         | 241 |  |  | uint64_t f_mul_h(CThread * thread);
 | 
      
         | 242 |  |  | uint64_t insert_(CThread * t);
 | 
      
         | 243 |  |  | uint64_t extract_(CThread * t);
 | 
      
         | 244 |  |  | uint64_t bitscan_(CThread * t);
 | 
      
         | 245 |  |  | uint64_t popcount_(CThread * t);
 | 
      
         | 246 |  |  | int64_t  mul64_128s(uint64_t * low, int64_t a, int64_t b);
 | 
      
         | 247 |  |  | uint64_t mul64_128u(uint64_t * low, uint64_t a, uint64_t b);
 | 
      
         | 248 |  |  |  
 | 
      
         | 249 |  |  | // constants and functions for detecting NAN and infinity
 | 
      
         | 250 |  |  | const uint16_t inf_h   = 0x7C00;                 // float16 infinity
 | 
      
         | 251 |  |  | const uint16_t inf2h   = inf_h*2;                // for detecting infinity when sign bit has been shifted out
 | 
      
         | 252 |  |  | const uint32_t inf_f   = 0x7F800000;             // float infinity
 | 
      
         | 253 |  |  | const uint32_t inf2f   = inf_f*2;                // for detecting infinity when sign bit has been shifted out
 | 
      
         | 254 |  |  | const uint32_t nan_f   = 0x7FC00000;             // float nan
 | 
      
         | 255 |  |  | const uint32_t sign_f  = 0x80000000;             // float  sign bit
 | 
      
         | 256 |  |  | const uint32_t nsign_f = 0x7FFFFFFF;             // float not sign bit
 | 
      
         | 257 |  |  | const uint64_t inf_d   = 0x7FF0000000000000;     // double infinity
 | 
      
         | 258 |  |  | const uint64_t inf2d   = inf_d*2;                // for detecting infinity when sign bit has been shifted out
 | 
      
         | 259 |  |  | const uint64_t nan_d   = 0x7FF8000000000000;     // double nan
 | 
      
         | 260 |  |  | const uint64_t nsign_d = 0x7FFFFFFFFFFFFFFF;     // double not sign bit
 | 
      
         | 261 |  |  | const uint64_t sign_d  = 0x8000000000000000;     // double sign bit
 | 
      
         | 262 |  |  |  
 | 
      
         | 263 |  |  | // functions applied to the bit representations of floating point numbers to detect NAN and infinity:
 | 
      
         | 264 |  |  | static inline bool isnan_h(uint16_t x) {return uint16_t(x << 1) > inf2h;}
 | 
      
         | 265 |  |  | static inline bool isnan_f(uint32_t x) {return (x << 1) > inf2f;}
 | 
      
         | 266 |  |  | static inline bool isnan_d(uint64_t x) {return (x << 1) > inf2d;}
 | 
      
         | 267 |  |  | static inline bool isinf_h(uint16_t x) {return uint16_t(x << 1) == inf2h;}
 | 
      
         | 268 |  |  | static inline bool isinf_f(uint32_t x) {return (x << 1) == inf2f;}
 | 
      
         | 269 |  |  | static inline bool isinf_d(uint64_t x) {return (x << 1) == inf2d;}
 | 
      
         | 270 |  |  | static inline bool isnan_or_inf_h(uint16_t x) {return uint16_t(x << 1) >= inf2h;}
 | 
      
         | 271 |  |  | static inline bool isnan_or_inf_f(uint32_t x) {return (x << 1) >= inf2f;}
 | 
      
         | 272 |  |  | static inline bool isnan_or_inf_d(uint64_t x) {return (x << 1) >= inf2d;}
 | 
      
         | 273 |  |  | static inline bool is_zero_or_subnormal_h(uint16_t x) {return (x & 0x7C00) == 0;}
 | 
      
         | 274 |  |  |  
 |