| 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 |
|
|
|