OpenCores
URL https://opencores.org/ocsvn/forwardcom/forwardcom/trunk

Subversion Repositories forwardcom

[/] [forwardcom/] [bintools/] [emulator1.cpp] - Blame information for rev 141

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 52 Agner
/****************************  emulator1.cpp  ********************************
2
* Author:        Agner Fog
3
* date created:  2018-02-18
4
* Last modified: 2021-07-14
5
* Version:       1.11
6
* Project:       Binary tools for ForwardCom instruction set
7
* Description:
8
* Basic functionality of the emulator
9
*
10
* Copyright 2018-2021 GNU General Public License http://www.gnu.org/licenses
11
*****************************************************************************/
12
 
13
#include "stdafx.h"
14
 
15
 
16
///////////////////
17
// CEmulator class
18
///////////////////
19
 
20
// constructor
21
CEmulator::CEmulator() {
22
    memory = 0;                                  // initialize
23
    memsize = 0;
24
    stackp = 0;
25
    // set defaults. may be changed by command line or file header:
26
    MaxVectorLength = 0x80;                      // 128 bytes = 1024 bits
27
    maxNumThreads = 1;                           // multithreading not supported yet
28
    stackSize = 0x100000;                        // 1 MB. data stack size for main thread
29
    callStackSize = 0x800;                       // call stack size for main thread
30
    heapSize = 0;                                // heap size for main thread
31
    environmentSize = 0x100;                     // maximum size of environment and command line data
32
}
33
 
34
// destructor
35
CEmulator::~CEmulator() {
36
    if (memory) delete[] memory;                 // free allocated program memory
37
}
38
 
39
// start
40
void CEmulator::go() {
41
    threads.setSize(maxNumThreads);              // initialize threads
42
    load();                                      // load executable file
43
    if (err.number()) return;
44
    if (fileHeader.e_flags & EF_RELOCATE) relocate();
45
    if (err.number()) return;
46
 
47
    // set up disassembler for output list
48
    if (cmd.outputListFile) disassemble();
49
 
50
    // prepare main thread
51
    threads[0].setRegisters(this);
52
    // run main thread
53
    threads[0].run();
54
}
55
 
56
// load executable file into memory
57
void CEmulator::load() {
58
    const char * filename = cmd.getFilename(cmd.inputFile);
59
    read(filename);                              // read executable file
60
    if (err.number()) return;
61
    split();                                     // extract components
62
    if (getFileType() != FILETYPE_FWC || fileHeader.e_type != ET_EXEC) {
63
        err.submit(ERR_LINK_FILE_TYPE_EXE, filename);
64
        return;
65
    }
66
    // calculate necessary memory size
67
    uint64_t blocksize = 0;                      // size of block of segments with same base pointer
68
    uint32_t ph;                                 // program header index
69
    uint64_t align;                              // program header alignment
70
    uint64_t address;                            // current address
71
    uint32_t flags, lastflags;                   // flags of program header
72
    bool hasDataSegment = false;                 // check if there is a data segment header
73
    const uint32_t dataflags = SHF_READ | SHF_WRITE | SHF_ALLOC | SHF_DATAP; // expected flags for data segment
74
 
75
    memsize = environmentSize;                   // reserve space for environment in the beginning
76
    for (ph = 0; ph < programHeaders.numEntries(); ph++) {
77
        if (programHeaders[ph].p_vaddr == 0) {
78
            // start of a new block
79
            memsize += blocksize;
80
 
81
            if ((programHeaders[ph].p_flags & SHF_READ) && (ph+1 == programHeaders.numEntries())) {
82
                // This is the last data section
83
                // Make space for reading a vector beyond the end
84
                // Note: we cannot do this after the const section because the space there must have fixed size, provided by the linker
85
                uint32_t extraSpace = MaxVectorLength;
86
                if (extraSpace < DATA_EXTRA_SPACE) extraSpace = DATA_EXTRA_SPACE;
87
                programHeaders[ph].p_memsz += extraSpace;
88
            }
89
            align = (uint64_t)1 << programHeaders[ph].p_align;
90
            memsize = (memsize + align - 1) & -(int64_t)align;
91
            blocksize = programHeaders[ph].p_memsz;
92
        }
93
        else {
94
            // continuation of previous block
95
            blocksize += programHeaders[ph].p_vaddr + programHeaders[ph].p_memsz;
96
        }
97
        if ((programHeaders[ph].p_flags & dataflags) == dataflags) hasDataSegment = true;
98
    }
99
    if (!hasDataSegment) { // there is no data segment. make one for the stack
100
        ElfFwcPhdr dataSegment;
101
        zeroAllMembers(dataSegment);
102
        dataSegment.p_type = PT_LOAD;
103
        dataSegment.p_flags = dataflags;
104
        dataSegment.p_align = 3;
105
        programHeaders.push(dataSegment);
106
    }
107
 
108
    // end of last block
109
    memsize += blocksize;
110
    align = (uint64_t)1 << MEMORY_MAP_ALIGN;
111
    memsize = (memsize + align - 1) & -(int64_t)align;
112
    // add stack and heap
113
    memsize += stackSize + heapSize;
114
    // allocate memory
115
    memory = new int8_t[size_t(memsize)];
116
    if (!memory) {
117
        err.submit(ERR_MEMORY_ALLOCATION);
118
        return;
119
    }
120
    memset(memory, 0, size_t(memsize));
121
    // start making memory map
122
    address = 0;
123
    flags = SHF_READ | SHF_IP;  lastflags = flags;
124
    SMemoryMap mapentry = {address, flags};
125
    memoryMap.push(mapentry);
126
    // make space for environment
127
    address = environmentSize;
128
    for (ph = 0; ph < programHeaders.numEntries(); ph++) {
129
        flags = programHeaders[ph].p_flags & (SHF_PERMISSIONS | SHF_BASEPOINTER);
130
        if (flags != lastflags && (lastflags & SHF_IP) && (!(flags & SHF_IP))) {
131
            // insert stack here
132
            align = 8;
133
            address = (address + align - 1) & -(int64_t)align;
134
            flags = SHF_DATAP | SHF_READ | SHF_WRITE;
135
            mapentry.startAddress = address;
136
            mapentry.access_addend = flags;
137
            memoryMap.push(mapentry);
138
            address += stackSize;
139
            stackp = address;
140
            lastflags = flags;
141
            flags = programHeaders[ph].p_flags & (SHF_PERMISSIONS | SHF_BASEPOINTER);
142
        }
143
        if ((flags & SHF_PERMISSIONS) != (lastflags & SHF_PERMISSIONS)) {
144
            // start new map entry
145
            align = (uint64_t)1 << programHeaders[ph].p_align;
146
            address = (address + align - 1) & -(int64_t)align;
147
            mapentry.startAddress = address;
148
            mapentry.access_addend = flags;
149
            memoryMap.push(mapentry);
150
        }
151
        if (programHeaders[ph].p_vaddr == 0) {
152
            switch (flags & SHF_BASEPOINTER) {
153
            case SHF_IP:
154
                ip0 = address;  break;
155
            case SHF_DATAP:
156
                datap0 = address;  break;
157
            case SHF_THREADP:
158
                threadp0 = address;  break;
159
            }
160
        }
161
        // check integrity before copying data
162
        if (address + programHeaders[ph].p_filesz > memsize
163
            || programHeaders[ph].p_filesz > programHeaders[ph].p_memsz
164
            || programHeaders[ph].p_offset + programHeaders[ph].p_filesz > dataSize()) {
165
            err.submit(ERR_ELF_INDEX_RANGE);
166
            return;
167
        }
168
        // store address in program header
169
        programHeaders[ph].p_vaddr = address;
170
        // copy data
171
        memcpy(memory + address, dataBuffer.buf() + programHeaders[ph].p_offset, size_t(programHeaders[ph].p_filesz));
172
        address += programHeaders[ph].p_memsz;
173
        lastflags = flags;
174
    }
175
    // make terminating entry
176
    mapentry.startAddress = address;
177
    mapentry.access_addend = 0;
178
    memoryMap.push(mapentry);
179
}
180
 
