MicroController/microcontroller.lua
2019-03-05 19:50:21 +08:00

280 lines
9.7 KiB
Lua

local Compiler = require('compiler')
local Entity = require("stdlib/entity/entity")
require('constants')
PSTATE_HALTED = 0
PSTATE_RUNNING = 1
PSTATE_SLEEPING = 2
PSTATE_SYNC = 3
function linepairs(s)
if s:sub(-1)~="\n" then s=s.."\n" end
return s:gmatch("(.-)\n")
end
local microcontroller = {}
microcontroller.event_error = script.generate_event_name()
microcontroller.event_halt = script.generate_event_name()
function microcontroller.init( mc, state )
state.program_lines = {}
state.program_text = ""
state.program_counter = 1
state.program_ast = {}
state.program_state = PSTATE_HALTED
Entity.set_data(mc, state)
local control = mc.get_or_create_control_behavior()
control.parameters = {
parameters = {
first_signal = nil,
second_signal = nil,
first_constant = 0,
second_constant = 1,
operation = "*",
output_signal = NULL_SIGNAL.signal
}
}
microcontroller.init_memory(mc, state)
end
function microcontroller.init_memory( mc, state )
if state.memory == nil then
state.memory = {}
for i = 1, 4 do
state.memory[i] = NULL_SIGNAL
end
end
if not state.clock then
state.clock = 1
end
if not state.adjacent_modules then
state.adjacent_modules = {}
for i = 1, 4 do
state.adjacent_modules[i] = nil
end
end
end
function microcontroller.update_program_text( mc, program_text )
local state = Entity.get_data(mc)
state.program_text = program_text
Entity.set_data(mc, state)
end
function microcontroller.attach_module( mc, module, direction )
local state = Entity.get_data(mc)
if not state then
state = {}
microcontroller.init(mc, state)
microcontroller.init_memory(mc, state)
end
if mc.last_user then
mc.last_user.print("Attached module to MicroController. Memory mapped to mem"..(direction*10 + 1).."-mem"..(direction*10+4)..".")
end
state.adjacent_modules[direction] = module
Entity.set_data(mc, state)
end
function microcontroller.compile( mc, state )
local program_lines = {}
for line in linepairs(state.program_text) do
table.insert(program_lines, line)
end
state.program_ast = Compiler.compile(program_lines)
Entity.set_data(mc, state)
end
function microcontroller.set_error_message( mc, state, error_message )
state.error_message = "line "..state.program_counter..": "..(error_message or "")
state.error_line = state.program_counter
script.raise_event(microcontroller.event_error, {entity = mc, ['state'] = state, message = state.error_message})
end
function microcontroller.set_program_counter( mc, state, value )
state.program_counter = value
if #state.program_ast == 0 or state.program_counter > #state.program_ast then
state.program_counter = 1
state.program_state = PSTATE_HALTED
state.do_step = false
script.raise_event(microcontroller.event_halt, {entity = mc, ['state'] = state})
else
local next_ast = state.program_ast[state.program_counter]
while(next_ast and (next_ast.type == 'nop' or next_ast.type == 'label')) do
state.program_counter = state.program_counter + 1
if state.program_counter > #state.program_ast then
break
end
next_ast = state.program_ast[state.program_counter]
end
if state.program_counter > #state.program_ast then
state.program_state = PSTATE_HALTED
state.do_step = false
script.raise_event(microcontroller.event_halt, {entity = mc, ['state'] = state})
end
end
end
function microcontroller.tick( mc, state )
microcontroller.init_memory(mc, state)
state.clock = state.clock + 1
-- Interrupts
local control = mc.get_control_behavior()
local red_input = control.get_circuit_network(defines.wire_type.red, defines.circuit_connector_id.combinator_input)
local green_input = control.get_circuit_network(defines.wire_type.green, defines.circuit_connector_id.combinator_input)
local get_signal = function(signal)
if red_input then
local result = red_input.get_signal(signal.signal)
if result ~= nil then
return result
end
end
if green_input then
local result = green_input.get_signal(signal.signal)
if result ~= nil then
return result
end
end
return 0
end
if state.program_state == PSTATE_RUNNING and get_signal(HALT_SIGNAL) > 0 then
microcontroller.halt(mc, state)
end
if state.program_state == PSTATE_HALTED and get_signal(RUN_SIGNAL) > 0 then
microcontroller.run( mc, state, state.program_counter )
end
if state.program_state == PSTATE_HALTED and get_signal(STEP_SIGNAL) > 0 then
microcontroller.step( mc, state )
end
if state.program_state == PSTATE_RUNNING and get_signal(SLEEP_SIGNAL) > 0 then
local value = get_signal(SLEEP_SIGNAL)
if value then
state.program_state = PSTATE_SLEEPING
state.sleep_time = value
end
end
if get_signal(JUMP_SIGNAL) > 0 then
local value = get_signal(JUMP_SIGNAL)
if value then
microcontroller.set_program_counter(mc, state, value)
end
end
-- Run microcontroller code.
if state.program_state == PSTATE_RUNNING then
local ast = state.program_ast[state.program_counter]
local success, result = Compiler.eval(ast, control, state)
if not success then
microcontroller.set_error_message(mc, state, result)
microcontroller.halt(mc, state)
elseif result then
if result.type == 'halt' then
microcontroller.halt(mc, state)
microcontroller.set_program_counter(mc, state, state.program_counter + 1)
elseif result.type == 'sleep' then
state.program_state = PSTATE_SLEEPING
state.sleep_time = result.val
elseif result.type == 'jump' then
if result.label then
for line_num, node in ipairs(state.program_ast) do
if node.type == 'label' and node.label == result.label then
microcontroller.set_program_counter(mc, state, line_num + 1)
break
end
end
else
microcontroller.set_program_counter(mc, state, result.val)
end
elseif result.type == 'skip' then
microcontroller.set_program_counter(mc, state, state.program_counter + 2)
elseif result.type == 'sync' then
state.program_state = PSTATE_SYNC
elseif result.type == 'block' then
-- Do nothing, keeping the program_counter the same.
end
else
microcontroller.set_program_counter(mc, state, state.program_counter + 1)
end
elseif state.program_state == PSTATE_SLEEPING then
state.sleep_time = state.sleep_time - 1
if state.sleep_time <= 1 then
state.program_state = PSTATE_RUNNING
microcontroller.set_program_counter(mc, state, state.program_counter + 1)
end
elseif state.program_state == PSTATE_SYNC then
local all_sync = true
for i, module in pairs(state.adjacent_modules) do
if module.name == "microcontroller" then
local other_state = Entity.get_data(module)
if other_state.program_state ~= PSTATE_SYNC then
all_sync = false
break
end
end
end
if all_sync then
microcontroller.set_program_counter(mc, state, state.program_counter + 1)
state.program_state = PSTATE_RUNNING
for i, module in pairs(state.adjacent_modules) do
if module.name == "microcontroller" then
local other_state = Entity.get_data(module)
microcontroller.set_program_counter(module, other_state, other_state.program_counter + 1)
other_state.program_state = PSTATE_RUNNING
end
end
end
end
if state.do_step and state.program_state == PSTATE_RUNNING then
state.do_step = false
microcontroller.halt(mc, state)
end
Entity.set_data(mc, state)
end
function microcontroller.run( mc, state )
state.program_state = PSTATE_RUNNING
-- if line == nil then
-- microcontroller.set_program_counter(mc, state, 1)
-- state.program_counter = 1
-- else
-- if line > #state.program_ast then
-- line = 1
-- end
-- microcontroller.set_program_counter(mc, state, line)
-- end
state.error_message = nil
state.error_line = nil
state.do_step = false
Entity.set_data(mc, state)
end
function microcontroller.step( mc, state )
if state.program_counter > #state.program_ast then
microcontroller.set_program_counter(mc, state, 1)
end
state.program_state = PSTATE_RUNNING
state.do_step = true
state.error_message = nil
state.error_line = nil
Entity.set_data(mc, state)
end
function microcontroller.halt( mc, state )
if state.program_state == PSTATE_HALTED then
microcontroller.set_program_counter(mc, state, 1)
end
state.program_state = PSTATE_HALTED
state.do_step = false
Entity.set_data(mc, state)
script.raise_event(microcontroller.event_halt, {entity = mc, ['state'] = state})
end
function microcontroller.is_running( mc )
return Entity.get_data(mc).program_state ~= PSTATE_HALTED
end
return microcontroller