Skip to content

Commit 07d0740

Browse files
authoredJan 28, 2017
FPGAs (#315)
1 parent 5188853 commit 07d0740

18 files changed

+657
-0
lines changed
 

Diff for: ‎documentation.json

+2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
},
4343
"Logic" : {
4444
"Luacontroller" : "mesecons_luacontroller/doc/luacontroller",
45+
"FPGA" : "mesecons_fpga/doc/fpga",
46+
"FPGA Programmer" : "mesecons_fpga/doc/programmer",
4547
"Torch" : "mesecons_torch/doc/torch",
4648
"Delayer" : "mesecons_delayer/doc/delayer",
4749
"Gates" : {

Diff for: ‎mesecons_fpga/depends.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
mesecons

Diff for: ‎mesecons_fpga/doc/fpga/description.html

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
FPGAs can be used to chain multiple logic gates together in a compact manner.
2+
They come with 4 I/O ports and 10 internal registers,
3+
which can then be connected with eachother to form logic circuits.<br />
4+
Supported gate types: <b>AND</b>, <b>OR</b>, <b>NOT</b>, <b>XOR</b>, <b>NAND</b>, <b>XNOR</b>, <b>Buffer</b> (=)<br />
5+
I/O ports: <b>A B C D</b>; Registers: numbered <b>0</b> to <b>9</b>

Diff for: ‎mesecons_fpga/doc/fpga/preview.png

20.8 KB
Loading

Diff for: ‎mesecons_fpga/doc/fpga/recipe.png

3.13 KB
Loading

Diff for: ‎mesecons_fpga/doc/programmer/description.html

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
The FPGA programmer can be used to copy gate configurations from one FPGA to another.<br />
2+
Shift+Right-Click an FPGA to read its configuration and "remember" it.
3+
Left-click (punch) FPGAs to write the saved configuration to them.

Diff for: ‎mesecons_fpga/doc/programmer/preview.png

598 Bytes
Loading

Diff for: ‎mesecons_fpga/doc/programmer/recipe.png

1.86 KB
Loading

Diff for: ‎mesecons_fpga/init.lua

+374
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,374 @@
1+
local plg = {}
2+
plg.rules = {}
3+
4+
local lcore = dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/logic.lua")
5+
dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/tool.lua")(plg)
6+
7+
8+
plg.register_nodes = function(template)
9+
-- each loop is for one of the 4 IO ports
10+
for a = 0, 1 do
11+
for b = 0, 1 do
12+
for c = 0, 1 do
13+
for d = 0, 1 do
14+
local ndef = table.copy(template)
15+
local nodename = "mesecons_fpga:fpga"
16+
.. tostring(d) .. tostring(c) .. tostring(b) .. tostring(a)
17+
18+
-- build top texture string
19+
local texture = "jeija_fpga_top.png"
20+
if a == 1 then texture = texture .. "^jeija_microcontroller_LED_A.png" end
21+
if b == 1 then texture = texture .. "^jeija_microcontroller_LED_B.png" end
22+
if c == 1 then texture = texture .. "^jeija_microcontroller_LED_C.png" end
23+
if d == 1 then texture = texture .. "^jeija_microcontroller_LED_D.png" end
24+
ndef.tiles[1] = texture
25+
ndef.inventory_image = texture
26+
27+
if (a + b + c + d) > 0 then
28+
ndef.groups["not_in_creative_inventory"] = 1
29+
end
30+
31+
-- interaction with mesecons (input / output)
32+
local rules_out = {}
33+
if a == 1 then table.insert(rules_out, {x = -1, y = 0, z = 0}) end
34+
if b == 1 then table.insert(rules_out, {x = 0, y = 0, z = 1}) end
35+
if c == 1 then table.insert(rules_out, {x = 1, y = 0, z = 0}) end
36+
if d == 1 then table.insert(rules_out, {x = 0, y = 0, z = -1}) end
37+
plg.rules[nodename] = rules_out
38+
39+
local rules_in = {}
40+
if a == 0 then table.insert(rules_in, {x = -1, y = 0, z = 0}) end
41+
if b == 0 then table.insert(rules_in, {x = 0, y = 0, z = 1}) end
42+
if c == 0 then table.insert(rules_in, {x = 1, y = 0, z = 0}) end
43+
if d == 0 then table.insert(rules_in, {x = 0, y = 0, z = -1}) end
44+
ndef.mesecons.effector.rules = rules_in
45+
46+
if (a + b + c + d) > 0 then
47+
ndef.mesecons.receptor = {
48+
state = mesecon.state.on,
49+
rules = rules_out,
50+
}
51+
end
52+
53+
minetest.register_node(nodename, ndef)
54+
end
55+
end
56+
end
57+
end
58+
end
59+
60+
plg.register_nodes({
61+
description = "FPGA",
62+
drawtype = "nodebox",
63+
tiles = {
64+
"", -- replaced later
65+
"jeija_microcontroller_bottom.png",
66+
"jeija_fpga_sides.png",
67+
"jeija_fpga_sides.png",
68+
"jeija_fpga_sides.png",
69+
"jeija_fpga_sides.png"
70+
},
71+
inventory_image = "", -- replaced later
72+
sunlight_propagates = true,
73+
paramtype = "light",
74+
walkable = true,
75+
groups = {dig_immediate = 2, mesecon = 3},
76+
drop = "mesecons_fpga:fpga0000",
77+
selection_box = {
78+
type = "fixed",
79+
fixed = { -8/16, -8/16, -8/16, 8/16, -5/16, 8/16 },
80+
},
81+
node_box = {
82+
type = "fixed",
83+
fixed = {
84+
{ -8/16, -8/16, -8/16, 8/16, -7/16, 8/16 }, -- bottom slab
85+
{ -5/16, -7/16, -5/16, 5/16, -6/16, 5/16 }, -- circuit board
86+
{ -3/16, -6/16, -3/16, 3/16, -5/16, 3/16 }, -- IC
87+
}
88+
},
89+
on_construct = function(pos)
90+
local meta = minetest.get_meta(pos)
91+
local is = { {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {} }
92+
93+
meta:set_string("instr", lcore.serialize(is))
94+
meta:set_int("valid", 0)
95+
meta:set_string("formspec", plg.to_formspec_string(is))
96+
meta:set_string("infotext", "FPGA")
97+
end,
98+
on_receive_fields = function(pos, formname, fields, sender)
99+
if fields.program == nil then return end -- we only care when the user clicks "Program"
100+
local meta = minetest.get_meta(pos)
101+
local is = plg.from_formspec_fields(fields)
102+
103+
meta:set_string("instr", lcore.serialize(is))
104+
plg.update_formspec(pos, is)
105+
end,
106+
sounds = default.node_sound_stone_defaults(),
107+
mesecons = {
108+
effector = {
109+
rules = {}, -- replaced later
110+
action_change = function(pos, node, rule, newstate)
111+
plg.ports_changed(pos, rule, newstate)
112+
plg.update(pos)
113+
end
114+
}
115+
},
116+
after_dig_node = function(pos, node)
117+
mesecon.receptor_off(pos, plg.rules[node.name])
118+
end,
119+
})
120+
121+
122+
plg.to_formspec_string = function(is)
123+
local function dropdown_op(x, y, name, val)
124+
local s = "dropdown[" .. tostring(x) .. "," .. tostring(y) .. ";"
125+
.. "0.75,0.5;" .. name .. ";" -- the height seems to be ignored?
126+
s = s .. " ,A,B,C,D,0,1,2,3,4,5,6,7,8,9;"
127+
if val == nil then
128+
s = s .. "0" -- actually selects no field at all
129+
elseif val.type == "io" then
130+
local mapping = {
131+
["A"] = 1,
132+
["B"] = 2,
133+
["C"] = 3,
134+
["D"] = 4,
135+
}
136+
s = s .. tostring(1 + mapping[val.port])
137+
else -- "reg"
138+
s = s .. tostring(6 + val.n)
139+
end
140+
return s .. "]"
141+
end
142+
local function dropdown_action(x, y, name, val)
143+
local s = "dropdown[" .. tostring(x) .. "," .. tostring(y) .. ";"
144+
.. "1.125,0.5;" .. name .. ";" -- the height seems to be ignored?
145+
s = s .. " , AND, OR, NOT, XOR,NAND, =,XNOR;"
146+
if val == nil then
147+
return s .. "0]" -- actually selects no field at all
148+
end
149+
local mapping = {
150+
["and"] = 1,
151+
["or"] = 2,
152+
["not"] = 3,
153+
["xor"] = 4,
154+
["nand"] = 5,
155+
["buf"] = 6,
156+
["xnor"] = 7,
157+
}
158+
return s .. tostring(1 + mapping[val]) .. "]"
159+
end
160+
local s = "size[9,9]"..
161+
"label[3.4,-0.15;FPGA gate configuration]"..
162+
"button_exit[7,7.5;2,2.5;program;Program]"..
163+
"box[4.2,0.5;0.03,7;#ffffff]"..
164+
"label[0.25,0.25;op. 1]"..
165+
"label[1.0,0.25;gate type]"..
166+
"label[2.125,0.25;op. 2]"..
167+
"label[3.15,0.25;dest]"..
168+
"label[4.5,0.25;op. 1]"..
169+
"label[5.25,0.25;gate type]"..
170+
"label[6.375,0.25;op. 2]"..
171+
"label[7.4,0.25;dest]"
172+
local x = 1 - 0.75
173+
local y = 1 - 0.25
174+
for i = 1, 14 do
175+
local cur = is[i]
176+
s = s .. dropdown_op (x , y, tostring(i).."op1", cur.op1)
177+
s = s .. dropdown_action(x+0.75 , y, tostring(i).."act", cur.action)
178+
s = s .. dropdown_op (x+1.875, y, tostring(i).."op2", cur.op2)
179+
s = s .. "label[" .. tostring(x+2.625) .. "," .. tostring(y+0.1) .. "; ->]"
180+
s = s .. dropdown_op (x+2.9 , y, tostring(i).."dst", cur.dst)
181+
y = y + 1
182+
183+
if i == 7 then
184+
x = 4.5
185+
y = 1 - 0.25
186+
end
187+
end
188+
return s
189+
end
190+
191+
plg.from_formspec_fields = function(fields)
192+
local function read_op(s)
193+
if s == nil or s == " " then
194+
return nil
195+
elseif s == "A" or s == "B" or s == "C" or s == "D" then
196+
return {type = "io", port = s}
197+
else
198+
return {type = "reg", n = tonumber(s)}
199+
end
200+
end
201+
local function read_action(s)
202+
if s == nil or s == " " then
203+
return nil
204+
end
205+
local mapping = {
206+
[" AND"] = "and",
207+
[" OR"] = "or",
208+
[" NOT"] = "not",
209+
[" XOR"] = "xor",
210+
["NAND"] = "nand",
211+
[" ="] = "buf",
212+
["XNOR"] = "xnor",
213+
}
214+
return mapping[s]
215+
end
216+
local is = {}
217+
for i = 1, 14 do
218+
local cur = {}
219+
cur.op1 = read_op(fields[tonumber(i) .. "op1"])
220+
cur.action = read_action(fields[tonumber(i) .. "act"])
221+
cur.op2 = read_op(fields[tonumber(i) .. "op2"])
222+
cur.dst = read_op(fields[tonumber(i) .. "dst"])
223+
is[#is + 1] = cur
224+
end
225+
return is
226+
end
227+
228+
plg.update_formspec = function(pos, is)
229+
if type(is) == "string" then -- serialized string
230+
is = lcore.deserialize(is)
231+
end
232+
local meta = minetest.get_meta(pos)
233+
local form = plg.to_formspec_string(is)
234+
235+
local err = lcore.validate(is)
236+
if err == nil then
237+
meta:set_int("valid", 1)
238+
meta:set_string("infotext", "FPGA (functional)")
239+
else
240+
meta:set_int("valid", 0)
241+
meta:set_string("infotext", "FPGA")
242+
local fmsg = minetest.colorize("#ff0000", minetest.formspec_escape(err.msg))
243+
form = form .. plg.red_box_around(err.i) ..
244+
"label[0.25,8.25;The gate configuration is erroneous in the marked area:]"..
245+
"label[0.25,8.5;" .. fmsg .. "]"
246+
end
247+
248+
meta:set_string("formspec", form)
249+
250+
-- reset ports and run programmed logic
251+
plg.setports(pos, false, false, false, false)
252+
plg.update(pos)
253+
end
254+
255+
plg.red_box_around = function(i)
256+
local x, y
257+
if i > 7 then
258+
x = 4.5
259+
y = 0.75 + (i - 8)
260+
else
261+
x = 0.25
262+
y = 0.75 + (i - 1)
263+
end
264+
return string.format("box[%f,%f;3.8,0.8;#ff0000]", x-0.1, y-0.05)
265+
end
266+
267+
268+
plg.update = function(pos)
269+
local meta = minetest.get_meta(pos)
270+
if meta:get_int("valid") ~= 1 then
271+
return
272+
end
273+
274+
local is = lcore.deserialize(meta:get_string("instr"))
275+
local A, B, C, D = plg.getports(pos)
276+
A, B, C, D = lcore.interpret(is, A, B, C, D)
277+
plg.setports(pos, A, B, C, D)
278+
end
279+
280+
plg.ports_changed = function(pos, rule, newstate)
281+
if rule == nil then return end
282+
local meta = minetest.get_meta(pos)
283+
local states
284+
285+
local s = meta:get_string("portstates")
286+
if s == nil then
287+
states = {false, false, false, false}
288+
else
289+
states = {
290+
s:sub(1, 1) == "1",
291+
s:sub(2, 2) == "1",
292+
s:sub(3, 3) == "1",
293+
s:sub(4, 4) == "1",
294+
}
295+
end
296+
297+
-- trick to transform rules (see register_node) into port number
298+
local portno = ({4, 1, nil, 3, 2})[3 + rule.x + 2*rule.z]
299+
states[portno] = (newstate == "on")
300+
301+
meta:set_string("portstates",
302+
(states[1] and "1" or "0") .. (states[2] and "1" or "0") ..
303+
(states[3] and "1" or "0") .. (states[4] and "1" or "0")
304+
)
305+
end
306+
307+
plg.getports = function(pos) -- gets merged states of INPUT & OUTPUT
308+
local sin, sout
309+
310+
local s = minetest.get_meta(pos):get_string("portstates")
311+
if s == nil then
312+
sin = {false, false, false, false}
313+
else
314+
sin = {
315+
s:sub(1, 1) == "1",
316+
s:sub(2, 2) == "1",
317+
s:sub(3, 3) == "1",
318+
s:sub(4, 4) == "1",
319+
}
320+
end
321+
322+
local name = minetest.get_node(pos).name
323+
assert(name:find("mesecons_fpga:fpga") == 1)
324+
local off = #"mesecons_fpga:fpga"
325+
sout = {
326+
name:sub(off+4, off+4) == "1",
327+
name:sub(off+3, off+3) == "1",
328+
name:sub(off+2, off+2) == "1",
329+
name:sub(off+1, off+1) == "1",
330+
}
331+
332+
return unpack({
333+
sin[1] or sout[1],
334+
sin[2] or sout[2],
335+
sin[3] or sout[3],
336+
sin[4] or sout[4],
337+
})
338+
end
339+
340+
plg.setports = function(pos, A, B, C, D) -- sets states of OUTPUT
341+
local base = "mesecons_fpga:fpga"
342+
343+
local name = base
344+
.. (D and "1" or "0") .. (C and "1" or "0")
345+
.. (B and "1" or "0") .. (A and "1" or "0")
346+
minetest.swap_node(pos, {name = name, param2 = minetest.get_node(pos).param2})
347+
348+
if A ~= nil then
349+
local ru = plg.rules[base .. "0001"]
350+
if A then mesecon.receptor_on(pos, ru) else mesecon.receptor_off(pos, ru) end
351+
end
352+
if B ~= nil then
353+
local ru = plg.rules[base .. "0010"]
354+
if B then mesecon.receptor_on(pos, ru) else mesecon.receptor_off(pos, ru) end
355+
end
356+
if C ~= nil then
357+
local ru = plg.rules[base .. "0100"]
358+
if C then mesecon.receptor_on(pos, ru) else mesecon.receptor_off(pos, ru) end
359+
end
360+
if D ~= nil then
361+
local ru = plg.rules[base .. "1000"]
362+
if D then mesecon.receptor_on(pos, ru) else mesecon.receptor_off(pos, ru) end
363+
end
364+
end
365+
366+
367+
minetest.register_craft({
368+
output = "mesecons_fpga:fpga0000 2",
369+
recipe = {
370+
{'group:mesecon_conductor_craftable', 'group:mesecon_conductor_craftable'},
371+
{'mesecons_materials:silicon', 'mesecons_materials:silicon'},
372+
{'group:mesecon_conductor_craftable', 'group:mesecon_conductor_craftable'},
373+
}
374+
})

Diff for: ‎mesecons_fpga/logic.lua

+210
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
local lg = {}
2+
3+
-- (de)serialize
4+
lg.serialize = function(t)
5+
local function _op(t)
6+
if t == nil then
7+
return " "
8+
elseif t.type == "io" then
9+
return t.port
10+
else -- t.type == "reg"
11+
return tostring(t.n)
12+
end
13+
end
14+
local function _action(s)
15+
if s == nil then
16+
return " "
17+
end
18+
local mapping = {
19+
["and"] = "&",
20+
["or"] = "|",
21+
["not"] = "~",
22+
["xor"] = "^",
23+
["nand"] = "?", --dunno
24+
["buf"] = "_",
25+
["xnor"] = "=",
26+
}
27+
return mapping[s]
28+
end
29+
30+
local s = ""
31+
for i = 1, 14 do
32+
local cur = t[i]
33+
if next(cur) ~= nil then
34+
s = s .. _op(cur.op1) .. _action(cur.action) .. _op(cur.op2) .. _op(cur.dst)
35+
end
36+
s = s .. "/"
37+
end
38+
return s
39+
end
40+
41+
lg.deserialize = function(s)
42+
local function _op(c)
43+
if c == "A" or c == "B" or c == "C" or c == "D" then
44+
return {type = "io", port = c}
45+
elseif c == " " then
46+
return nil
47+
else
48+
return {type = "reg", n = tonumber(c)}
49+
end
50+
end
51+
local function _action(c)
52+
local mapping = {
53+
["&"] = "and",
54+
["|"] = "or",
55+
["~"] = "not",
56+
["^"] = "xor",
57+
["?"] = "nand",
58+
["_"] = "buf",
59+
["="] = "xnor",
60+
[" "] = nil,
61+
}
62+
return mapping[c]
63+
end
64+
65+
local ret = {}
66+
for part in s:gmatch("(.-)/") do
67+
local parsed
68+
if part == "" then
69+
parsed = {}
70+
else
71+
parsed = {
72+
action = _action( part:sub(2,2) ),
73+
op1 = _op( part:sub(1,1) ),
74+
op2 = _op( part:sub(3,3) ),
75+
dst = _op( part:sub(4,4) ),
76+
}
77+
end
78+
ret[#ret + 1] = parsed
79+
end
80+
-- More than 14 instructions (write to all 10 regs + 4 outputs)
81+
-- will not pass the write-once requirement of the validator
82+
assert(#ret == 14)
83+
return ret
84+
end
85+
86+
-- validation
87+
lg.validate_single = function(t, i)
88+
local function is_reg_written_to(t, n, max)
89+
for i = 1, max-1 do
90+
if next(t[i]) ~= nil
91+
and t[i].dst and t[i].dst.type == "reg"
92+
and t[i].dst.n == n then
93+
return true
94+
end
95+
end
96+
return false
97+
end
98+
local function compare_op(t1, t2, allow_same_io)
99+
if t1 == nil or t2 == nil then
100+
return false
101+
elseif t1.type ~= t2.type then
102+
return false
103+
end
104+
if t1.type == "reg" and t1.n == t2.n then
105+
return true
106+
elseif t1.type == "io" and t1.port == t2.port then
107+
return not allow_same_io
108+
end
109+
return false
110+
end
111+
local elem = t[i]
112+
-- check for completeness
113+
if elem.action == nil then
114+
return {i = i, msg = "Gate type required"}
115+
elseif elem.action == "not" or elem.action == "buf" then
116+
if elem.op1 ~= nil or elem.op2 == nil or elem.dst == nil then
117+
return {i = i, msg = "Second operand (only) and destination required"}
118+
end
119+
else
120+
if elem.op1 == nil or elem.op2 == nil or elem.dst == nil then
121+
return {i = i, msg = "Operands and destination required"}
122+
end
123+
end
124+
-- check whether operands/destination are identical
125+
if compare_op(elem.op1, elem.op2) then
126+
return {i = i, msg = "Operands cannot be identical"}
127+
end
128+
if compare_op(elem.op1, elem.dst, true) or compare_op(elem.op2, elem.dst, true) then
129+
return {i = i, msg = "Destination and operands must be different"}
130+
end
131+
-- check whether operands point to defined registers
132+
if elem.op1 ~= nil and elem.op1.type == "reg"
133+
and not is_reg_written_to(t, elem.op1.n, i) then
134+
return {i = i, msg = "First operand is undefined register"}
135+
end
136+
if elem.op2.type == "reg" and not is_reg_written_to(t, elem.op2.n, i) then
137+
return {i = i, msg = "Second operand is undefined register"}
138+
end
139+
-- check whether destination points to undefined register
140+
if elem.dst.type == "reg" and is_reg_written_to(t, elem.dst.n, i) then
141+
return {i = i, msg = "Destination is already used register"}
142+
end
143+
144+
return nil
145+
end
146+
147+
lg.validate = function(t)
148+
for i = 1, 14 do
149+
if next(t[i]) ~= nil then
150+
local r = lg.validate_single(t, i)
151+
if r ~= nil then
152+
return r
153+
end
154+
end
155+
end
156+
return nil
157+
end
158+
159+
-- interpreter
160+
lg.interpret = function(t, a, b, c, d)
161+
local function _action(s, v1, v2)
162+
if s == "and" then
163+
return v1 and v2
164+
elseif s == "or" then
165+
return v1 or v2
166+
elseif s == "not" then
167+
return not v2
168+
elseif s == "xor" then
169+
return v1 ~= v2
170+
elseif s == "nand" then
171+
return not (v1 and v2)
172+
elseif s == "buf" then
173+
return v2
174+
else -- s == "xnor"
175+
return v1 == v2
176+
end
177+
end
178+
local function _op(t, regs, io_in)
179+
if t.type == "reg" then
180+
return regs[t.n]
181+
else -- t.type == "io"
182+
return io_in[t.port]
183+
end
184+
end
185+
186+
local io_in = {A=a, B=b, C=c, D=d}
187+
local regs = {}
188+
local io_out = {}
189+
for i = 1, 14 do
190+
local cur = t[i]
191+
if next(cur) ~= nil then
192+
local v1, v2
193+
if cur.op1 ~= nil then
194+
v1 = _op(cur.op1, regs, io_in)
195+
end
196+
v2 = _op(cur.op2, regs, io_in)
197+
198+
local result = _action(cur.action, v1, v2)
199+
200+
if cur.dst.type == "reg" then
201+
regs[cur.dst.n] = result
202+
else -- cur.dst.type == "io"
203+
io_out[cur.dst.port] = result
204+
end
205+
end
206+
end
207+
return io_out.A, io_out.B, io_out.C, io_out.D
208+
end
209+
210+
return lg

Diff for: ‎mesecons_fpga/textures/jeija_fpga_programmer.png

311 Bytes
Loading

Diff for: ‎mesecons_fpga/textures/jeija_fpga_sides.png

536 Bytes
Loading

Diff for: ‎mesecons_fpga/textures/jeija_fpga_top.png

816 Bytes
Loading

Diff for: ‎mesecons_fpga/tool.lua

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
return function(plg)
2+
3+
4+
minetest.register_tool("mesecons_fpga:programmer", {
5+
description = "FPGA Programmer",
6+
inventory_image = "jeija_fpga_programmer.png",
7+
stack_max = 1,
8+
on_place = function(itemstack, placer, pointed_thing)
9+
if pointed_thing.type ~= "node" then
10+
return itemstack
11+
end
12+
13+
local pos = pointed_thing.under
14+
if minetest.get_node(pos).name:find("mesecons_fpga:fpga") ~= 1 then
15+
return itemstack
16+
end
17+
18+
local meta = minetest.get_meta(pos)
19+
if meta:get_string("instr") == "//////////////" then
20+
minetest.chat_send_player(placer:get_player_name(), "This FPGA is unprogrammed.")
21+
return itemstack
22+
end
23+
itemstack:set_metadata(meta:get_string("instr"))
24+
minetest.chat_send_player(placer:get_player_name(), "FPGA gate configuration was successfully copied!")
25+
26+
return itemstack
27+
end,
28+
on_use = function(itemstack, user, pointed_thing)
29+
if pointed_thing.type ~= "node" then
30+
return itemstack
31+
end
32+
33+
local pos = pointed_thing.under
34+
if minetest.get_node(pos).name:find("mesecons_fpga:fpga") ~= 1 then
35+
return itemstack
36+
end
37+
38+
local imeta = itemstack:get_metadata()
39+
if imeta == "" then
40+
minetest.chat_send_player(user:get_player_name(), "Use shift+right-click to copy a gate configuration first.")
41+
return itemstack
42+
end
43+
44+
local meta = minetest.get_meta(pos)
45+
meta:set_string("instr", imeta)
46+
plg.update_formspec(pos, imeta)
47+
minetest.chat_send_player(user:get_player_name(), "Gate configuration was successfully written to FPGA!")
48+
49+
return itemstack
50+
end
51+
})
52+
53+
minetest.register_craft({
54+
output = "mesecons_fpga:programmer",
55+
recipe = {
56+
{'group:mesecon_conductor_craftable'},
57+
{'mesecons_materials:silicon'},
58+
}
59+
})
60+
61+
62+
end

0 commit comments

Comments
 (0)
Please sign in to comment.