181
// relocate any absolute addresses and system function id's
182
void CEmulator::relocate() {
183
    uint32_t r;                                  // relocation index
184
    uint32_t ph;                                 // program header index
185
    uint32_t rsection;                           // relocated section
186
    uint32_t phFistSection;                      // first section covered by program header
187
    uint32_t phNumSections;                      // number of sections covered by program header
188
    uint64_t sourceAddress;                      // address of relocation source
189
    uint64_t targetAddress;                      // address of relocation target
190
    const char * symbolname;                     // name of target symbol
191
    bool found;                                  // program header found
192
    for (r = 0; r < relocations.numEntries(); r++) {
193
        // loadtime relocations are listed first. stop at first non-loadtime record
194
        if (!(relocations[r].r_type & R_FORW_LOADTIME)) break;
195
        // find program header containing relocated section
196
        rsection = relocations[r].r_section;
197
        found = false;
198
        for (ph = 0; ph < programHeaders.numEntries(); ph++) {
199
            phFistSection = (uint32_t)programHeaders[ph].p_paddr;
200
            phNumSections = (uint32_t)(programHeaders[ph].p_paddr >> 32);
201
            if (rsection >= phFistSection && rsection < phFistSection + phNumSections) {
202
                found = true;  break;
203
            }
204
        }
205
        if (!found) {
206
            err.submit(ERR_REL_SYMBOL_NOT_FOUND);  continue;
207
        }
208
        // calculate address of relocation source
209
        sourceAddress = programHeaders[ph].p_vaddr + sectionHeaders[rsection].sh_addr - sectionHeaders[phFistSection].sh_addr + relocations[r].r_offset;
210
        if (sourceAddress >= memsize) {
211
            err.submit(ERR_ELF_INDEX_RANGE);  continue;
212
        }
213
        if ((relocations[r].r_type & R_FORW_RELTYPEMASK) == R_FORW_ABS) {
214
            // needs absolute address of target
215
            uint32_t symi = relocations[r].r_sym;
216
            if (symi >= symbols.numEntries()) {
217
                err.submit(ERR_ELF_INDEX_RANGE);  return;
218
            }
219
            ElfFwcSym & targetSym = symbols[symi];
220
            uint32_t tsec = targetSym.st_section;  // section of target symbol
221
            // find program header containing target section
222
            found = false;
223
            for (ph = 0; ph < programHeaders.numEntries(); ph++) {
224
                phFistSection = (uint32_t)programHeaders[ph].p_paddr;
225
                phNumSections = (uint32_t)(programHeaders[ph].p_paddr >> 32);
226
                if (tsec >= phFistSection && tsec < phFistSection + phNumSections) {
227
                    found = true;  break;
228
                }
229
            }
230
            if (!found) {
231
                err.submit(ERR_REL_SYMBOL_NOT_FOUND);  continue;
232
            }
233
            // calculate target address
234
            targetAddress = programHeaders[ph].p_vaddr + sectionHeaders[rsection].sh_addr - sectionHeaders[phFistSection].sh_addr + targetSym.st_value;
235
            if (targetAddress >= memsize) {
236
                err.submit(ERR_ELF_INDEX_RANGE);  continue;
237
            }
238
            // scale (scaling of absolute addresses is rarely used, but allowed)
239
            targetAddress >>= (relocations[r].r_type & R_FORW_RELSCALEMASK);
240
            // insert relocation of desired size
241
            switch (relocations[r].r_type & R_FORW_RELSIZEMASK) {
242
            case R_FORW_8:     // 8  bit relocation size
243
                if (targetAddress >> 8) goto OVERFLW;
244
                *(memory + sourceAddress) = int8_t(targetAddress);
245
                break;
246
            case R_FORW_16:    // 16 bit relocation size
247
                if (targetAddress >> 16) goto OVERFLW;
248
                *(uint16_t*)(memory + sourceAddress) = uint16_t(targetAddress);
249
                break;
250
            case R_FORW_32:    // 32 bit relocation size
251
                if (targetAddress >> 32) goto OVERFLW;
252
                *(uint32_t*)(memory + sourceAddress) = uint32_t(targetAddress);
253
                break;
254
            case R_FORW_32LO:  // Low  16 of 32 bits relocation
255
                *(uint16_t*)(memory + sourceAddress) = uint16_t(targetAddress);
256
                break;
257
            case R_FORW_32HI:  // High 16 of 32 bits relocation
258
                if (targetAddress >> 32) goto OVERFLW;
259
                *(uint16_t*)(memory + sourceAddress) = uint16_t(targetAddress >> 16);
260
                break;
261
            case R_FORW_64:    // 64 bit relocation size
262
                *(uint64_t*)(memory + sourceAddress) = uint64_t(targetAddress);
263
                break;
264
            case R_FORW_64LO:  // Low  32 of 64 bits relocation
265
                *(uint32_t*)(memory + sourceAddress) = uint32_t(targetAddress);
266
                break;
267
            case R_FORW_64HI:  // High 32 of 64 bits relocation
268
                *(uint32_t*)(memory + sourceAddress) = uint32_t(targetAddress >> 32);
269
                break;
270
            default:
271
            OVERFLW:
272
                symbolname = symbolNameBuffer.getString(targetSym.st_name);
273
                err.submit(ERR_LINK_RELOCATION_OVERFLOW, symbolname);
274
            }
275
        }
276
        else {
277
            // to do: get system function id from name
278
        }
279
    }
280
}
281
 
282
void CEmulator::disassemble() {              // make disassembly listing for debug output
283
    disassembler.copy(*this);                // copy ELF file
284
    disassembler.getComponents1();           // set up instruction list, etc.
285
    if (err.number()) return;
286
    //disassembler.outputFile = cmd.fileNameBuffer.pushString("ddd.txt");
287
    disassembler.debugMode = 1;              // produce disassembly for debug display/list
288
    disassembler.go();                       // disassemble
289
    if (err.number()) return;
290
    disassembler.getLineList(lineList);      // get cross reference list from address to disassembly output file
291
    lineList.sort();                         // only needed if multiple segments in lineList
292
    disassembler.getOutFile(disassemOut);    // get disassembly output file
293
    // replace all linefeeds by end of string
294
    for (uint32_t i = 0; i < disassemOut.dataSize(); i++) {
295
        if ((uint8_t)disassemOut.buf()[i] < ' ') disassemOut.buf()[i] = 0;
296
    }
297
}
298
 
299
 
300
/////////////////
301
// CThread class
302
/////////////////
303
 
