X-Git-Url: http://git.subgeniuskitty.com/vvhitespace/.git/blobdiff_plain/9686c90139fc19b70f42283b1504ab9d5be7932c..4fba07dca7fb9275b51e5095a774fe69eeab4660:/vv_interpreter.c diff --git a/vv_interpreter.c b/vv_interpreter.c index 39210f1..b548a50 100644 --- a/vv_interpreter.c +++ b/vv_interpreter.c @@ -11,12 +11,13 @@ #include #include #include +#include #define VERSION 1 -#define STACKSIZE 1024 -#define HEAPSIZE 1024 -#define RETURNSTACKSIZE 1024 +#define HEAPSIZE 1024 /* Size of heap in words */ +#define DATASTACKSIZE 1024 /* Size of stack in words */ +#define RETURNSTACKSIZE 1024 /* Max subroutine call depth */ void print_usage(char ** argv) @@ -46,29 +47,55 @@ stdin_empty(void) return 1; } +void +set_terminal_mode(void) +{ + struct termios options; + tcgetattr(STDIN_FILENO, &options); + /* Create a cbreak-like environment through the following options. */ + options.c_lflag &= ~ECHO; /* Disable echoing of input characters. */ + options.c_lflag &= ~ICANON; /* Disable cooked/line-oriented mode. */ + options.c_cc[VMIN] = 1; + options.c_cc[VTIME] = 0; + tcsetattr(STDIN_FILENO, TCSANOW, &options); +} + +void +unset_terminal_mode(void) +{ + struct termios options; + tcgetattr(STDIN_FILENO, &options); + /* Undo the changes made in set_terminal_mode(). */ + options.c_lflag |= ECHO; /* Enable echoing of input characters. */ + options.c_lflag |= ICANON; /* Enable cooked/line-oriented mode. */ + options.c_cc[VMIN] = 1; /* Default value from /usr/src/sys/sys/ttydefaults.h */ + options.c_cc[VTIME] = 0; /* Default value from /usr/src/sys/sys/ttydefaults.h */ + tcsetattr(STDIN_FILENO, TCSANOW, &options); +} + void ws_die(size_t * pc, char * msg) { - printf("\n"); printf("SIM_ERROR @ PC %lu: %s\n", *pc, msg); fflush(stdout); + unset_terminal_mode(); exit(EXIT_FAILURE); } void -stack_push(int32_t ** sp, int32_t word) +stack_push(int64_t ** sp, int64_t word) { *((*sp)++) = word; } -int32_t -stack_pop(int32_t ** sp) +int64_t +stack_pop(int64_t ** sp) { return *(--(*sp)); } -int32_t -stack_peek(int32_t ** sp, size_t offset) +int64_t +stack_peek(int64_t ** sp, size_t offset) /* offset=0 peeks TOS, offset=1 peeks NOS, etc. */ { return *((*sp)-offset-1); @@ -91,35 +118,42 @@ parse_label(uint8_t * code, size_t * pc) uint8_t c; while ((c = code[(*pc)++]) != '\n') { label = label << 1; - if (c == ' ') label++; + if (c == '\t') label++; + } + return label; +} + +uint16_t +check_label(size_t * labels, uint16_t label, size_t * pc) +{ + if (!labels[label]) { + fprintf(stderr, "Trying to process label 0x%X.\n", label); + ws_die(pc, "uninitialized label (forgot an include?)"); } - // TODO: Where should I handle attempts to access an unitialized label? - // For now, leave it undefined in a nasal demon sense. return label; } void -populate_labels(uint32_t * labels, uint8_t * code, size_t code_size) +populate_labels(size_t * labels, uint8_t * code, size_t code_size) { size_t cp = 0; while (cp <= code_size) { - if (code[cp] == '\v') { + if (code[cp++] == '\v') { uint16_t temp_label = parse_label(code, &cp); labels[temp_label] = cp; } - cp++; } } void -process_imp_stack(uint8_t * code, size_t * pc, int32_t ** sp) +process_imp_stack(uint8_t * code, size_t * pc, int64_t ** sp) { switch (next_code_byte(code,pc)) { case ' ': /* Push number onto TOS. */ { /* First, pick off the sign */ - int32_t sign = 0; + int64_t sign = 0; switch (next_code_byte(code,pc)) { case ' ' : sign = 1; break; case '\t': sign = -1; break; @@ -128,13 +162,26 @@ process_imp_stack(uint8_t * code, size_t * pc, int32_t ** sp) /* Now, construct the number and push to TOS. */ /* I'm assuming the numbers are read MSb first. */ - int32_t temp, number = 0; + uint64_t number = 0; /* Unsigned to accomodate magnitude of most negative number. */ + uint8_t temp; while ((temp = next_code_byte(code,pc)) != '\n') { if (temp == '\v') ws_die(pc, "non-binary digit in number"); number <<= 1; if (temp == '\t') number++; } - stack_push(sp, number*sign); + /* Without temporarily casting to something >64-bit, the most negative */ + /* number will overflow when performing 'number*sign'. Instead, we */ + /* pick off the most negative number as a special case. */ + if (number == (1ULL << 63) && sign == -1) { + /* C parses negative integer literals first as signed positive */ + /* integer literals, then applying a unary negation operator. */ + /* Thus, the most negative value is unreachable directly. */ + int64_t number_temp = -9223372036854775807LL; /* First store -((2^63)-1) */ + number_temp--; /* Now turn it into -(2^63) */ + stack_push(sp, number_temp); + } else { + stack_push(sp, number*sign); + } } break; case '\n': @@ -148,8 +195,8 @@ process_imp_stack(uint8_t * code, size_t * pc, int32_t ** sp) /* Swap TOS and NOS. */ case '\t': { - int32_t t1 = stack_pop(sp); - int32_t t2 = stack_pop(sp); + int64_t t1 = stack_pop(sp); + int64_t t2 = stack_pop(sp); stack_push(sp, t1); stack_push(sp, t2); } @@ -169,9 +216,9 @@ process_imp_stack(uint8_t * code, size_t * pc, int32_t ** sp) } void -process_imp_arithmetic(uint8_t * code, size_t * pc, int32_t ** sp) +process_imp_arithmetic(uint8_t * code, size_t * pc, int64_t ** sp) { - int32_t temp; + int64_t temp; switch (next_code_byte(code,pc)) { case ' ': { @@ -206,7 +253,7 @@ process_imp_arithmetic(uint8_t * code, size_t * pc, int32_t ** sp) case '\t': /* Modulo */ temp = stack_pop(sp); - stack_push(sp, stack_pop(sp)%temp); + stack_push(sp, llabs(stack_pop(sp) % llabs(temp))); break; default: ws_die(pc, "malformed arithmetic IMP"); break; } @@ -217,14 +264,15 @@ process_imp_arithmetic(uint8_t * code, size_t * pc, int32_t ** sp) } void -process_imp_flowcontrol(uint8_t * code, size_t * pc, int32_t ** sp, uint32_t * labels, - uint32_t ** rsp) +process_imp_flowcontrol(uint8_t * code, size_t * pc, int64_t ** sp, size_t * labels, + size_t ** rsp) { + size_t temp_pc; switch (next_code_byte(code,pc)) { case '\n': /* Technically another LF is required but we ignore it. */ - printf("\n"); fflush(stdout); + unset_terminal_mode(); exit(EXIT_SUCCESS); case ' ': { @@ -234,16 +282,17 @@ process_imp_flowcontrol(uint8_t * code, size_t * pc, int32_t ** sp, uint32_t * l if (next_code_byte(code,pc) != '\v') ws_die(pc,"expected vtab, " "perhaps a whitespace program, rather than vvhitespace?"); /* Jump to next instruction since labels were parsed during startup. */ - parse_label( code, pc); + parse_label(code, pc); break; case '\t': /* Call a subroutine. */ + temp_pc = labels[check_label(labels, parse_label(code, pc), pc)]; *((*rsp)++) = *pc; - *pc = labels[parse_label(code, pc)]; + *pc = temp_pc; break; case '\n': /* Jump unconditionally to a label. */ - *pc = labels[parse_label(code, pc)]; + *pc = labels[check_label(labels, parse_label(code, pc), pc)]; break; default: ws_die(pc, "malformed flow control IMP"); @@ -256,11 +305,13 @@ process_imp_flowcontrol(uint8_t * code, size_t * pc, int32_t ** sp, uint32_t * l switch (next_code_byte(code,pc)) { case ' ': /* Jump to a label if TOS == 0 */ - if (stack_peek(sp,0) == 0) *pc = labels[parse_label(code, pc)]; + temp_pc = labels[check_label(labels, parse_label(code, pc), pc)]; + if (stack_pop(sp) == 0) *pc = temp_pc; break; case '\t': /* Jump to a label if TOS < 0. */ - if (stack_peek(sp,0) < 0) *pc = labels[parse_label(code, pc)]; + temp_pc = labels[check_label(labels, parse_label(code, pc), pc)]; + if (stack_pop(sp) < 0) *pc = temp_pc; break; case '\n': /* Return from subroutine. */ @@ -279,25 +330,37 @@ process_imp_flowcontrol(uint8_t * code, size_t * pc, int32_t ** sp, uint32_t * l } void -process_imp_heap(uint8_t * code, size_t * pc, int32_t ** sp, int32_t ** hp) +process_imp_heap(uint8_t * code, size_t * pc, int64_t ** sp, int64_t ** hp) { switch (next_code_byte(code,pc)) { - case ' ' : /* Store to heap */ *(*hp + *((*sp)-1)) = **sp; *sp -= 2; break; - case '\t': /* Retrieve from heap */ **sp = *(*hp + **sp); break; - default : ws_die(pc, "malformed heap IMP"); break; + case ' ' : + /* Store to heap */ + { + int64_t value = stack_pop(sp); + int64_t addr = stack_pop(sp); + *(*hp + addr) = value; + } + break; + case '\t': + /* Retrieve from heap */ + stack_push(sp, *(*hp + stack_pop(sp))); + break; + default: + ws_die(pc, "malformed heap IMP"); + break; } } void -process_imp_io(uint8_t * code, size_t * pc, int32_t ** sp, int32_t ** hp) +process_imp_io(uint8_t * code, size_t * pc, int64_t ** sp, int64_t ** hp) { switch (next_code_byte(code,pc)) { case ' ': /* Output */ { switch (next_code_byte(code,pc)) { - case ' ' : /* Output character from TOS */ printf("%c", stack_pop(sp)); break; - case '\t': /* Output number from TOS */ printf("%d", stack_pop(sp)); break; + case ' ' : /* Output char from TOS */ printf("%c", (uint8_t) stack_pop(sp)); break; + case '\t': /* Output digit from TOS */ printf("%c", (uint8_t) stack_pop(sp)+'0'); break; default : ws_die(pc, "malformed output IMP"); break; } fflush(stdout); @@ -310,7 +373,7 @@ process_imp_io(uint8_t * code, size_t * pc, int32_t ** sp, int32_t ** hp) char c = getchar(); switch (next_code_byte(code,pc)) { case '\t': /* Input digit */ c -= '0'; /* fallthrough */ - case ' ' : /* Input character */ *(*hp + *((*sp)--)) = c; break; + case ' ' : /* Input character */ *(*hp + *(--(*sp))) = c; break; default : ws_die(pc, "malformed input IMP"); break; } } @@ -349,7 +412,7 @@ main(int argc, char ** argv) } /* - * Read just the VVhitespace source code into memory. + * Read just the VVhitespace source code into memory, stripping comment characters. * We will use the array indices as addresses for the virtual PC when jumping to labels. */ size_t ws_code_size = 0; @@ -371,32 +434,30 @@ main(int argc, char ** argv) /* * Setup a stack and heap. - * Assume a 32-bit word size. */ - // TODO: Make everything 64-bit. - int32_t * hp = malloc(HEAPSIZE*4); - int32_t * sp = malloc(STACKSIZE*4); + int64_t * hp = malloc(HEAPSIZE*sizeof(int64_t)); + int64_t * sp = malloc(DATASTACKSIZE*sizeof(int64_t)); /* * Setup the return stack and the label array. */ - uint32_t * rsp = malloc(RETURNSTACKSIZE*4); - uint32_t labels[65536] = {0}; + size_t * rsp = malloc(RETURNSTACKSIZE*sizeof(size_t)); + size_t labels[65536] = {0}; /* 65536 = 2^16 => Holds all possible labels. */ + /* Default value of zero indicates an uninitialized label. */ populate_labels(labels, ws_code_space, ws_code_size); /* * Main Loop */ - + set_terminal_mode(); size_t pc = 0; /* Virtual program counter. Operates in the ws_code_space[] address space. */ while (1) { if (pc >= ws_code_size) { fprintf(stderr, "SIM_ERROR: PC Overrun\n Requested PC: %lu\n Max Address: %lu\n", pc, ws_code_size-1); exit(EXIT_FAILURE); + unset_terminal_mode(); } -// TODO: Have the SIGTERM signal handler and normal term point return the value -// on TOS so I can do rudimentary automated tests. /* Decode the IMPs */ switch (ws_code_space[pc++]) { @@ -427,12 +488,7 @@ main(int argc, char ** argv) } } break; - default: ws_die(&pc, "unexpected VTab"); break; + default: ws_die(&pc, "unexpected byte"); break; } } - - printf("\n"); - printf("Program executed.\n"); - - exit(EXIT_SUCCESS); }