-
Notifications
You must be signed in to change notification settings - Fork 4
/
microcontroller.lua
291 lines (268 loc) · 10 KB
/
microcontroller.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
local Compiler = require('compiler')
local Entity = require("stdlib/entity/entity")
require('constants')
local PSTATE_HALTED = 0
local PSTATE_RUNNING = 1
local PSTATE_SLEEPING = 2
local 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 = {
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.get_max_lines(force)
local techs=force.technologies
if techs["microcontroller-program-size-3"].researched then
return techs["microcontroller-program-size-4"].level*16 + 16
elseif techs["microcontroller-program-size-2"].researched then
return 64
elseif techs["microcontroller-program-size-1"].researched then
return 48
end
return 32
end
function microcontroller.update_program_text( mc, program_text )
if not (mc or mc.valid) then return log("BUG?") end -- TODO: recheck
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
program_lines[#program_lines+1] = 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_connector_id.combinator_input_red)
local green_input = control.get_circuit_network(defines.wire_connector_id.combinator_input_green)
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