304
// constructor
305
CThread::CThread() {
306
    numContr = 1 | (1<<MSK_SUBNORMAL);                          // default numContr. Bit 0 must be 1;
307
    enableSubnormals (numContr & (1<<MSK_SUBNORMAL));           // enable or disable subnormal numbers
308
    lastMask = numContr;
309
    ninstructions = 0;
310
    mapIndex1 = mapIndex2 = mapIndex3 = 0;                 // indexes into memory map
311
    callDept = 0;
312
    listLines = 0;
313
    tempBuffer = 0;
314
}
315
 
316
// destructor
317
CThread::~CThread() {
318
    if (tempBuffer != 0) {
319
        delete[] tempBuffer;                               // free temporary buffer
320
    }
321
}
322
 
323
// initialize registers etc. from values in emulator
324
void CThread::setRegisters(CEmulator * emulator) {
325
    this->emulator = emulator;
326
    this->memory = emulator->memory;                       // program memory
327
    memoryMap.copy(emulator->memoryMap);                   // memory map
328
    // ip_base = emulator->ip_base;                        // reference point for code and read-only data
329
    ip0 = emulator->ip0;                                   // reference point for code and read-only data
330
    datap = emulator->datap0 + emulator->fileHeader.e_datap_base;  // base pointer for writeable data
331
    threadp = emulator->threadp0 + emulator->fileHeader.e_threadp_base; // base pointer for thread-local data
332
    ip = entry_point = emulator->fileHeader.e_entry + ip0; // start value of instruction pointer
333
    MaxVectorLength = emulator->MaxVectorLength;
334
    tempBuffer = new int8_t[MaxVectorLength * 2];          // temporary buffer for vector operands
335
    memset(registers, 0, sizeof(registers));               // clear all registers
336
    memset(vectorLength, 0, sizeof(vectorLength));
337
    vectors.setDataSize(32*MaxVectorLength);
338
    registers[31] = emulator->stackp;                      // stack pointer
339
    memset(perfCounters, 0, sizeof(perfCounters));         // reset performance counters
340
    // initialize capability registers
341
    memset(capabilyReg, 0, sizeof(capabilyReg));           // reset capability registers
342
    capabilyReg[0] = 'E';                                  // brand ID. E = emulator
343
    capabilyReg[1] = FORWARDCOM_VERSION * 0x10000 + FORWARDCOM_SUBVERSION * 0x100; // ForwardCom version
344
    capabilyReg[8] = 0b1111;                               // support for operand sizes in g.p. registers
345
    capabilyReg[9] = 0b101101111;                          // support for operand sizes in vector registers
346
    capabilyReg[12] = MaxVectorLength;                     // maximum vector length
347
    capabilyReg[13] = MaxVectorLength;                     // maximum vector length for permute
348
    capabilyReg[14] = MaxVectorLength;                     // maximum block size for permute??
349
    capabilyReg[15] = MaxVectorLength;                     // maximum vector length compress_sparse and expand_sparse    
350
    listFileName = cmd.outputListFile;                     // name for output list file. to do: add thread number to list file name if multiple threads
351
}
352
 
353
// start running
354
void CThread::run() {
355
    listStart();                                 // start writing debug output list
356
    running = 1;  terminate = false;
357
    while (running && !terminate) {
358
 
359
        fetch();                                 // fetch next instruction
360
        if (terminate) break;
361
        decode();                                // decode instruction
362
        if (terminate) break;
363
        execute();                               // execute instruction
364
    }
365
    // write debug output
366
    if (listFileName) {
367
        listOut.write(cmd.getFilename(listFileName));
368
    }
369
}
370
 
371
// fetch next instruction
372
void CThread::fetch() {
373
    // find memory map entry
374
    while (ip < memoryMap[mapIndex1].startAddress) {
375
        if (mapIndex1 > 0) mapIndex1--;
376
        else {
377
            interrupt(INT_ACCESS_EXE);  return;
378
        }
379
    }
380
    while (ip >= memoryMap[mapIndex1 + 1].startAddress) {
381
        if (mapIndex1 + 2 < memoryMap.numEntries()) mapIndex1++;
382
        else {
383
            interrupt(INT_ACCESS_EXE);  return;
384
        }
385
    }
386
    // check execute permission
387
    if (!(memoryMap[mapIndex1].access_addend & SHF_EXEC)) interrupt(INT_ACCESS_EXE);
388
    // get instruction
389
    pInstr = (STemplate const *)(memory + ip);
390
}
391
 
392
// List of instructionlengths, used in decode()
393
static const uint8_t lengthList[8] = {1,1,1,1,2,2,3,4};
394
 
