initial interpreter, base and stack
This commit is contained in:
parent
a515b56f58
commit
e9801283e2
20
base.h
Normal file
20
base.h
Normal 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
|
145
interpreter/interpreter.c
Normal file
145
interpreter/interpreter.c
Normal 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;
|
||||||
|
}
|
79
interpreter/interpreter.h
Normal file
79
interpreter/interpreter.h
Normal 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
stack.c
Normal file
26
stack.c
Normal 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
stack.h
Normal file
14
stack.h
Normal 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
|
Loading…
Reference in New Issue
Block a user