-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathemulator6.cpp
More file actions
444 lines (428 loc) · 21.2 KB
/
emulator6.cpp
File metadata and controls
444 lines (428 loc) · 21.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
/**************************** emulator6.cpp ********************************
* Author: Agner Fog
* date created: 2018-02-18
* Last modified: 2024-08-05
* Version: 1.13
* Project: Binary tools for ForwardCom instruction set
* Description:
* Emulator: System functions
*
* Copyright 2018-2024 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
char * trailing = 0; // any text following format specifier
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
trailing = percentp1+i; // point to next character
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;
if (isnan_d(uu.a)) { // NaN. write diagnostic exception code
uint32_t exceptionCode = uint32_t(uu.a >> 42) & 0x1FF;
returnValue = fprintf(stream, "NaN(%s)", exceptionCodeName(exceptionCode));
if (trailing != 0 && trailing < percentp2) {
// there is text following the format specifier
returnValue += fprintf(stream, trailing);
}
}
else {
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;
}
}
}