395
// decode current instruction
396
void CThread::decode() {
397
 
398
    listInstruction(ip - ip0);            // make debug listing
399
    // decoding similar to CDisassembler::parseInstruction()
400
    op = pInstr->a.op1;
401
    //rs = pInstr->a.rs;
402
 
403
    // Get format
404
    uint32_t format = (pInstr->a.il << 8) + (pInstr->a.mode << 4); // Construct format = (il,mode,submode)
405
 
406
    // Get submode
407
    switch (format) {
408
    case 0x200: case 0x220: case 0x300: case 0x320:        // submode in mode2
409
        format += pInstr->a.mode2;
410
        break;
411
    case 0x250: case 0x310:                                // Submode for jump instructions etc.
412
        if (op < 8) {
413
            format += op;                                  // op1 defines sub-format
414
            op = pInstr->b[0] & 0x3F;                      // OPJ is in IM1 (other positions for opj fixed below
415
        }
416
        else {
417
            format += 8;
418
        }
419
        break;
420
    }
421
    // Look up format details (lookupFormat() is in emulator2.cpp)
422
    fInstr = &formatList[lookupFormat(pInstr->q)];
423
    format = fInstr->format2;                              // Include subformat depending on op1
424
 
425
    if (fInstr->imm2 & 0x80) {                             // alternative position of opj
426
        if (fInstr->imm2 & 0x40) {                         // no opj
427
            op = 63;
428
        }
429
        else if (fInstr->imm2 & 0x10) {
430
            op = pInstr->b[7] & 0x3F;                      // OPJ is in high part of IM2 in format A2
431
        }
432
    }
433
    if (fInstr->tmplate == 0xE && pInstr->a.op2 && !(fInstr->imm2 & 0x100)) {
434
        // Single format instruction if op2 != 0 in E template and op2 not used as immediate operand
435
        static SFormat form;                               // don't initialize static object.
436
        form = *fInstr;                                    // copy format record
437
        form.category = 1;                                 // change category
438
        fInstr = &form;                                    // point to static object
439
        // operand tables for single-format instructions
440
        if (format == 0x207 && pInstr->a.op2 == 1) nOperands = numOperands2071[op]; // table for format 2.0.7
441
        else if (format == 0x226 && pInstr->a.op2 == 1) nOperands = numOperands2261[op]; // table for format 2.2.6
442
        else if (format == 0x227 && pInstr->a.op2 == 1) nOperands = numOperands2271[op]; // table for format 2.2.7
443
        else nOperands = 0xB;                              // default value when there is no table
444
    }
445
    else {
446
        // operand tables for multi-format instructions
447
        nOperands = numOperands[fInstr->exeTable][op];     // number of source operands (see bit definitions in format_tables.cpp)
448
    }
449
 
450
    ignoreMask     = (nOperands & 0x08) != 0;              // bit 3: ignore mask
451
    noVectorLength = (nOperands & 0x10) != 0;              // bit 4: vector length determined by execution function
452
    doubleStep     = (nOperands & 0x20) != 0;              // bit 5: take double steps
453
    dontRead       = (nOperands & 0x40) != 0;              // bit 6: don't read source operand
454
    unchangedRd    = (nOperands & 0x80) != 0;              // bit 7: RD is unchanged, not destination
455
    nOperands &= 0x7;                                      // bit 0-2: number of operands
456
 
457
    // Get operand type
458
    if (fInstr->ot == 0) {                                 // Operand type determined by OT field
459
        operandType = pInstr->a.ot;                        // Operand type
460
        if (!(pInstr->a.mode & 6) && !(fInstr->vect & 0x11)) {
461
            // Check use of M bit
462
            format |= (operandType & 4) << 5;              // Add M bit to format
463
            operandType &= ~4;                             // Remove M bit from operand type
464
        }
465
    }
466
    else if ((fInstr->ot & 0xF0) == 0x10) {                // Operand type fixed. Value in formatList
467
        operandType = fInstr->ot & 7;
468
    }
469
    else if (fInstr->ot == 0x32) {                         // int32 for even op1, int64 for odd op1
470
        operandType = 2 + (pInstr->a.op1 & 1);
471
    }
472
    else if (fInstr->ot == 0x35) {                         // Float for even op1, double for odd op1
473
        operandType = 5 + (pInstr->a.op1 & 1);
474
    }
475
    else {
476
        operandType = 0;                                   // Error in formatList. Should not occur
477
    }
478
 
479
    // Find instruction length
480
    uint8_t instrLength = lengthList[pInstr->i[0] >> 29];  // Length up to 3 determined by il. Length 4 by upper bit of mode
481
    ip += instrLength * 4;  // next ip
482
 
483
    // get address of memory operand
484
    if (fInstr->mem) memAddress = getMemoryAddress();
485
 
486
    // find operands
487
    if (fInstr->category == 4 && fInstr->jumpSize) {
488
        // jump instruction with self-relative jump address
489
        // check if it uses vector registers
490
        vect = (fInstr->vect & 0x10) && fInstr->tmplate != 0xC && (pInstr->a.ot & 4);
491
        // pointer to address field
492
        const uint8_t * pa = &pInstr->b[0] + fInstr->jumpPos;
493
        // store relative address in addrOperand
494
        switch (fInstr->jumpSize) {
495
        case 1:    // sign extend 8-bit offset
496
            addrOperand = *(int8_t*)pa;
497
            break;
498
        case 2:    // sign extend 16-bit offset
499
            addrOperand = *(int16_t*)pa;
500
            break;
501
        case 3:    // sign extend 24-bit offset
502
            addrOperand = *(int32_t*)pa << 8 >> 8;
503
            break;
504
        case 4:    // sign extend 32-bit offset
505
            addrOperand = *(int32_t*)pa;
506
            break;
507
        case 8:    // 64-bit offset
508
            addrOperand = *(int64_t*)pa;
509
            break;
510
        default:
511
            addrOperand = 0;
512
            err.submit(ERR_INTERNAL);
513
        }
514
        // pointer to immediate field
515
        const uint8_t * pi = &pInstr->b[0] + fInstr->immPos;
516
        // get immediate operand or last register operand
517
        if (fInstr->opAvail & 1) {
518
            // last operand is immediate. sign extend or convert it into parm[2]
519
            switch (fInstr->immSize) {
520
            case 1:
521
                parm[2].qs = parm[4].qs = *(int8_t*)pi;              // sign extend
522
                if (pInstr->a.ot == 5) parm[2].f = parm[4].bs;       // convert to float
523
                if (pInstr->a.ot == 6) parm[2].d = parm[4].bs;       // convert to double
524
                break;
525
            case 2:
526
                parm[2].qs = parm[4].qs = *(int16_t*)pi;             // sign extend
527
                if (pInstr->a.ot == 5) parm[2].f = half2float(*(uint16_t*)pi); // convert from half precision
528
                if (pInstr->a.ot == 6) parm[2].d = half2float(*(uint16_t*)pi); // convert from half precision
529
                break;
530
            case 4:
531
                parm[2].qs = parm[4].qs = *(int32_t*)pi;             // sign extend
532
                if (pInstr->a.ot == 6) parm[2].d = *(float*)pi;      // convert to double
533
                break;
534
            case 8:
535
                parm[2].qs = parm[4].qs = *(int64_t*)pi;  break;     // just copy
536
            default:
537
                err.submit(ERR_INTERNAL);
538
            }
539
            operands[5] = 0x20;
540
            // first source operand
541
            if (fInstr->opAvail & 0x20) operands[4] = pInstr->a.rs;
542
            else operands[4] = pInstr->a.rd;
543
        }
544
        else if (fInstr->opAvail & 2) {
545
            // last operand is memory
546
            parm[2].q = readMemoryOperand(memAddress);
547
            operands[5] = 0x40;
548
            // first source operand
549
            if (fInstr->opAvail & 0x20) operands[4] = pInstr->a.rs;
550
            else operands[4] = pInstr->a.rd;
551
        }
552
        else {
553
            // last source operand is a register
554
            operands[4] = pInstr->a.rd;
555
            if ((fInstr->opAvail & 0x30) == 0x30) {
556
                // three registers
557
                operands[4] = pInstr->a.rs;
558
                operands[5] = pInstr->a.rt;
559
            }
560
            else if (fInstr->opAvail & 0x20) operands[5] = pInstr->a.rs;
561
            else operands[5] = pInstr->a.rd;
562
            // read register containing last operand
563
            parm[2].q = readRegister(operands[5]);
564
        }
565
        operands[0] = pInstr->a.rd;                         // destination
566
        operands[1] = 0xFF;                                 // no mask
567
        // read register containing first source operand
568
        parm[1].q = readRegister(operands[4]);
569
        // return type for debug output. may be changed by execution function
570
        returnType = operandType | 0x1010;
571
        return;
572
    }
573
 
574
    // single format, multi-format, and indirect jump instructions:
575
 
576
    // Make list of operands from available operands.
577
    // The operands[] array must have 6 elements to avoid overflow here,
578
    // even if some elements are later overwritten and used for other purposes
579
    uint8_t opAvail = fInstr->opAvail;    // Bit index of available operands
580
    // opAvail bits: 1 = immediate, 2 = memory,
581
    // 0x10 = RT, 0x20 = RS, 0x40 = RU, 0x80 = RD 
582
    int j = 5;
583
    if (opAvail & 0x01) operands[j--] = 0x20;              // immediate operand
584
    if (opAvail & 0x02) operands[j--] = 0x40;              // memory operand
585
    if (opAvail & 0x10) operands[j--] = pInstr->a.rt;      // register RT
586
    if (opAvail & 0x20) operands[j--] = pInstr->a.rs;      // register RS
587
    if (opAvail & 0x40) operands[j--] = pInstr->a.ru;      // register RU
588
    if (opAvail & 0x80) operands[j--] = pInstr->a.rd;      // register RD
589
    operands[0] = pInstr->a.rd;                            // destination
590
 
591
    // find mask register
592
    if (fInstr->tmplate == 0xA || fInstr->tmplate == 0xE) {
593
        operands[1] = pInstr->a.mask;
594
        // find fallback register
595
        uint8_t fb = findFallback(fInstr,  pInstr, nOperands);
596
        operands[2] = fb;                                  // fallback register, or 0xFF if zero fallback
597
    }
598
    else {
599
        operands[1] = operands[2] = 0xFF;                  // no mask, no fallback
600
    }
601
 
602
    // determine if vector registers are used
603
    vect = (fInstr->vect & 1) || ((fInstr->vect & 0x10) && (pInstr->a.ot & 4));
604
 
605
    // return type for debug output. may be changed by execution function
606
    returnType = operandType | 0x10 | vect << 8;
607
 
608
    // get value of last operand if not a vector
609
    if (opAvail & 0x01) {
610
        // pointer to immediate field
611
        const uint8_t * pi = &pInstr->b[0] + fInstr->immPos;
612
        // get value, sign extended
613
        switch (fInstr->immSize) {
614
        case 1:
615
            parm[2].qs = *(int8_t*)pi;
616
            break;
617
        case 2:
618
            parm[2].qs = *(int16_t*)pi;
619
            break;
620
        case 4:
621
            parm[2].qs = *(int32_t*)pi;
622
            break;
623
        case 8:
624
            parm[2].qs = *(uint64_t*)pi;
625
            break;
626
        case 14:  // 4 bits
627
            parm[2].q = *(uint8_t*)pi & 0xF;
628
            break;
629
        default:
630
            err.submit(ERR_INTERNAL);
631
        }
632
        // extend, shift, or convert
633
        parm[4].q = parm[2].q;                             // preserve original value
634
        switch (operandType) {
635
        case 5:  // float
636
            if (fInstr->immSize == 1) { // convert integer
637
                parm[2].f = (float)(int8_t)parm[2].b;
638
            }
639
            else if (fInstr->immSize == 2) { // convert half precision
640
                parm[2].f = half2float(parm[2].i);
641
            }
642
            break;
643
        case 6:  // double precision
644
            if (fInstr->immSize == 1) { // convert integer
645
                parm[2].d = (double)(int8_t)parm[2].b;
646
            }
647
            else if (fInstr->immSize == 2) { // convert half precision
648
                parm[2].d = half2float(parm[2].i);
649
            }
650
            else if (fInstr->immSize == 4) { // convert single precision
651
                parm[2].d = parm[2].f;
652
            }
653
            break;
654
        case 7:  // quadruple precision
655
            // to do
656
            break;
657
        default: // all integer types. shift value if needed
658
            if (fInstr->imm2 & 4) parm[2].q <<= pInstr->a.im3;
659
            else if (fInstr->imm2 & 8) parm[2].q <<= pInstr->a.im2;
660
        }
661
        if (opAvail & 2) {
662
            // both memory and immediate operand
663
            if ((!vect || (fInstr->vect & 4)) && !dontRead) {
664
                // scalar or broadcast memory operand
665
                parm[1].q = readMemoryOperand(memAddress);
666
            }
667
            if (nOperands > 2) parm[0].q = readRegister(operands[3] & 0x1F);
668
            return;
669
        }
670
    }
671
    else if ((!vect || (fInstr->vect & 4)) && (opAvail & 0x02) && !dontRead) {
672
        // scalar or broadcast memory operand and no immediate operand
673
        parm[2].q = readMemoryOperand(memAddress);
674
    }
675
    else if (!vect) {
676
        // general purpose register
677
        parm[2].q = readRegister(operands[5] & 0x1F);
678
    }
679
    // get values of remaining operands
680
    if (nOperands > 1) parm[1].q = readRegister(operands[4] & 0x1F);
681
    if (nOperands > 2) parm[0].q = readRegister(operands[3] & 0x1F);
682
}
683
 
