Implemented subroutine instructions in runtime
Very easy overall, printing the call stack not so much.
This commit is contained in:
18
vm/main.c
18
vm/main.c
@@ -37,8 +37,7 @@ int main(int argc, char *argv[])
|
|||||||
const char *filename = argv[1];
|
const char *filename = argv[1];
|
||||||
|
|
||||||
#if VERBOSE >= 1
|
#if VERBOSE >= 1
|
||||||
printf("[%sINTERPRETER%s]: Interpreting `%s`\n", TERM_YELLOW, TERM_RESET,
|
printf("[" TERM_YELLOW "INTERPRETER" TERM_RESET "]: `%s`\n", filename);
|
||||||
filename);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FILE *fp = fopen(filename, "rb");
|
FILE *fp = fopen(filename, "rb");
|
||||||
@@ -47,8 +46,8 @@ int main(int argc, char *argv[])
|
|||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
#if VERBOSE >= 1
|
#if VERBOSE >= 1
|
||||||
printf("\t[%sBYTECODE-READER%s]: Read %lu instructions\n", TERM_GREEN,
|
printf("\t[" TERM_GREEN "SETUP" TERM_RESET "]: Read %lu instructions\n",
|
||||||
TERM_RESET, number);
|
number);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
size_t stack_size = 256;
|
size_t stack_size = 256;
|
||||||
@@ -57,16 +56,22 @@ int main(int argc, char *argv[])
|
|||||||
darr_init(®isters, 8 * WORD_SIZE);
|
darr_init(®isters, 8 * WORD_SIZE);
|
||||||
heap_t heap = {0};
|
heap_t heap = {0};
|
||||||
heap_create(&heap);
|
heap_create(&heap);
|
||||||
|
size_t call_stack_size = 256;
|
||||||
|
word *call_stack = calloc(call_stack_size, sizeof(call_stack));
|
||||||
|
|
||||||
vm_t vm = {0};
|
vm_t vm = {0};
|
||||||
vm_load_stack(&vm, stack, stack_size);
|
vm_load_stack(&vm, stack, stack_size);
|
||||||
vm_load_program(&vm, instructions, number);
|
vm_load_program(&vm, instructions, number);
|
||||||
vm_load_registers(&vm, registers);
|
vm_load_registers(&vm, registers);
|
||||||
vm_load_heap(&vm, heap);
|
vm_load_heap(&vm, heap);
|
||||||
|
vm_load_call_stack(&vm, call_stack, call_stack_size);
|
||||||
|
|
||||||
#if VERBOSE >= 1
|
#if VERBOSE >= 1
|
||||||
printf("\t[%sVM-SETUP%s]: Loaded stack and program into VM\n", TERM_GREEN,
|
printf("\t[" TERM_GREEN "SETUP" TERM_RESET
|
||||||
TERM_RESET);
|
"]: Loaded stack and program into VM\n");
|
||||||
|
#endif
|
||||||
|
#if VERBOSE >= 1
|
||||||
|
printf("[" TERM_YELLOW "INTERPRETER" TERM_RESET "]: Beginning execution\n");
|
||||||
#endif
|
#endif
|
||||||
err_t err = vm_execute_all(&vm);
|
err_t err = vm_execute_all(&vm);
|
||||||
|
|
||||||
@@ -78,6 +83,7 @@ int main(int argc, char *argv[])
|
|||||||
vm_print_all(&vm, stderr);
|
vm_print_all(&vm, stderr);
|
||||||
ret = 255 - err;
|
ret = 255 - err;
|
||||||
}
|
}
|
||||||
|
|
||||||
vm_stop(&vm);
|
vm_stop(&vm);
|
||||||
|
|
||||||
#if VERBOSE >= 1
|
#if VERBOSE >= 1
|
||||||
|
|||||||
80
vm/runtime.c
80
vm/runtime.c
@@ -25,25 +25,22 @@ const char *err_as_cstr(err_t err)
|
|||||||
{
|
{
|
||||||
case ERR_OK:
|
case ERR_OK:
|
||||||
return "OK";
|
return "OK";
|
||||||
break;
|
|
||||||
case ERR_STACK_UNDERFLOW:
|
case ERR_STACK_UNDERFLOW:
|
||||||
return "STACK_UNDERFLOW";
|
return "STACK_UNDERFLOW";
|
||||||
break;
|
|
||||||
case ERR_STACK_OVERFLOW:
|
case ERR_STACK_OVERFLOW:
|
||||||
return "STACK_OVERFLOW";
|
return "STACK_OVERFLOW";
|
||||||
break;
|
case ERR_CALL_STACK_UNDERFLOW:
|
||||||
|
return "CALL_STACK_UNDERFLOW";
|
||||||
|
case ERR_CALL_STACK_OVERFLOW:
|
||||||
|
return "CALL_STACK_OVERFLOW";
|
||||||
case ERR_INVALID_OPCODE:
|
case ERR_INVALID_OPCODE:
|
||||||
return "INVALID_OPCODE";
|
return "INVALID_OPCODE";
|
||||||
break;
|
|
||||||
case ERR_INVALID_REGISTER_BYTE:
|
case ERR_INVALID_REGISTER_BYTE:
|
||||||
return "INVALID_REGISTER_BYTE";
|
return "INVALID_REGISTER_BYTE";
|
||||||
break;
|
|
||||||
case ERR_INVALID_REGISTER_HWORD:
|
case ERR_INVALID_REGISTER_HWORD:
|
||||||
return "INVALID_REGISTER_HWORD";
|
return "INVALID_REGISTER_HWORD";
|
||||||
break;
|
|
||||||
case ERR_INVALID_REGISTER_WORD:
|
case ERR_INVALID_REGISTER_WORD:
|
||||||
return "INVALID_REGISTER_WORD";
|
return "INVALID_REGISTER_WORD";
|
||||||
break;
|
|
||||||
case ERR_INVALID_PROGRAM_ADDRESS:
|
case ERR_INVALID_PROGRAM_ADDRESS:
|
||||||
return "INVALID_PROGRAM_ADDRESS";
|
return "INVALID_PROGRAM_ADDRESS";
|
||||||
case ERR_INVALID_PAGE_ADDRESS:
|
case ERR_INVALID_PAGE_ADDRESS:
|
||||||
@@ -52,7 +49,6 @@ const char *err_as_cstr(err_t err)
|
|||||||
return "OUT_OF_BOUNDS";
|
return "OUT_OF_BOUNDS";
|
||||||
case ERR_END_OF_PROGRAM:
|
case ERR_END_OF_PROGRAM:
|
||||||
return "END_OF_PROGRAM";
|
return "END_OF_PROGRAM";
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@@ -60,7 +56,7 @@ const char *err_as_cstr(err_t err)
|
|||||||
|
|
||||||
err_t vm_execute(vm_t *vm)
|
err_t vm_execute(vm_t *vm)
|
||||||
{
|
{
|
||||||
static_assert(NUMBER_OF_OPCODES == 95, "vm_execute: Out of date");
|
static_assert(NUMBER_OF_OPCODES == 98, "vm_execute: Out of date");
|
||||||
struct Program *prog = &vm->program;
|
struct Program *prog = &vm->program;
|
||||||
if (prog->ptr >= prog->max)
|
if (prog->ptr >= prog->max)
|
||||||
return ERR_END_OF_PROGRAM;
|
return ERR_END_OF_PROGRAM;
|
||||||
@@ -153,6 +149,30 @@ err_t vm_execute(vm_t *vm)
|
|||||||
else
|
else
|
||||||
++prog->ptr;
|
++prog->ptr;
|
||||||
}
|
}
|
||||||
|
else if (instruction.opcode == OP_CALL)
|
||||||
|
{
|
||||||
|
if (vm->call_stack.ptr >= vm->call_stack.max)
|
||||||
|
return ERR_CALL_STACK_OVERFLOW;
|
||||||
|
vm->call_stack.address_pointers[vm->call_stack.ptr++] = vm->program.ptr + 1;
|
||||||
|
return vm_jump(vm, instruction.operand.as_word);
|
||||||
|
}
|
||||||
|
else if (instruction.opcode == OP_CALL_STACK)
|
||||||
|
{
|
||||||
|
if (vm->call_stack.ptr >= vm->call_stack.max)
|
||||||
|
return ERR_CALL_STACK_OVERFLOW;
|
||||||
|
vm->call_stack.address_pointers[vm->call_stack.ptr++] = vm->program.ptr + 1;
|
||||||
|
data_t ret = {0};
|
||||||
|
err_t err = vm_pop_word(vm, &ret);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
return vm_jump(vm, ret.as_word);
|
||||||
|
}
|
||||||
|
else if (instruction.opcode == OP_RET)
|
||||||
|
{
|
||||||
|
if (vm->call_stack.ptr == 0)
|
||||||
|
return ERR_CALL_STACK_UNDERFLOW;
|
||||||
|
return vm_jump(vm, vm->call_stack.address_pointers[--vm->call_stack.ptr]);
|
||||||
|
}
|
||||||
else if (OPCODE_IS_TYPE(instruction.opcode, OP_PRINT))
|
else if (OPCODE_IS_TYPE(instruction.opcode, OP_PRINT))
|
||||||
{
|
{
|
||||||
data_t datum = {0};
|
data_t datum = {0};
|
||||||
@@ -259,6 +279,7 @@ err_t vm_execute_all(vm_t *vm)
|
|||||||
registers_t prev_registers = vm->registers;
|
registers_t prev_registers = vm->registers;
|
||||||
size_t prev_sptr = 0;
|
size_t prev_sptr = 0;
|
||||||
size_t prev_pages = 0;
|
size_t prev_pages = 0;
|
||||||
|
size_t prev_cptr = 0;
|
||||||
#endif
|
#endif
|
||||||
while (program->instructions[program->ptr].opcode != OP_HALT &&
|
while (program->instructions[program->ptr].opcode != OP_HALT &&
|
||||||
program->ptr < program->max)
|
program->ptr < program->max)
|
||||||
@@ -274,6 +295,15 @@ err_t vm_execute_all(vm_t *vm)
|
|||||||
"----------------------------------------------------------------------"
|
"----------------------------------------------------------------------"
|
||||||
"----------\n",
|
"----------\n",
|
||||||
stdout);
|
stdout);
|
||||||
|
if (prev_cptr != vm->call_stack.ptr)
|
||||||
|
{
|
||||||
|
vm_print_call_stack(vm, stdout);
|
||||||
|
prev_cptr = vm->call_stack.ptr;
|
||||||
|
fputs("------------------------------------------------------------------"
|
||||||
|
"----"
|
||||||
|
"----------\n",
|
||||||
|
stdout);
|
||||||
|
}
|
||||||
if (prev_pages != vm->heap.pages)
|
if (prev_pages != vm->heap.pages)
|
||||||
{
|
{
|
||||||
vm_print_heap(vm, stdout);
|
vm_print_heap(vm, stdout);
|
||||||
@@ -342,6 +372,12 @@ void vm_load_heap(vm_t *vm, heap_t heap)
|
|||||||
vm->heap = heap;
|
vm->heap = heap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void vm_load_call_stack(vm_t *vm, word *buffer, size_t size)
|
||||||
|
{
|
||||||
|
vm->call_stack =
|
||||||
|
(struct CallStack){.address_pointers = buffer, .ptr = 0, .max = size};
|
||||||
|
}
|
||||||
|
|
||||||
void vm_stop(vm_t *vm)
|
void vm_stop(vm_t *vm)
|
||||||
{
|
{
|
||||||
free(vm->registers.data);
|
free(vm->registers.data);
|
||||||
@@ -460,12 +496,38 @@ void vm_print_heap(vm_t *vm, FILE *fp)
|
|||||||
fprintf(fp, "]\n");
|
fprintf(fp, "]\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void vm_print_call_stack(vm_t *vm, FILE *fp)
|
||||||
|
{
|
||||||
|
struct CallStack cs = vm->call_stack;
|
||||||
|
fprintf(fp, "CallStack.max = %lu\nCallStack.ptr = %lu\nCallStack.data = [",
|
||||||
|
cs.max, cs.ptr);
|
||||||
|
if (cs.ptr == 0)
|
||||||
|
{
|
||||||
|
fprintf(fp, "]\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
for (size_t i = cs.ptr; i > 0; --i)
|
||||||
|
{
|
||||||
|
word w = cs.address_pointers[i - 1];
|
||||||
|
fprintf(fp, "\t%lu: %lX", cs.ptr - i, w);
|
||||||
|
if (i != 1)
|
||||||
|
fprintf(fp, ", ");
|
||||||
|
fprintf(fp, "\n");
|
||||||
|
}
|
||||||
|
fprintf(fp, "]\n");
|
||||||
|
}
|
||||||
|
|
||||||
void vm_print_all(vm_t *vm, FILE *fp)
|
void vm_print_all(vm_t *vm, FILE *fp)
|
||||||
{
|
{
|
||||||
fputs("----------------------------------------------------------------------"
|
fputs("----------------------------------------------------------------------"
|
||||||
"----------\n",
|
"----------\n",
|
||||||
fp);
|
fp);
|
||||||
vm_print_program(vm, fp);
|
vm_print_program(vm, fp);
|
||||||
|
fputs("----------------------------------------------------------------------"
|
||||||
|
"----------\n",
|
||||||
|
fp);
|
||||||
|
vm_print_call_stack(vm, fp);
|
||||||
fputs("----------------------------------------------------------------------"
|
fputs("----------------------------------------------------------------------"
|
||||||
"----------\n",
|
"----------\n",
|
||||||
fp);
|
fp);
|
||||||
|
|||||||
13
vm/runtime.h
13
vm/runtime.h
@@ -24,6 +24,8 @@ typedef enum
|
|||||||
ERR_OK = 0,
|
ERR_OK = 0,
|
||||||
ERR_STACK_UNDERFLOW,
|
ERR_STACK_UNDERFLOW,
|
||||||
ERR_STACK_OVERFLOW,
|
ERR_STACK_OVERFLOW,
|
||||||
|
ERR_CALL_STACK_UNDERFLOW,
|
||||||
|
ERR_CALL_STACK_OVERFLOW,
|
||||||
ERR_INVALID_OPCODE,
|
ERR_INVALID_OPCODE,
|
||||||
ERR_INVALID_REGISTER_BYTE,
|
ERR_INVALID_REGISTER_BYTE,
|
||||||
ERR_INVALID_REGISTER_HWORD,
|
ERR_INVALID_REGISTER_HWORD,
|
||||||
@@ -46,14 +48,19 @@ typedef struct
|
|||||||
struct Stack
|
struct Stack
|
||||||
{
|
{
|
||||||
byte *data;
|
byte *data;
|
||||||
word ptr, max;
|
size_t ptr, max;
|
||||||
} stack;
|
} stack;
|
||||||
heap_t heap;
|
heap_t heap;
|
||||||
struct Program
|
struct Program
|
||||||
{
|
{
|
||||||
inst_t *instructions;
|
inst_t *instructions;
|
||||||
word ptr, max;
|
size_t ptr, max;
|
||||||
} program;
|
} program;
|
||||||
|
struct CallStack
|
||||||
|
{
|
||||||
|
word *address_pointers;
|
||||||
|
size_t ptr, max;
|
||||||
|
} call_stack;
|
||||||
} vm_t;
|
} vm_t;
|
||||||
|
|
||||||
err_t vm_execute(vm_t *);
|
err_t vm_execute(vm_t *);
|
||||||
@@ -63,6 +70,7 @@ void vm_load_stack(vm_t *, byte *, size_t);
|
|||||||
void vm_load_registers(vm_t *, registers_t);
|
void vm_load_registers(vm_t *, registers_t);
|
||||||
void vm_load_heap(vm_t *, heap_t);
|
void vm_load_heap(vm_t *, heap_t);
|
||||||
void vm_load_program(vm_t *, inst_t *, size_t);
|
void vm_load_program(vm_t *, inst_t *, size_t);
|
||||||
|
void vm_load_call_stack(vm_t *, word *, size_t);
|
||||||
void vm_stop(vm_t *);
|
void vm_stop(vm_t *);
|
||||||
|
|
||||||
// Print routines
|
// Print routines
|
||||||
@@ -71,6 +79,7 @@ void vm_print_registers(vm_t *, FILE *);
|
|||||||
void vm_print_stack(vm_t *, FILE *);
|
void vm_print_stack(vm_t *, FILE *);
|
||||||
void vm_print_program(vm_t *, FILE *);
|
void vm_print_program(vm_t *, FILE *);
|
||||||
void vm_print_heap(vm_t *, FILE *);
|
void vm_print_heap(vm_t *, FILE *);
|
||||||
|
void vm_print_call_stack(vm_t *, FILE *);
|
||||||
void vm_print_all(vm_t *, FILE *);
|
void vm_print_all(vm_t *, FILE *);
|
||||||
|
|
||||||
// Execution routines
|
// Execution routines
|
||||||
|
|||||||
Reference in New Issue
Block a user