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