Warning: this is an htmlized version!
The original is here, and the conversion rules are here. |
/* Ngaro VM ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Copyright (c) 2008 - 2011, Charles Childers Copyright (c) 2009 - 2010, Luke Parrish Copyright (c) 2010, Marc Simpson Copyright (c) 2010, Jay Skeer Copyright (c) 2011, Kenneth Keating ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include <stdio.h> #include <stdlib.h> #include <time.h> #include <unistd.h> #include <string.h> #include <termios.h> #include <sys/ioctl.h> /* Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +---------+---------+---------+ | 16 bit | 32 bit | 64 bit | +------------+---------+---------+---------+ | IMAGE_SIZE | 32000 | 1000000 | 1000000 | +------------+---------+---------+---------+ | CELL | int16_t | int32_t | int64_t | +------------+---------+---------+---------+ If memory is tight, cut the MAX_FILE_NAME and MAX_REQUEST_LENGTH. You can also cut the ADDRESSES stack size down, but if you have heavy nesting or recursion this may cause problems. If you do modify it and experience odd problems, try raising it a bit higher. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #define CELL int32_t #define IMAGE_SIZE 1000000 #define ADDRESSES 1024 #define STACK_DEPTH 128 #define PORTS 32 #define MAX_FILE_NAME 1024 #define MAX_REQUEST_LENGTH 1024 #define MAX_OPEN_FILES 8 #define GLOBAL "/usr/local/share/retro/retroImage" #define LOCAL "retroImage" enum vm_opcode {VM_NOP, VM_LIT, VM_DUP, VM_DROP, VM_SWAP, VM_PUSH, VM_POP, VM_LOOP, VM_JUMP, VM_RETURN, VM_GT_JUMP, VM_LT_JUMP, VM_NE_JUMP,VM_EQ_JUMP, VM_FETCH, VM_STORE, VM_ADD, VM_SUB, VM_MUL, VM_DIVMOD, VM_AND, VM_OR, VM_XOR, VM_SHL, VM_SHR, VM_ZERO_EXIT, VM_INC, VM_DEC, VM_IN, VM_OUT, VM_WAIT }; #define NUM_OPS VM_WAIT + 1 typedef struct { CELL sp, rsp, ip; CELL data[STACK_DEPTH]; CELL address[ADDRESSES]; CELL ports[PORTS]; FILE *files[MAX_OPEN_FILES]; FILE *input[MAX_OPEN_FILES]; CELL isp; CELL inputSource; char *inputString; CELL strIndex; CELL image[IMAGE_SIZE]; CELL shrink, padding; int stats[NUM_OPS + 1]; int max_sp, max_rsp; char filename[MAX_FILE_NAME]; char request[MAX_REQUEST_LENGTH]; struct termios new_termios, old_termios; } VM; /* Macros ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #define IP vm->ip #define SP vm->sp #define RSP vm->rsp #define DROP vm->data[SP] = 0; if (--SP < 0) IP = IMAGE_SIZE; #define TOS vm->data[SP] #define NOS vm->data[SP-1] #define TORS vm->address[RSP] /* Helper Functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ void rxGetString(VM *vm, int starting) { CELL i = 0; while(vm->image[starting] && i < MAX_REQUEST_LENGTH) vm->request[i++] = (char)vm->image[starting++]; vm->request[i] = 0; } /* Console I/O Support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ void rxWriteConsole(VM *vm, CELL c) { (c > 0) ? putchar((char)c) : printf("\033[2J\033[1;1H"); /* Erase the previous character if c = backspace */ if (c == 8) { putchar(32); putchar(8); } } CELL rxReadConsole(VM *vm) { CELL c; if (vm->inputSource == 0) { if ((c = getc(vm->input[vm->isp])) == EOF && vm->input[vm->isp] != stdin) { fclose(vm->input[vm->isp--]); c = 0; } if (c == EOF && vm->input[vm->isp] == stdin) exit(0); } else { c = vm->inputString[vm->strIndex++]; if (c == '\0') { c = 32; vm->inputSource = 0; } } return c; } void rxIncludeFile(VM *vm, char *s) { FILE *file; if ((file = fopen(s, "r"))) vm->input[++vm->isp] = file; } void rxPrepareInput(VM *vm) { vm->isp = 0; vm->input[vm->isp] = stdin; } void rxPrepareOutput(VM *vm) { tcgetattr(0, &vm->old_termios); vm->new_termios = vm->old_termios; vm->new_termios.c_iflag &= ~(BRKINT+ISTRIP+IXON+IXOFF); vm->new_termios.c_iflag |= (IGNBRK+IGNPAR); vm->new_termios.c_lflag &= ~(ICANON+ISIG+IEXTEN+ECHO); vm->new_termios.c_cc[VMIN] = 1; vm->new_termios.c_cc[VTIME] = 0; tcsetattr(0, TCSANOW, &vm->new_termios); } void rxRestoreIO(VM *vm) { tcsetattr(0, TCSANOW, &vm->old_termios); } /* File I/O Support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ CELL rxGetFileHandle(VM *vm) { CELL i; for(i = 1; i < MAX_OPEN_FILES; i++) if (vm->files[i] == 0) return i; return 0; } void rxAddInputSource(VM *vm) { CELL name = TOS; DROP; rxGetString(vm, name); rxIncludeFile(vm, vm->request); } CELL rxOpenFile(VM *vm) { CELL slot = rxGetFileHandle(vm); CELL mode = TOS; DROP; CELL name = TOS; DROP; rxGetString(vm, name); if (slot > 0) { if (mode == 0) vm->files[slot] = fopen(vm->request, "r"); if (mode == 1) vm->files[slot] = fopen(vm->request, "w"); if (mode == 2) vm->files[slot] = fopen(vm->request, "a"); if (mode == 3) vm->files[slot] = fopen(vm->request, "r+"); } if (vm->files[slot] == NULL) { vm->files[slot] = 0; slot = 0; } return slot; } CELL rxReadFile(VM *vm) { CELL c = fgetc(vm->files[TOS]); DROP; return (c == EOF) ? 0 : c; } CELL rxWriteFile(VM *vm) { CELL slot = TOS; DROP; CELL c = TOS; DROP; CELL r = fputc(c, vm->files[slot]); return (r == EOF) ? 0 : 1; } CELL rxCloseFile(VM *vm) { fclose(vm->files[TOS]); vm->files[TOS] = 0; DROP; return 0; } CELL rxGetFilePosition(VM *vm) { CELL slot = TOS; DROP; return (CELL) ftell(vm->files[slot]); } CELL rxSetFilePosition(VM *vm) { CELL slot = TOS; DROP; CELL pos = TOS; DROP; CELL r = fseek(vm->files[slot], pos, SEEK_SET); return r; } CELL rxGetFileSize(VM *vm) { CELL slot = TOS; DROP; CELL current = ftell(vm->files[slot]); CELL r = fseek(vm->files[slot], 0, SEEK_END); CELL size = ftell(vm->files[slot]); fseek(vm->files[slot], current, SEEK_SET); return (r == 0) ? size : 0; } CELL rxDeleteFile(VM *vm) { CELL name = TOS; DROP; rxGetString(vm, name); return (unlink(vm->request) == 0) ? -1 : 0; } CELL rxLoadImage(VM *vm, char *image) { FILE *fp; CELL x = 0; if ((fp = fopen(image, "rb")) != NULL) { x = fread(&vm->image, sizeof(CELL), IMAGE_SIZE, fp); fclose(fp); } else { if ((fp = fopen(GLOBAL, "rb")) != NULL) { x = fread(&vm->image, sizeof(CELL), IMAGE_SIZE, fp); fclose(fp); } } return x; } CELL rxSaveImage(VM *vm, char *image) { FILE *fp; CELL x = 0; if ((fp = fopen(image, "wb")) == NULL) { fprintf(stderr, "Sorry, but I couldn't open %s\n", image); rxRestoreIO(vm); exit(-1); } if (vm->shrink == 0) x = fwrite(&vm->image, sizeof(CELL), IMAGE_SIZE, fp); else x = fwrite(&vm->image, sizeof(CELL), vm->image[3], fp); fclose(fp); return x; } /* Environment Query ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ void rxQueryEnvironment(VM *vm) { CELL req = TOS; DROP; CELL dest = TOS; DROP; char *r; rxGetString(vm, req); r = getenv(vm->request); if (r != 0) while (*r != '\0') { vm->image[dest] = *r; dest++; r++; } else vm->image[dest] = 0; } /* Device I/O Handler ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ void rxDeviceHandler(VM *vm) { struct winsize w; if (vm->ports[0] != 1) { /* Input */ if (vm->ports[0] == 0 && vm->ports[1] == 1) { vm->ports[1] = rxReadConsole(vm); vm->ports[0] = 1; } /* Output (character generator) */ if (vm->ports[2] == 1) { rxWriteConsole(vm, TOS); DROP vm->ports[2] = 0; vm->ports[0] = 1; } /* File IO and Image Saving */ if (vm->ports[4] != 0) { vm->ports[0] = 1; switch (vm->ports[4]) { case 1: rxSaveImage(vm, vm->filename); vm->ports[4] = 0; break; case 2: rxAddInputSource(vm); vm->ports[4] = 0; break; case -1: vm->ports[4] = rxOpenFile(vm); break; case -2: vm->ports[4] = rxReadFile(vm); break; case -3: vm->ports[4] = rxWriteFile(vm); break; case -4: vm->ports[4] = rxCloseFile(vm); break; case -5: vm->ports[4] = rxGetFilePosition(vm); break; case -6: vm->ports[4] = rxSetFilePosition(vm); break; case -7: vm->ports[4] = rxGetFileSize(vm); break; case -8: vm->ports[4] = rxDeleteFile(vm); break; default: vm->ports[4] = 0; } } /* Capabilities */ if (vm->ports[5] != 0) { vm->ports[0] = 1; switch(vm->ports[5]) { case -1: vm->ports[5] = IMAGE_SIZE; break; case -2: vm->ports[5] = 0; break; case -3: vm->ports[5] = 0; break; case -4: vm->ports[5] = 0; break; case -5: vm->ports[5] = SP; break; case -6: vm->ports[5] = RSP; break; case -7: vm->ports[5] = 0; break; case -8: vm->ports[5] = time(NULL); break; case -9: vm->ports[5] = 0; IP = IMAGE_SIZE; break; case -10: vm->ports[5] = 0; rxQueryEnvironment(vm); break; case -11: ioctl(0, TIOCGWINSZ, &w); vm->ports[5] = w.ws_col; break; case -12: ioctl(0, TIOCGWINSZ, &w); vm->ports[5] = w.ws_row; break; default: vm->ports[5] = 0; } } } } /* The VM ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ void rxProcessOpcode(VM *vm) { CELL a, b, opcode; opcode = vm->image[IP]; if (opcode > NUM_OPS) vm->stats[NUM_OPS]++; else vm->stats[opcode]++; switch(opcode) { case VM_NOP: break; case VM_LIT: SP++; IP++; TOS = vm->image[IP]; if (vm->max_sp < SP) vm->max_sp = SP; break; case VM_DUP: SP++; vm->data[SP] = NOS; if (vm->max_sp < SP) vm->max_sp = SP; break; case VM_DROP: DROP break; case VM_SWAP: a = TOS; TOS = NOS; NOS = a; break; case VM_PUSH: RSP++; TORS = TOS; DROP if (vm->max_rsp < RSP) vm->max_rsp = RSP; break; case VM_POP: SP++; TOS = TORS; RSP--; break; case VM_LOOP: TOS--; if (TOS != 0 && TOS > -1) { IP++; IP = vm->image[IP] - 1; } else { IP++; DROP; } break; case VM_JUMP: IP++; IP = vm->image[IP] - 1; if (IP < 0) IP = IMAGE_SIZE; else { if (vm->image[IP+1] == 0) IP++; if (vm->image[IP+1] == 0) IP++; } break; case VM_RETURN: IP = TORS; RSP--; if (IP < 0) IP = IMAGE_SIZE; else { if (vm->image[IP+1] == 0) IP++; if (vm->image[IP+1] == 0) IP++; } break; case VM_GT_JUMP: IP++; if(NOS > TOS) IP = vm->image[IP] - 1; DROP DROP break; case VM_LT_JUMP: IP++; if(NOS < TOS) IP = vm->image[IP] - 1; DROP DROP break; case VM_NE_JUMP: IP++; if(TOS != NOS) IP = vm->image[IP] - 1; DROP DROP break; case VM_EQ_JUMP: IP++; if(TOS == NOS) IP = vm->image[IP] - 1; DROP DROP break; case VM_FETCH: TOS = vm->image[TOS]; break; case VM_STORE: vm->image[TOS] = NOS; DROP DROP break; case VM_ADD: NOS += TOS; DROP break; case VM_SUB: NOS -= TOS; DROP break; case VM_MUL: NOS *= TOS; DROP break; case VM_DIVMOD: a = TOS; b = NOS; TOS = b / a; NOS = b % a; break; case VM_AND: a = TOS; b = NOS; DROP TOS = a & b; break; case VM_OR: a = TOS; b = NOS; DROP TOS = a | b; break; case VM_XOR: a = TOS; b = NOS; DROP TOS = a ^ b; break; case VM_SHL: a = TOS; b = NOS; DROP TOS = b << a; break; case VM_SHR: a = TOS; b = NOS; DROP TOS = b >>= a; break; case VM_ZERO_EXIT: if (TOS == 0) { DROP IP = TORS; RSP--; } break; case VM_INC: TOS += 1; break; case VM_DEC: TOS -= 1; break; case VM_IN: a = TOS; TOS = vm->ports[a]; vm->ports[a] = 0; break; case VM_OUT: vm->ports[0] = 0; vm->ports[TOS] = NOS; DROP DROP break; case VM_WAIT: rxDeviceHandler(vm); break; default: RSP++; TORS = IP; IP = vm->image[IP] - 1; if (IP < 0) IP = IMAGE_SIZE; else { if (vm->image[IP+1] == 0) IP++; if (vm->image[IP+1] == 0) IP++; } if (vm->max_rsp < RSP) vm->max_rsp = RSP; break; } vm->ports[3] = 1; } /* Stats ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ void rxDisplayStats(VM *vm) { int s = 0; int i = 0; printf("Runtime Statistics\n"); printf("NOP: %d\n", vm->stats[VM_NOP]); printf("LIT: %d\n", vm->stats[VM_LIT]); printf("DUP: %d\n", vm->stats[VM_DUP]); printf("DROP: %d\n", vm->stats[VM_DROP]); printf("SWAP: %d\n", vm->stats[VM_SWAP]); printf("PUSH: %d\n", vm->stats[VM_PUSH]); printf("POP: %d\n", vm->stats[VM_POP]); printf("LOOP: %d\n", vm->stats[VM_LOOP]); printf("JUMP: %d\n", vm->stats[VM_JUMP]); printf("RETURN: %d\n", vm->stats[VM_RETURN]); printf(">JUMP: %d\n", vm->stats[VM_GT_JUMP]); printf("<JUMP: %d\n", vm->stats[VM_LT_JUMP]); printf("!JUMP: %d\n", vm->stats[VM_NE_JUMP]); printf("=JUMP: %d\n", vm->stats[VM_EQ_JUMP]); printf("FETCH: %d\n", vm->stats[VM_FETCH]); printf("STORE: %d\n", vm->stats[VM_STORE]); printf("ADD: %d\n", vm->stats[VM_ADD]); printf("SUB: %d\n", vm->stats[VM_SUB]); printf("MUL: %d\n", vm->stats[VM_MUL]); printf("DIVMOD: %d\n", vm->stats[VM_DIVMOD]); printf("AND: %d\n", vm->stats[VM_AND]); printf("OR: %d\n", vm->stats[VM_OR]); printf("XOR: %d\n", vm->stats[VM_XOR]); printf("SHL: %d\n", vm->stats[VM_SHL]); printf("SHR: %d\n", vm->stats[VM_SHR]); printf("0;: %d\n", vm->stats[VM_ZERO_EXIT]); printf("INC: %d\n", vm->stats[VM_INC]); printf("DEC: %d\n", vm->stats[VM_DEC]); printf("IN: %d\n", vm->stats[VM_IN]); printf("OUT: %d\n", vm->stats[VM_OUT]); printf("WAIT: %d\n", vm->stats[VM_WAIT]); printf("CALL: %d\n", vm->stats[NUM_OPS]); printf("Max SP: %d\n", vm->max_sp); printf("Max RSP: %d\n", vm->max_rsp); for (s = i = 0; s < NUM_OPS; s++) i += vm->stats[s]; printf("Total opcodes processed: %d\n", i); } /* String Evaluation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ void rxEvaluateString(VM *vm, char *string) { vm->inputString = string; vm->inputSource = 1; vm->strIndex = 0; while ((vm->inputSource != 0) && (IP < IMAGE_SIZE)) { rxProcessOpcode(vm); IP++; } } /* Main ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ int main(int argc, char **argv) { int i; int wantsStats = 0; VM *vm = calloc(sizeof(VM), sizeof(char)); strcpy(vm->filename, LOCAL); rxPrepareInput(vm); /* Parse the command line arguments */ for (i = 1; i < argc; i++) { if (strcmp(argv[i], "--with") == 0) rxIncludeFile(vm, argv[++i]); if (strcmp(argv[i], "--image") == 0) strcpy(vm->filename, argv[++i]); if (strcmp(argv[i], "--shrink") == 0) vm->shrink = 1; if (strcmp(argv[i], "--stats") == 0) wantsStats = 1; } if (rxLoadImage(vm, vm->filename) == 0) { printf("Sorry, unable to find %s\n", vm->filename); exit(1); } rxPrepareOutput(vm); // for (IP = 0; IP < IMAGE_SIZE; IP++) // rxProcessOpcode(vm); rxEvaluateString(vm, "words bye"); rxRestoreIO(vm); if (wantsStats == 1) rxDisplayStats(vm); return 0; }