BCI-base/interpreter/interpreter.c

146 lines
3.0 KiB
C

#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;
}