684
 
685
// execute current instruction
686
void CThread::execute() {
687
    uint64_t result = 0;                         // destination value
688
    PFunc functionPointer = 0;                   // pointer to execution function
689
    running = 1;
690
 
691
    // find function pionter
692
    if (fInstr->exeTable == 0) {
693
        interrupt(INT_UNKNOWN_INST);  return;
694
    }
695
    if (fInstr->tmplate == 0xE && pInstr->a.op2 != 0 && !(fInstr->imm2 & 0x100)) {
696
        // single format instruction with E template
697
        uint8_t index; // index into EDispatchTable
698
        // bit 0-2 = mode2
699
        // bit   3 = mode bit 1
700
        // bit   4 = il bit 0
701
        // bit 5-6 = op2 - 1
702
        index = pInstr->a.mode2 | (pInstr->a.mode << 2 & 8) | (pInstr->a.il << 4 & 0x10) | (pInstr->a.op2 - 1) << 5;
703
        functionPointer = EDispatchTable[index];
704
    }
705
    else {  // all other instructions. fInstr->exeTable indicates which function table to look into  
706
        functionPointer = metaFunctionTable[fInstr->exeTable][op];
707
    }
708
    if (!functionPointer || !fInstr->exeTable) {
709
        interrupt(INT_UNKNOWN_INST);
710
        return;
711
    }
712
    if (vect) { // vector instruction
713
        // length of each element
714
        uint32_t elementSize = dataSizeTable[operandType];
715
        // get vector length
716
        // vector length of result = length of first source operand register
717
        switch (nOperands) {
718
        case 0:  // no source operands. vector length will be set by instruction
719
            vectorLengthR = 8;  break;
720
        case 1:  // one source operand
721
            if (operands[5] & 0x20) {  // source operand is immediate. 
722
                vectorLengthR = dataSizeTable[operandType]; // vector length may be modified by instruction
723
            }
724
            else if (operands[5] & 0x40) {  // source operand is memory                
725
                vectorLengthR = vectorLengthM;
726
            }
727
            else { // source operand is register
728
                vectorLengthR = vectorLength[operands[5]];
729
            }
730
            break;
731
        case 2:  // two source operands
732
            if (operands[4] & 0x40) {  // first source operand is memory                
733
                vectorLengthR = vectorLengthM;
734
            }
735
            else {   // first source operand is register
736
                vectorLengthR = vectorLength[operands[4]];
737
            }
738
            break;
739
        case 3: default:  // three source operands. first source operand must be register
740
            vectorLengthR = vectorLength[operands[3]];
741
            break;
742
        }
743
        if (noVectorLength                       // vector length determined by execution function
744
            || fInstr->category == 4) {          // call compare/jump function even if vector is empty
745
            vectorLengthR = elementSize;         // make sure it is called at least once
746
        }
747
        // set vector length of destination
748
        if (!noVectorLength && !unchangedRd) {
749
            vectorLength[operands[0]] = vectorLengthR;
750
        }
751
 
752
        // loop through vector
753
        vect = 1;
754
        for (vectorOffset = 0; vectorOffset < vectorLengthR; vectorOffset += elementSize) {
755
            if (vect & 4) break;  // stop loop
756
 
757
            // read nOperands operands
758
            for (int iOp = 3 - nOperands; iOp <= 2; iOp++) {
759
                if (operands[iOp+3] & 0x20) { // immediate
760
                    // has already been read into parm[2]
761
                }
762
                else if (operands[iOp+3] & 0x40) { // memory
763
                    if (fInstr->vect & 4) { // broadcast memory operand
764
                        if (vectorOffset + elementSize > vectorLengthM) {
765
                            parm[iOp].q = 0; // beyond broadcast length
766
                        }
767
                        else { // read broadcast memory operand
768
                            parm[iOp].q = readMemoryOperand(memAddress);
769
                        }
770
                    }
771
                    else {  // memory vector 
772
                        if (!dontRead) {
773
                            if (vectorOffset + elementSize > vectorLengthM) {
774
                                parm[iOp].q = 0; // beyond memory operand length
775
                            }
776
                            else {  // read memory vector                          
777
                                parm[iOp].q = readMemoryOperand(memAddress + vectorOffset);
778
                            }
779
                        }
780
                    }
781
                }
782
                else { // vector register
783
                    parm[iOp].q = readVectorElement(operands[iOp+3], vectorOffset);
784
                }
785
            }
786
 
787
            // get mask
788
            if ((operands[1] & 7) != 7) {
789
                parm[3].q = readVectorElement(operands[1], vectorOffset);
790
            }
791
            else {
792
                parm[3].q = numContr;
793
            }
794
            // skip instruction if mask = 0, except for certain instructions
795
            if ((parm[3].q & 1) == 0 && !ignoreMask) {
796
                // result is masked off. find fallback
797
                if (operands[2] == 0xFF) result = 0;               // fallback = 0
798
                else result = readVectorElement(operands[2], vectorOffset); // fallback register          
799
                if (doubleStep) {
800
                    if (operands[2] == 0xFF) result = 0;
801
                    else result = readVectorElement(operands[2], vectorOffset + elementSize);
802
                }
803
            }
804
            else {
805
                // normal operation. execute instruction
806
                result = (*functionPointer)(this);
807
            }
808
            // store in destination register
809
            if ((running & 1) && !(returnType & 0x20)) {
810
                vectorLength[operands[0]] = vectorLengthR;
811
                // get mask for operand size (operandType may have been changed by function)
812
                //uint64_t opmask = dataSizeMask[operandType];
813
                // write result to vector
814
                writeVectorElement(operands[0], result, vectorOffset);
815
                if (dataSizeTable[operandType] >= 16) {  // 128 bits
816
                    writeVectorElement(operands[0], parm[5].q, vectorOffset + (elementSize>>1)); // high part of double size result
817
                }
818
                if (doubleStep) {  // double step
819
                    writeVectorElement(operands[0], parm[5].q, vectorOffset + elementSize); // high part of double size result
820
                }
821
            }
822
            vect ^= 3;                                     // toggle between 1 for even elements, 2 for odd
823
            if (doubleStep) vectorOffset += elementSize;   // skip next element if instruction takes two elements at a time            
824
        }
825
        listResult(result);                                // debug output
826
    }
827
    else {
828
        // general purpose registers
829
        // get mask
830
        if ((operands[1] & 7) != 7) {
831
            parm[3].q = readRegister(operands[1]);
832
        }
833
        else {
834
            parm[3].q = numContr;
835
        }
836
        // skip instruction if mask = 0, except for certain instructions
837
        if ((parm[3].q & 1) == 0 && !ignoreMask) {
838
            // result is masked off. find fallback
839
            if (operands[2] == 0xFF) result = 0;
840
            else result = readRegister(operands[2]);
841
        }
842
        else {
843
            // normal operation. 
844
            // execute instruction
845
            result = (*functionPointer)(this);
846
        }
847
        // get mask for operand size (operandType may have been changed by function)
848
        // store in destination register, zero extended from operand size
849
        if (running & 1) registers[operands[0]] = result & dataSizeMask[operandType];
850
        listResult(result);                                // debug output
851
    }
852
    performanceCounters();  // update performance counters
853
}
854
 
