initial interpreter, base and stack

This commit is contained in:
Daniel Knüttel 2018-09-26 17:50:12 +02:00
parent 0a40beacf7
commit 5a5db35123
5 changed files with 284 additions and 0 deletions

20
interpreter/base.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef bci_base_h__
#define bci_base_h__
#include <inttypes.h>
#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

View File

@ -0,0 +1,145 @@
#include <stdlib.h>
#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;
}

View File

@ -0,0 +1,79 @@
#ifndef bci_interpreter_h__
#define bci_interpreter_h__
#include <stddef.h>
#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

26
interpreter/stack.c Normal file
View File

@ -0,0 +1,26 @@
#include <stdlib.h>
#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;
}

14
interpreter/stack.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef bci_stack_h__
#define bci_stack_h__
#include <inttypes.h>
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