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

Subversion Repositories forwardcom

[/] [forwardcom/] [bintools/] [emulator6.cpp] - Rev 73

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

/****************************  emulator6.cpp  ********************************
* Author:        Agner Fog
* date created:  2018-02-18
* Last modified: 2021-02-19
* Version:       1.11
* Project:       Binary tools for ForwardCom instruction set
* Description:
* Emulator: System functions
*
* Copyright 2018-2021 GNU General Public License http://www.gnu.org/licenses
*****************************************************************************/
 
#include "stdafx.h"
 
// Data encoding names
 
// Interrupt names
SIntTxt interruptNames[] = {
    // Error interrupts
    {INT_UNKNOWN_INST,       "Unknown instruction"},
    {INT_WRONG_PARAMETERS,       "Illegal instruction code"},
    {INT_ACCESS_READ,        "Memory read access violation"},
    {INT_ACCESS_WRITE,       "Memory write access violation"},
    {INT_ACCESS_EXE,         "Memory execute access violation"},
    {INT_CALL_STACK,         "Call stack overflow or underflow"},
    {INT_ARRAY_BOUNDS,       "Array bounds violation"},
    {INT_MISALIGNED_JUMP,    "Jump to misaligned address"},
    {INT_MISALIGNED_MEM,     "Misaligned memory address"},
 
    // Software traps. Not necessarily supported
    {INT_OVERFL_UNSIGN,      "Unsigned integer overflow"},
    {INT_OVERFL_SIGN,        "Signed integer overflow"},
    {INT_OVERFL_FLOAT,       "Floating point overflow"},
    {INT_FLOAT_INVALID,      "Floating point invalid operation"},
    {INT_FLOAT_UNDERFL,      "Floating point underflow"},
    {INT_FLOAT_NAN_LOSS,     "Floating point NAN in compare or conversion to integer"},
    {0xFFFF,                 "Filler interrupt"},
};
 
// System function names
SIntTxt systemFunctionNames[] = {
    {SYSF_EXIT,              "exit"},       // terminate program
    {SYSF_ABORT,             "abort"},      // abort program
    {SYSF_TIME,              "time"},       // time in seconds since jan 1, 1970    
 
// input/output functions
    {SYSF_PUTS,              "puts"},       // write string to stdout
    {SYSF_PUTCHAR,           "putchar"},    // write character to stdout
    {SYSF_PRINTF,            "printf"},     // write formatted output to stdout
    {SYSF_FPRINTF,           "fprintf"},    // write formatted output to file
    {SYSF_SNPRINTF,          "snprintf"},    // write formatted output to string buffer 
    {SYSF_FOPEN,             "fopen"},      //  open file
    {SYSF_FCLOSE,            "fclose"},     // SYSF_FCLOSE
    {SYSF_FREAD,             "fread"},      // read from file
    {SYSF_FWRITE,            "fwrite"},     // write to file 
    {SYSF_FFLUSH,            "fflush"},     // flush file 
    {SYSF_FEOF,              "feof"},       // check if end of file 
    {SYSF_FTELL,             "ftell"},      // get file position 
    {SYSF_FSEEK,             "fseek"},      // set file position 
    {SYSF_FERROR,            "ferror"},     // get file error    
    {SYSF_GETCHAR,           "getchar"},    // read character from stdin 
    {SYSF_FGETC,             "fgetc"},      // read character from file 
    {SYSF_FGETS,             "fgets"},      // read string from file 
    {SYSF_SCANF,             "scanf"},      // read formatted input from stdio 
    {SYSF_FSCANF,            "fscanf"},     // read formatted input from file 
    {SYSF_SSCANF,            "sscanf"},     // read formatted input from string buffer 
    {SYSF_REMOVE,            "remove"},     // delete file 
};
 