855
// update performance counters
856
void CThread::performanceCounters() {
857
    perfCounters[perf_cpu_clock_cycles]++;       // clock cycles
858
    perfCounters[perf_instructions]++;       // instructions
859
    if ((fInstr->format2 & 0xF00) == 0x200)  perfCounters[perf_2size_instructions]++;  // double size instructions
860
    if ((fInstr->format2 & 0xF00) == 0x300)  perfCounters[perf_3size_instructions]++;  // triple size instructions
861
    if (vect) {
862
        perfCounters[perf_vector_instructions]++;  // vector instructions
863
    }
864
    else {
865
        perfCounters[perf_gp_instructions]++;  // g.p. instructions
866
        if ((parm[3].q & 1) == 0 && !ignoreMask) perfCounters[perf_gp_instructions_mask0]++;  // g.p. instructions masked off
867
    }
868
    if (fInstr->category == 4) {  // jump instructions
869
        perfCounters[perf_control_transfer_instructions]++;  // all jumps, calls, returns
870
        if (fInstr->tmplate == 0xD) { // direct jump/call
871
            perfCounters[perf_direct_jumps]++;  // g.p. instructions
872
        }
873
        else if (fInstr->exeTable == 2) {
874
            if (op == 62 && fInstr->format2 >> 4 == 0x16) {
875
                perfCounters[perf_direct_jumps]++; // simple return
876
            }
877
            else if (op >= 56) perfCounters[perf_indirect_jumps]++; // indirect jumps and calls
878
            else perfCounters[perf_cond_jumps]++; // conditional jumps 
879
        }
880
    }
881
}
882
 
883
// read vector element
884
uint64_t CThread::readVectorElement(uint32_t v, uint32_t vectorOffset) {
885
    uint32_t size;   // element size
886
    uint64_t returnval = 0;
887
    if (operandType == 8) size = 2;
888
    else size = dataSizeTableMax8[operandType];
889
    v &= 0x1F;  // protect against array overflow
890
    //if (vectorOffset < vectorLength[v]) {
891
    if (vectorOffset + size <= vectorLength[v]) {
892
        switch (size) {  // zero-extend from element size
893
        case 1:
894
            returnval = *(uint8_t*)(vectors.buf() + MaxVectorLength*v + vectorOffset);
895
            break;
896
        case 2:
897
            returnval = *(uint16_t*)(vectors.buf() + MaxVectorLength*v + vectorOffset);
898
            break;
899
        case 4:
900
            returnval = *(uint32_t*)(vectors.buf() + MaxVectorLength*v + vectorOffset);
901
            break;
902
        case 8:
903
            returnval = *(uint64_t*)(vectors.buf() + MaxVectorLength*v + vectorOffset);
904
            break;
905
        }
906
        uint32_t sizemax = vectorLength[v] - vectorOffset;
907
        if (size > sizemax) {  // reading beyond end of vector. cut off element to max size
908
            returnval &= (uint64_t(1) << sizemax*8) - 1;
909
        }
910
    }
911
    return returnval;
912
}
913
 
914
// write vector element
915
void CThread::writeVectorElement(uint32_t v, uint64_t value, uint32_t vectorOffset) {
916
    uint32_t size = dataSizeTableMax8[operandType];
917
    v &= 0x1F;  // protect against array overflow
918
    if (vectorOffset + size <= vectorLength[v]) {
919
        switch (size) {  // zero-extend from element size
920
        case 1:
921
            *(uint8_t*)(vectors.buf() + MaxVectorLength*v + vectorOffset) = (uint8_t)value;
922
            break;
923
        case 2:
924
            *(uint16_t*)(vectors.buf() + MaxVectorLength*v + vectorOffset) = (uint16_t)value;
925
            break;
926
        case 4:
927
            *(uint32_t*)(vectors.buf() + MaxVectorLength*v + vectorOffset) = (uint32_t)value;
928
            break;
929
        case 8:
930
            *(uint64_t*)(vectors.buf() + MaxVectorLength*v + vectorOffset) = value;
931
            break;
932
        }
933
    }
934
}
935
 
