diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..73f7ffb --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +CC=gcc +objects= stack.o method_dispatcher/method_dispatcher.o interpreter/core_methods.o interpreter/interpreter.o + +all: $(objects) test + +CFLAG= -c -O -o + +%.o: %.c + $(CC) $(CFLAG) $@ $< + +.PHONY: test + +test: + cd test && make + cd method_dispatcher/test && make + cd interpreter/test && make + +clean: + - rm $(objects) 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/core_methods.c b/interpreter/core_methods.c new file mode 100644 index 0000000..b7c26a2 --- /dev/null +++ b/interpreter/core_methods.c @@ -0,0 +1,370 @@ +#include "core_methods.h" + +#define BCI_CORE_REGISTER_CHECK(var) if(var > BCI_CORE_NUM_REG) return 1 + +// #1 -> $0 +char bci_cm_ldi(uint8_t small_arg, machine_state_t state) +{ + BCI_CORE_REGISTER_CHECK(small_arg); + + uint16_t arg; + char res; + res = machine_state_t_get_word(state, state->program_counter + 1, &arg); + if(res) + { + return res; + } + + state->data_reg[small_arg] = arg; + + return machine_state_t_default_afterexec(state, 1); +} + +// @($1) -> $0 +char bci_cm_ld(uint8_t small_arg, machine_state_t state) +{ + BCI_CORE_REGISTER_CHECK(small_arg); + + uint16_t arg; + char res; + res = machine_state_t_get_word(state, state->program_counter + 1, &arg); + if(res) + { + return res; + } + + BCI_CORE_REGISTER_CHECK(arg); + arg = state->data_reg[arg]; + + res = machine_state_t_get_mem(state, arg, &arg); + if(res) + { + return res; + } + state->data_reg[small_arg] = arg; + + return machine_state_t_default_afterexec(state, 1); +} + +// $0 -> @($1) +char bci_cm_st(uint8_t small_arg, machine_state_t state) +{ + BCI_CORE_REGISTER_CHECK(small_arg); + char res; + + uint16_t arg; + res = machine_state_t_get_word(state, state->program_counter + 1, &arg); + if(res) + { + return res; + } + + BCI_CORE_REGISTER_CHECK(arg); + arg = state->data_reg[arg]; + + res = machine_state_t_set_mem(state, arg, state->data_reg[small_arg]); + if(res) + { + return res; + } + + return machine_state_t_default_afterexec(state, 1); +} + +// $0 + 1 -> $0 +char bci_cm_inc(uint8_t small_arg, machine_state_t state) +{ + int32_t result; + BCI_CORE_REGISTER_CHECK(small_arg); + result = state->data_reg[small_arg] + 1; + state->data_reg[small_arg] = result & 0xffff; + state->status_reg = result >> 16; + return machine_state_t_default_afterexec(state, 0); +} + +// $0 - 1 -> $0 +char bci_cm_dec(uint8_t small_arg, machine_state_t state) +{ + BCI_CORE_REGISTER_CHECK(small_arg); + int32_t result = state->data_reg[small_arg] - 1; + state->data_reg[small_arg] = result & 0xffff; + state->status_reg = result >> 16; + return machine_state_t_default_afterexec(state, 0); +} + +// $0 + $1 -> $0 +char bci_cm_add(uint8_t small_arg, machine_state_t state) +{ + BCI_CORE_REGISTER_CHECK(small_arg); + uint16_t arg; + char res; + res = machine_state_t_get_word(state, state->program_counter + 1, &arg); + if(res) + { + return res; + } + + BCI_CORE_REGISTER_CHECK(arg); + int32_t result = state->data_reg[small_arg] + state->data_reg[arg]; + state->data_reg[small_arg] = result & 0xffff; + state->status_reg = result >> 16; + + return machine_state_t_default_afterexec(state, 1); +} + + +// $0 - $1 -> $0 +char bci_cm_sub(uint8_t small_arg, machine_state_t state) +{ + BCI_CORE_REGISTER_CHECK(small_arg); + uint16_t arg; + char res; + res = machine_state_t_get_word(state, state->program_counter + 1, &arg); + if(res) + { + return res; + } + + BCI_CORE_REGISTER_CHECK(arg); + int32_t result = state->data_reg[small_arg] - state->data_reg[arg]; + + state->data_reg[small_arg] = result & 0xffff; + state->status_reg = result >> 16; + + return machine_state_t_default_afterexec(state, 1); +} + + +// $0 * $1 -> $0 +char bci_cm_mul(uint8_t small_arg, machine_state_t state) +{ + BCI_CORE_REGISTER_CHECK(small_arg); + uint16_t arg; + char res; + res = machine_state_t_get_word(state, state->program_counter + 1, &arg); + if(res) + { + return res; + } + + BCI_CORE_REGISTER_CHECK(arg); + int32_t result = state->data_reg[small_arg] * state->data_reg[arg]; + state->data_reg[small_arg] = result & 0xffff; + state->status_reg = result >> 16; + return machine_state_t_default_afterexec(state, 1); +} + +// $0 / $1 -> $0 +char bci_cm_div(uint8_t small_arg, machine_state_t state) +{ + BCI_CORE_REGISTER_CHECK(small_arg); + uint16_t arg; + char res; + res = machine_state_t_get_word(state, state->program_counter + 1, &arg); + if(res) + { + return res; + } + + BCI_CORE_REGISTER_CHECK(arg); + int32_t result = state->data_reg[small_arg] / state->data_reg[arg]; + state->data_reg[small_arg] = result & 0xffff; + state->status_reg = result >> 16; + return machine_state_t_default_afterexec(state, 1); +} + + +// if($0 > 0): 1 -> $st_reg +// else: 0 -> $st_reg +char bci_cm_gt(uint8_t small_arg, machine_state_t state) +{ + BCI_CORE_REGISTER_CHECK(small_arg); + + if(state->data_reg[small_arg] > 0) + { + state->status_reg = 1; + } + else + { + state->status_reg = 0; + } + return machine_state_t_default_afterexec(state, 0); +} + +// if($0 >= 0): 1 -> $st_reg +// else: 0 -> $st_reg +char bci_cm_ge(uint8_t small_arg, machine_state_t state) +{ + BCI_CORE_REGISTER_CHECK(small_arg); + + if(state->data_reg[small_arg] >= 0) + { + state->status_reg = 1; + } + else + { + state->status_reg = 0; + } + return machine_state_t_default_afterexec(state, 0); +} +// if($0 < 0): 1 -> $st_reg +// else: 0 -> $st_reg +char bci_cm_lt(uint8_t small_arg, machine_state_t state) +{ + BCI_CORE_REGISTER_CHECK(small_arg); + + if(state->data_reg[small_arg] < 0) + { + state->status_reg = 1; + } + else + { + state->status_reg = 0; + } + return machine_state_t_default_afterexec(state, 0); +} +// if($0 <= 0): 1 -> $st_reg +// else: 0 -> $st_reg +char bci_cm_le(uint8_t small_arg, machine_state_t state) +{ + BCI_CORE_REGISTER_CHECK(small_arg); + + if(state->data_reg[small_arg] <= 0) + { + state->status_reg = 1; + } + else + { + state->status_reg = 0; + } + return machine_state_t_default_afterexec(state, 0); +} +// if($0 == 0): 1 -> $st_reg +// else: 0 -> $st_reg +char bci_cm_eq(uint8_t small_arg, machine_state_t state) +{ + BCI_CORE_REGISTER_CHECK(small_arg); + + if(state->data_reg[small_arg] == 0) + { + state->status_reg = 1; + } + else + { + state->status_reg = 0; + } + return machine_state_t_default_afterexec(state, 0); +} + +// $0 -> $pc; 0 -> $st_reg +char bci_cm_jmp(uint8_t small_arg, machine_state_t state) +{ + BCI_CORE_REGISTER_CHECK(small_arg); + uint16_t arg = state->data_reg[arg]; + + state->status_reg = 0; + state->program_counter = arg; + return 0; +} + +// if($st_reg): $0 -> $pc; 0 -> $st_reg +// else: do nothing +char bci_cm_cjmp(uint8_t small_arg, machine_state_t state) +{ + uint16_t arg = small_arg; + BCI_CORE_REGISTER_CHECK(small_arg); +#ifdef DEBUG + printf("MSG: cjmp: arg=0x%x (=0x%x)\n", arg, state->data_reg[arg]); +#endif + arg = state->data_reg[arg]; + + if(state->status_reg) + { + state->program_counter = arg; + state->status_reg = 0; + return 0; + } + + return machine_state_t_default_afterexec(state, 0); +} +// $pc -> @stack +// $0 -> $pc +char bci_cm_call(uint8_t small_arg, machine_state_t state) +{ + uint16_t arg; + BCI_CORE_REGISTER_CHECK(small_arg); + arg = state->data_reg[arg]; + + char res = bci_stack_t_push(&(state->stack), state->program_counter); + if(res) + { + return res; + } + state->program_counter = arg; + return 0; +} +// if($st_reg): $pc -> @stack; $0 -> $pc; 0 -> $st_reg +// else: do nothing +char bci_cm_ccall(uint8_t small_arg, machine_state_t state) +{ + uint16_t arg; + char res; + BCI_CORE_REGISTER_CHECK(small_arg); + arg = state->data_reg[small_arg]; + + if(state->status_reg) + { + state->program_counter = arg; + res = bci_stack_t_push(&(state->stack), state->program_counter); + if(res) + { + return res; + } + return 0; + } + + state->status_reg = 0; + return machine_state_t_default_afterexec(state, 0); +} +// @stack -> $pc +char bci_cm_ret(uint8_t small_arg, machine_state_t state) +{ + uint16_t result; + char res = bci_stack_t_pop(&(state->stack), &result); + if(res) + { + return res; + } + + state->program_counter = result; + return 0; +} + +// 0 -> $st_reg +char bci_cm_cl(uint8_t small_arg, machine_state_t state) +{ + state->status_reg = 0; + return machine_state_t_default_afterexec(state, 0); +} + +// 1 -> $su_reg +char bci_cm_stop(uint8_t small_arg, machine_state_t state) +{ + state->shutdown_reg = 1; + return machine_state_t_default_afterexec(state, 0); +} + +// if($st_reg): 0 -> $st_reg +// else: 1 -> $st_reg +char bci_cm_not(uint8_t small_arg, machine_state_t state) +{ + if(state->status_reg) + { + state->status_reg = 0; + } + else + { + state->status_reg = 1; + } + return machine_state_t_default_afterexec(state, 0); +} diff --git a/interpreter/core_methods.h b/interpreter/core_methods.h new file mode 100644 index 0000000..e36892a --- /dev/null +++ b/interpreter/core_methods.h @@ -0,0 +1,114 @@ +#ifndef bci_core_methods_h__ +#define bci_core_methods_h__ +#include "interpreter.h" + + +/* + * This library brings a set of essential core methods. + * */ + + +/* + * Naming conventions: + * + * x = 0, 1, 2, 3...: arguments. + * 0: small_arg (6 bit) + * 1, 2, ...: 16 bit optional arguments + * + * $x: value of data register x + * @x: value of memory word x + * #x: value of argument x + * 0, 1, 2, ...: integer 0, 1, 2, ... + * + * -> write result in register/word on the right + * + * st_reg: status register + * su_reg: shutdown register + * pc: program counter + * + * stack: the builtin stack + * */ + +// #1 -> $0 +char bci_cm_ldi(uint8_t small_arg, machine_state_t state); + +// @($1) -> $0 +char bci_cm_ld(uint8_t small_arg, machine_state_t state); + +// $0 -> @($1) +char bci_cm_st(uint8_t small_arg, machine_state_t state); + + +// NOTES on inc, dec, add, sub, mul, div: +// +// These methods will store the overflow-word in $st_reg. + + +// $0 + 1 -> $0 +char bci_cm_inc(uint8_t small_arg, machine_state_t state); + +// $0 - 1 -> $0 +char bci_cm_dec(uint8_t small_arg, machine_state_t state); + +// $0 + $1 -> $0 +char bci_cm_add(uint8_t small_arg, machine_state_t state); + +// $0 - $1 -> $0 +char bci_cm_sub(uint8_t small_arg, machine_state_t state); + +// $0 * $1 -> $0 +char bci_cm_mul(uint8_t small_arg, machine_state_t state); + +// $0 / $1 -> $0 +char bci_cm_div(uint8_t small_arg, machine_state_t state); + +// if($0 > 0): 1 -> $st_reg +// else: 0 -> $st_reg +char bci_cm_gt(uint8_t small_arg, machine_state_t state); + +// if($0 >= 0): 1 -> $st_reg +// else: 0 -> $st_reg +char bci_cm_ge(uint8_t small_arg, machine_state_t state); + +// if($0 < 0): 1 -> $st_reg +// else: 0 -> $st_reg +char bci_cm_lt(uint8_t small_arg, machine_state_t state); + +// if($0 <= 0): 1 -> $st_reg +// else: 0 -> $st_reg +char bci_cm_le(uint8_t small_arg, machine_state_t state); + +// if($0 == 0): 1 -> $st_reg +// else: 0 -> $st_reg +char bci_cm_eq(uint8_t small_arg, machine_state_t state); + +// if($st_reg): 0 -> $st_reg +// else: 1 -> $st_reg +char bci_cm_not(uint8_t small_arg, machine_state_t state); + +// $1 -> $pc +char bci_cm_jmp(uint8_t small_arg, machine_state_t state); + +// if($st_reg): $1 -> $pc +// else: do nothing +char bci_cm_cjmp(uint8_t small_arg, machine_state_t state); + +// $pc -> @stack +// $0 -> $pc +char bci_cm_call(uint8_t small_arg, machine_state_t state); + +// if($st_reg): $pc -> @stack; $0 -> $pc +// else: do nothing +char bci_cm_ccall(uint8_t small_arg, machine_state_t state); + +// @stack -> $pc +char bci_cm_ret(uint8_t small_arg, machine_state_t state); + +// 0 -> $st_reg +char bci_cm_cl(uint8_t small_arg, machine_state_t state); + +// 1 -> $su_reg +char bci_cm_stop(uint8_t small_arg, machine_state_t state); + + +#endif diff --git a/interpreter/interpreter.c b/interpreter/interpreter.c new file mode 100644 index 0000000..4089fdd --- /dev/null +++ b/interpreter/interpreter.c @@ -0,0 +1,217 @@ +#include +#include + +#include "interpreter.h" + + + +machine_state_t machine_state_t_new( void * cache_function_data + , size_t program_size + , size_t cache_size + , size_t memory_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; + } + uint16_t * memory = calloc(sizeof(uint16_t), memory_size); + if(!memory) + { + free(state); + free(cache); + return NULL; + } + + + state->program_size = program_size; + state->cache = cache; + state->cache_block = cache_block; + state->cache_size = cache_size; + state->cache_loaded = 0; + state->cache_offset = 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[i] = 0; + } + state->stack = NULL; + + state->memory = memory; + state->memory_size = memory_size; + + state->method_dispatcher = method_dispatcher; + + return state; +} + +void machine_state_t_del(machine_state_t state) +{ + free(state->cache); + free(state->memory); + dispatch_tree_t_del(state->method_dispatcher); + bci_stack_t_del(&(state->stack)); + free(state); +} + + +char update_cache(machine_state_t state, uint16_t offset) +{ + // Calculate a clever offset for caching. + uint16_t cache_offset = offset; + 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) + { + #ifdef DEBUG + printf("ERROR: should have cached %zu words, but got %zu.\n", state->cache_size, cached); + #endif + return 1; + } + state->cache_offset = cache_offset; + state->cache_loaded = 1; + return 0; +} + +char machine_state_t_get_word(machine_state_t state, uint16_t offset, uint16_t * result) +{ + + // This would be bad. + if(offset >= state->program_size) + { + return 0b01000000 | 1; + } + + // Calculate the in-cache offset of the desired offset. + int32_t in_cache_offset = offset - state->cache_offset; + + // Check if we need to update the cache. + if( in_cache_offset < 0 + || !(state->cache_loaded) + ) + { + if(update_cache(state, offset)) + { + #ifdef DEBUG + printf("ERROR: failed to update cache\n"); + #endif + state->error = 0b01000000 | 2; + return 0b01000000 | 2; + } + // Re-calculate the in-cache offset + in_cache_offset = offset - state->cache_offset; + } + + *result = state->cache[in_cache_offset]; + return 0; + +} + +char machine_state_t_exec_cycle(machine_state_t state) +{ + char res = 0; + uint16_t opcode; + res = machine_state_t_get_word(state, state->program_counter, &opcode); + + if(res) + { + return res; + } + +#ifdef DEBUG + printf("MSG: opcode = 0x%x, small_arg = 0x%x\n" + , opcode & BCI_CORE_OPCODE_MASK + , opcode & BCI_CORE_SMALL_ARG_MASK); +#endif + + bci_core_method_t method = dispatch_tree_t_dispatch( + state->method_dispatcher + , opcode & BCI_CORE_OPCODE_MASK); + if(!method) + { + state->error = res; + return 0b10000000 | 1; + } + + return BCI_CORE_RESULT_APPLICATION_BITS + & method( 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; +} + + +char machine_state_t_get_mem(machine_state_t state, uint16_t offset, uint16_t * result) +{ + if(offset > state->memory_size) + { + state->error = 0b01000000 | 15; + return 0b01000000 | 15; + } + + *result = state->memory[offset]; + return 0; +} +char machine_state_t_set_mem(machine_state_t state, uint16_t offset, uint16_t data) +{ + if(offset > state->memory_size) + { + state->error = 0b01000000 | 15; + return 0b01000000 | 15; + } + + state->memory[offset] = data; + return 0; +} + + + + +char machine_state_t_exec(machine_state_t state) +{ + char status = 0; + while((!status) && (!state->shutdown_reg)) + { + status = machine_state_t_exec_cycle(state); + } + state->error = status; + return status; +} diff --git a/interpreter/interpreter.h b/interpreter/interpreter.h new file mode 100644 index 0000000..4829148 --- /dev/null +++ b/interpreter/interpreter.h @@ -0,0 +1,121 @@ +#ifndef bci_interpreter_h__ +#define bci_interpreter_h__ + +#include +#include "../base.h" +#include "../method_dispatcher/method_dispatcher.h" +#include "../stack.h" + +typedef struct 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; + + size_t memory_size; + uint16_t * memory; + + + dispatch_tree_t method_dispatcher; + + // Possible Error Codes: + // 0b01xxxxxx: memory related internal error + // 0b10xxxxxx: core method related internal error + // 0b00xxxxxx: application related internal error + // + // + // 0b01000000 | 1: Program offset out of bounds + // 0b01000000 | 2: Failed to cache new data + // 0b01000000 | 15: Memory offset out of bounds + // + // + // 0b10000000 | 1: Unknown opcode + char error; + +} * machine_state_t; + +machine_state_t machine_state_t_new( void * cache_function_data + , size_t program_size + , size_t cache_size + , size_t memory_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 or argument offset > program_size + * - 0b01000000 | 2: failed to cache new data + * + * - 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); + + +/* + * Runs ``machine_state_t_exec_cycle`` until either this function + * returns a non-zero value or a non-zero value is stored in + * ``state->shutdown_reg``. + * Sets ``state->error`` to the last return value of ``machine_state_t_exec_cycle`` + * and returns it. + * */ +char machine_state_t_exec(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); + +/* + * Get the word at offset in the program memory. + * Returns 0 on success, a non-zero value otherwise. + * + * It will set state->error appropiately. + * */ +char machine_state_t_get_word(machine_state_t state, uint16_t offset, uint16_t * result); + +/* + * Get the word at offset in the RAM-alike memory. + * Works like machine_state_t_get_word. + * */ +char machine_state_t_get_mem(machine_state_t state, uint16_t offset, uint16_t * result); +char machine_state_t_set_mem(machine_state_t state, uint16_t offset, uint16_t data); +#endif diff --git a/interpreter/test/Makefile b/interpreter/test/Makefile new file mode 100644 index 0000000..6703b38 --- /dev/null +++ b/interpreter/test/Makefile @@ -0,0 +1,14 @@ +CC=gcc + +all: test_interpreter + +interpreter: clean + $(CC) -g ../../stack.c ../interpreter.c ../core_methods.c ../../method_dispatcher/method_dispatcher.c test_interpreter.c -o interpreter + +test_interpreter: interpreter + valgrind ./interpreter + + + +clean: + diff --git a/interpreter/test/test_interpreter.c b/interpreter/test/test_interpreter.c new file mode 100644 index 0000000..62db833 --- /dev/null +++ b/interpreter/test/test_interpreter.c @@ -0,0 +1,69 @@ +#include +#include +#include +#include "../interpreter.h" +#include "../core_methods.h" +#include "../../method_dispatcher/method_dispatcher.h" + +size_t copy_cache(uint16_t * cache + , uint16_t offset + , size_t cache_size + , void * cache_function_data); + +int main(void) +{ + dispatch_tree_t tree = dispatch_tree_t_new(); + dispatch_tree_autoinserter_t inserter = dispatch_tree_autoinserter_t_new(tree); + uint16_t ldi = dispatch_tree_autoinserter_t_insert(inserter, (bci_core_method_t) bci_cm_ldi); + uint16_t add = dispatch_tree_autoinserter_t_insert(inserter, (bci_core_method_t) bci_cm_add); + + uint16_t * program = calloc(sizeof(uint16_t), 20); + program[0] = ldi; + program[1] = 0xfefe; + program[2] = ldi | 1; + program[3] = 2; + program[4] = ldi | 2; + program[5] = 3; + program[6] = add | 1; + program[7] = 2; + + machine_state_t state = machine_state_t_new( + program + , 8 + , 20 + , 20 + , copy_cache + , tree); + + machine_state_t_exec_cycle(state); + machine_state_t_exec_cycle(state); + machine_state_t_exec_cycle(state); + machine_state_t_exec_cycle(state); + printf("data_reg[0]: %x\n", state->data_reg[0]); + assert(state->data_reg[0] == 0xfefe); + printf("data_reg[1]: %x\n", state->data_reg[1]); + assert(state->data_reg[1] == 5); + printf("data_reg[2]: %x\n", state->data_reg[2]); + assert(state->data_reg[2] == 3); + printf("program_counter: %d\n", state->program_counter); + assert(state->program_counter == 8); + dispatch_tree_autoinserter_t_del(inserter); + machine_state_t_del(state); + free(program); + return 0; +} + +size_t copy_cache(uint16_t * cache + , uint16_t offset + , size_t cache_size + , void * cache_function_data) +{ + size_t i; + for(i = 0; i < cache_size; i++) + { + printf("\tCP [%zd]->[%zd]: %x\n", i + offset, i, ((uint16_t *) cache_function_data)[i + offset]); + cache[i] = ((uint16_t *) cache_function_data)[i + offset]; + } + return i; +} + diff --git a/method_dispatcher/method_dispatcher.c b/method_dispatcher/method_dispatcher.c new file mode 100644 index 0000000..e26c553 --- /dev/null +++ b/method_dispatcher/method_dispatcher.c @@ -0,0 +1,158 @@ +#include +#include "method_dispatcher.h" + + +dispatch_tree_t dispatch_tree_t_new(void) +{ + dispatch_tree_t tree = malloc(sizeof(struct dispatch_tree_s)); + tree->root = NULL; + return tree; +} + +void dispatch_tree_t_del(dispatch_tree_t tree) +{ + dispatch_tree_node_t_del(tree->root); + free(tree); +} + +dispatch_tree_node_t dispatch_tree_node_t_new(uint16_t word + , bci_core_method_t method) +{ + dispatch_tree_node_t node = malloc(sizeof(struct dispatch_tree_node_s)); + node->higher = NULL; + node->lower = 0; + node->method = method; + node->word = word; + return node; +} + +void dispatch_tree_node_t_del(dispatch_tree_node_t node) +{ + if(node->higher) + { + dispatch_tree_node_t_del(node->higher); + } + if(node->lower) + { + dispatch_tree_node_t_del(node->lower); + } + free(node); +} + +char dispatch_tree_t_insert(dispatch_tree_t tree + , uint16_t word + , bci_core_method_t method) +{ + dispatch_tree_node_t new_node = dispatch_tree_node_t_new(word, method); + if(!new_node) + { + return 1; + } + // explicitly handle empty tree + if(!tree->root) + { + tree->root = new_node; + return 0; + } + + // non-empty tree + dispatch_tree_node_t this_node = tree->root; + dispatch_tree_node_t parent_node = NULL; + // search for place + do + { + parent_node = this_node; + if(parent_node->word < word) + { + this_node = parent_node->higher; + continue; + } + if(parent_node->word > word) + { + this_node = parent_node->lower; + continue; + } + if(parent_node->word == word) + { + // we already have that key. + return 2; + } + + } + while(this_node); + if(parent_node->word < word) + { + parent_node->higher = new_node; + } + if(parent_node->word > word) + { + parent_node->lower = new_node; + } + + return 0; + +} + + +bci_core_method_t dispatch_tree_t_dispatch(dispatch_tree_t tree + , uint16_t word) +{ + dispatch_tree_node_t this_node = tree->root; + while(this_node) + { + if(this_node->word < word) + { + this_node = this_node->higher; + continue; + } + if(this_node->word > word) + { + this_node = this_node->lower; + continue; + } + if(this_node->word == word) + { + return this_node->method; + } + } + return NULL; +} + +dispatch_tree_autoinserter_t dispatch_tree_autoinserter_t_new(dispatch_tree_t tree) +{ + dispatch_tree_autoinserter_t autoinserter = malloc( + sizeof(struct dispatch_tree_autoinserter_s)); + if(!autoinserter) + { + return NULL; + } + autoinserter->mayor = 2; + autoinserter->minor = 1; + autoinserter->tree = tree; + return autoinserter; +} + +void dispatch_tree_autoinserter_t_del(dispatch_tree_autoinserter_t autoinserter) +{ + free(autoinserter); +} + + +uint16_t dispatch_tree_autoinserter_t_insert( + dispatch_tree_autoinserter_t autoinserter + , bci_core_method_t method) +{ + uint16_t opcode = 0b1111111111; + opcode *= autoinserter->minor; + opcode /= autoinserter->mayor; + opcode <<= 6; + + dispatch_tree_t_insert(autoinserter->tree, opcode, method); + + autoinserter->minor = (autoinserter->minor + 2) % autoinserter->mayor; + if(autoinserter->minor == 1) + { + autoinserter->mayor *= 2; + } + return opcode; +} diff --git a/method_dispatcher/method_dispatcher.h b/method_dispatcher/method_dispatcher.h new file mode 100644 index 0000000..6b6dac9 --- /dev/null +++ b/method_dispatcher/method_dispatcher.h @@ -0,0 +1,87 @@ +#ifndef bci_method_dispatcher_h__ +#define bci_method_dispatcher_h__ + +#include +#include "../base.h" + +typedef struct dispatch_tree_node_s +{ + struct dispatch_tree_node_s * higher; + struct dispatch_tree_node_s * lower; + uint16_t word; + bci_core_method_t method; +} * dispatch_tree_node_t; + +typedef struct dispatch_tree_s +{ + dispatch_tree_node_t root; +} * dispatch_tree_t; + +dispatch_tree_t dispatch_tree_t_new(void); +/* + * This deletes the tree and will invoke dispatch_tree_node_t_del on + * tree->root (which will delete all the other nodes recursively). + * If you want to keep nodes you have to explicityl remove the reference to + * them by setting them to NULL. + * */ +void dispatch_tree_t_del(dispatch_tree_t tree); + + +dispatch_tree_node_t dispatch_tree_node_t_new(uint16_t word + , bci_core_method_t method); +/* + * This deletes the node an will invoke dispatch_tree_node_t_del on + * node->higher and node->lower. If you want to keep those nodes + * you have to explicitly clear the reference by setting them to NULL. + * */ +void dispatch_tree_node_t_del(dispatch_tree_node_t node); + + +/* + * Insert a new key-value-pair into the tree. + * + * Return codes: + * 0: OK + * 1: out of memory + * 2: key already in tree + * */ +char dispatch_tree_t_insert(dispatch_tree_t tree + , uint16_t word + , bci_core_method_t method); + + +bci_core_method_t dispatch_tree_t_dispatch(dispatch_tree_t tree + , uint16_t word); + + + +typedef struct dispatch_tree_autoinserter_s +{ + uint16_t mayor; + uint16_t minor; + dispatch_tree_t tree; +} * dispatch_tree_autoinserter_t; + +dispatch_tree_autoinserter_t dispatch_tree_autoinserter_t_new(dispatch_tree_t tree); +void dispatch_tree_autoinserter_t_del(dispatch_tree_autoinserter_t autoinserter); + +/* + * This function inserts a new method into the tree and automatically + * chooses the optimal word to prevent degeneration. + * + * NOTE: + * When inserting mutliple methods this function acts deterministic, + * IF AND ONLY IF the order of the methods is not changed. + * + * The assembler will bring an analogous function that allows one + * to determine the opcodes of the methods. + * + * NOTE: + * This algorithm will not work for more that 1023 registered opcodes. + * This limit is however considered acceptable. + * + * */ +uint16_t dispatch_tree_autoinserter_t_insert( + dispatch_tree_autoinserter_t autoinserter + , bci_core_method_t method); +#endif diff --git a/method_dispatcher/test/Makefile b/method_dispatcher/test/Makefile new file mode 100644 index 0000000..c9fc7c9 --- /dev/null +++ b/method_dispatcher/test/Makefile @@ -0,0 +1,14 @@ +CC=gcc + +all: test_method_dispatcher + +method_dispatcher: clean + $(CC) -g ../method_dispatcher.c test_method_dispatcher.c -o method_dispatcher + +test_method_dispatcher: method_dispatcher + valgrind ./method_dispatcher + + + +clean: + -rm method_dispatcher diff --git a/method_dispatcher/test/test_method_dispatcher.c b/method_dispatcher/test/test_method_dispatcher.c new file mode 100644 index 0000000..917e901 --- /dev/null +++ b/method_dispatcher/test/test_method_dispatcher.c @@ -0,0 +1,63 @@ +#include +#include "../method_dispatcher.h" + +char m1(uint8_t small_arg, void * state); +char m2(uint8_t small_arg, void * state); +char m3(uint8_t small_arg, void * state); +char m4(uint8_t small_arg, void * state); +char m5(uint8_t small_arg, void * state); + +int main(void) +{ + printf("sizeof struct dispatch_tree_node_s: %zd\n", sizeof(struct dispatch_tree_node_s)); + dispatch_tree_t tree = dispatch_tree_t_new(); + + dispatch_tree_t_insert(tree, 3 << 6, m3); + dispatch_tree_t_insert(tree, 2 << 6, m2); + dispatch_tree_t_insert(tree, 1 << 6, m1); + dispatch_tree_t_insert(tree, 4 << 6, m4); + dispatch_tree_t_insert(tree, 5 << 6, m5); + + int i; + for(i = 1; i < 6; i++) + { + dispatch_tree_t_dispatch(tree, i << 6)(i, NULL); + } + dispatch_tree_t_del(tree); + + + bci_core_method_t methods[5] = {m1, m2, m3, m4, m5}; + tree = dispatch_tree_t_new(); + dispatch_tree_autoinserter_t inserter = dispatch_tree_autoinserter_t_new(tree); + for(i = 0; i < 5; i++) + { + printf("%u\n", dispatch_tree_autoinserter_t_insert(inserter, methods[i])); + } + dispatch_tree_autoinserter_t_del(inserter); + dispatch_tree_t_del(tree); + + return 0; +} + + + +char m1(uint8_t small_arg, void * state) +{ + printf("m1: %d\n", small_arg); +} +char m2(uint8_t small_arg, void * state) +{ + printf("m2: %d\n", small_arg); +} +char m3(uint8_t small_arg, void * state) +{ + printf("m3: %d\n", small_arg); +} +char m4(uint8_t small_arg, void * state) +{ + printf("m4: %d\n", small_arg); +} +char m5(uint8_t small_arg, void * state) +{ + printf("m5: %d\n", small_arg); +} diff --git a/stack.c b/stack.c new file mode 100644 index 0000000..97d4a8c --- /dev/null +++ b/stack.c @@ -0,0 +1,40 @@ +#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) +{ + bci_stack_t this_node; + if(!*stack) + { + return 1; + } + this_node = *stack; + *result = this_node->value; + *stack = this_node->next; + free(this_node); + return 0; +} + +void bci_stack_t_del(bci_stack_t * stack) +{ + bci_stack_t this_node; + + while(*stack) + { + this_node = *stack; + *stack = this_node->next; + free(this_node); + } +} diff --git a/stack.h b/stack.h new file mode 100644 index 0000000..0522db0 --- /dev/null +++ b/stack.h @@ -0,0 +1,20 @@ +#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); + +/* + * Deletes the entire (!) stack. + * + * */ +void bci_stack_t_del(bci_stack_t * stack); +#endif diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000..a90518d --- /dev/null +++ b/test/Makefile @@ -0,0 +1,12 @@ +CC=gcc + +all: test_stack + +stack: clean + $(CC) -g ../stack.c test_stack.c -o stack + +test_stack: stack + valgrind ./stack + +clean: + -rm stack diff --git a/test/test_stack.c b/test/test_stack.c new file mode 100644 index 0000000..328c35c --- /dev/null +++ b/test/test_stack.c @@ -0,0 +1,34 @@ +#include +#include "../stack.h" +#include + +int main(void) +{ + bci_stack_t stack = NULL; + int i; + uint16_t res; + char status = 0; + + + for(i = 0; i < 10; i++) + { + bci_stack_t_push(&stack, (uint16_t) i); + } + --i; + while(!status) + { + status = bci_stack_t_pop(&stack, &res); + if(!status) + { + assert(res == i--); + } + } + + + for(i = 0; i < 10; i++) + { + bci_stack_t_push(&stack, (uint16_t) i); + } + bci_stack_t_del(&stack); + return 0; +}