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