936
// get address of a memory operand
937
uint64_t CThread::getMemoryAddress() {
938
    // find base register
939
    if ((fInstr->mem & 3) == 0) err.submit(ERR_INTERNAL);
940
    //uint8_t basereg = (fInstr->mem & 1) ? pInstr->a.rt : pInstr->a.rs;
941
    uint8_t basereg = pInstr->a.rs;
942
    readonly = false;
943
    memory_error = false;
944
    // base register value
945
    uint64_t baseval = registers[basereg];
946
    if (fInstr->addrSize > 1 && !(fInstr->mem & 0x20)) {
947
        // special registers
948
        switch (basereg) {
949
        case 28:   // threadp
950
            baseval = threadp;  break;
951
        case 29:   // datap
952
            baseval = datap;  break;
953
        case 30:   // ip
954
            baseval = ip;  readonly = true;
955
            break;
956
        }
957
    }
958
    // pointer to memory field
959
    const uint8_t * pa = &pInstr->b[0] + fInstr->addrPos;
960
 
961
    // find index register
962
    uint64_t indexval = 0;
963
    if ((fInstr->mem & 4) && (pInstr->a.rt != 0x1F)) {
964
        // rt is index register
965
        indexval = registers[pInstr->a.rt & 0x1F];
966
        // check limit
967
        if (fInstr->mem & 0x20) {
968
            const uint8_t * pi = &pInstr->b[0] + fInstr->addrPos; // pointer to immediate field
969
            uint64_t limit = *(uint64_t*)pi;
970
            limit &= (uint64_t(1) << (fInstr->addrSize * 8)) - 1;
971
            if (indexval > limit) {
972
                interrupt(INT_ARRAY_BOUNDS);
973
                memory_error = true;
974
                //return 0;
975
            }
976
        }
977
    }
978
    // get offset, sign-extended
979
    int64_t offset = 0;
980
    if (fInstr->mem & 0x10) {
981
        switch (fInstr->addrSize) {
982
        case 0:
983
            break;
984
        case 1:
985
            offset = *(int8_t*)pa;
986
            break;
987
        case 2:
988
            offset = *(int16_t*)pa;
989
            break;
990
        case 4:
991
            offset = *(int32_t*)pa;
992
            break;
993
        case 8:
994
            offset = *(int64_t*)pa;
995
            break;
996
        default:
997
            err.submit(ERR_INTERNAL);
998
        }
999
    }
1000
    // scale
1001
    switch (fInstr->scale) {
1002
    case 1: // offset is scaled
1003
        offset <<= dataSizeTableLog[operandType];
1004
        break;
1005
    case 2: // index is scaled by OS
1006
        indexval <<= dataSizeTableLog[operandType];
1007
        break;
1008
    case 4: // 4 = scale factor is -1
1009
        indexval = uint64_t(-(int64_t)indexval);
1010
        break;
1011
    }
1012
    // get length
1013
    if ((fInstr->vect & 6) && pInstr->a.rt < 0x1F) { // vector length or broadcast length is in RT        
1014
        if (registers[pInstr->a.rt] > MaxVectorLength) vectorLengthM = MaxVectorLength;
1015
        else vectorLengthM = (uint32_t)registers[pInstr->a.rt];
1016
    }
1017
    else { // scalar
1018
        vectorLengthM = dataSizeTable[operandType & 7];
1019
    }
1020
    // offset and index may be negative, but the result must be positive
1021
    return baseval + indexval + (uint64_t)offset;
1022
}
1023
 
1024
// read a memory operand
1025
uint64_t CThread::readMemoryOperand(uint64_t address) {
1026
    // get most likely memory map index
1027
    uint32_t * indexp = readonly ? &mapIndex2 : &mapIndex3;
1028
    uint32_t index = * indexp;
1029
 
1030
    // find memory map entry
1031
    while (address < memoryMap[index].startAddress) {
1032
        if (index > 0) index--;
1033
        else {
1034
            interrupt(INT_ACCESS_READ);  return 0;
1035
        }
1036
    }
1037
    while (address >= memoryMap[index + 1].startAddress) {
1038
        if (index + 2 < memoryMap.numEntries()) index++;
1039
        else {
1040
            interrupt(INT_ACCESS_READ);  return 0;
1041
        }
1042
    }
1043
    // check read permission
1044
    if (!(memoryMap[index].access_addend & SHF_READ)) {
1045
        interrupt(INT_ACCESS_READ);  return 0;
1046
    }
1047
 
1048
    // check if map boundary crossed
1049
    if (address + dataSizeTable[operandType] > memoryMap[index+1].startAddress
1050
    && !(memoryMap[index+1].access_addend & SHF_READ)) {
1051
        interrupt(INT_ACCESS_READ);
1052
    }
1053
 
1054
    // check alignment
1055
 
1056
 
1057
    // return zero if any kind of error
1058
    if (memory_error) return 0;
1059
 
1060
    // save index for next time
1061
    *indexp = index;
1062
 
1063
    // get value, zero extended    
1064
    const int8_t * p = memory + address;  // pointer to data
1065
    switch (dataSizeTableMax8[operandType]) {
1066
    case 0:
1067
        break;
1068
    case 1:
1069
        return *(uint8_t*)p;
1070
    case 2:
1071
        if (address & 1) interrupt(INT_MISALIGNED_MEM);
1072
        return *(uint16_t*)p;
1073
    case 4:
1074
        if (address & 3) interrupt(INT_MISALIGNED_MEM);
1075
        return *(uint32_t*)p;
1076
    case 8:
1077
        if (address & 7) interrupt(INT_MISALIGNED_MEM);
1078
        return *(uint64_t*)p;
1079
    }
1080
    return 0;
1081
}
1082
 
1083
// write a memory operand
1084
void CThread::writeMemoryOperand(uint64_t val, uint64_t address) {
1085
    // most likely memory map index is saved in mapIndex3
1086
    // find memory map entry
1087
    while (address < memoryMap[mapIndex3].startAddress) {
1088
        if (mapIndex3 > 0) mapIndex3--;
1089
        else {
1090
            interrupt(INT_ACCESS_WRITE);  return;
1091
        }
1092
    }
1093
    while (address >= memoryMap[mapIndex3+1].startAddress) {
1094
        if (mapIndex3 + 2 < memoryMap.numEntries()) mapIndex3++;
1095
        else {
1096
            interrupt(INT_ACCESS_WRITE);  return;
1097
        }
1098
    }
1099
    // check write permission
1100
    if (!(memoryMap[mapIndex3].access_addend & SHF_WRITE)) {
1101
        interrupt(INT_ACCESS_WRITE);  return;
1102
    }
1103
 
1104
    // check if map boundary crossed
1105
    if (address + dataSizeTable[operandType] > memoryMap[mapIndex3+1].startAddress
1106
    && !(memoryMap[mapIndex3+1].access_addend & SHF_WRITE)) {
1107
        interrupt(INT_ACCESS_WRITE);
1108
    }
1109
 
1110
    // write value
1111
    // get value, zero extended    
1112
    int8_t * p = memory + address;  // pointer to data
1113
    switch (dataSizeTableMax8[operandType]) {
1114
    case 0:
1115
        break;
1116
    case 1:
1117
        *(uint8_t*)p = (uint8_t)val;
1118
        break;
1119
    case 2:
1120
        if (address & 1) interrupt(INT_MISALIGNED_MEM);
1121
        *(uint16_t*)p = (uint16_t)val;
1122
        break;
1123
    case 4:
1124
        if (address & 3) interrupt(INT_MISALIGNED_MEM);
1125
        *(uint32_t*)p = (uint32_t)val;
1126
        break;
1127
    case 8:
1128
        if (address & 7) interrupt(INT_MISALIGNED_MEM);
1129
        *(uint64_t*)p = val;
1130
        break;
1131
    }
1132
}
1133
 
