From e9801283e220088bf5765205c58de5f9ec584087 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Kn=C3=BCttel?= Date: Wed, 26 Sep 2018 17:50:12 +0200 Subject: [PATCH] initial interpreter, base and stack --- base.h | 20 ++++++ interpreter/interpreter.c | 145 ++++++++++++++++++++++++++++++++++++++ interpreter/interpreter.h | 79 +++++++++++++++++++++ stack.c | 26 +++++++ stack.h | 14 ++++ 5 files changed, 284 insertions(+) create mode 100644 base.h create mode 100644 interpreter/interpreter.c create mode 100644 interpreter/interpreter.h create mode 100644 stack.c create mode 100644 stack.h diff --git a/base.h b/base.h new file mode 100644 index 0000000..31c918f --- /dev/null +++ b/base.h @@ -0,0 +1,20 @@ +#ifndef bci_base_h__ +#define bci_base_h__ +#include +#include "stack.h" + +#define BCI_CORE_NUM_REG 40 + +// Note that void * state will later be casted to +// machine_state_t. +typedef char (* bci_core_method_t)(uint8_t small_arg + , void * state); + + +#define BCI_CORE_RESULT_RESERVED_BITS 0b11000000 +#define BCI_CORE_RESULT_APPLICATION_BITS 0b00111111 + +#define BCI_CORE_SMALL_ARG_MASK 0b0000000000111111 +#define BCI_CORE_OPCODE_MASK 0b1111111111000000 + +#endif diff --git a/interpreter/interpreter.c b/interpreter/interpreter.c new file mode 100644 index 0000000..5b87998 --- /dev/null +++ b/interpreter/interpreter.c @@ -0,0 +1,145 @@ +#include + +#include "interpreter.h" + + + +machine_state_t machine_state_t_new( void * cache_function_data + , size_t cache_size + , size_t (* cache_block)(uint16_t * cache + , uint16_t offset + , size_t cache_size + , void * cache_function_data) + , dispatch_tree_t method_dispatcher) +{ + machine_state_t state = malloc(sizeof(struct machine_state_s)); + if(!state) + { + return NULL; + } + uint16_t * cache = calloc(sizeof(uint16_t), cache_size); + if(!cache) + { + free(state); + return NULL; + } + + state->cache = cache; + state->cache_block = cache_block; + state->cache_size = cache_size; + state->cache_loaded = 0; + state->cache_function_data = cache_function_data; + + state->program_counter = 0; + state->status_reg = 0; + state->shutdown_reg = 0; + int i; + for(i = 0; i < BCI_CORE_NUM_REG; i++) + { + state->data_reg[BCI_CORE_NUM_REG] = 0; + } + state->stack = NULL; + + state->method_dispatcher = method_dispatcher; + + return state; +} + +void machine_state_t_del(machine_state_t state) +{ + free(state->cache); + dispatch_tree_t_del(state->method_dispatcher); + free(state); +} + + +char update_cache(machine_state_t state) +{ + // Calculate a clever offset for caching. + uint16_t cache_offset = state->program_counter; + if((cache_offset + state->cache_size) + > state->program_size) + { + if(state->program_size < state->cache_size) + { + cache_offset = 0; + } + else + { + cache_offset = state->program_size - state->cache_size; + } + } + size_t cached = state->cache_block(state->cache + , cache_offset + , state->cache_size + , state->cache_function_data); + + if(cached != state->cache_size) + { + return 1; + } + return 0; +} + +char machine_state_t_exec_cycle(machine_state_t state) +{ + uint16_t opcode, arg; + + // This would be bad. + if(state->program_counter >= state->program_size) + { + return 0b01000000 | 1; + } + + // Calculate the in-cache offset of the program counter. + int32_t offset = state->program_counter - state->cache_offset; + + // Check if we need to update the cache. + if( offset < 0 + || !(state->cache_loaded) + // Note: + // +1 for cache starting with 0 + // +1 for the argument + || (offset + 2) > state->cache_size) + { + if(update_cache(state)) + { + return 0b01000000 | 2; + } + // Re-calculate the in-cache offset + offset = state->program_counter - state->cache_offset; + } + + // Check if the argument is cached. + if(offset + 2 > state->cache_size) + { + // Because we have cached the program memory above + // there is no way that we can include the argument + // now. There is something wrong. + return 0b01000000 | 3; + } + + opcode = state->cache[offset]; + arg = state->cache[offset + 1]; + + bci_core_method_t method = dispatch_tree_t_dispatch( + state->method_dispatcher + , opcode & BCI_CORE_OPCODE_MASK); + if(!method) + { + return 0b10000000 | 1; + } + + return BCI_CORE_RESULT_APPLICATION_BITS + & method(arg + , opcode & BCI_CORE_SMALL_ARG_MASK + , state); + +} + + +char machine_state_t_default_afterexec(machine_state_t state, uint8_t argc) +{ + state->program_counter += 1 + argc; + return 0; +} diff --git a/interpreter/interpreter.h b/interpreter/interpreter.h new file mode 100644 index 0000000..b9213ed --- /dev/null +++ b/interpreter/interpreter.h @@ -0,0 +1,79 @@ +#ifndef bci_interpreter_h__ +#define bci_interpreter_h__ + +#include +#include "base.h" +#include "method_dispatcher.h" +#include "stack.h" + +typedef machine_state_s +{ + // The size of the program in 16-bit words + uint16_t program_size; + void * cache_function_data; + + size_t cache_size; + uint16_t cache_offset; + uint16_t * cache; + size_t (* cache_block)(uint16_t * cache + , uint16_t offset + , size_t cache_size + , void * cache_function_data); + char cache_loaded; + + uint16_t program_counter; + uint16_t status_reg; + // Any non-zero value in this register will + // break the program execution. + uint16_t shutdown_reg; + uint16_t data_reg[BCI_CORE_NUM_REG]; + bci_stack_t stack; + + + dispatch_tree_t method_dispatcher; + +} * machine_state_t; + +machine_state_t machine_state_t_new( void * cache_function_data + , size_t cache_size + , size_t (* cache_block)(uint16_t * cache + , uint16_t offset + , size_t cache_size + , void * cache_function_data) + , dispatch_tree_t method_dispatcher); + +void machine_state_t_del(machine_state_t state); + +/* + * This function essentially executes one cycle of the machine execution. + * It will fetch the opcode and argument, dispatch the method and execute it. + * + * Also this will update the cache if needed. + * + * Possible return codes: + * + * - 0bxyXXXXXX + * Error while executing the cycle. + * - 0b01XXXXXX + * Error in the internal memory management (i.e. program counter out + * of bounds) + * - 0b01000000 | 1: program_counter > program_size + * - 0b01000000 | 2: failed to cache new data + * - 0b01000000 | 3: argument is out of program bounds + * + * - 0b10XXXXXX + * Error while dispatching the method + * - 0b10000000 | 1: unknown opcode + * + * - 0b00XXXXXX + * XXXXXX is the return value of the bci_core_method_t method. + * */ +char machine_state_t_exec_cycle(machine_state_t state); + +/* + * Do the default updates on the machine state. + * This will be basically increasing the program counter. + * */ +char machine_state_t_default_afterexec(machine_state_t state, uint8_t argc); + +#endif diff --git a/stack.c b/stack.c new file mode 100644 index 0000000..706ae7d --- /dev/null +++ b/stack.c @@ -0,0 +1,26 @@ +#include +#include "stack.h" + +char bci_stack_t_push(bci_stack_t * stack, uint16_t value) +{ + bci_stack_t node = malloc(sizeof(struct bci_stack_s)); + if(!node) + { + return 1; + } + node->next = *stack; + node->value = value; + stack = &node; + return 0; +} +char bci_stack_t_pop(bci_stack_t * stack, uint16_t * result) +{ + if(!*stack) + { + return 1; + } + *result = (*stack)->value; + *stack = (*stack)->next; + return 0; +} + diff --git a/stack.h b/stack.h new file mode 100644 index 0000000..894790e --- /dev/null +++ b/stack.h @@ -0,0 +1,14 @@ +#ifndef bci_stack_h__ +#define bci_stack_h__ + +#include + +typedef struct bci_stack_s +{ + struct bci_stack_s * next; + uint16_t value; +} * bci_stack_t; + +char bci_stack_t_push(bci_stack_t * stack, uint16_t value); +char bci_stack_t_pop(bci_stack_t * stack, uint16_t * result); +#endif