From b44a61be41e44b415b1293fcbc1f1e8c4ce3373d Mon Sep 17 00:00:00 2001 From: Aryadev Chavali Date: Mon, 23 Oct 2023 03:58:34 +0100 Subject: src->vm, Makefile is now a bit more abstracted and pretty colours Changed folder names for sake of clarity (will be introducing a new build target soon), and Makefile can now easily support more targets. --- src/runtime.c | 777 ---------------------------------------------------------- 1 file changed, 777 deletions(-) delete mode 100644 src/runtime.c (limited to 'src/runtime.c') diff --git a/src/runtime.c b/src/runtime.c deleted file mode 100644 index ccda9d0..0000000 --- a/src/runtime.c +++ /dev/null @@ -1,777 +0,0 @@ -/* Copyright (C) 2023 Aryadev Chavali - - * You may distribute and modify this code under the terms of the - * GPLv2 license. You should have received a copy of the GPLv2 - * license with this file. If not, please write to: - * aryadev@aryadevchavali.com. - - * Created: 2023-10-15 - * Author: Aryadev Chavali - * Description: Virtual machine implementation - */ - -#include -#include -#include -#include -#include - -#include "./runtime.h" - -const char *err_as_cstr(err_t err) -{ - switch (err) - { - case ERR_OK: - return "OK"; - break; - case ERR_STACK_UNDERFLOW: - return "STACK_UNDERFLOW"; - break; - case ERR_STACK_OVERFLOW: - return "STACK_OVERFLOW"; - break; - case ERR_INVALID_OPCODE: - return "INVALID_OPCODE"; - break; - case ERR_INVALID_REGISTER_BYTE: - return "INVALID_REGISTER_BYTE"; - break; - case ERR_INVALID_REGISTER_HWORD: - return "INVALID_REGISTER_HWORD"; - break; - case ERR_INVALID_REGISTER_WORD: - return "INVALID_REGISTER_WORD"; - break; - case ERR_INVALID_PROGRAM_ADDRESS: - return "INVALID_PROGRAM_ADDRESS"; - case ERR_END_OF_PROGRAM: - return "END_OF_PROGRAM"; - break; - default: - return ""; - } -} - -err_t vm_execute(vm_t *vm) -{ - static_assert(NUMBER_OF_OPCODES == 46, "vm_execute: Out of date"); - struct Program *prog = &vm->program; - if (prog->ptr >= prog->max) - return ERR_END_OF_PROGRAM; - inst_t instruction = prog->instructions[prog->ptr]; - - if (OPCODE_IS_TYPE(instruction.opcode, OP_PUSH)) - { - prog->ptr++; - return PUSH_ROUTINES[instruction.opcode](vm, instruction.operand); - } - else if (OPCODE_IS_TYPE(instruction.opcode, OP_MOV) || - OPCODE_IS_TYPE(instruction.opcode, OP_PUSH_REGISTER)) - { - prog->ptr++; - return REG_ROUTINES[instruction.opcode](vm, instruction.operand.as_byte); - } - else if (OPCODE_IS_TYPE(instruction.opcode, OP_POP)) - { - // NOTE: We use the first register to hold the result of this pop - data_type_t type = OPCODE_DATA_TYPE(instruction.opcode, OP_POP); - prog->ptr++; - switch (type) - { - case DATA_TYPE_NIL: - break; - case DATA_TYPE_BYTE: - return vm_mov_byte(vm, 0); - break; - case DATA_TYPE_HWORD: - return vm_mov_hword(vm, 0); - break; - case DATA_TYPE_WORD: - return vm_mov_word(vm, 0); - break; - } - return ERR_OK; - } - else if (OPCODE_IS_TYPE(instruction.opcode, OP_DUP)) - { - prog->ptr++; - return DUP_ROUTINES[instruction.opcode](vm, instruction.operand.as_word); - } - else if (OPCODE_IS_TYPE(instruction.opcode, OP_NOT) || - OPCODE_IS_TYPE(instruction.opcode, OP_OR) || - OPCODE_IS_TYPE(instruction.opcode, OP_AND) || - OPCODE_IS_TYPE(instruction.opcode, OP_XOR) || - OPCODE_IS_TYPE(instruction.opcode, OP_EQ) || - OPCODE_IS_TYPE(instruction.opcode, OP_PLUS)) - { - prog->ptr++; - return STACK_ROUTINES[instruction.opcode](vm); - } - else if (instruction.opcode == OP_JUMP_ABS) - return vm_jump(vm, instruction.operand.as_word); - else if (instruction.opcode == OP_JUMP_STACK) - { - // Set prog->ptr to the word on top of the stack - 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_JUMP_REGISTER) - { - if (instruction.operand.as_byte >= 8) - return ERR_INVALID_REGISTER_WORD; - word addr = vm->registers.reg[instruction.operand.as_byte]; - return vm_jump(vm, addr); - } - else if (OPCODE_IS_TYPE(instruction.opcode, OP_JUMP_IF)) - { - data_t datum = {0}; - err_t err = ERR_OK; - if (instruction.opcode == OP_JUMP_IF_BYTE) - err = vm_pop_byte(vm, &datum); - else if (instruction.opcode == OP_JUMP_IF_HWORD) - err = vm_pop_hword(vm, &datum); - else if (instruction.opcode == OP_JUMP_IF_WORD) - err = vm_pop_word(vm, &datum); - - if (err) - return err; - - // If datum != 0 then jump, else go to the next instruction - if (datum.as_word != 0) - return vm_jump(vm, instruction.operand.as_word); - else - ++prog->ptr; - } - else if (instruction.opcode >= OP_PRINT_CHAR && - instruction.opcode <= OP_PRINT_WORD) - { - data_t datum = {0}; - enum - { - TYPE_CHAR, - TYPE_BYTE, - TYPE_INT, - TYPE_HWORD, - TYPE_LONG, - TYPE_WORD - } print_type; - err_t err = ERR_OK; - if (instruction.opcode == OP_PRINT_BYTE || - instruction.opcode == OP_PRINT_CHAR) - { - print_type = instruction.opcode == OP_PRINT_BYTE ? TYPE_BYTE : TYPE_CHAR; - err = vm_pop_byte(vm, &datum); - } - else if (instruction.opcode == OP_PRINT_HWORD || - instruction.opcode == OP_PRINT_INT) - { - print_type = instruction.opcode == OP_PRINT_HWORD ? TYPE_HWORD : TYPE_INT; - err = vm_pop_hword(vm, &datum); - } - else if (instruction.opcode == OP_PRINT_WORD || - instruction.opcode == OP_PRINT_LONG) - { - print_type = instruction.opcode == OP_PRINT_WORD ? TYPE_WORD : TYPE_LONG; - err = vm_pop_word(vm, &datum); - } - - if (err) - return err; - - switch (print_type) - { - case TYPE_CHAR: { - char c = 0; - memcpy(&c, &datum.as_byte, 1); - printf("%c", c); - break; - } - case TYPE_BYTE: - printf("0x%x", datum.as_byte); - break; - case TYPE_INT: { - int32_t i = 0; - memcpy(&i, &datum.as_hword, HWORD_SIZE); - printf("%" PRId32, i); - break; - } - case TYPE_HWORD: - printf("%" PRIu32, datum.as_hword); - break; - case TYPE_LONG: { - int64_t i = 0; - memcpy(&i, &datum.as_word, WORD_SIZE); - printf("%" PRId64, i); - break; - } - case TYPE_WORD: - printf("%" PRIu64, datum.as_word); - break; - } - - prog->ptr++; - } - else if (instruction.opcode == OP_HALT) - { - // Do nothing here. Should be caught by callers of vm_execute - } - else - return ERR_INVALID_OPCODE; - return ERR_OK; -} - -err_t vm_execute_all(vm_t *vm) -{ - struct Program *program = &vm->program; - err_t err = ERR_OK; -#if VERBOSE == 1 - struct Registers prev_registers = vm->registers; - size_t cycles = 0; - size_t prev_sptr = 0; -#endif - while (program->instructions[program->ptr].opcode != OP_HALT && - program->ptr < program->max) - { -#if VERBOSE >= 1 - fprintf(stdout, "[vm_execute_all]: Trace(Cycle %lu)\n", cycles); - fputs( - "----------------------------------------------------------------------" - "----------\n", - stdout); - vm_print_program(vm, stdout); - fputs( - "----------------------------------------------------------------------" - "----------\n", - stdout); - if (memcmp(prev_registers.reg, vm->registers.reg, - ARR_SIZE(vm->registers.reg)) != 0) - { - vm_print_registers(vm, stdout); - prev_registers = vm->registers; - fputs("------------------------------------------------------------------" - "----" - "----------\n", - stdout); - } - if (prev_sptr != vm->stack.ptr) - { - vm_print_stack(vm, stdout); - prev_sptr = vm->stack.ptr; - fputs("------------------------------------------------------------------" - "----" - "----------\n", - stdout); - } - ++cycles; -#endif - err = vm_execute(vm); - if (err) - return err; - } - -#if VERBOSE >= 1 - fprintf(stdout, "[vm_execute_all]: Final VM state(Cycle %lu)\n", cycles); - vm_print_all(vm, stdout); -#endif - return err; -} - -void vm_load_stack(vm_t *vm, byte *bytes, size_t size) -{ - vm->stack.data = bytes; - vm->stack.max = size; - vm->stack.ptr = 0; -} - -void vm_load_program(vm_t *vm, inst_t *instructions, size_t size) -{ - vm->program.instructions = instructions; - vm->program.max = size; - vm->program.ptr = 0; -} - -void vm_print_registers(vm_t *vm, FILE *fp) -{ - struct Registers reg = vm->registers; - fprintf(fp, "Registers.reg = ["); - for (size_t i = 0; i < VM_REGISTERS; ++i) - { - fprintf(fp, "{%lu:%lX}", i, reg.reg[i]); - if (i != VM_REGISTERS - 1) - fprintf(fp, ", "); - } - fprintf(fp, "]\n"); -} - -void vm_print_stack(vm_t *vm, FILE *fp) -{ - struct Stack stack = vm->stack; - fprintf(fp, "Stack.max = %lu\nStack.ptr = %lu\nStack.data = [", stack.max, - stack.ptr); - if (stack.ptr == 0) - { - fprintf(fp, "]\n"); - return; - } - printf("\n"); - for (size_t i = stack.ptr; i > 0; --i) - { - byte b = stack.data[i - 1]; - fprintf(fp, "\t%lu: %X", stack.ptr - i, b); - if (i != 1) - fprintf(fp, ", "); - fprintf(fp, "\n"); - } - fprintf(fp, "]\n"); -} - -void vm_print_program(vm_t *vm, FILE *fp) -{ - struct Program program = vm->program; - fprintf(fp, - "Program.max = %lu\nProgram.ptr = " - "%lu\nProgram.instructions = [\n", - program.max, program.ptr); - size_t beg = 0; - if (program.ptr >= VM_PRINT_PROGRAM_EXCERPT) - { - fprintf(fp, "\t...\n"); - beg = program.ptr - VM_PRINT_PROGRAM_EXCERPT; - } - else - beg = 0; - size_t end = MIN(program.ptr + VM_PRINT_PROGRAM_EXCERPT, program.max); - for (size_t i = beg; i < end; ++i) - { - fprintf(fp, "\t%lu: ", i); - inst_print(program.instructions[i], fp); - if (i == program.ptr) - fprintf(fp, " <---"); - fprintf(fp, "\n"); - } - if (end != program.max) - fprintf(fp, "\t...\n"); - fprintf(fp, "]\n"); -} - -void vm_print_all(vm_t *vm, FILE *fp) -{ - fputs("----------------------------------------------------------------------" - "----------\n", - fp); - vm_print_program(vm, fp); - fputs("----------------------------------------------------------------------" - "----------\n", - fp); - vm_print_registers(vm, fp); - fputs("----------------------------------------------------------------------" - "----------\n", - fp); - vm_print_stack(vm, fp); - fputs("----------------------------------------------------------------------" - "----------\n", - fp); -} - -err_t vm_jump(vm_t *vm, word w) -{ - if (w >= vm->program.max) - return ERR_INVALID_PROGRAM_ADDRESS; - vm->program.ptr = w; - return ERR_OK; -} - -err_t vm_push_byte(vm_t *vm, data_t b) -{ - if (vm->stack.ptr >= vm->stack.max) - return ERR_STACK_OVERFLOW; - vm->stack.data[vm->stack.ptr++] = b.as_byte; - return ERR_OK; -} - -err_t vm_push_hword(vm_t *vm, data_t f) -{ - if (vm->stack.ptr + HWORD_SIZE >= vm->stack.max) - return ERR_STACK_OVERFLOW; - byte bytes[HWORD_SIZE] = {0}; - convert_hword_to_bytes(f.as_hword, bytes); - for (size_t i = 0; i < HWORD_SIZE; ++i) - { - byte b = bytes[HWORD_SIZE - i - 1]; - vm_push_byte(vm, DBYTE(b)); - } - return ERR_OK; -} - -err_t vm_push_word(vm_t *vm, data_t w) -{ - if (vm->stack.ptr + WORD_SIZE >= vm->stack.max) - return ERR_STACK_OVERFLOW; - byte bytes[WORD_SIZE] = {0}; - convert_word_to_bytes(w.as_word, bytes); - for (size_t i = 0; i < WORD_SIZE; ++i) - { - byte b = bytes[WORD_SIZE - i - 1]; - vm_push_byte(vm, DBYTE(b)); - } - return ERR_OK; -} - -err_t vm_push_byte_register(vm_t *vm, byte reg) -{ - if (reg >= VM_REGISTERS * 8) - return ERR_INVALID_REGISTER_BYTE; - - // Interpret each word based register as 8 byte registers - byte b = WORD_NTH_BYTE(vm->registers.reg[reg / 8], reg % 8); - - return vm_push_byte(vm, DBYTE(b)); -} - -err_t vm_push_hword_register(vm_t *vm, byte reg) -{ - if (reg >= VM_REGISTERS * 2) - return ERR_INVALID_REGISTER_HWORD; - else if (vm->stack.ptr >= vm->stack.max) - return ERR_STACK_OVERFLOW; - // Interpret each word based register as 2 hword registers - hword hw = WORD_NTH_HWORD(vm->registers.reg[reg / 2], reg % 2); - return vm_push_hword(vm, DHWORD(hw)); -} - -err_t vm_push_word_register(vm_t *vm, byte reg) -{ - if (reg >= VM_REGISTERS) - return ERR_INVALID_REGISTER_WORD; - else if (vm->stack.ptr >= vm->stack.max) - return ERR_STACK_OVERFLOW; - return vm_push_word(vm, DWORD(vm->registers.reg[reg])); -} - -err_t vm_mov_byte(vm_t *vm, byte reg) -{ - if (reg >= (VM_REGISTERS * 8)) - return ERR_INVALID_REGISTER_BYTE; - data_t ret = {0}; - err_t err = vm_pop_byte(vm, &ret); - if (err) - return err; - word *reg_ptr = &vm->registers.reg[reg / 8]; - size_t shift = (reg % 8) * 8; - // This resets the bits in the specific byte register - *reg_ptr = *reg_ptr & ~(0xFF << shift); - // This sets the bits - *reg_ptr = (*reg_ptr) | (ret.as_word << shift); - return ERR_OK; -} - -err_t vm_mov_hword(vm_t *vm, byte reg) -{ - if (reg >= (VM_REGISTERS * 2)) - return ERR_INVALID_REGISTER_HWORD; - else if (vm->stack.ptr < sizeof(f64)) - return ERR_STACK_UNDERFLOW; - data_t ret = {0}; - err_t err = vm_pop_hword(vm, &ret); - if (err) - return err; - word *reg_ptr = &vm->registers.reg[reg / 2]; - size_t shift = (reg % 2) * 2; - // This resets the bits in the specific hword register - *reg_ptr = *reg_ptr & ~(0xFFFFFFFF << shift); - // This sets the bits - *reg_ptr = (*reg_ptr) | (ret.as_word << shift); - return ERR_OK; -} - -err_t vm_mov_word(vm_t *vm, byte reg) -{ - if (reg >= VM_REGISTERS) - return ERR_INVALID_REGISTER_WORD; - else if (vm->stack.ptr < sizeof(word)) - return ERR_STACK_UNDERFLOW; - data_t ret = {0}; - err_t err = vm_pop_word(vm, &ret); - if (err) - return err; - vm->registers.reg[reg] = ret.as_word; - return ERR_OK; -} - -err_t vm_dup_byte(vm_t *vm, word w) -{ - if (vm->stack.ptr < w + 1) - return ERR_STACK_UNDERFLOW; - return vm_push_byte(vm, DBYTE(vm->stack.data[vm->stack.ptr - 1 - w])); -} - -err_t vm_dup_hword(vm_t *vm, word w) -{ - if (vm->stack.ptr < HWORD_SIZE * (w + 1)) - return ERR_STACK_UNDERFLOW; - byte bytes[HWORD_SIZE] = {0}; - for (size_t i = 0; i < HWORD_SIZE; ++i) - bytes[HWORD_SIZE - i - 1] = - vm->stack.data[vm->stack.ptr - (HWORD_SIZE * (w + 1)) + i]; - return vm_push_hword(vm, DHWORD(convert_bytes_to_hword(bytes))); -} - -err_t vm_dup_word(vm_t *vm, word w) -{ - if (vm->stack.ptr < WORD_SIZE * (w + 1)) - return ERR_STACK_UNDERFLOW; - byte bytes[WORD_SIZE] = {0}; - for (size_t i = 0; i < WORD_SIZE; ++i) - bytes[WORD_SIZE - i - 1] = - vm->stack.data[vm->stack.ptr - (WORD_SIZE * (w + 1)) + i]; - return vm_push_word(vm, DWORD(convert_bytes_to_word(bytes))); -} - -err_t vm_pop_byte(vm_t *vm, data_t *ret) -{ - if (vm->stack.ptr == 0) - return ERR_STACK_UNDERFLOW; - *ret = DBYTE(vm->stack.data[--vm->stack.ptr]); - return ERR_OK; -} - -err_t vm_pop_hword(vm_t *vm, data_t *ret) -{ - if (vm->stack.ptr < HWORD_SIZE) - return ERR_STACK_UNDERFLOW; - byte bytes[HWORD_SIZE] = {0}; - for (size_t i = 0; i < HWORD_SIZE; ++i) - { - data_t b = {0}; - vm_pop_byte(vm, &b); - bytes[i] = b.as_byte; - } - *ret = DWORD(convert_bytes_to_hword(bytes)); - return ERR_OK; -} - -err_t vm_pop_word(vm_t *vm, data_t *ret) -{ - if (vm->stack.ptr < WORD_SIZE) - return ERR_STACK_UNDERFLOW; - byte bytes[WORD_SIZE] = {0}; - for (size_t i = 0; i < WORD_SIZE; ++i) - { - data_t b = {0}; - vm_pop_byte(vm, &b); - bytes[i] = b.as_byte; - } - *ret = DWORD(convert_bytes_to_word(bytes)); - return ERR_OK; -} - -err_t vm_not_byte(vm_t *vm) -{ - data_t a = {0}; - err_t err = vm_pop_byte(vm, &a); - if (err) - return err; - return vm_push_byte(vm, DBYTE(!a.as_byte)); -} - -err_t vm_not_hword(vm_t *vm) -{ - data_t a = {0}; - err_t err = vm_pop_hword(vm, &a); - if (err) - return err; - return vm_push_hword(vm, DHWORD(!a.as_hword)); -} - -err_t vm_not_word(vm_t *vm) -{ - data_t a = {0}; - err_t err = vm_pop_word(vm, &a); - if (err) - return err; - return vm_push_word(vm, DWORD(!a.as_word)); -} - -err_t vm_or_byte(vm_t *vm) -{ - data_t a = {0}, b = {0}; - err_t err = vm_pop_byte(vm, &a); - if (err) - return err; - err = vm_pop_byte(vm, &b); - if (err) - return err; - return vm_push_byte(vm, DBYTE(a.as_byte | b.as_byte)); -} - -err_t vm_or_hword(vm_t *vm) -{ - data_t a = {0}, b = {0}; - err_t err = vm_pop_hword(vm, &a); - if (err) - return err; - err = vm_pop_hword(vm, &b); - if (err) - return err; - return vm_push_hword(vm, DHWORD(a.as_hword | b.as_hword)); -} - -err_t vm_or_word(vm_t *vm) -{ - data_t a = {0}, b = {0}; - err_t err = vm_pop_word(vm, &a); - if (err) - return err; - err = vm_pop_word(vm, &b); - if (err) - return err; - return vm_push_word(vm, DWORD(a.as_word | b.as_word)); -} - -err_t vm_and_byte(vm_t *vm) -{ - data_t a = {0}, b = {0}; - err_t err = vm_pop_byte(vm, &a); - if (err) - return err; - err = vm_pop_byte(vm, &b); - if (err) - return err; - return vm_push_byte(vm, DBYTE(a.as_byte & b.as_byte)); -} - -err_t vm_and_hword(vm_t *vm) -{ - data_t a = {0}, b = {0}; - err_t err = vm_pop_hword(vm, &a); - if (err) - return err; - err = vm_pop_hword(vm, &b); - if (err) - return err; - return vm_push_hword(vm, DHWORD(a.as_hword & b.as_hword)); -} - -err_t vm_and_word(vm_t *vm) -{ - data_t a = {0}, b = {0}; - err_t err = vm_pop_word(vm, &a); - if (err) - return err; - err = vm_pop_word(vm, &b); - if (err) - return err; - return vm_push_word(vm, DWORD(a.as_word & b.as_word)); -} - -err_t vm_xor_byte(vm_t *vm) -{ - data_t a = {0}, b = {0}; - err_t err = vm_pop_byte(vm, &a); - if (err) - return err; - err = vm_pop_byte(vm, &b); - if (err) - return err; - return vm_push_byte(vm, DBYTE(a.as_byte ^ b.as_byte)); -} - -err_t vm_xor_hword(vm_t *vm) -{ - data_t a = {0}, b = {0}; - err_t err = vm_pop_hword(vm, &a); - if (err) - return err; - err = vm_pop_hword(vm, &b); - if (err) - return err; - return vm_push_hword(vm, DHWORD(a.as_hword ^ b.as_hword)); -} - -err_t vm_xor_word(vm_t *vm) -{ - data_t a = {0}, b = {0}; - err_t err = vm_pop_word(vm, &a); - if (err) - return err; - err = vm_pop_word(vm, &b); - if (err) - return err; - return vm_push_word(vm, DWORD(a.as_word ^ b.as_word)); -} - -err_t vm_eq_byte(vm_t *vm) -{ - data_t a = {0}, b = {0}; - err_t err = vm_pop_byte(vm, &a); - if (err) - return err; - err = vm_pop_byte(vm, &b); - if (err) - return err; - return vm_push_byte(vm, DBYTE(a.as_byte == b.as_byte)); -} - -err_t vm_eq_hword(vm_t *vm) -{ - data_t a = {0}, b = {0}; - err_t err = vm_pop_hword(vm, &a); - if (err) - return err; - err = vm_pop_hword(vm, &b); - if (err) - return err; - return vm_push_hword(vm, DHWORD(a.as_hword == b.as_hword)); -} - -err_t vm_eq_word(vm_t *vm) -{ - data_t a = {0}, b = {0}; - err_t err = vm_pop_word(vm, &a); - if (err) - return err; - err = vm_pop_word(vm, &b); - if (err) - return err; - return vm_push_word(vm, DWORD(a.as_word == b.as_word)); -} - -err_t vm_plus_byte(vm_t *vm) -{ - data_t a = {0}, b = {0}; - err_t err = vm_pop_byte(vm, &a); - if (err) - return err; - err = vm_pop_byte(vm, &b); - if (err) - return err; - return vm_push_byte(vm, DBYTE(a.as_byte + b.as_byte)); -} - -err_t vm_plus_hword(vm_t *vm) -{ - data_t a = {0}, b = {0}; - err_t err = vm_pop_hword(vm, &a); - if (err) - return err; - err = vm_pop_hword(vm, &b); - if (err) - return err; - return vm_push_hword(vm, DHWORD(a.as_hword + b.as_hword)); -} - -err_t vm_plus_word(vm_t *vm) -{ - data_t a = {0}, b = {0}; - err_t err = vm_pop_word(vm, &a); - if (err) - return err; - err = vm_pop_word(vm, &b); - if (err) - return err; - return vm_push_word(vm, DWORD(a.as_word + b.as_word)); -} -- cgit v1.2.3-13-gbd6f