diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | src/base.h | 8 | ||||
-rw-r--r-- | src/inst.c | 146 | ||||
-rw-r--r-- | src/inst.h | 10 |
4 files changed, 165 insertions, 1 deletions
@@ -7,7 +7,7 @@ OUT=ovm.out SRC=src DIST=build -CODE=$(addprefix $(SRC)/, darr.c runtime.c main.c) +CODE=$(addprefix $(SRC)/, darr.c inst.c runtime.c main.c) OBJECTS=$(CODE:$(SRC)/%.c=$(DIST)/%.o) DEPS=$(OBJECTS:%.o=%.d) @@ -35,6 +35,14 @@ typedef union f64 as_float; } data_t; +typedef enum +{ + DATA_TYPE_NIL = 0, + DATA_TYPE_BYTE = 1, + DATA_TYPE_WORD = 3, + DATA_TYPE_FLOAT = 5, +} data_type_t; + #define DBYTE(BYTE) ((data_t){.as_byte = (BYTE)}) #define DWORD(WORD) ((data_t){.as_word = (WORD)}) #define DFLOAT(FLOAT) ((data_t){.as_float = (FLOAT)}) diff --git a/src/inst.c b/src/inst.c new file mode 100644 index 0000000..49d35d3 --- /dev/null +++ b/src/inst.c @@ -0,0 +1,146 @@ +/* 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 <stdbool.h> +#include <string.h> + +#include "./inst.h" + +data_type_t get_opcode_data_type(opcode_t opcode) +{ + data_type_t type = DATA_TYPE_NIL; + if (OPCODE_IS_TYPE(opcode, OP_TYPE_PUSH)) + type = opcode >> 1; + else if (OPCODE_IS_TYPE(opcode, OP_TYPE_PUSH_REGISTER)) + type = opcode >> 2; + else if (OPCODE_IS_TYPE(opcode, OP_TYPE_POP)) + type = opcode >> 3; + else if (OPCODE_IS_TYPE(opcode, OP_TYPE_MOV)) + type = opcode >> 4; + return type; +} + +size_t inst_bytecode_size(inst_t inst) +{ + size_t size = 1; // for opcode + if (OPCODE_IS_TYPE(inst.opcode, OP_TYPE_PUSH)) + { + if (inst.opcode == OP_PUSH_BYTE) + ++size; + else if (inst.opcode == OP_PUSH_WORD) + size += sizeof(word); + else if (inst.opcode == OP_PUSH_FLOAT) + size += sizeof(f64); + } + else if (OPCODE_IS_TYPE(inst.opcode, OP_TYPE_PUSH_REGISTER)) + ++size; + else if (OPCODE_IS_TYPE(inst.opcode, OP_TYPE_POP)) + { + // No operand or register so leave as is + } + else if (OPCODE_IS_TYPE(inst.opcode, OP_TYPE_MOV)) + { + if (inst.opcode == OP_MOV_BYTE) + ++size; + else if (inst.opcode == OP_MOV_WORD) + size += sizeof(word); + else if (inst.opcode == OP_MOV_FLOAT) + size += sizeof(f64); + // For the register + ++size; + } + return size; +} + +void inst_write_bytecode(inst_t inst, darr_t *darr) +{ + // 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_TYPE_PUSH)) + to_append = (data_type_t)inst.opcode; + else if (OPCODE_IS_TYPE(inst.opcode, OP_TYPE_PUSH_REGISTER) || + OPCODE_IS_TYPE(inst.opcode, OP_TYPE_MOV)) + to_append = DATA_TYPE_BYTE; + + switch (to_append) + { + case DATA_TYPE_NIL: + break; + case DATA_TYPE_BYTE: + darr_append_byte(darr, inst.operand.as_byte); + break; + case DATA_TYPE_WORD: + darr_append_bytes(darr, (byte *)&inst.operand.as_word, sizeof(word)); + break; + case DATA_TYPE_FLOAT: + darr_append_bytes(darr, (byte *)&inst.operand.as_float, sizeof(f64)); + break; + } +} + +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_WORD: + if (darr->used + sizeof(word) >= darr->available) + // TODO: Error (darr has no space left) + return DWORD(0); + word w = 0; + memcpy(&w, darr->data + darr->used, sizeof(w)); + darr->used += sizeof(w); + return DWORD(w); + break; + case DATA_TYPE_FLOAT: + if (darr->used + sizeof(word) >= darr->available) + // TODO: Error (darr has no space left) + return DWORD(0); + f64 f = 0; + memcpy(&f, darr->data + darr->used, sizeof(f)); + darr->used += sizeof(f); + return DFLOAT(f); + break; + } + // TODO: Error (unrecognised type) + return DBYTE(0); +} + +inst_t inst_read_bytecode(darr_t *darr) +{ + if (darr->used >= darr->available) + return (inst_t){0}; + inst_t inst = {0}; + opcode_t opcode = darr->data[darr->used++]; + if (opcode > OP_HALT) + // Translate to NOOP + return inst; + // Read operands + if (OPCODE_IS_TYPE(opcode, OP_TYPE_PUSH)) + inst.operand = read_type_from_darr(darr, get_opcode_data_type(opcode)); + // Read register (as a byte) + if (OPCODE_IS_TYPE(opcode, OP_TYPE_PUSH_REGISTER) || + OPCODE_IS_TYPE(opcode, OP_TYPE_MOV)) + inst.operand = read_type_from_darr(darr, DATA_TYPE_BYTE); + // Otherwise opcode doesn't take operands + + return inst; +} @@ -13,7 +13,10 @@ #ifndef INST_H #define INST_H +#include <stdlib.h> + #include "./base.h" +#include "./darr.h" typedef enum { @@ -57,6 +60,13 @@ typedef struct data_t operand; } inst_t; +size_t inst_bytecode_size(inst_t); +void inst_write_bytecode(inst_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 *); + #define INST_BPUSH(BYTE) \ ((inst_t){.opcode = OP_PUSH_BYTE, .operand = DBYTE(BYTE)}) |