2018-09-26 15:50:12 +00:00
|
|
|
#include <stdlib.h>
|
2018-10-03 21:26:27 +00:00
|
|
|
#include <stdio.h>
|
2018-09-26 15:50:12 +00:00
|
|
|
|
|
|
|
#include "interpreter.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
machine_state_t machine_state_t_new( void * cache_function_data
|
2018-10-03 21:26:27 +00:00
|
|
|
, size_t program_size
|
2018-09-26 15:50:12 +00:00
|
|
|
, size_t cache_size
|
2018-10-03 21:26:27 +00:00
|
|
|
, size_t memory_size
|
2018-09-26 15:50:12 +00:00
|
|
|
, size_t (* cache_block)(uint16_t * cache
|
|
|
|
, uint16_t offset
|
|
|
|
, size_t cache_size
|
|
|
|
, void * cache_function_data)
|
|
|
|
, dispatch_tree_t method_dispatcher)
|
|
|
|
{
|
2018-10-03 21:26:27 +00:00
|
|
|
|
2018-09-26 15:50:12 +00:00
|
|
|
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;
|
|
|
|
}
|
2018-10-03 21:26:27 +00:00
|
|
|
uint16_t * memory = calloc(sizeof(uint16_t), memory_size);
|
|
|
|
if(!memory)
|
|
|
|
{
|
|
|
|
free(state);
|
|
|
|
free(cache);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-09-26 15:50:12 +00:00
|
|
|
|
2018-10-03 21:26:27 +00:00
|
|
|
state->program_size = program_size;
|
2018-09-26 15:50:12 +00:00
|
|
|
state->cache = cache;
|
|
|
|
state->cache_block = cache_block;
|
|
|
|
state->cache_size = cache_size;
|
|
|
|
state->cache_loaded = 0;
|
2018-10-03 21:26:27 +00:00
|
|
|
state->cache_offset = 0;
|
2018-09-26 15:50:12 +00:00
|
|
|
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++)
|
|
|
|
{
|
2018-10-27 14:53:40 +00:00
|
|
|
state->data_reg[i] = 0;
|
2018-09-26 15:50:12 +00:00
|
|
|
}
|
|
|
|
state->stack = NULL;
|
|
|
|
|
2018-10-03 21:26:27 +00:00
|
|
|
state->memory = memory;
|
|
|
|
state->memory_size = memory_size;
|
|
|
|
|
2018-09-26 15:50:12 +00:00
|
|
|
state->method_dispatcher = method_dispatcher;
|
|
|
|
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
|
|
|
void machine_state_t_del(machine_state_t state)
|
|
|
|
{
|
|
|
|
free(state->cache);
|
2018-10-03 21:26:27 +00:00
|
|
|
free(state->memory);
|
2018-09-26 15:50:12 +00:00
|
|
|
dispatch_tree_t_del(state->method_dispatcher);
|
2018-10-03 21:26:27 +00:00
|
|
|
bci_stack_t_del(&(state->stack));
|
2018-09-26 15:50:12 +00:00
|
|
|
free(state);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-10-03 21:26:27 +00:00
|
|
|
char update_cache(machine_state_t state, uint16_t offset)
|
2018-09-26 15:50:12 +00:00
|
|
|
{
|
|
|
|
// Calculate a clever offset for caching.
|
2018-10-03 21:26:27 +00:00
|
|
|
uint16_t cache_offset = offset;
|
2018-09-26 15:50:12 +00:00
|
|
|
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)
|
|
|
|
{
|
2018-10-27 14:53:40 +00:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf("ERROR: should have cached %zu words, but got %zu.\n", state->cache_size, cached);
|
|
|
|
#endif
|
2018-09-26 15:50:12 +00:00
|
|
|
return 1;
|
|
|
|
}
|
2018-10-03 21:26:27 +00:00
|
|
|
state->cache_offset = cache_offset;
|
|
|
|
state->cache_loaded = 1;
|
2018-09-26 15:50:12 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-10-03 21:26:27 +00:00
|
|
|
char machine_state_t_get_word(machine_state_t state, uint16_t offset, uint16_t * result)
|
2018-09-26 15:50:12 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
// This would be bad.
|
2018-10-03 21:26:27 +00:00
|
|
|
if(offset >= state->program_size)
|
2018-09-26 15:50:12 +00:00
|
|
|
{
|
|
|
|
return 0b01000000 | 1;
|
|
|
|
}
|
|
|
|
|
2018-10-03 21:26:27 +00:00
|
|
|
// Calculate the in-cache offset of the desired offset.
|
|
|
|
int32_t in_cache_offset = offset - state->cache_offset;
|
2018-09-26 15:50:12 +00:00
|
|
|
|
|
|
|
// Check if we need to update the cache.
|
2018-10-03 21:26:27 +00:00
|
|
|
if( in_cache_offset < 0
|
2018-09-26 15:50:12 +00:00
|
|
|
|| !(state->cache_loaded)
|
2018-10-03 21:26:27 +00:00
|
|
|
)
|
2018-09-26 15:50:12 +00:00
|
|
|
{
|
2018-10-03 21:26:27 +00:00
|
|
|
if(update_cache(state, offset))
|
2018-09-26 15:50:12 +00:00
|
|
|
{
|
2018-10-27 14:53:40 +00:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf("ERROR: failed to update cache\n");
|
|
|
|
#endif
|
2018-10-03 21:26:27 +00:00
|
|
|
state->error = 0b01000000 | 2;
|
2018-09-26 15:50:12 +00:00
|
|
|
return 0b01000000 | 2;
|
|
|
|
}
|
|
|
|
// Re-calculate the in-cache offset
|
2018-10-03 21:26:27 +00:00
|
|
|
in_cache_offset = offset - state->cache_offset;
|
2018-09-26 15:50:12 +00:00
|
|
|
}
|
|
|
|
|
2018-10-03 21:26:27 +00:00
|
|
|
*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)
|
2018-09-26 15:50:12 +00:00
|
|
|
{
|
2018-10-03 21:26:27 +00:00
|
|
|
return res;
|
2018-09-26 15:50:12 +00:00
|
|
|
}
|
|
|
|
|
2018-10-27 14:53:40 +00:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf("MSG: opcode = 0x%x, small_arg = 0x%x\n"
|
|
|
|
, opcode & BCI_CORE_OPCODE_MASK
|
|
|
|
, opcode & BCI_CORE_SMALL_ARG_MASK);
|
|
|
|
#endif
|
|
|
|
|
2018-09-26 15:50:12 +00:00
|
|
|
bci_core_method_t method = dispatch_tree_t_dispatch(
|
|
|
|
state->method_dispatcher
|
|
|
|
, opcode & BCI_CORE_OPCODE_MASK);
|
|
|
|
if(!method)
|
|
|
|
{
|
2018-10-03 21:26:27 +00:00
|
|
|
state->error = res;
|
2018-09-26 15:50:12 +00:00
|
|
|
return 0b10000000 | 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return BCI_CORE_RESULT_APPLICATION_BITS
|
2018-10-03 21:26:27 +00:00
|
|
|
& method( opcode & BCI_CORE_SMALL_ARG_MASK
|
2018-09-26 15:50:12 +00:00
|
|
|
, state);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char machine_state_t_default_afterexec(machine_state_t state, uint8_t argc)
|
|
|
|
{
|
|
|
|
state->program_counter += 1 + argc;
|
|
|
|
return 0;
|
|
|
|
}
|
2018-10-03 21:26:27 +00:00
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2018-10-27 14:53:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|