// number of entries in list
const int numSystemFunctionNames = sizeof(systemFunctionNames) / sizeof(SIntTxt);
 
 
// interrupt or trap
// To trace a runtime error, set a breakpoint here
void CThread::interrupt(uint32_t n) {
    // check if error is disabled
    uint32_t capabbit = 0;                  // bit in capabilities register
    switch (n) {
    case INT_BREAKPOINT:                    // debug breakpoint
        listOut.tabulate(emulator->disassembler.asmTab0);
        listOut.put("breakpoint");
        listOut.newLine();
        return;
    case INT_UNKNOWN_INST:                  // unknown instruction
        capabbit = 1;
        perfCounters[perf_unknown_instruction]++;
        break;
    case INT_WRONG_PARAMETERS:              // unsupported parameters for instruction
    case INT_CALL_STACK:                    // call stack overflow or underflow
        capabbit = 2;
        perfCounters[perf_wrong_operands]++;
        break;
    case INT_ACCESS_READ:                   // memory access violation, read
        capabbit = 8;
        perfCounters[perf_read_violation]++;
        break;
    case INT_ACCESS_WRITE:                  // memory access violation, write
        capabbit = 0x10;
        perfCounters[perf_write_violation]++;
        break;
    case INT_ACCESS_EXE:                    // memory access violation, execute
        capabbit = 8;
        perfCounters[perf_read_violation]++;
        break;
    case INT_ARRAY_BOUNDS:                  // array bounds overflow, unsigned
        capabbit = 4;
        perfCounters[perf_array_overflow]++;
        break;
    case INT_MISALIGNED_MEM:                // misaligned memory access.
        capabbit = 0x20;
        perfCounters[perf_misaligned]++;
        break; 
    case INT_MISALIGNED_JUMP:               // jump to an address not divisible by 4
    default:
        capabbit = 0;
    }
    if (perfCounters[perf_type_of_first_error] == 0) {    
        // save first error
        perfCounters[perf_type_of_first_error] = bitScanReverse(capabbit);
        uint8_t instrLength = pInstr->i[0] >> 30;  if (instrLength == 0) instrLength = 1;
        perfCounters[perf_address_of_first_error] = ((ip - ip0) >> 2) - instrLength;
    }
 
    if (!(capabilyReg[disable_errors_capability_register] & capabbit)) {
        terminate = true;                   // stop execution unless error is disabled
    }
    if (listFileName && cmd.maxLines != 0) {   // write interrupt to debug output
        listOut.tabulate(emulator->disassembler.asmTab0);
        const char * iname = Lookup(interruptNames, n);
        listOut.put(iname);
        if (terminate) listOut.put(". Terminating");
        listOut.newLine();
    }
}
 
/*
// give error message if compiled for 32 bit
void checkVa_listSize() {
    // C variable argument list va_list is not compatible with ForwardCom in 32 bit mode
    if (sizeof(void*) < 8) { // 32 bit host system. va_list has 32-bit entries except for %f 
        puts("\nError: forw must be compiled in 64 bit mode. printf function may fail\n");
    }
} */
 
// check if system function has access to a particular address
uint64_t CThread::checkSysMemAccess(uint64_t address, uint64_t size, uint8_t rd, uint8_t rs, uint8_t mode) {
    // rd = register pointing to beginning of shared memory area, rs = size of shared memory area
    // mode = SHF_READ or SHF_WRITE
    // return value is possibly reduced size, or zero if no access
    if ((rd | rs) == 0) return 0;                // no access if both are r0
    uint64_t base = registers[rd];               // beginning of shared area
    uint64_t bsize = registers[rs];              // size of shared area
    if (address + size < address) size = ~address; // avoid overflow
    if (base + bsize < base) bsize = ~base;      // avoid overflow
    if ((rd & rs & 0x1F) != 0x1F) {              // share all if both are r31
        // check if within shared area
        if (address < base) return 0;
        if (address + size > base + bsize) size = base + bsize - address;
    }
    // check application's memory map
    uint32_t index = mapIndex3;
    // find index
    while (address < memoryMap[index].startAddress) {
        if (index > 0) index--;
        else return 0;
    }
    while (address >= memoryMap[index+1].startAddress) {
        if (index+2 < memoryMap.numEntries()) index++;
        else return 0;
    }
    // check read/write permission
    if ((memoryMap[index].access_addend & mode) != mode) return 0;
 
    // check if multiple map entries covered
    uint32_t index2 = index;
    while (address + size >= memoryMap[index2+1].startAddress
        && index2+2 < memoryMap.numEntries() 
        && (memoryMap[index2+1].access_addend & mode) == mode) {
            index2++;
        }
    uint64_t size2 = memoryMap[index2+1].startAddress - address;  // maximum possible size
    if (size < size2) size = size2;
    return size;
}
 