1134
// start writing debug list
1135
void CThread::listStart() {
1136
    if (!listFileName) return;                   // nothing if no list file
1137
    listOut.put("Debug listing of ");
1138
    listOut.put(cmd.getFilename(cmd.inputFile));
1139
    listOut.newLine();
1140
    // Date and time. (Will fail after year 2038 on computers that use 32-bit time_t)
1141
    time_t time1 = time(0);
1142
    char * timestring = ctime(&time1);
1143
    if (timestring) {
1144
        for (char *c = timestring; *c; c++) {            // Remove terminating '\n' in timestring
1145
            if (*c < ' ') *c = 0;
1146
        }
1147
        listOut.put(timestring);
1148
        listOut.newLine(); listOut.newLine();
1149
    }
1150
}
1151
 
1152
static uint32_t listIndex = 0;                   // index into lineList
1153
// write current instruction to debug list
1154
void CThread::listInstruction(uint64_t address) {
1155
    if (listFileName == 0 || cmd.maxLines == 0) return;    // stop listing
1156
    SLineRef rec = {address, 1, 0};
1157
    const char * text = 0;
1158
    if (listIndex + 1 < emulator->lineList.numEntries() && emulator->lineList[listIndex+1] == rec) {
1159
        // just the next record. no need to search
1160
        listIndex = listIndex+1;
1161
    }
1162
    else {  // we may have jumped. Find address in list
1163
        listIndex = (uint32_t)emulator->lineList.findFirst(rec);
1164
    }
1165
    if (listIndex < emulator->lineList.numEntries()) {
1166
        text = emulator->disassemOut.getString(emulator->lineList[listIndex].textPos); // get line from disassembly
1167
        listOut.put(text);
1168
    }
1169
    else {  // corresponding disassembly not found
1170
        listOut.putHex((uint32_t)address, 2);
1171
        listOut.tabulate(emulator->disassembler.asmTab0);
1172
        listOut.put("???");
1173
    }
1174
    listOut.newLine();
1175
}
1176
 
1177
// write result of current instruction to debug list
1178
void CThread::listResult(uint64_t result) {
1179
    if (++listLines >= cmd.maxLines) cmd.maxLines = 0;  // stop listing 
1180
    if (listFileName == 0 || returnType == 0 || cmd.maxLines == 0) return;      // nothing if no list file or no return value
1181
    listOut.tabulate(emulator->disassembler.asmTab0);
1182
    if (!(returnType & 0x100)) { // general purpose register
1183
        if (returnType & 0x20) { // memory destination
1184
            result = readMemoryOperand(getMemoryAddress());
1185
        }
1186
        if (returnType & 0x30) { // register or memory
1187
            switch (returnType & 0xF) {
1188
            case 0:  // int8
1189
                listOut.putHex((uint8_t)result); break;
1190
            case 1:  // int16
1191
                listOut.putHex((uint16_t)result); break;
1192
            case 2: case 5:  // int32
1193
                listOut.putHex((uint32_t)result); break;
1194
            case 3: case 6:  // int64
1195
                listOut.putHex(result); break;
1196
            case 4:  // int128
1197
                listOut.putHex(parm[5].q, 2); listOut.putHex(result, 2); break;
1198
            default:
1199
                listOut.put("?");
1200
            }
1201
        }
1202
    }
1203
    else if (returnType & 0x30) { // vector
1204
        uint8_t destinationReg = operands[0] & 0x1F;
1205
        //uint32_t vectorLengthR = vectorLength[destinationReg];
1206
        if (!(returnType & 0x20)) vectorLengthR = vectorLength[destinationReg];
1207
        uint8_t type = returnType & 0xF;
1208
        operandType = type;
1209
        uint32_t elementSize = dataSizeTable[type & 7];
1210
        if (type == 8) elementSize = 2;          // half precision
1211
        if (elementSize > 8) elementSize = 8;    // int128 and float128 listed as two int64
1212
        union {                                  // union to convert types
1213
            uint64_t q;
1214
            double d;
1215
            float f;
1216
        } u;
1217
        if (vectorLengthR == 0) listOut.put("Empty");
1218
        //if (returnType & 0x40) vectorLengthR += elementSize;  // one extra element (save_cp instruction)
1219
        for (uint32_t vectorOffset = 0; vectorOffset < vectorLengthR; vectorOffset += elementSize) {
1220
            if (returnType & 0x20) { // memory destination
1221
                result = readMemoryOperand(getMemoryAddress() + vectorOffset);
1222
            }
1223
            else {
1224
                result = readVectorElement(destinationReg, vectorOffset);
1225
            }
1226
            switch (returnType & 0xF) {
1227
            case 0:  // int8
1228
                listOut.putHex((uint8_t)result); break;
1229
            case 1:  // int16
1230
                listOut.putHex((uint16_t)result); break;
1231
            case 2:  // int32
1232
                listOut.putHex((uint32_t)result); break;
1233
            case 3: case 4: case 7: // int64
1234
                listOut.putHex(result); break;
1235
            case 5:  // float
1236
                u.q = result;
1237
                listOut.putFloat(u.f); break;
1238
            case 6:  // double
1239
                u.q = result;
1240
                listOut.putFloat(u.d); break;
1241
            case 8:  // float16
1242
                listOut.putFloat16((uint16_t)result); break;
1243
            default:
1244
                listOut.put("???");
1245
            }
1246
            listOut.put(' ');
1247
        }
1248
    }
1249
    if (returnType & 0x3000) {
1250
        // conditional jump instruction
1251
        if (returnType & 0x30) listOut.put(",  ");    // space after value
1252
        listOut.put((returnType & 0x2000) ? "jump" : "no jump"); // tell if jump or not
1253
    }
1254
    listOut.newLine();
1255
}
1256
 
1257
// make a NAN with exception code and address in payload
1258
uint64_t CThread::makeNan(uint32_t code, uint32_t operandTyp) {
1259
    uint64_t retval = 0;
1260
    uint8_t instrLength = lengthList[pInstr->a.il];  // instruction length
1261
    uint64_t iaddress = ((ip - ip0) >> 2) - instrLength;     // instruction address
1262
    iaddress = ~iaddress;                            // invert bits
1263
    switch (operandTyp) {
1264
    case 1:  // half precision
1265
        retval = (uint8_t)code | 0x7E00 | (iaddress & 1) << 8;
1266
        break;
1267
    case 5:  // single precision
1268
        retval = (uint8_t)code | 0x7FC00000 | uint32_t(iaddress & ((1 << 14) - 1)) << 8;
1269
        break;
1270
    case 6:  // double precision
1271
        retval = (uint8_t)code | 0x7FF8000000000000 | (iaddress & (((uint64_t)1 << 43) - 1)) << 8;
1272
        break;
1273
    }
1274
    return retval;
1275
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.