ca2f77264ffa803f13e44bb5fd80e63ca8f20280
* (c) 2019 Aaron Taylor <ataylor at subgeniuskitty dot com>
#define RETURNSTACKSIZE 1024
print_usage(char ** argv
)
printf( "VVhitespace Interpreter v%d (www.subgeniuskitty.com)\n"
" -h Help (prints this message)\n"
" -i <file> Specify a VVhitespace source file to interpret.\n"
FD_SET(STDIN_FILENO
, &read_fds
);
int retval
= select(1, &read_fds
, NULL
, NULL
, &timeout
);
/* retval could be -1. Ignoring that for now. */
if (retval
> 0) return 0;
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. */
tcsetattr(STDIN_FILENO
, TCSANOW
, &options
);
unset_terminal_mode(void)
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
);
ws_die(size_t * pc
, char * msg
)
printf("SIM_ERROR @ PC %lu: %s\n", *pc
, msg
);
stack_push(int32_t ** sp
, int32_t word
)
stack_peek(int32_t ** sp
, size_t offset
)
/* offset=0 peeks TOS, offset=1 peeks NOS, etc. */
return *((*sp
)-offset
-1);
next_code_byte(uint8_t * code
, size_t * pc
)
* In addition to returning the parsed label, this function advances the PC to
parse_label(uint8_t * code
, size_t * pc
)
while ((c
= code
[(*pc
)++]) != '\n') {
// TODO: Where should I handle attempts to access an unitialized label?
// For now, leave it undefined in a nasal demon sense.
populate_labels(uint32_t * labels
, uint8_t * code
, size_t code_size
)
while (cp
<= code_size
) {
uint16_t temp_label
= parse_label(code
, &cp
);
process_imp_stack(uint8_t * code
, size_t * pc
, int32_t ** sp
)
switch (next_code_byte(code
,pc
)) {
/* Push number onto TOS. */
/* First, pick off the sign */
switch (next_code_byte(code
,pc
)) {
case ' ' : sign
= 1; break;
case '\t': sign
= -1; break;
default : ws_die(pc
, "expected sign"); break;
/* Now, construct the number and push to TOS. */
/* I'm assuming the numbers are read MSb first. */
int32_t temp
, number
= 0;
while ((temp
= next_code_byte(code
,pc
)) != '\n') {
if (temp
== '\v') ws_die(pc
, "non-binary digit in number");
if (temp
== '\t') number
++;
stack_push(sp
, number
*sign
);
switch (next_code_byte(code
,pc
)) {
stack_push(sp
, stack_peek(sp
,0));
int32_t t1
= stack_pop(sp
);
int32_t t2
= stack_pop(sp
);
ws_die(pc
, "malformed stack IMP");
default: ws_die(pc
, "malformed stack IMP"); break;
process_imp_arithmetic(uint8_t * code
, size_t * pc
, int32_t ** sp
)
switch (next_code_byte(code
,pc
)) {
switch (next_code_byte(code
,pc
)) {
stack_push(sp
, stack_pop(sp
)+stack_pop(sp
));
stack_push(sp
, stack_pop(sp
)-temp
);
stack_push(sp
, stack_pop(sp
)*stack_pop(sp
));
ws_die(pc
, "malformed arithmetic IMP");
switch (next_code_byte(code
,pc
)) {
stack_push(sp
, stack_pop(sp
)/temp
);
stack_push(sp
, stack_pop(sp
)%temp
);
default: ws_die(pc
, "malformed arithmetic IMP"); break;
default: ws_die(pc
, "malformed arithmetic IMP"); break;
process_imp_flowcontrol(uint8_t * code
, size_t * pc
, int32_t ** sp
, uint32_t * labels
,
switch (next_code_byte(code
,pc
)) {
/* Technically another LF is required but we ignore it. */
switch (next_code_byte(code
,pc
)) {
/* Mark a location in the program. */
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. */
size_t temp_pc
= labels
[parse_label(code
, pc
)];
/* Jump unconditionally to a label. */
*pc
= labels
[parse_label(code
, pc
)];
ws_die(pc
, "malformed flow control IMP");
switch (next_code_byte(code
,pc
)) {
/* Jump to a label if TOS == 0 */
/* TODO: Does WS pop or peek the TOS? */
if (stack_peek(sp
,0) == 0) *pc
= labels
[parse_label(code
, pc
)];
/* Jump to a label if TOS < 0. */
/* TODO: Does WS pop or peek the TOS? */
if (stack_peek(sp
,0) < 0) *pc
= labels
[parse_label(code
, pc
)];
/* Return from subroutine. */
ws_die(pc
, "malformed flow control IMP");
ws_die(pc
, "malformed flow control IMP");
process_imp_heap(uint8_t * code
, size_t * pc
, int32_t ** sp
, int32_t ** hp
)
switch (next_code_byte(code
,pc
)) {
int32_t value
= stack_pop(sp
);
int32_t addr
= stack_pop(sp
);
stack_push(sp
, *(*hp
+ stack_pop(sp
)));
ws_die(pc
, "malformed heap IMP");
process_imp_io(uint8_t * code
, size_t * pc
, int32_t ** sp
, int32_t ** hp
)
switch (next_code_byte(code
,pc
)) {
switch (next_code_byte(code
,pc
)) {
case ' ' : /* Output char from TOS */ printf("%c", stack_pop(sp
)); break;
case '\t': /* Output digit from TOS */ printf("%c", stack_pop(sp
)+'0'); break;
default : ws_die(pc
, "malformed output IMP"); break;
while (stdin_empty()) continue;
switch (next_code_byte(code
,pc
)) {
case '\t': /* Input digit */ c
-= '0'; /* fallthrough */
case ' ' : /* Input character */ *(*hp
+ *(--(*sp
))) = c
; break;
default : ws_die(pc
, "malformed input IMP"); break;
default: ws_die(pc
, "malformed i/o IMP"); break;
main(int argc
, char ** argv
)
* Process command line arguments
while ((c
= getopt(argc
,argv
,"i:h")) != -1) {
if ((input
= fopen(optarg
, "r")) == NULL
) {
fprintf(stderr
, "ERROR: %s: %s\n", optarg
, strerror(errno
));
fprintf(stderr
, "ERROR: Must specify a VVhitespace source file with -f flag.\n");
* Read just the VVhitespace source code into memory.
* We will use the array indices as addresses for the virtual PC when jumping to labels.
while (fread(&temp_byte
, 1, 1, input
)) {
if (temp_byte
== ' ' || temp_byte
== '\t' || temp_byte
== '\n' || temp_byte
== '\v') {
uint8_t * ws_code_space
= malloc(ws_code_size
);
while (fread(&temp_byte
, 1, 1, input
)) {
if (temp_byte
== ' ' || temp_byte
== '\t' || temp_byte
== '\n' || temp_byte
== '\v') {
ws_code_space
[ws_code_size
++] = temp_byte
;
* 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);
* Setup the return stack and the label array.
uint32_t * rsp
= malloc(RETURNSTACKSIZE
*4);
uint32_t labels
[65536] = {0};
populate_labels(labels
, ws_code_space
, ws_code_size
);
size_t pc
= 0; /* Virtual program counter. Operates in the ws_code_space[] address space. */
if (pc
>= ws_code_size
) {
fprintf(stderr
, "SIM_ERROR: PC Overrun\n Requested PC: %lu\n Max Address: %lu\n",
// TODO: Have the SIGTERM signal handler and normal term point return the value
// on TOS so I can do rudimentary automated tests.
switch (ws_code_space
[pc
++]) {
process_imp_stack(ws_code_space
, &pc
, &sp
);
process_imp_flowcontrol(ws_code_space
, &pc
, &sp
, labels
, &rsp
);
/* Arithmetic, Heap Access, or I/O */
switch (ws_code_space
[pc
++]) {
process_imp_arithmetic(ws_code_space
, &pc
, &sp
);
process_imp_heap(ws_code_space
, &pc
, &sp
, &hp
);
process_imp_io(ws_code_space
, &pc
, &sp
, &hp
);
default: ws_die(&pc
, "unexpected VTab"); break;
printf("Program executed.\n");