// emulate fprintf with ForwardCom argument list
int CThread::fprintfEmulated(FILE * stream, const char * format, uint64_t * argumentList) {
    // a ForwardCom argument list is compatible with a va_list in 64-bit windows but not in Linux
    static CMemoryBuffer fstringbuf;             // buffer containing format string
    fstringbuf.setSize(0);                       // discard any previously stored string
    fstringbuf.pushString(format);               // copy format string
    // split the format string into substrings with a single format specifier in each
    uint32_t arg = 0;                            // argument index
    int returnValue;                             // return value;
    int returnSum = 0;                           // sum of return values;
    char * startp;                               // start of current substring in format string
    char * percentp1;                            // percent sign in current substring
    char * percentp2;                            // next percent sign starting next substring
    startp = fstringbuf.getString(0);            // start of string buffer
    percentp1 = startp;                          // search for first % sign
    while (true) {
        percentp1 = strchr(percentp1, '%');
        if (percentp1 && percentp1[1] == '%') percentp1 += 2; // skip "%%" which is not a format code
        else break;
    }
    // loop for substrings of format string containing only one format specifier each
    do {
        char c = 0;                                      // format character
        int asterisks = 0;
        bool isString = false;
        if (percentp1) {
            percentp2 = percentp1 + 1;               // search for next % sign
            while (true) {
                percentp2 = strchr(percentp2, '%');
                if (percentp2 && percentp2[1] == '%') percentp2 += 2; // skip "%%" which is not a format code
                else break;
            }
            if (percentp2) *percentp2 = 0;           // put temporary end of string at next % sign
            // check if argument is a string, and count asterisks
            int i = 1;
            while (true) {
                c = percentp1[i++];                  // read character in format specifier
                if (c == 0) break;                   // end of string
                if (c == '*') asterisks++;           // count asterisks
                c |= 0x20;                           // lower case
                if (c == 's') isString = true;       // %s means string
                if (c >= 'a' && c <= 'z') break;     // a letter terminates the format specifier
            }
        }
        else {
            percentp2 = 0;
        }
        uint64_t argument = argumentList[arg];   // The argument list can contain any type of argument with size up to 64 bits
        union {
            uint64_t a;
            double d;
        } uu;
        if (isString) argument += (uint64_t)memory; // translate string address
        // Print current argument with format substring.
        if (asterisks) { // asterisks indicate extra arguments
            if (c == 'a' || c == 'e' || c == 'f' || c == 'g') {
                // floating point argument
                if (asterisks == 1) {
                    uu.a = argumentList[arg+1];
                    returnValue = fprintf(stream, startp, argument, uu.d, argumentList[arg+2]);
                }
                else { // asterisks = 2
                    uu.a = argumentList[arg+2];
                    returnValue = fprintf(stream, startp, argument, argumentList[arg+1], uu.d);
                }
            }
            else { // integer argument
                returnValue = fprintf(stream, startp, argument, argumentList[arg+1], argumentList[arg+2]);
            }
            arg += asterisks + 1;
        }
        else {
            if (c == 'a' || c == 'e' || c == 'f' || c == 'g') {
                // floating point argument
                uu.a = argument;
                returnValue = fprintf(stream, startp, uu.d);
            }
            else {            
                returnValue = fprintf(stream, startp, argument);
            }
            arg++;
        }
        if (returnValue < 0) return returnValue; // return error
        else returnSum += returnValue;           // sum of return values
        if (percentp2) *percentp2 = '%';         // re-insert next % sign
        startp = percentp1 = percentp2;
    }
    while (startp);                              // loop to next substring
    return returnSum;                            // return total number of characters written
}
 
 
// entry for system calls
void CThread::systemCall(uint32_t mod, uint32_t funcid, uint8_t rd, uint8_t rs) {
    if (listFileName) {    
        // debug listing
        listOut.tabulate(emulator->disassembler.asmTab0);
        listOut.put("system call: ");
        if (mod == SYSM_SYSTEM) { // search for function name
            for (int i = 0; i < numSystemFunctionNames; i++) {
                if (systemFunctionNames[i].a == funcid) { // name is in list
                    listOut.put(systemFunctionNames[i].b);
                    goto NAME_WRITTEN;
                }
            }
        }
        // name not found. write id
        listOut.putHex(mod);  listOut.put(":");  listOut.putHex(funcid);
        NAME_WRITTEN:
        listOut.newLine();
    }
    uint64_t temp;    // temporary
    uint64_t dsize;   // data size
    const char * str = 0;    // string
    if (mod == SYSM_SYSTEM) {// system function
        // dispatch by function id
        switch (funcid) {
        case SYSF_EXIT:      // terminate program
            cmd.mainReturnValue = (int)registers[0];
            terminate = true;  break;
        case SYSF_ABORT:     // abort program
            cmd.mainReturnValue = (int)registers[0];
            terminate = true;  break;
        case SYSF_TIME:      // time
            temp = time(0);
            if (registers[0] && checkSysMemAccess(registers[0], 8, rd, rs, SHF_WRITE)) *(uint64_t*)(memory + registers[0]) = temp;
            registers[0] = temp;  break;
        case SYSF_PUTS:      // write string to stdout
            str = (const char*)memory + registers[0];
            if (strlen(str) > checkSysMemAccess(registers[0], -1, rd, rs, SHF_READ)) {
                interrupt(INT_ACCESS_READ);
            }
            else puts(str);
            break;
        case SYSF_PUTCHAR:   // write character to stdout
            putchar((char)registers[0]); 
            break;
        case SYSF_PRINTF:    // write formatted output to stdout
            registers[0] = fprintfEmulated(stdout, (const char*)memory + registers[0], (uint64_t*)(memory + registers[1]));
            break; 
        case SYSF_FPRINTF:   // write formatted output to file
            registers[0] = fprintfEmulated((FILE *)(registers[0]), (const char*)memory + registers[1], (uint64_t*)(memory + registers[2]));
            break;
            /*
        case SYSF_SNPRINTF:   // write formatted output to string buffer 
            // this works only in 64 bit windows
            dsize = registers[1];  // size of data to read
            if (checkSysMemAccess(registers[0], dsize, rd, rs, SHF_WRITE) < dsize) {
                interrupt(INT_ACCESS_WRITE); // write access violation
                ret = 0;
            }
            else ret = snprintf((char*)memory + registers[0], registers[1], (const char*)memory + registers[2], (const char*)memory + registers[3]);
            registers[0] = ret;
            break;*/
        case SYSF_FOPEN:     //  open file
            registers[0] = (uint64_t)fopen((const char*)memory + registers[0], (const char*)memory + registers[1]);
            break;
        case SYSF_FCLOSE:    // SYSF_FCLOSE
            registers[0] = (uint64_t)fclose((FILE*)registers[0]);
            break;
        case SYSF_FREAD:     // read from file
            dsize = registers[1] * registers[2];  // size of data to read
            if (checkSysMemAccess(registers[0], dsize, rd, rs, SHF_WRITE) < dsize) {
                interrupt(INT_ACCESS_WRITE); // write access violation
                registers[0] = 0;
            }
            else registers[0] = (uint64_t)fread(memory + registers[0], (size_t)registers[1], (size_t)registers[2], (FILE *)(size_t)registers[3]);
            break;
        case SYSF_FWRITE:    // write to file 
            dsize = registers[1] * registers[2];  // size of data to write
            if (checkSysMemAccess(registers[0], dsize, rd, rs, SHF_READ) < dsize) {
                interrupt(INT_ACCESS_READ); // write access violation
                registers[0] = 0;
            }
            else registers[0] = (uint64_t)fwrite(memory + registers[0], (size_t)registers[1], (size_t)registers[2], (FILE *)(size_t)registers[3]);
            break;
        case SYSF_FFLUSH:    // flush file 
            registers[0] = (uint64_t)fflush((FILE *)registers[0]);
            break;
        case SYSF_FEOF:      // check if end of file 
            registers[0] = (uint64_t)feof((FILE *)registers[0]);
            break;
        case SYSF_FTELL:     // get file position 
            registers[0] = (uint64_t)ftell((FILE *)registers[0]);
            break;
        case SYSF_FSEEK:     // set file position 
            registers[0] = (uint64_t)fseek((FILE *)registers[0], (long int)registers[1], (int)registers[2]);
            break;
        case SYSF_FERROR:    // get file error
            registers[0] = (uint64_t)ferror((FILE *)registers[0]);
            break;
        case SYSF_GETCHAR:   // read character from stdin 
            registers[0] = (uint64_t)getchar();
            break;
        case SYSF_FGETC:     // read character from file 
            registers[0] = (uint64_t)fgetc((FILE *)registers[0]);
            break;
        case SYSF_FGETS:     // read string from file 
            dsize = registers[1];  // size of data to read
            if (checkSysMemAccess(registers[0], dsize, rd, rs, SHF_WRITE) < dsize) {
                interrupt(INT_ACCESS_WRITE); // write access violation
                registers[0] = 0;
            }
            else {
                registers[0] = (uint64_t)fgets((char *)(memory+registers[0]), (int)registers[1], (FILE *)registers[2]);
            }
            break;
        case SYSF_GETS_S:     // read string from stdin 
            dsize = registers[1];  // size of data to read
            if (checkSysMemAccess(registers[0], dsize, rd, rs, SHF_WRITE) < dsize) {
                interrupt(INT_ACCESS_WRITE); // write access violation
                registers[0] = 0;
            }
            else {
                char * r = fgets((char *)(memory+registers[0]), (int)registers[1], stdin);
                if (r == 0) registers[0] = 0;  // registers[0] unchanged if success
            }
            break;
            /*
        case SYSF_SCANF:     // read formatted input from stdio 
            ret = vscanf((char *)(memory+registers[0]), (va_list)(memory + registers[1]));
            if (checkSysMemAccess(registers[0], ret, rd, rs, SHF_WRITE) < ret) {
                interrupt(INT_ACCESS_WRITE); // write access violation
            }
            registers[0] = ret;
            break;
        case SYSF_FSCANF:    // read formatted input from file 
            ret = vfscanf((FILE *)registers[0], (char *)(memory+registers[1]), (va_list)(memory + registers[2]));
            if (checkSysMemAccess(registers[0], ret, rd, rs, SHF_WRITE) < ret) {
                interrupt(INT_ACCESS_WRITE); // write access violation
            }
            registers[0] = ret;
            break;
        case SYSF_SSCANF:    // read formatted input from string buffer 
            ret = vsscanf((char *)(memory+registers[0]), (char *)(memory+registers[1]), (va_list)(memory + registers[2]));
            if (checkSysMemAccess(registers[0], ret, rd, rs, SHF_WRITE) < ret) {
                interrupt(INT_ACCESS_WRITE); // write access violation
            }
            registers[0] = ret;
            break; */
        case SYSF_REMOVE:    // delete file 
            registers[0] = (uint64_t)remove((char *)(memory+registers[0]));
            break;
        }
    }
}
 

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

powered by: WebSVN 2.1.0

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