URL
https://opencores.org/ocsvn/forwardcom/forwardcom/trunk
Subversion Repositories forwardcom
Compare Revisions
- This comparison shows the changes necessary to convert path
/forwardcom/bintools
- from Rev 56 to Rev 57
- ↔ Reverse comparison
Rev 56 → Rev 57
/emulator6.cpp
0,0 → 1,432
/**************************** 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; |
} |
} |
} |