diff options
author | Aryadev Chavali <aryadev@aryadevchavali.com> | 2023-10-23 03:58:34 +0100 |
---|---|---|
committer | Aryadev Chavali <aryadev@aryadevchavali.com> | 2023-10-23 03:58:34 +0100 |
commit | b44a61be41e44b415b1293fcbc1f1e8c4ce3373d (patch) | |
tree | 218cdb4901d23089ab0b54b4ff3097202c442acd /src | |
parent | 587f31a63b26fe3789acd0ba129474a2be200d37 (diff) | |
download | ovm-b44a61be41e44b415b1293fcbc1f1e8c4ce3373d.tar.gz ovm-b44a61be41e44b415b1293fcbc1f1e8c4ce3373d.tar.bz2 ovm-b44a61be41e44b415b1293fcbc1f1e8c4ce3373d.zip |
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.
Diffstat (limited to 'src')
-rw-r--r-- | src/base.h | 70 | ||||
-rw-r--r-- | src/darr.c | 77 | ||||
-rw-r--r-- | src/darr.h | 39 | ||||
-rw-r--r-- | src/fib.c | 79 | ||||
-rw-r--r-- | src/inst.c | 399 | ||||
-rw-r--r-- | src/inst.h | 156 | ||||
-rw-r--r-- | src/main.c | 62 | ||||
-rw-r--r-- | src/runtime.c | 777 | ||||
-rw-r--r-- | src/runtime.h | 165 |
9 files changed, 0 insertions, 1824 deletions
diff --git a/src/base.h b/src/base.h deleted file mode 100644 index dbeec80..0000000 --- a/src/base.h +++ /dev/null @@ -1,70 +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: Basic types and routines - */ - -#ifndef BASE_H -#define BASE_H - -#include <stdint.h> - -#define ARR_SIZE(xs) (sizeof(xs) / sizeof(xs[0])) -#define MAX(a, b) ((a) > (b) ? (a) : (b)) -#define MIN(a, b) ((a) > (b) ? (b) : (a)) - -// Flags -#ifndef VERBOSE -#define VERBOSE 0 -#endif - -typedef uint64_t u64; -typedef uint32_t u32; -typedef int32_t i32; -typedef int64_t i64; -typedef float f32; -typedef double f64; - -typedef uint8_t byte; -typedef u32 hword; -typedef u64 word; - -typedef union -{ - byte as_byte; - hword as_hword; - word as_word; -} data_t; - -typedef enum -{ - DATA_TYPE_NIL = 0, - DATA_TYPE_BYTE, - DATA_TYPE_HWORD, - DATA_TYPE_WORD, -} data_type_t; - -#define DBYTE(BYTE) ((data_t){.as_byte = (BYTE)}) -#define DHWORD(HWORD) ((data_t){.as_hword = (HWORD)}) -#define DWORD(WORD) ((data_t){.as_word = (WORD)}) - -#define HWORD_SIZE sizeof(hword) -#define WORD_SIZE sizeof(word) - -// Macros to extract the nth byte or nth hword from a word -#define WORD_NTH_BYTE(WORD, N) (((WORD) >> ((N)*8)) & 0xff) -#define WORD_NTH_HWORD(WORD, N) (((WORD) >> ((N)*2)) & 0xFFFFFFFF) - -// Assume array contains 4 bytes. -hword convert_bytes_to_hword(byte *); -void convert_hword_to_bytes(hword, byte *); -// Assume array contains 8 bytes. -word convert_bytes_to_word(byte *); -void convert_word_to_bytes(word, byte *); - -#endif diff --git a/src/darr.c b/src/darr.c deleted file mode 100644 index 4393c4b..0000000 --- a/src/darr.c +++ /dev/null @@ -1,77 +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: Dynamically sized byte array - */ - -#include <assert.h> -#include <malloc.h> -#include <string.h> - -#include "./darr.h" - -void darr_init(darr_t *darr, size_t size) -{ - if (size == 0) - size = DARR_DEFAULT_SIZE; - *darr = (darr_t){ - .data = calloc(size, 1), - .used = 0, - .available = size, - }; -} - -void darr_ensure_capacity(darr_t *darr, size_t requested) -{ - if (darr->used + requested >= darr->available) - { - darr->available = - MAX(darr->used + requested, darr->available * DARR_REALLOC_MULT); - darr->data = realloc(darr->data, darr->available); - } -} - -void darr_append_byte(darr_t *darr, byte byte) -{ - darr_ensure_capacity(darr, 1); - darr->data[darr->used++] = byte; -} - -void darr_append_bytes(darr_t *darr, byte *bytes, size_t n) -{ - darr_ensure_capacity(darr, n); - memcpy(darr->data + darr->used, bytes, n); - darr->used += n; -} - -byte darr_at(darr_t *darr, size_t index) -{ - if (index >= darr->used) - // TODO: Error (index is out of bounds) - return 0; - return darr->data[index]; -} - -void darr_write_file(darr_t *bytes, FILE *fp) -{ - size_t size = fwrite(bytes->data, bytes->used, 1, fp); - assert(size == 1); -} - -darr_t darr_read_file(FILE *fp) -{ - darr_t darr = {0}; - fseek(fp, 0, SEEK_END); - long size = ftell(fp); - darr_init(&darr, size); - fseek(fp, 0, SEEK_SET); - size_t read = fread(darr.data, size, 1, fp); - assert(read == 1); - return darr; -} diff --git a/src/darr.h b/src/darr.h deleted file mode 100644 index 1d5820b..0000000 --- a/src/darr.h +++ /dev/null @@ -1,39 +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: Dynamically sized byte array - */ - -#ifndef DARR_H -#define DARR_H - -#include <stdio.h> -#include <stdlib.h> - -#include "./base.h" - -typedef struct -{ - byte *data; - size_t used, available; -} darr_t; - -#define DARR_DEFAULT_SIZE 8 -#define DARR_REALLOC_MULT 1.5 - -void darr_init(darr_t *, size_t); -void darr_ensure_capacity(darr_t *, size_t); -void darr_append_byte(darr_t *, byte); -void darr_append_bytes(darr_t *, byte *, size_t); -byte darr_at(darr_t *, size_t); - -void darr_write_file(darr_t *, FILE *); -darr_t darr_read_file(FILE *); - -#endif diff --git a/src/fib.c b/src/fib.c deleted file mode 100644 index 7107c3e..0000000 --- a/src/fib.c +++ /dev/null @@ -1,79 +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-23 - * Author: Aryadev Chavali - * Description: An example virtual machine program which computes and - * prints fibonacci numbers. Note that by default the virtual machine - * just rolls overflows over, so this program will never terminate. - */ - -#include <assert.h> -#include <stdio.h> -#include <string.h> - -#include "./inst.h" -#include "./runtime.h" - -int main(void) -{ - inst_t instructions[] = { - // MOV the values 1 and 1 to REG[0] and REG[1] respectively - INST_PUSH(WORD, 1), - INST_MOV(WORD, 0), - INST_PUSH(WORD, 1), - INST_MOV(WORD, 1), - - // Print value at register 0 with newline. - INST_PUSH_REG(WORD, 0), // <-- # - INST_PRINT(WORD), - INST_PUSH(BYTE, '\n'), - INST_PRINT(CHAR), - - // Print value at register 1 with newline - INST_PUSH_REG(WORD, 1), - INST_PRINT(WORD), - INST_PUSH(BYTE, '\n'), - INST_PRINT(CHAR), - - /* Compute the next pair of fibonacci numbers */ - // REG[0] + REG[1] - INST_PUSH_REG(WORD, 0), - INST_PUSH_REG(WORD, 1), - INST_PLUS(WORD), - - // Mov REG[0] + REG[1] to REG[0] - INST_MOV(WORD, 0), - - // REG[0] + REG[1] - INST_PUSH_REG(WORD, 0), - INST_PUSH_REG(WORD, 1), - INST_PLUS(WORD), - - // Mov REG[0] + REG[1] to REG[1] - INST_MOV(WORD, 1), - - // Jump to the point # - INST_JUMP_ABS(4), - INST_HALT, - }; - - byte stack[256]; - vm_t vm = {0}; - vm_load_stack(&vm, stack, ARR_SIZE(stack)); - vm_load_program(&vm, instructions, ARR_SIZE(instructions)); - err_t err = vm_execute_all(&vm); - - if (err) - { - const char *error_str = err_as_cstr(err); - fprintf(stderr, "[ERROR]: %s\n", error_str); - fprintf(stderr, "[ERROR]: VM Trace:\n"); - vm_print_all(&vm, stderr); - return 255 - err; - } - return 0; -} diff --git a/src/inst.c b/src/inst.c deleted file mode 100644 index 3980173..0000000 --- a/src/inst.c +++ /dev/null @@ -1,399 +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: Implementation of bytecode for instructions - */ - -#include <assert.h> -#include <stdbool.h> -#include <stdio.h> -#include <string.h> - -#include "./inst.h" - -const char *opcode_as_cstr(opcode_t code) -{ - switch (code) - { - case OP_NOOP: - return "NOOP"; - break; - case OP_PUSH_BYTE: - return "PUSH_BYTE"; - break; - case OP_PUSH_WORD: - return "PUSH_WORD"; - break; - case OP_PUSH_HWORD: - return "PUSH_HWORD"; - break; - case OP_PUSH_REGISTER_BYTE: - return "PUSH_REGISTER_BYTE"; - break; - case OP_PUSH_REGISTER_WORD: - return "PUSH_REGISTER_WORD"; - break; - case OP_PUSH_REGISTER_HWORD: - return "PUSH_REGISTER_HWORD"; - break; - case OP_POP_BYTE: - return "POP_BYTE"; - break; - case OP_POP_WORD: - return "POP_WORD"; - break; - case OP_POP_HWORD: - return "POP_HWORD"; - break; - case OP_MOV_BYTE: - return "MOV_BYTE"; - break; - case OP_MOV_WORD: - return "MOV_WORD"; - break; - case OP_MOV_HWORD: - return "MOV_HWORD"; - break; - case OP_DUP_BYTE: - return "DUP_BYTE"; - break; - case OP_DUP_HWORD: - return "DUP_HWORD"; - break; - case OP_DUP_WORD: - return "DUP_WORD"; - break; - case OP_NOT_BYTE: - return "NOT_BYTE"; - break; - case OP_NOT_HWORD: - return "NOT_HWORD"; - break; - case OP_NOT_WORD: - return "NOT_WORD"; - break; - case OP_OR_BYTE: - return "OR_BYTE"; - break; - case OP_OR_HWORD: - return "OR_HWORD"; - break; - case OP_OR_WORD: - return "OR_WORD"; - break; - case OP_AND_BYTE: - return "AND_BYTE"; - break; - case OP_AND_HWORD: - return "AND_HWORD"; - break; - case OP_AND_WORD: - return "AND_WORD"; - break; - case OP_XOR_BYTE: - return "XOR_BYTE"; - break; - case OP_XOR_HWORD: - return "XOR_HWORD"; - break; - case OP_XOR_WORD: - return "XOR_WORD"; - break; - case OP_EQ_BYTE: - return "EQ_BYTE"; - break; - case OP_EQ_HWORD: - return "EQ_HWORD"; - break; - case OP_EQ_WORD: - return "EQ_WORD"; - break; - case OP_PLUS_BYTE: - return "PLUS_BYTE"; - break; - case OP_PLUS_HWORD: - return "PLUS_HWORD"; - break; - case OP_PLUS_WORD: - return "PLUS_WORD"; - break; - case OP_JUMP_ABS: - return "JUMP_ABS"; - break; - case OP_JUMP_STACK: - return "JUMP_STACK"; - break; - case OP_JUMP_REGISTER: - return "JUMP_REGISTER"; - break; - case OP_JUMP_IF_BYTE: - return "JUMP_IF_BYTE"; - break; - case OP_JUMP_IF_HWORD: - return "JUMP_IF_HWORD"; - break; - case OP_JUMP_IF_WORD: - return "JUMP_IF_WORD"; - break; - case OP_PRINT_CHAR: - return "PRINT_CHAR"; - break; - case OP_PRINT_BYTE: - return "PRINT_BYTE"; - break; - case OP_PRINT_INT: - return "PRINT_INT"; - break; - case OP_PRINT_HWORD: - return "PRINT_HWORD"; - break; - case OP_PRINT_LONG: - return "PRINT_LONG"; - break; - case OP_PRINT_WORD: - return "PRINT_WORD"; - break; - case OP_HALT: - return "HALT"; - break; - case NUMBER_OF_OPCODES: - return ""; - break; - } - return ""; -} - -void data_print(data_t datum, data_type_t type, FILE *fp) -{ - switch (type) - { - case DATA_TYPE_NIL: - break; - case DATA_TYPE_BYTE: - fprintf(fp, "%X", datum.as_byte); - break; - case DATA_TYPE_HWORD: - fprintf(fp, "%d", datum.as_hword); - break; - case DATA_TYPE_WORD: - fprintf(fp, "%lX", datum.as_word); - break; - } -} - -hword convert_bytes_to_hword(byte *bytes) -{ - hword h = 0; - memcpy(&h, bytes, HWORD_SIZE); - return h; -} - -void convert_hword_to_bytes(hword w, byte *bytes) -{ - memcpy(bytes, &w, HWORD_SIZE); -} - -void convert_word_to_bytes(word w, byte *bytes) -{ - memcpy(bytes, &w, WORD_SIZE); -} - -word convert_bytes_to_word(byte *bytes) -{ - word w = 0; - memcpy(&w, bytes, WORD_SIZE); - return w; -} - -void inst_print(inst_t instruction, FILE *fp) -{ - static_assert(NUMBER_OF_OPCODES == 46, "inst_bytecode_size: Out of date"); - fprintf(fp, "%s(", opcode_as_cstr(instruction.opcode)); - if (OPCODE_IS_TYPE(instruction.opcode, OP_PUSH)) - { - data_type_t type = (data_type_t)instruction.opcode; - fprintf(fp, "datum=0x"); - data_print(instruction.operand, type, fp); - } - else if (OPCODE_IS_TYPE(instruction.opcode, OP_PUSH_REGISTER) || - OPCODE_IS_TYPE(instruction.opcode, OP_MOV) || - instruction.opcode == OP_JUMP_REGISTER) - { - fprintf(fp, "reg=0x"); - data_print(instruction.operand, DATA_TYPE_BYTE, fp); - } - else if (OPCODE_IS_TYPE(instruction.opcode, OP_DUP)) - { - fprintf(fp, "n=%lu", instruction.operand.as_word); - } - else if (instruction.opcode == OP_JUMP_ABS || - OPCODE_IS_TYPE(instruction.opcode, OP_JUMP_IF)) - { - fprintf(fp, "address=0x"); - data_print(instruction.operand, DATA_TYPE_WORD, fp); - } - fprintf(fp, ")"); -} - -size_t inst_bytecode_size(inst_t inst) -{ - static_assert(NUMBER_OF_OPCODES == 46, "inst_bytecode_size: Out of date"); - size_t size = 1; // for opcode - if (OPCODE_IS_TYPE(inst.opcode, OP_PUSH)) - { - if (inst.opcode == OP_PUSH_BYTE) - ++size; - else if (inst.opcode == OP_PUSH_HWORD) - size += HWORD_SIZE; - else if (inst.opcode == OP_PUSH_WORD) - size += WORD_SIZE; - } - else if (OPCODE_IS_TYPE(inst.opcode, OP_PUSH_REGISTER) || - OPCODE_IS_TYPE(inst.opcode, OP_MOV) || - inst.opcode == OP_JUMP_REGISTER) - // Only need a byte for the register - ++size; - else if (OPCODE_IS_TYPE(inst.opcode, OP_DUP) || inst.opcode == OP_JUMP_ABS || - OPCODE_IS_TYPE(inst.opcode, OP_JUMP_IF)) - size += WORD_SIZE; - return size; -} - -void inst_write_bytecode(inst_t inst, darr_t *darr) -{ - static_assert(NUMBER_OF_OPCODES == 46, "inst_write_bytecode: Out of date"); - // Append opcode - darr_append_byte(darr, inst.opcode); - // Then append 0 or more operands - data_type_t to_append = DATA_TYPE_NIL; - if (OPCODE_IS_TYPE(inst.opcode, OP_PUSH)) - to_append = (data_type_t)inst.opcode; - else if (OPCODE_IS_TYPE(inst.opcode, OP_PUSH_REGISTER) || - OPCODE_IS_TYPE(inst.opcode, OP_MOV) || - inst.opcode == OP_JUMP_REGISTER) - to_append = DATA_TYPE_BYTE; - else if (OPCODE_IS_TYPE(inst.opcode, OP_DUP) || inst.opcode == OP_JUMP_ABS || - OPCODE_IS_TYPE(inst.opcode, OP_JUMP_IF)) - to_append = DATA_TYPE_WORD; - - switch (to_append) - { - case DATA_TYPE_NIL: - break; - case DATA_TYPE_BYTE: - darr_append_byte(darr, inst.operand.as_byte); - break; - case DATA_TYPE_HWORD: - darr_append_bytes(darr, (byte *)&inst.operand.as_hword, HWORD_SIZE); - break; - case DATA_TYPE_WORD: - darr_append_bytes(darr, (byte *)&inst.operand.as_word, WORD_SIZE); - break; - } -} - -void insts_write_bytecode(inst_t *insts, size_t size, darr_t *darr) -{ - for (size_t i = 0; i < size; ++i) - inst_write_bytecode(insts[i], darr); -} - -data_t read_type_from_darr(darr_t *darr, data_type_t type) -{ - switch (type) - { - case DATA_TYPE_NIL: - break; - case DATA_TYPE_BYTE: - if (darr->used >= darr->available) - // TODO: Error (darr has no space left) - return DBYTE(0); - return DBYTE(darr->data[darr->used++]); - break; - case DATA_TYPE_HWORD: - if (darr->used + HWORD_SIZE >= darr->available) - // TODO: Error (darr has no space left) - return DWORD(0); - hword u = 0; - memcpy(&u, darr->data + darr->used, HWORD_SIZE); - darr->used += HWORD_SIZE; - return DHWORD(u); - break; - case DATA_TYPE_WORD: - if (darr->used + WORD_SIZE >= darr->available) - // TODO: Error (darr has no space left) - return DWORD(0); - word w = 0; - memcpy(&w, darr->data + darr->used, WORD_SIZE); - darr->used += WORD_SIZE; - return DWORD(w); - break; - } - // TODO: Error (unrecognised type) - return DBYTE(0); -} - -inst_t inst_read_bytecode(darr_t *darr) -{ - static_assert(NUMBER_OF_OPCODES == 46, "inst_read_bytecode: Out of date"); - if (darr->used >= darr->available) - return (inst_t){0}; - inst_t inst = {0}; - opcode_t opcode = darr->data[darr->used++]; - if (opcode > OP_HALT || opcode == NUMBER_OF_OPCODES || opcode < OP_NOOP) - // Translate to NOOP - return inst; - // Read operands - if (OPCODE_IS_TYPE(opcode, OP_PUSH)) - inst.operand = read_type_from_darr(darr, (data_type_t)opcode); - // Read register (as a byte) - else if (OPCODE_IS_TYPE(opcode, OP_PUSH_REGISTER) || - OPCODE_IS_TYPE(opcode, OP_MOV) || inst.opcode == OP_JUMP_STACK) - inst.operand = read_type_from_darr(darr, DATA_TYPE_BYTE); - else if (OPCODE_IS_TYPE(opcode, OP_DUP) || opcode == OP_JUMP_ABS || - OPCODE_IS_TYPE(opcode, OP_JUMP_IF)) - inst.operand = read_type_from_darr(darr, DATA_TYPE_WORD); - // Otherwise opcode doesn't take operands - - inst.opcode = opcode; - - return inst; -} - -inst_t *insts_read_bytecode(darr_t *bytes, size_t *ret_size) -{ - *ret_size = 0; - // NOTE: Here we use the darr as a dynamic array of inst_t. - darr_t instructions = {0}; - darr_init(&instructions, sizeof(inst_t)); - while (bytes->used < bytes->available) - { - inst_t instruction = inst_read_bytecode(bytes); - darr_append_bytes(&instructions, (byte *)&instruction, sizeof(instruction)); - } - *ret_size = instructions.used / sizeof(inst_t); - return (inst_t *)instructions.data; -} - -void insts_write_bytecode_file(inst_t *instructions, size_t size, FILE *fp) -{ - darr_t darr = {0}; - darr_init(&darr, 0); - insts_write_bytecode(instructions, size, &darr); - darr_write_file(&darr, fp); - free(darr.data); -} - -inst_t *insts_read_bytecode_file(FILE *fp, size_t *ret) -{ - darr_t darr = darr_read_file(fp); - inst_t *instructions = insts_read_bytecode(&darr, ret); - free(darr.data); - return instructions; -} diff --git a/src/inst.h b/src/inst.h deleted file mode 100644 index 8902757..0000000 --- a/src/inst.h +++ /dev/null @@ -1,156 +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: Instructions and opcodes - */ - -#ifndef INST_H -#define INST_H - -#include <stdio.h> -#include <stdlib.h> - -#include "./base.h" -#include "./darr.h" - -typedef enum -{ - OP_NOOP = 0, - - // Dealing with data and registers - OP_PUSH_BYTE, - OP_PUSH_HWORD, - OP_PUSH_WORD, - - OP_POP_BYTE, - OP_POP_HWORD, - OP_POP_WORD, - - OP_PUSH_REGISTER_BYTE, - OP_PUSH_REGISTER_HWORD, - OP_PUSH_REGISTER_WORD, - - OP_MOV_BYTE, - OP_MOV_HWORD, - OP_MOV_WORD, - - OP_DUP_BYTE, - OP_DUP_HWORD, - OP_DUP_WORD, - - // Boolean operations - OP_NOT_BYTE, - OP_NOT_HWORD, - OP_NOT_WORD, - - OP_OR_BYTE, - OP_OR_HWORD, - OP_OR_WORD, - - OP_AND_BYTE, - OP_AND_HWORD, - OP_AND_WORD, - - OP_XOR_BYTE, - OP_XOR_HWORD, - OP_XOR_WORD, - - OP_EQ_BYTE, - OP_EQ_HWORD, - OP_EQ_WORD, - - // Mathematical operations - OP_PLUS_BYTE, - OP_PLUS_HWORD, - OP_PLUS_WORD, - - // Simple I/O - OP_PRINT_CHAR, - OP_PRINT_BYTE, - OP_PRINT_INT, - OP_PRINT_HWORD, - OP_PRINT_LONG, - OP_PRINT_WORD, - - // Program control flow - OP_JUMP_ABS, - OP_JUMP_STACK, - OP_JUMP_REGISTER, - OP_JUMP_IF_BYTE, - OP_JUMP_IF_HWORD, - OP_JUMP_IF_WORD, - - // Should not be an opcode - NUMBER_OF_OPCODES, - OP_HALT = 0b11111111, // top of the byte is a HALT -} opcode_t; - -const char *opcode_as_cstr(opcode_t); - -#define OPCODE_IS_TYPE(OPCODE, OP_TYPE) \ - (((OPCODE) >= OP_TYPE##_BYTE) && ((OPCODE) <= OP_TYPE##_WORD)) - -#define OPCODE_DATA_TYPE(OPCODE, OP_TYPE) \ - ((OPCODE) == OP_TYPE##_BYTE ? DATA_TYPE_BYTE \ - : ((OPCODE) == OP_TYPE##_HWORD) ? DATA_TYPE_HWORD \ - : DATA_TYPE_WORD) - -typedef struct -{ - opcode_t opcode; - data_t operand; -} inst_t; - -void inst_print(inst_t, FILE *); - -size_t inst_bytecode_size(inst_t); -void inst_write_bytecode(inst_t, darr_t *); -void insts_write_bytecode(inst_t *, size_t, darr_t *); -// Here the dynamic array is a preloaded buffer of bytes, where -// darr.available is the number of overall bytes and used is the -// cursor (where we are in the buffer). -inst_t inst_read_bytecode(darr_t *); -inst_t *insts_read_bytecode(darr_t *, size_t *); - -void insts_write_bytecode_file(inst_t *, size_t, FILE *); -inst_t *insts_read_bytecode_file(FILE *, size_t *); - -#define INST_NOOP ((inst_t){0}) -#define INST_HALT ((inst_t){.opcode = OP_HALT}) - -#define INST_PUSH(TYPE, OP) \ - ((inst_t){.opcode = OP_PUSH_##TYPE, .operand = D##TYPE(OP)}) - -#define INST_MOV(TYPE, OP) \ - ((inst_t){.opcode = OP_MOV_##TYPE, .operand = D##TYPE(OP)}) - -#define INST_POP(TYPE) ((inst_t){.opcode = OP_POP_##TYPE}) - -#define INST_PUSH_REG(TYPE, REG) \ - ((inst_t){.opcode = OP_PUSH_REGISTER_##TYPE, .operand = D##TYPE(REG)}) - -#define INST_DUP(TYPE, OP) \ - ((inst_t){.opcode = OP_DUP_##TYPE, .operand = DWORD(OP)}) - -#define INST_NOT(TYPE) ((inst_t){.opcode = OP_NOT_##TYPE}) -#define INST_OR(TYPE) ((inst_t){.opcode = OP_OR_##TYPE}) -#define INST_AND(TYPE) ((inst_t){.opcode = OP_AND_##TYPE}) -#define INST_XOR(TYPE) ((inst_t){.opcode = OP_XOR_##TYPE}) -#define INST_EQ(TYPE) ((inst_t){.opcode = OP_EQ_##TYPE}) -#define INST_PLUS(TYPE) ((inst_t){.opcode = OP_PLUS_##TYPE}) - -#define INST_JUMP_ABS(OP) \ - ((inst_t){.opcode = OP_JUMP_ABS, .operand = DWORD(OP)}) -#define INST_JUMP_STACK ((inst_t){.opcode = OP_JUMP_STACK}) -#define INST_JUMP_REGISTER ((inst_t){.opcode = OP_JUMP_REGISTER}) -#define INST_JUMP_IF(TYPE, OP) \ - ((inst_t){.opcode = OP_JUMP_IF_##TYPE, .operand = DWORD(OP)}) - -#define INST_PRINT(TYPE) ((inst_t){.opcode = OP_PRINT_##TYPE}) -#endif diff --git a/src/main.c b/src/main.c deleted file mode 100644 index 005b61e..0000000 --- a/src/main.c +++ /dev/null @@ -1,62 +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: Entrypoint to program - */ - -#include <assert.h> -#include <stdio.h> -#include <string.h> - -#include "./inst.h" -#include "./runtime.h" - -int interpret_bytecode(const char *filepath) -{ - FILE *fp = fopen(filepath, "rb"); - size_t number = 0; - inst_t *instructions = insts_read_bytecode_file(fp, &number); - fclose(fp); - - byte stack[256]; - vm_t vm = {0}; - vm_load_stack(&vm, stack, ARR_SIZE(stack)); - vm_load_program(&vm, instructions, number); - err_t err = vm_execute_all(&vm); - - int ret = 0; - if (err) - { - const char *error_str = err_as_cstr(err); - fprintf(stderr, "[ERROR]: %s\n", error_str); - vm_print_all(&vm, stderr); - ret = 255 - err; - } - free(instructions); - return ret; -} - -int assemble_instructions(inst_t *instructions, size_t number, - const char *filepath) -{ - FILE *fp = fopen(filepath, "wb"); - insts_write_bytecode_file(instructions, number, fp); - fclose(fp); - return 0; -} - -int main(int argc, char *argv[]) -{ - const char *filename = "out.bin"; - if (argc >= 2) - filename = argv[1]; - inst_t instructions[] = {INST_HALT}; - assemble_instructions(instructions, ARR_SIZE(instructions), filename); - return interpret_bytecode(filename); -} 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 <assert.h> -#include <inttypes.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#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)); -} diff --git a/src/runtime.h b/src/runtime.h deleted file mode 100644 index 83ffa17..0000000 --- a/src/runtime.h +++ /dev/null @@ -1,165 +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 - */ - -#ifndef RUNTIME_H -#define RUNTIME_H - -#include <stdio.h> -#include <stdlib.h> - -#include "./base.h" -#include "./inst.h" - -typedef enum -{ - ERR_OK = 0, - ERR_STACK_UNDERFLOW, - ERR_STACK_OVERFLOW, - ERR_INVALID_OPCODE, - ERR_INVALID_REGISTER_BYTE, - ERR_INVALID_REGISTER_HWORD, - ERR_INVALID_REGISTER_WORD, - ERR_INVALID_PROGRAM_ADDRESS, - ERR_END_OF_PROGRAM, -} err_t; - -const char *err_as_cstr(err_t); - -#define VM_REGISTERS 8 -typedef struct -{ - struct Registers - { - word reg[VM_REGISTERS]; - } registers; - struct Stack - { - byte *data; - word ptr, max; - } stack; - struct Program - { - inst_t *instructions; - word ptr, max; - } program; -} vm_t; - -#define VM_REG_BYTE(REG) ((REG)&0b11111111) -#define VM_REG_HWORD(REG) ((REG)&0b11111111111111111111111111111111) -#define VM_REG_WORD(REG) ((REG)) - -err_t vm_execute(vm_t *); -err_t vm_execute_all(vm_t *); - -void vm_load_stack(vm_t *, byte *, size_t); -void vm_load_program(vm_t *, inst_t *, size_t); - -// Print routines -#define VM_PRINT_PROGRAM_EXCERPT 5 -void vm_print_registers(vm_t *, FILE *); -void vm_print_stack(vm_t *, FILE *); -void vm_print_program(vm_t *, FILE *); -void vm_print_all(vm_t *, FILE *); - -// Execution routines -err_t vm_jump(vm_t *, word); - -err_t vm_pop_byte(vm_t *, data_t *); -err_t vm_pop_hword(vm_t *, data_t *); -err_t vm_pop_word(vm_t *, data_t *); - -err_t vm_push_byte(vm_t *, data_t); -err_t vm_push_hword(vm_t *, data_t); -err_t vm_push_word(vm_t *, data_t); - -typedef err_t (*push_f)(vm_t *, data_t); -static const push_f PUSH_ROUTINES[] = { - [OP_PUSH_BYTE] = vm_push_byte, - [OP_PUSH_HWORD] = vm_push_hword, - [OP_PUSH_WORD] = vm_push_word, -}; - -err_t vm_push_byte_register(vm_t *, byte); -err_t vm_push_hword_register(vm_t *, byte); -err_t vm_push_word_register(vm_t *, byte); - -err_t vm_mov_byte(vm_t *, byte); -err_t vm_mov_hword(vm_t *, byte); -err_t vm_mov_word(vm_t *, byte); - -typedef err_t (*reg_f)(vm_t *, byte); -static const reg_f REG_ROUTINES[] = { - [OP_PUSH_REGISTER_BYTE] = vm_push_byte_register, - [OP_PUSH_REGISTER_HWORD] = vm_push_hword_register, - [OP_PUSH_REGISTER_WORD] = vm_push_word_register, - [OP_MOV_BYTE] = vm_mov_byte, - [OP_MOV_HWORD] = vm_mov_hword, - [OP_MOV_WORD] = vm_mov_word, -}; - -err_t vm_dup_byte(vm_t *, word); -err_t vm_dup_hword(vm_t *, word); -err_t vm_dup_word(vm_t *, word); - -typedef err_t (*dup_f)(vm_t *, word); -static const dup_f DUP_ROUTINES[] = { - [OP_DUP_BYTE] = vm_dup_byte, - [OP_DUP_HWORD] = vm_dup_hword, - [OP_DUP_WORD] = vm_dup_word, -}; - -err_t vm_not_byte(vm_t *); -err_t vm_not_hword(vm_t *); -err_t vm_not_word(vm_t *); - -err_t vm_or_byte(vm_t *); -err_t vm_or_hword(vm_t *); -err_t vm_or_word(vm_t *); - -err_t vm_and_byte(vm_t *); -err_t vm_and_hword(vm_t *); -err_t vm_and_word(vm_t *); - -err_t vm_xor_byte(vm_t *); -err_t vm_xor_hword(vm_t *); -err_t vm_xor_word(vm_t *); - -err_t vm_eq_byte(vm_t *); -err_t vm_eq_hword(vm_t *); -err_t vm_eq_word(vm_t *); - -err_t vm_plus_byte(vm_t *); -err_t vm_plus_hword(vm_t *); -err_t vm_plus_word(vm_t *); - -typedef err_t (*stack_f)(vm_t *); -static const stack_f STACK_ROUTINES[] = { - [OP_NOT_BYTE] = vm_not_byte, [OP_NOT_HWORD] = vm_not_hword, - [OP_NOT_WORD] = vm_not_word, - - [OP_OR_BYTE] = vm_or_byte, [OP_OR_HWORD] = vm_or_hword, - [OP_OR_WORD] = vm_or_word, - - [OP_AND_BYTE] = vm_and_byte, [OP_AND_HWORD] = vm_and_hword, - [OP_AND_WORD] = vm_and_word, - - [OP_XOR_BYTE] = vm_xor_byte, [OP_XOR_HWORD] = vm_xor_hword, - [OP_XOR_WORD] = vm_xor_word, - - [OP_EQ_BYTE] = vm_eq_byte, [OP_EQ_HWORD] = vm_eq_hword, - [OP_EQ_WORD] = vm_eq_word, - - [OP_PLUS_BYTE] = vm_plus_byte, [OP_PLUS_HWORD] = vm_plus_hword, - [OP_PLUS_WORD] = vm_plus_word, -}; - -#endif |