diff --git a/sim/main.lua b/sim/main.lua
index 2a566fe..b9bbc1e 100644
--- a/sim/main.lua
+++ b/sim/main.lua
@@ -1,19 +1,22 @@
+-- External requirements
+local socket = require("socket")
+local ffi = require("ffi")
+
+-- Disallow access to undefined global variables (helps detect errors)
assert(getmetatable(_G)==nil, "_G already has a metatable")
setmetatable(_G, {
__index = function(t, i) error("attempt to access nil variable "..i, 2) end
})
+-- Set save directory
OPT_SAVE_DIR = arg[1] or error("must specify save location")
OPT_SAVE_DIR = OPT_SAVE_DIR:gsub("\\", "/")
OPT_SAVE_DIR = OPT_SAVE_DIR:gsub("/$", "")
print("Save location set to \""..OPT_SAVE_DIR.."\"")
-local socket = require("socket")
-local ffi = require("ffi")
-
+-- Local includes
dofile("iosafe.lua")
-
FFI = ffi
dofile("utility.lua")
dofile("simulation.lua")
@@ -24,301 +27,34 @@ dofile("port.lua")
dofile("gate.lua")
dofile("save.lua")
dofile("compile.lua")
+dofile("network.lua")
FFI = nil
+-- Default settings
OPT_TICK_ENABLED = true
OPT_TICK_TIME = 0.032
OPT_TICK_MULT = 1
OPT_FX_UPDATES = true
OPT_FX_TIME = 0.032
-OPT_TICK_INF = 29
-
+-- Tick rate measurement state
local tickdelay = 0
local ticksperinterval = 0
-
-bool_to_int = {[false] = 0, [true] = 1}
-
local lastticktime = 0
local ticks = 0
local tickrate = 0
local lastmeasuretime = 0
local lastfxtime = 0
-
local avgticks = {}
local totalticks = 0
+-- Create new simulation
local sim = Simulation.new(Simulation)
GSim = sim
-local units = {
- "uHz",
- "mHz",
- "Hz",
- "kHz",
- "MHz",
- "GHz",
-}
+network_accept_client()
-local function round(x)
- return math.floor(x+0.5)
-end
-
-local function unitize(v)
- local unit = 1
- v = v*1000000
-
- while v >= 1000 do
- v = v/1000
- unit = unit+1
- end
-
- local s
- if v >= 100 then
- s = "" .. round(v/10)*10
- elseif v >= 10 then
- s = "" .. round(v)
- elseif v >= 1 then
- s = "" .. round(v*10)/10
- if #s == 1 then s = s .. ".0" end
- else
- s = 0
- end
-
- return s .. " " .. units[unit]
-end
-
-function vectotable(vec)
- local tbl = {}
- for comp in string.gmatch(vec, "([^%s]+)") do
- tbl[#tbl+1] = tonumber(comp)
- end
- return tbl
-end
-
-function tabletostring(table)
- local str = tostring(table[1])
- for i = 2, #table do
- str = str .. " " .. tostring(table[i])
- end
- return str
-end
-
-function toboolean(value)
- local num = tonumber(value)
- if num == 1 then
- return true
- else
- return false
- end
-end
-
-local function acceptclient()
- client = server:accept()
- client:settimeout(0)
- local ip, port = client:getsockname()
- print("Connection from " .. ip .. ":" .. port)
-end
-
-server = assert(socket.bind("*", 25000))
-client = nil
-
-local ip, port = server:getsockname()
-print("Server listening on " .. ip .. ":" .. port)
-
-acceptclient()
-
-while 1 do
- local line, err = client:receive()
-
- if not err then
- local data = {}
- local i = 1
- line = line:gsub(";;", "; ;")
- line = line:gsub(";$", "; ")
-
- for str in string.gmatch(line, "([^;]+)") do
- data[i] = str or ""
- i = i + 1
- end
-
- local i = 1
- while i <= #data do
- if data[i] == "W" then
- local min = vectotable(data[i+3])
- local max = vectotable(data[i+4])
- local bounds = {min[1], min[2], min[3], max[1], max[2], max[3]}
-
- local wire = Wire.new(tonumber(data[i+1]), tonumber(data[i+2]), bounds)
- Simulation.addwire(sim, wire)
-
- i = i + 4
- elseif data[i] == "G" then
- local objref = tonumber(data[i+1])
- local definition = Simulation.getdefinitionbyref(sim, tonumber(data[i+2]))
-
- assert(definition, "No gate definition for objref "..objref.." defref "..tonumber(data[i+1]))
-
- local position = vectotable(data[i+3])
- local rotation = tonumber(data[i+4])
- local gate = GateDefinition.constructgate(definition, objref, position, rotation)
-
- Simulation.addgate(sim, gate)
- --print(gate.objref)
- --Gate.init(gate)
- --Gate.logic(gate)
-
- i = i + 4
- elseif data[i] == "RW" then
- Simulation.removewire(sim, tonumber(data[i+1]))
- i = i + 1
- elseif data[i] == "RG" then
- Simulation.removegate(sim, tonumber(data[i+1]))
- i = i + 1
- elseif data[i] == "GD" then
- --print("---------------------------------------[[[[")
- --print(table.concat(data, "]]]]\n[[[[", i, math.min(#data, i+100)))
- --print("]]]]---------------------------------------")
- local objref = tonumber(data[i+1])
- local name = data[i+2]
- local desc = data[i+3]
- local init = data[i+4]
- local logic = data[i+5]
- local input = data[i+6]
- local global = data[i+7]
- local numports = tonumber(data[i+8])
- local ports = {}
-
- for a = i+9, numports*5+i+8, 5 do
- local portd = {
- type = tonumber(data[a]),
- position = vectotable(data[a+1]),
- direction = tonumber(data[a+2]),
- causeupdate = toboolean(data[a+3]),
- name = data[a+4],
- }
- ports[#ports+1] = portd
-
- if not portd.direction then print(line) end
- end
-
- local definition = GateDefinition.new(objref, name, desc, init, logic, input, global, ports)
- Simulation.addgatedefinition(sim, definition)
-
- i = i + 8 + numports*5
- elseif data[i] == "SL" then
- local wire = Simulation.getwirebyref(sim, tonumber(data[i+1]))
- if wire ~= nil then
- Wire.setlayer(wire, tonumber(data[i+2]))
- end
-
- i = i + 2
- elseif data[i] == "OPT" then
- local option = data[i+1]
- local value = tonumber(data[i+2])
-
- if option == "TICK_ENABLED" then
- OPT_TICK_ENABLED = toboolean(value)
- elseif option == "TICK_TIME" then
- if value < 0 or value > 999999 then
- value = 0
- end
- if value<=0.001 then value = 0.0001 end
- OPT_TICK_TIME = value
- elseif option == "FX_UPDATES" then
- OPT_FX_UPDATES = toboolean(value)
- elseif option == "FX_TIME" then
- if value < 0 or value > 999999 then
- value = 0
- end
- OPT_FX_TIME = value
- elseif option=="TICK_MULT" then
- OPT_TICK_MULT = value
- end
-
- i = i + 2
- elseif data[i] == "GINFO" then
- local userid = data[i+1]
- local objref = tonumber(data[i+2])
-
- local info = ""
-
- local wire = Simulation.getwirebyref(sim, objref)
- if wire then
- local group = Wire.getgroup(wire)
- local numwires = 0; for k, wire2 in pairs(group.wires ) do numwires = numwires +1 end
- local numportsi = 0; for k, port in pairs(group.in_ports ) do numportsi = numportsi+1 end
- local numgatesu = group.num_gates_update
- local numportso = 0; local numportson=0;
- for k, port in pairs(group.out_ports) do
- numportso = numportso+1
- if Port.getstate(port)==1 then numportson = numportson+1 end
- end
-
- info = "\\c5Net " .. tostring(group):match("table: 0x(.+)"):upper() .. "\n" .. (Wire.getgroup(wire).state==1 and "\\c2On" or "\\c0Off") .. "\n" ..
- "Wires: "..numwires.."\n"..
- "In Ports: " ..numportsi.."\n"..
- "Out Ports: "..numportso.."\n"..
- "Gates Update: "..numgatesu.."\n"..
- "Out Ports On: "..(group.state_num)
- ;
- end
-
- local gate = Simulation.getgatebyref(sim, objref)
- if gate then
- local def = Gate.getdefinition(gate)
- info = "\\c5" .. def.name .. "
"
- for i = 1, #gate.ports do
- local port = gate.ports[i]
- local state
- if port.type==PortTypes.input then
- state = Gate.getportstate(gate, i)
- else
- state = Port.getstate(port)
- end
- info = info .. (state==1 and "\\c2" or "\\c0") .. def.ports[i].name .. (i ~= #gate.ports and " " or "")
- end
- end
-
- if info ~= "" then
- client:send("GINFO\t" .. userid .. "\t" .. expandescape(info) .. "\n")
- end
-
- i = i + 2
- elseif data[i] == "SINFO" then
- client:send("SINFO\t" .. data[i+1] .. "\t" .. sim.nwires .. "\t" .. sim.ngates .. "\t" .. sim.ninports .. "\t" .. sim.noutports .. "\n")
- i = i + 1
- elseif data[i] == "TICK" then
- Simulation.tickinit(sim)
- Simulation.tickinput(sim)
- Simulation.ticklogic(sim)
- ticks = ticks + 1
- elseif data[i] == "IN" then
- local gate = Simulation.getgatebyref(sim, tonumber(data[i+1]))
- local argc = tonumber(data[i+2])
- local argv = {}
- for a = i+3, i+3+argc-1 do
- argv[#argv+1] = collapseescape(data[a])
- end
- if gate then
- Simulation.queuegateinput(sim, gate, argv)
- end
-
- i = i+2+argc
- elseif data[i] == "SAVE" then
- print("saving all data")
- logicsave()
- else
- print("invalid data "..data[i])
- end
-
- i = i + 1
- end
- elseif err == "closed" then
- sim = Simulation.new(Simulation)
- acceptclient()
- end
-
+while true do
local time = os.clock()
if OPT_TICK_ENABLED then
@@ -348,6 +84,8 @@ while 1 do
end
if time-lastfxtime >= OPT_FX_TIME then
+ network_update()
+
if OPT_FX_UPDATES then
Simulation.sendfxupdate(sim)
end
@@ -365,7 +103,7 @@ while 1 do
ticks = 0
- client:send("TPS\t" .. unitize((totalticks/#avgticks)/0.1) .. "\n")
+ network_send("TPS\t" .. unitize((totalticks/#avgticks)/0.1) .. "\n")
lastmeasuretime = os.clock()
end
end
diff --git a/sim/network.lua b/sim/network.lua
new file mode 100644
index 0000000..0b91bc6
--- /dev/null
+++ b/sim/network.lua
@@ -0,0 +1,213 @@
+
+function network_send(data)
+ client:send(data)
+end
+
+function network_accept_client()
+ server = assert(socket.bind("*", 25000))
+ client = nil
+
+ local ip, port = server:getsockname()
+ print("Server listening on " .. ip .. ":" .. port)
+
+ client = server:accept()
+ client:settimeout(0)
+ local ip, port = client:getsockname()
+ print("Connection from " .. ip .. ":" .. port)
+end
+
+function network_update()
+ local sim = GSim
+
+ local line, err = client:receive()
+
+ if not err then
+ local data = {}
+ local i = 1
+ line = line:gsub(";;", "; ;")
+ line = line:gsub(";$", "; ")
+
+ for str in string.gmatch(line, "([^;]+)") do
+ data[i] = str or ""
+ i = i + 1
+ end
+
+ local i = 1
+ while i <= #data do
+ if data[i] == "W" then
+ local min = vectotable(data[i+3])
+ local max = vectotable(data[i+4])
+ local bounds = {min[1], min[2], min[3], max[1], max[2], max[3]}
+
+ local wire = Wire.new(tonumber(data[i+1]), tonumber(data[i+2]), bounds)
+ Simulation.addwire(sim, wire)
+
+ i = i + 4
+ elseif data[i] == "G" then
+ local objref = tonumber(data[i+1])
+ local definition = Simulation.getdefinitionbyref(sim, tonumber(data[i+2]))
+
+ assert(definition, "No gate definition for objref "..objref.." defref "..tonumber(data[i+1]))
+
+ local position = vectotable(data[i+3])
+ local rotation = tonumber(data[i+4])
+ local gate = GateDefinition.constructgate(definition, objref, position, rotation)
+
+ Simulation.addgate(sim, gate)
+ --print(gate.objref)
+ --Gate.init(gate)
+ --Gate.logic(gate)
+
+ i = i + 4
+ elseif data[i] == "RW" then
+ Simulation.removewire(sim, tonumber(data[i+1]))
+ i = i + 1
+ elseif data[i] == "RG" then
+ Simulation.removegate(sim, tonumber(data[i+1]))
+ i = i + 1
+ elseif data[i] == "GD" then
+ --print("---------------------------------------[[[[")
+ --print(table.concat(data, "]]]]\n[[[[", i, math.min(#data, i+100)))
+ --print("]]]]---------------------------------------")
+ local objref = tonumber(data[i+1])
+ local name = data[i+2]
+ local desc = data[i+3]
+ local init = data[i+4]
+ local logic = data[i+5]
+ local input = data[i+6]
+ local global = data[i+7]
+ local numports = tonumber(data[i+8])
+ local ports = {}
+
+ for a = i+9, numports*5+i+8, 5 do
+ local portd = {
+ type = tonumber(data[a]),
+ position = vectotable(data[a+1]),
+ direction = tonumber(data[a+2]),
+ causeupdate = toboolean(data[a+3]),
+ name = data[a+4],
+ }
+ ports[#ports+1] = portd
+
+ if not portd.direction then print(line) end
+ end
+
+ local definition = GateDefinition.new(objref, name, desc, init, logic, input, global, ports)
+ Simulation.addgatedefinition(sim, definition)
+
+ i = i + 8 + numports*5
+ elseif data[i] == "SL" then
+ local wire = Simulation.getwirebyref(sim, tonumber(data[i+1]))
+ if wire ~= nil then
+ Wire.setlayer(wire, tonumber(data[i+2]))
+ end
+
+ i = i + 2
+ elseif data[i] == "OPT" then
+ local option = data[i+1]
+ local value = tonumber(data[i+2])
+
+ if option == "TICK_ENABLED" then
+ OPT_TICK_ENABLED = toboolean(value)
+ elseif option == "TICK_TIME" then
+ if value < 0 or value > 999999 then
+ value = 0
+ end
+ if value<=0.001 then value = 0.0001 end
+ OPT_TICK_TIME = value
+ elseif option == "FX_UPDATES" then
+ OPT_FX_UPDATES = toboolean(value)
+ elseif option == "FX_TIME" then
+ if value < 0 or value > 999999 then
+ value = 0
+ end
+ OPT_FX_TIME = value
+ elseif option=="TICK_MULT" then
+ OPT_TICK_MULT = value
+ end
+
+ i = i + 2
+ elseif data[i] == "GINFO" then
+ local userid = data[i+1]
+ local objref = tonumber(data[i+2])
+
+ local info = ""
+
+ local wire = Simulation.getwirebyref(sim, objref)
+ if wire then
+ local group = Wire.getgroup(wire)
+ local numwires = 0; for k, wire2 in pairs(group.wires ) do numwires = numwires +1 end
+ local numportsi = 0; for k, port in pairs(group.in_ports ) do numportsi = numportsi+1 end
+ local numgatesu = group.num_gates_update
+ local numportso = 0; local numportson=0;
+ for k, port in pairs(group.out_ports) do
+ numportso = numportso+1
+ if Port.getstate(port)==1 then numportson = numportson+1 end
+ end
+
+ info = "\\c5Net " .. tostring(group):match("table: 0x(.+)"):upper() .. "\n" .. (Wire.getgroup(wire).state==1 and "\\c2On" or "\\c0Off") .. "\n" ..
+ "Wires: "..numwires.."\n"..
+ "In Ports: " ..numportsi.."\n"..
+ "Out Ports: "..numportso.."\n"..
+ "Gates Update: "..numgatesu.."\n"..
+ "Out Ports On: "..(group.state_num)
+ ;
+ end
+
+ local gate = Simulation.getgatebyref(sim, objref)
+ if gate then
+ local def = Gate.getdefinition(gate)
+ info = "\\c5" .. def.name .. "
"
+ for i = 1, #gate.ports do
+ local port = gate.ports[i]
+ local state
+ if port.type==PortTypes.input then
+ state = Gate.getportstate(gate, i)
+ else
+ state = Port.getstate(port)
+ end
+ info = info .. (state==1 and "\\c2" or "\\c0") .. def.ports[i].name .. (i ~= #gate.ports and " " or "")
+ end
+ end
+
+ if info ~= "" then
+ network_send("GINFO\t" .. userid .. "\t" .. expandescape(info) .. "\n")
+ end
+
+ i = i + 2
+ elseif data[i] == "SINFO" then
+ network_send("SINFO\t" .. data[i+1] .. "\t" .. sim.nwires .. "\t" .. sim.ngates .. "\t" .. sim.ninports .. "\t" .. sim.noutports .. "\n")
+ i = i + 1
+ elseif data[i] == "TICK" then
+ Simulation.tickinit(sim)
+ Simulation.tickinput(sim)
+ Simulation.ticklogic(sim)
+ ticks = ticks + 1
+ elseif data[i] == "IN" then
+ local gate = Simulation.getgatebyref(sim, tonumber(data[i+1]))
+ local argc = tonumber(data[i+2])
+ local argv = {}
+ for a = i+3, i+3+argc-1 do
+ argv[#argv+1] = collapseescape(data[a])
+ end
+ if gate then
+ Simulation.queuegateinput(sim, gate, argv)
+ end
+
+ i = i+2+argc
+ elseif data[i] == "SAVE" then
+ print("saving all data")
+ logicsave()
+ else
+ print("invalid data "..data[i])
+ end
+
+ i = i + 1
+ end
+ elseif err == "closed" then
+ --sim = Simulation.new(Simulation)
+ --acceptclient()
+ print("Connection closed")
+ error()
+ end
+end
\ No newline at end of file
diff --git a/sim/simulation.lua b/sim/simulation.lua
index 4d2a86e..6eb8de9 100644
--- a/sim/simulation.lua
+++ b/sim/simulation.lua
@@ -365,7 +365,7 @@ function Simulation.sendfxupdate(sim)
data = data .. "\t" .. Wire.getobjref(wire)
end
- client:send("WU\t" .. data .. "\n")
+ network_send("WU\t" .. data .. "\n")
end
end
@@ -384,7 +384,7 @@ function Simulation.sendcallbacks(sim)
data = data .. "\t" .. objref .. "\t"..(#escargs)..(#escargs>0 and ("\t"..table.concat(escargs, "\t")) or "")
end
- client:send(data .. "\n")
+ network_send(data .. "\n")
sim.callbacks = nil
end
end
diff --git a/sim/utility.lua b/sim/utility.lua
index b9520b7..e8df85d 100644
--- a/sim/utility.lua
+++ b/sim/utility.lua
@@ -1,4 +1,6 @@
+bool_to_int = {[false] = 0, [true] = 1}
+
local escapes = {
{"\\", "b"},
{"\t", "t"},
@@ -85,3 +87,64 @@ function array_add(array, value)
end
table.insert(array, value)
end
+
+function round(x)
+ return math.floor(x+0.5)
+end
+
+local units = {
+ "uHz",
+ "mHz",
+ "Hz",
+ "kHz",
+ "MHz",
+ "GHz",
+}
+function unitize(v)
+ local unit = 1
+ v = v*1000000
+
+ while v >= 1000 do
+ v = v/1000
+ unit = unit+1
+ end
+
+ local s
+ if v >= 100 then
+ s = "" .. round(v/10)*10
+ elseif v >= 10 then
+ s = "" .. round(v)
+ elseif v >= 1 then
+ s = "" .. round(v*10)/10
+ if #s == 1 then s = s .. ".0" end
+ else
+ s = 0
+ end
+
+ return s .. " " .. units[unit]
+end
+
+function vectotable(vec)
+ local tbl = {}
+ for comp in string.gmatch(vec, "([^%s]+)") do
+ tbl[#tbl+1] = tonumber(comp)
+ end
+ return tbl
+end
+
+function tabletostring(table)
+ local str = tostring(table[1])
+ for i = 2, #table do
+ str = str .. " " .. tostring(table[i])
+ end
+ return str
+end
+
+function toboolean(value)
+ local num = tonumber(value)
+ if num == 1 then
+ return true
+ else
+ return false
+ end
+end
diff --git a/sim/wire.lua b/sim/wire.lua
index 93f290b..fee0308 100644
--- a/sim/wire.lua
+++ b/sim/wire.lua
@@ -20,7 +20,7 @@ function Wire.setlayer(wire, layer)
end
function Wire.update(wire)
- client:send("WU\t" .. (wire.group.state~=0 and "1" or "0") .. "\t" .. wire.objref .. "\n")
+ network_send("WU\t" .. (wire.group.state~=0 and "1" or "0") .. "\t" .. wire.objref .. "\n")
end
function Wire.setgroup(wire, group)