Lets upload the really bad emulator code!

This commit is contained in:
gamax92 2015-04-22 00:31:15 -06:00
parent 4a02fc5739
commit e2355d6dad
23 changed files with 2341 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
src/lua
src/loot
src/unifont.hex
ocemu2d.love
log.txt

21
Makefile Normal file
View File

@ -0,0 +1,21 @@
ASSETS=https://github.com/MightyPirates/OpenComputers/trunk/src/main/resources/assets/opencomputers
all: src/lua src/loot src/unifont.hex ocemu2d
src/lua:
svn export $(ASSETS)/lua src/lua
src/loot:
svn export $(ASSETS)/loot src/loot
src/unifont.hex:
svn export $(ASSETS)/unifont.hex src/unifont.hex
ocemu2d:
cd src; zip -r -9 ../ocemu2d.love *; cd ..
clean:
rm -f ocemu2d.love
rm -rf src/lua
rm -rf src/loot
rm -f src/unifont.hex

View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013 Hisham Muhammad
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,19 @@
Copyright © 2015 blitmap <coroutines@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the “Software”), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

29
list.lua Normal file
View File

@ -0,0 +1,29 @@
local args = { ... }
if #args ~= 1 then
return
end
local component = require("component")
local address = component.get(args[1])
local proxy = component.proxy(address)
local keys = {}
for k,v in pairs(proxy) do
if type(v) == "table" then
keys[#keys+1] = k
end
end
table.sort(keys,function(a,b) return a:reverse() < b:reverse() end)
local file = io.open("list.txt","wb")
file = file:write("-- " .. proxy.type .. " component\nlocal obj = {}\n\n")
for i = 1,#keys do
local k = keys[i]
local doc = ""
local comment = "-- no doc"
if component.doc(address,k) ~= nil then
doc = component.doc(address,k):match("%((.-)%)"):gsub("[%[%]]","") .. ","
doc = doc:gsub("(.-):.-,",function(a) return a .. "," end):sub(1,-2)
comment = component.doc(address,k):match("%-%-.*")
end
file:write("function obj." .. k .. "(" .. doc .. ") " .. comment .."\n\t--STUB\n\tcprint(\"" .. proxy.type .. "." .. k .. "\"" .. (doc ~= "" and "," or "") .. doc .. ")\nend\n")
end
file:write("\nlocal cec = {}\n\nreturn obj,cec")
file:close()

139
src/apis/component.lua Normal file
View File

@ -0,0 +1,139 @@
local env = ...
local r = math.random
function gen_uuid()
return string.format("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
r(0,255),r(0,255),r(0,255),r(0,255),
r(0,255),r(0,255),
r(64,79),r(0,255),
r(128,191),r(0,255),
r(0,255),r(0,255),r(0,255),r(0,255),r(0,255),r(0,255))
end
local proxylist = {}
local slotlist = {}
local emuicc = {}
local doclist = {}
-- Load components
local components = conf.components
for k,v in pairs(components) do
local address
if type(v[2]) == "string" then
address = v[2]
else
math.randomseed(type(v[2]) == "number" and v[2] or k)
address = gen_uuid()
end
v[2] = address
local proxy, cec, doc = love.filesystem.load("component/" .. v[1] .. ".lua")(table.unpack(v,2))
proxy.address = address
proxy.type = v[1]
proxylist[address] = proxy
emuicc[address] = cec
doclist[address] = doc
slotlist[address] = v[3]
end
component = {}
function component.exists(address)
checkArg(1,address,"string")
if proxylist[address] ~= nil then
return proxylist[address].type
end
end
function component.list(filter, exact)
checkArg(1,filter,"string","nil")
local data = {}
local tbl = {}
for k,v in pairs(proxylist) do
if filter == nil or (exact and v.type == filter) or (not exact and v.type:find(filter, nil, true)) then
data[#data + 1] = k
data[#data + 1] = v.type
tbl[k] = v.type
end
end
local place = 1
return setmetatable(tbl,{__call = function()
local addr,type = data[place], data[place + 1]
place = place + 2
return addr,type
end})
end
function component.invoke(address, method, ...)
checkArg(1,address,"string")
checkArg(2,method,"string")
if proxylist[address] ~= nil then
if proxylist[address][method] == nil then
error("no such method",2)
end
return proxylist[address][method](...)
end
end
function component.cecinvoke(address, method, ...)
checkArg(1,address,"string")
checkArg(2,method,"string")
if emuicc[address] ~= nil then
if emuicc[address][method] == nil then
error("no such method",2)
end
return emuicc[address][method](...)
end
end
env.component = {list = component.list}
function env.component.type(address)
checkArg(1,address,"string")
if proxylist[address] ~= nil then
return proxylist[address].type
end
return nil, "no such component"
end
function env.component.slot(address)
checkArg(1,address,"string")
if proxylist[address] ~= nil then
return slotlist[address] or -1
end
return nil, "no such component"
end
function env.component.methods(address)
checkArg(1,address,"string")
if proxylist[address] ~= nil then
local methods = {}
for k,v in pairs(proxylist[address]) do
if type(v) == "function" then
methods[k] = {direct=true} -- TODO: getter, setter?
end
end
return methods
end
end
function env.component.invoke(address, method, ...)
checkArg(1,address,"string")
checkArg(2,method,"string")
if proxylist[address] ~= nil then
if proxylist[address][method] == nil then
error("no such method",2)
end
return true, proxylist[address][method](...)
end
end
function env.component.doc(address, method)
checkArg(1,address,"string")
checkArg(2,method,"string")
if proxylist[address] ~= nil then
if proxylist[address][method] == nil then
error("no such method",2)
end
if doclist[address] ~= nil then
return doclist[address][method]
end
return nil
end
end

70
src/apis/computer.lua Normal file
View File

@ -0,0 +1,70 @@
local env = ...
local tmpaddr = "tmp-address"
computer = {}
function computer.setTempAddress(str)
tmpaddr = str
end
env.computer = {}
function env.computer.realTime()
--STUB
--cprint("computer.realTime") -- Spammy
return 0
end
function env.computer.uptime()
--STUB
cprint("computer.uptime")
return love.timer.getTime() - machine.starttime
end
function env.computer.address()
cprint("computer.address")
return component.list("computer",true)()
end
function env.computer.freeMemory()
--STUB
cprint("computer.freeMemory")
return 10000
end
function env.computer.totalMemory()
--STUB
cprint("computer.totalMemory")
return 10000
end
function env.computer.pushSignal(name, ...)
--TODO
cprint("computer.pushSignal", name, ...)
table.insert(machine.signals, {name, ... })
end
function env.computer.tmpAddress()
--STUB
cprint("computer.tmpAddress")
return tmpaddr
end
function env.computer.users()
--STUB
cprint("computer.users")
end
function env.computer.addUser(username)
--STUB
cprint("computer.addUser", username)
return nil, "player must be online"
end
function env.computer.removeUser(username)
--STUB
cprint("computer.removeUser", username)
return false
end
function env.computer.energy()
--STUB
cprint("computer.energy")
return math.huge
end
function env.computer.maxEnergy()
--STUB
cprint("computer.maxEnergy")
return math.huge
end

1
src/apis/os.lua Normal file
View File

@ -0,0 +1 @@
local env = ...

14
src/apis/system.lua Normal file
View File

@ -0,0 +1,14 @@
local env = ...
env.system = {}
function env.system.allowBytecode()
--STUB, move to a config
cprint("system.allowBytecode")
return false
end
function env.system.timeout()
--STUB, move to a config
cprint("system.timeout")
return math.huge
end

86
src/apis/unicode.lua Normal file
View File

@ -0,0 +1,86 @@
local env = ...
local utf8 = require('utf8_simple')
local lua_utf8 = require("utf8")
function z(val)
local size = val < 0x10000 and (val < 0x800 and (val < 0x80 and 1 or 2) or 3) or 4
if size == 1 then return string.char(val) end
local b = {string.char((240*2^(4-size)%256)+(val/2^(size*6-6))%(2^(7-size)))}
for i = size*6-12,0,-6 do
b[#b+1] = string.char(128+(val/2^i)%64)
end
return table.concat(b)
end
env.unicode = {}
function env.unicode.lower(str)
-- STUB
cprint("unicode.lower", str)
checkArg(1,str,"string")
return string.lower(str)
end
function env.unicode.upper(str)
-- STUB
cprint("unicode.upper", str)
checkArg(1,str,"string")
return string.upper(str)
end
function env.unicode.char(...)
-- TODO
cprint("unicode.char", ...)
--return lua_utf8.char(...) -- Why does this return "%U"
local output = {}
local codes = { ... }
for i = 1,#codes do
output[i] = z(codes[i])
end
return table.concat(output)
end
function env.unicode.len(str)
-- TODO
cprint("unicode.len", str)
checkArg(1,str,"string")
return lua_utf8.len(str)
end
function env.unicode.reverse(str)
-- TODO
cprint("unicode.reverse", str)
checkArg(1,str,"string")
return utf8.reverse(str)
end
function env.unicode.sub(str, i, j)
-- TODO
cprint("unicode.sub", str, i, j)
return utf8.sub(str, i, j)
end
function env.unicode.isWide(str)
-- STUB
cprint("unicode.isWide", str)
checkArg(1,str,"string")
return false
end
function env.unicode.charWidth(str)
-- STUB
cprint("unicode.charWidth", str)
checkArg(1,str,"string")
return 1
end
function env.unicode.wlen(str)
-- STUB
cprint("unicode.wlen", str)
checkArg(1,str,"string")
return lua_utf8.len(str)
end
function env.unicode.wtrunc(str, count)
-- STUB
cprint("unicode.wtrunc", str, count)
checkArg(1,str,"string")
checkArg(2,count,"number")
local len = lua_utf8.len(str)
if count >= len then
error("String index out of range: " .. len,2)
end
return utf8.sub(str, 1, count-1)
end

1
src/apis/userdata.lua Normal file
View File

@ -0,0 +1 @@
local env = ...

648
src/compat52.lua Normal file
View File

@ -0,0 +1,648 @@
-- utility module to make the Lua 5.1 standard libraries behave more like Lua 5.2
if _VERSION == "Lua 5.1" then
local _type, _select, _unpack, _error = type, select, unpack, error
bit32 = require("bit")
-- detect LuaJIT (including LUAJIT_ENABLE_LUA52COMPAT compilation flag)
local is_luajit = (string.dump(function() end) or ""):sub(1, 3) == "\027LJ"
local is_luajit52 = is_luajit and
#setmetatable({}, { __len = function() return 1 end }) == 1
local weak_meta = { __mode = "kv" }
-- table that maps each running coroutine to the coroutine that resumed it
-- this is used to build complete tracebacks when "coroutine-friendly" pcall
-- is used.
local pcall_previous = setmetatable({}, weak_meta)
local pcall_callOf = setmetatable({}, weak_meta)
local xpcall_running = setmetatable({}, weak_meta)
local coroutine_running = coroutine.running
-- the most powerful getmetatable we can get (preferably from debug)
local sudo_getmetatable = getmetatable
if _type(debug) == "table" then
if _type(debug.getmetatable) == "function" then
sudo_getmetatable = debug.getmetatable
end
if not is_luajit52 then
local _G, package = _G, package
local debug_setfenv = debug.setfenv
debug.setuservalue = function(obj, value)
if _type(obj) ~= "userdata" then
_error("bad argument #1 to 'setuservalue' (userdata expected, got "..
_type(obj)..")", 2)
end
if value == nil then value = _G end
if _type(value) ~= "table" then
_error("bad argument #2 to 'setuservalue' (table expected, got "..
_type(value)..")", 2)
end
return debug_setfenv(obj, value)
end
local debug_getfenv = debug.getfenv
debug.getuservalue = function(obj)
if _type(obj) ~= "userdata" then
return nil
else
local v = debug_getfenv(obj)
if v == _G or v == package then
return nil
end
return v
end
end
local debug_setmetatable = debug.setmetatable
if _type(debug_setmetatable) == "function" then
debug.setmetatable = function(value, tab)
debug_setmetatable(value, tab)
return value
end
end
end -- not luajit with compat52 enabled
if not is_luajit then
local debug_getinfo = debug.getinfo
local function calculate_trace_level(co, level)
if level ~= nil then
for out = 1, 1/0 do
local info = (co==nil) and debug_getinfo(out, "") or debug_getinfo(co, out, "")
if info == nil then
local max = out-1
if level <= max then
return level
end
return nil, level-max
end
end
end
return 1
end
local stack_pattern = "\nstack traceback:"
local stack_replace = ""
local debug_traceback = debug.traceback
debug.traceback = function (co, msg, level)
local lvl
local nilmsg
if _type(co) ~= "thread" then
co, msg, level = coroutine_running(), co, msg
end
if msg == nil then
msg = ""
nilmsg = true
elseif _type(msg) ~= "string" then
return msg
end
if co == nil then
msg = debug_traceback(msg, level or 1)
else
local xpco = xpcall_running[co]
if xpco ~= nil then
lvl, level = calculate_trace_level(xpco, level)
if lvl then
msg = debug_traceback(xpco, msg, lvl)
else
msg = msg..stack_pattern
end
lvl, level = calculate_trace_level(co, level)
if lvl then
local trace = debug_traceback(co, "", lvl)
msg = msg..trace:gsub(stack_pattern, stack_replace)
end
else
co = pcall_callOf[co] or co
lvl, level = calculate_trace_level(co, level)
if lvl then
msg = debug_traceback(co, msg, lvl)
else
msg = msg..stack_pattern
end
end
co = pcall_previous[co]
while co ~= nil do
lvl, level = calculate_trace_level(co, level)
if lvl then
local trace = debug_traceback(co, "", lvl)
msg = msg..trace:gsub(stack_pattern, stack_replace)
end
co = pcall_previous[co]
end
end
if nilmsg then
msg = msg:gsub("^\n", "")
end
msg = msg:gsub("\n\t%(tail call%): %?", "\000")
msg = msg:gsub("\n\t%.%.%.\n", "\001\n")
msg = msg:gsub("\n\t%.%.%.$", "\001")
msg = msg:gsub("(%z+)\001(%z+)", function(some, other)
return "\n\t(..."..#some+#other.."+ tail call(s)...)"
end)
msg = msg:gsub("\001(%z+)", function(zeros)
return "\n\t(..."..#zeros.."+ tail call(s)...)"
end)
msg = msg:gsub("(%z+)\001", function(zeros)
return "\n\t(..."..#zeros.."+ tail call(s)...)"
end)
msg = msg:gsub("%z+", function(zeros)
return "\n\t(..."..#zeros.." tail call(s)...)"
end)
msg = msg:gsub("\001", function(zeros)
return "\n\t..."
end)
return msg
end
end -- is not luajit
end -- debug table available
if not is_luajit52 then
local _pairs = pairs
pairs = function(t)
local mt = sudo_getmetatable(t)
if _type(mt) == "table" and _type(mt.__pairs) == "function" then
return mt.__pairs(t)
else
return _pairs(t)
end
end
local _ipairs = ipairs
ipairs = function(t)
local mt = sudo_getmetatable(t)
if _type(mt) == "table" and _type(mt.__ipairs) == "function" then
return mt.__ipairs(t)
else
return _ipairs(t)
end
end
end -- not luajit with compat52 enabled
if not is_luajit then
local function check_mode(mode, prefix)
local has = { text = false, binary = false }
for i = 1,#mode do
local c = mode:sub(i, i)
if c == "t" then has.text = true end
if c == "b" then has.binary = true end
end
local t = prefix:sub(1, 1) == "\27" and "binary" or "text"
if not has[t] then
return "attempt to load a "..t.." chunk (mode is '"..mode.."')"
end
end
local _setfenv = setfenv
local _load, _loadstring = load, loadstring
load = function(ld, source, mode, env)
mode = mode or "bt"
local chunk, msg
if _type( ld ) == "string" then
if mode ~= "bt" then
local merr = check_mode(mode, ld)
if merr then return nil, merr end
end
chunk, msg = _loadstring(ld, source)
else
local ld_type = _type(ld)
if ld_type ~= "function" then
_error("bad argument #1 to 'load' (function expected, got "..ld_type..")", 2)
end
if mode ~= "bt" then
local checked, merr = false, nil
local function checked_ld()
if checked then
return ld()
else
checked = true
local v = ld()
merr = check_mode(mode, v or "")
if merr then return nil end
return v
end
end
chunk, msg = _load(checked_ld, source)
if merr then return nil, merr end
else
chunk, msg = _load(ld, source)
end
end
if not chunk then
return chunk, msg
end
if env ~= nil then
_setfenv(chunk, env)
end
return chunk
end
loadstring = load
local _loadfile = loadfile
local io_open = io.open
loadfile = function(file, mode, env)
mode = mode or "bt"
if mode ~= "bt" then
local f = io_open(file, "rb")
if f then
local prefix = f:read(1)
f:close()
if prefix then
local merr = check_mode(mode, prefix)
if merr then return nil, merr end
end
end
end
local chunk, msg = _loadfile(file)
if not chunk then
return chunk, msg
end
if env ~= nil then
_setfenv(chunk, env)
end
return chunk
end
end -- not luajit
if not is_luajit52 then
function rawlen(v)
local t = _type(v)
if t ~= "string" and t ~= "table" then
_error("bad argument #1 to 'rawlen' (table or string expected)", 2)
end
return #v
end
end -- not luajit with compat52 enabled
local gc_isrunning = true
local _collectgarbage = collectgarbage
local math_floor = math.floor
collectgarbage = function(opt, ...)
opt = opt or "collect"
local v = 0
if opt == "collect" then
v = _collectgarbage(opt, ...)
if not gc_isrunning then _collectgarbage("stop") end
elseif opt == "stop" then
gc_isrunning = false
return _collectgarbage(opt, ...)
elseif opt == "restart" then
gc_isrunning = true
return _collectgarbage(opt, ...)
elseif opt == "count" then
v = _collectgarbage(opt, ...)
return v, (v-math_floor(v))*1024
elseif opt == "step" then
v = _collectgarbage(opt, ...)
if not gc_isrunning then _collectgarbage("stop") end
elseif opt == "isrunning" then
return gc_isrunning
elseif opt ~= "generational" and opt ~= "incremental" then
return _collectgarbage(opt, ...)
end
return v
end
if not is_luajit52 then
local os_execute = os.execute
local bit32_rshift = bit32.rshift
os.execute = function(cmd)
local code = os_execute(cmd)
-- Lua 5.1 does not report exit by signal.
if code == 0 then
return true, "exit", code
else
return nil, "exit", bit32_rshift(code, 8)
end
end
end -- not luajit with compat52 enabled
if not is_luajit52 then
table.pack = function(...)
return { n = _select('#', ...), ... }
end
table.unpack = _unpack
end -- not luajit with compat52 enabled
local main_coroutine = coroutine.create(function() end)
local _assert = assert
local _pcall = pcall
local coroutine_create = coroutine.create
coroutine.create = function (func)
local success, result = _pcall(coroutine_create, func)
if not success then
_assert(_type(func) == "function", "bad argument #1 (function expected)")
result = coroutine_create(function(...) return func(...) end)
end
return result
end
local pcall_mainOf = setmetatable({}, weak_meta)
if not is_luajit52 then
coroutine.running = function()
local co = coroutine_running()
if co then
return pcall_mainOf[co] or co, false
else
return main_coroutine, true
end
end
end
local coroutine_yield = coroutine.yield
coroutine.yield = function(...)
local co, flag = coroutine_running()
if co and not flag then
return coroutine_yield(...)
else
_error("attempt to yield from outside a coroutine", 0)
end
end
if not is_luajit then
local coroutine_resume = coroutine.resume
coroutine.resume = function(co, ...)
if co == main_coroutine then
return false, "cannot resume non-suspended coroutine"
else
return coroutine_resume(co, ...)
end
end
local coroutine_status = coroutine.status
coroutine.status = function(co)
local notmain = coroutine_running()
if co == main_coroutine then
return notmain and "normal" or "running"
else
return coroutine_status(co)
end
end
local function pcall_results(current, call, success, ...)
if coroutine_status(call) == "suspended" then
return pcall_results(current, call, coroutine_resume(call, coroutine_yield(...)))
end
if pcall_previous then
pcall_previous[call] = nil
local main = pcall_mainOf[call]
if main == current then current = nil end
pcall_callOf[main] = current
end
pcall_mainOf[call] = nil
return success, ...
end
local function pcall_exec(current, call, ...)
local main = pcall_mainOf[current] or current
pcall_mainOf[call] = main
if pcall_previous then
pcall_previous[call] = current
pcall_callOf[main] = call
end
return pcall_results(current, call, coroutine_resume(call, ...))
end
local coroutine_create52 = coroutine.create
local function pcall_coroutine(func)
if _type(func) ~= "function" then
local callable = func
func = function (...) return callable(...) end
end
return coroutine_create52(func)
end
pcall = function (func, ...)
local current = coroutine_running()
if not current then return _pcall(func, ...) end
return pcall_exec(current, pcall_coroutine(func), ...)
end
local _tostring = tostring
local function xpcall_catch(current, call, msgh, success, ...)
if not success then
xpcall_running[current] = call
local ok, result = _pcall(msgh, ...)
xpcall_running[current] = nil
if not ok then
return false, "error in error handling (".._tostring(result)..")"
end
return false, result
end
return true, ...
end
local _xpcall = xpcall
xpcall = function(f, msgh, ...)
local current = coroutine_running()
if not current then
local args, n = { ... }, _select('#', ...)
return _xpcall(function() return f(_unpack(args, 1, n)) end, msgh)
end
local call = pcall_coroutine(f)
return xpcall_catch(current, call, msgh, pcall_exec(current, call, ...))
end
end -- not luajit
if not is_luajit then
local math_log = math.log
math.log = function(x, base)
if base ~= nil then
return math_log(x)/math_log(base)
else
return math_log(x)
end
end
end
local package = package
if not is_luajit then
local io_open = io.open
local table_concat = table.concat
package.searchpath = function(name, path, sep, rep)
sep = (sep or "."):gsub("(%p)", "%%%1")
rep = (rep or package.config:sub(1, 1)):gsub("(%%)", "%%%1")
local pname = name:gsub(sep, rep):gsub("(%%)", "%%%1")
local msg = {}
for subpath in path:gmatch("[^;]+") do
local fpath = subpath:gsub("%?", pname)
local f = io_open(fpath, "r")
if f then
f:close()
return fpath
end
msg[#msg+1] = "\n\tno file '" .. fpath .. "'"
end
return nil, table_concat(msg)
end
end -- not luajit
local p_index = { searchers = package.loaders }
local _rawset = rawset
setmetatable(package, {
__index = p_index,
__newindex = function(p, k, v)
if k == "searchers" then
_rawset(p, "loaders", v)
p_index.searchers = v
else
_rawset(p, k, v)
end
end
})
local string_gsub = string.gsub
local function fix_pattern(pattern)
return (string_gsub(pattern, "%z", "%%z"))
end
local string_find = string.find
function string.find(s, pattern, ...)
return string_find(s, fix_pattern(pattern), ...)
end
local string_gmatch = string.gmatch
function string.gmatch(s, pattern)
return string_gmatch(s, fix_pattern(pattern))
end
function string.gsub(s, pattern, ...)
return string_gsub(s, fix_pattern(pattern), ...)
end
local string_match = string.match
function string.match(s, pattern, ...)
return string_match(s, fix_pattern(pattern), ...)
end
if not is_luajit then
local string_rep = string.rep
function string.rep(s, n, sep)
if sep ~= nil and sep ~= "" and n >= 2 then
return s .. string_rep(sep..s, n-1)
else
return string_rep(s, n)
end
end
end -- not luajit
if not is_luajit then
local _tostring = tostring
local string_format = string.format
do
local addqt = {
["\n"] = "\\\n",
["\\"] = "\\\\",
["\""] = "\\\""
}
local function addquoted(c)
return addqt[c] or string_format("\\%03d", c:byte())
end
function string.format(fmt, ...)
local args, n = { ... }, _select('#', ...)
local i = 0
local function adjust_fmt(lead, mods, kind)
if #lead % 2 == 0 then
i = i + 1
if kind == "s" then
args[i] = _tostring(args[i])
elseif kind == "q" then
args[i] = '"'..string_gsub(args[i], "[%z%c\\\"\n]", addquoted)..'"'
return lead.."%"..mods.."s"
end
end
end
fmt = string_gsub(fmt, "(%%*)%%([%d%.%-%+%# ]*)(%a)", adjust_fmt)
return string_format(fmt, _unpack(args, 1, n))
end
end
end -- not luajit
local io_open = io.open
local io_write = io.write
local io_output = io.output
function io.write(...)
local res, msg, errno = io_write(...)
if res then
return io_output()
else
return nil, msg, errno
end
end
if not is_luajit then
local lines_iterator
do
local function helper( st, var_1, ... )
if var_1 == nil then
if st.doclose then st.f:close() end
if (...) ~= nil then
_error((...), 2)
end
end
return var_1, ...
end
function lines_iterator(st)
return helper(st, st.f:read(_unpack(st, 1, st.n)))
end
end
local valid_format = { ["*l"] = true, ["*n"] = true, ["*a"] = true }
local io_input = io.input
function io.lines(fname, ...)
local doclose, file, msg
if fname ~= nil then
doclose, file, msg = true, io_open(fname, "r")
if not file then _error(msg, 2) end
else
doclose, file = false, io_input()
end
local st = { f=file, doclose=doclose, n=_select('#', ...), ... }
for i = 1, st.n do
if _type(st[i]) ~= "number" and not valid_format[st[i]] then
_error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2)
end
end
return lines_iterator, st
end
do
local io_stdout = io.stdout
local io_type = io.type
local file_meta = sudo_getmetatable(io_stdout)
if _type(file_meta) == "table" and _type(file_meta.__index) == "table" then
local file_write = file_meta.__index.write
file_meta.__index.write = function(self, ...)
local res, msg, errno = file_write(self, ...)
if res then
return self
else
return nil, msg, errno
end
end
file_meta.__index.lines = function(self, ...)
if io_type(self) == "closed file" then
_error("attempt to use a closed file", 2)
end
local st = { f=self, doclose=false, n=_select('#', ...), ... }
for i = 1, st.n do
if _type(st[i]) ~= "number" and not valid_format[st[i]] then
_error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2)
end
end
return lines_iterator, st
end
end
end
end -- not luajit
end

107
src/compat52/mstrict.lua Normal file
View File

@ -0,0 +1,107 @@
--- Stricter version of compat52.
-- Attempts to emulate Lua 5.2 when built without LUA_COMPAT_ALL.
if _VERSION == "Lua 5.1" then
require("compat52")
local function not_available()
error("This function is not available in Lua 5.2!", 2)
end
local exclude_from_G = {
module = not_available,
getfenv = not_available,
setfenv = not_available,
loadstring = not_available,
unpack = not_available,
loadlib = not_available,
math = {
log10 = not_available,
mod = not_available,
},
table = {
getn = not_available,
setn = not_available,
},
string = {
gfind = not_available,
},
}
local next = next
local function make_pairs_iterator(lookup)
return function(st, var)
local k, v = next(st, var)
if k ~= nil then
local new_v = lookup[k]
if new_v ~= nil then v = new_v end
return k, v
end
end
end
local rawget = rawget
local function make_ipairs_iterator(lookup)
return function(st, var)
var = var + 1
local v = rawget(st, var)
if v ~= nil then
local new_v = lookup[var]
if new_v ~= nil then v = new_v end
return var, v
end
end
end
local function make_copy(value, excl)
local v_type, e_type = type(value), type(excl)
if v_type == e_type then
if v_type == "table" then
local l_table = {}
for k, v in pairs(excl) do
l_table[k] = make_copy(rawget(value, k), v)
end
local pairs_iterator = make_pairs_iterator(l_table)
local ipairs_iterator = make_ipairs_iterator(l_table)
return setmetatable({}, {
__index = function(_, k)
local v = l_table[k]
if v ~= nil then
return v
else
return value[k]
end
end,
__newindex = function(_, k, v)
if l_table[k] ~= nil then
l_table[k] = nil
end
value[k] = v
end,
__pairs = function()
return pairs_iterator, value, nil
end,
__ipairs = function()
return ipairs_iterator, value, 0
end,
}), l_table
elseif v_type == "function" then
return excl
end
end
end
local new_G, G_lookup = make_copy(_G, exclude_from_G)
G_lookup._G = new_G
return function()
setfenv(2, new_G)
end
else
return function() end
end
-- vi: set expandtab softtabstop=3 shiftwidth=3 :

23
src/compat52/strict.lua Normal file
View File

@ -0,0 +1,23 @@
--- Stricter version of compat52.
-- Attempts to emulate Lua 5.2 when built without LUA_COMPAT_ALL.
require("compat52")
if _VERSION == "Lua 5.1" then
module = nil
setfenv = nil
getfenv = nil
math.log10 = nil
loadstring = nil
table.maxn = nil
unpack = nil
-- functions deprecated in Lua 5.1 are also not available:
table.getn = nil
table.setn = nil
loadlib = nil
math.mod = nil
string.gfind = nil
end

View File

@ -0,0 +1,23 @@
-- computer component
local obj = {}
function obj.isRunning() -- Returns whether the computer is running.
--STUB
cprint("computer.isRunning")
end
function obj.beep(frequency, duration) -- Plays a tone, useful to alert users via audible feedback.
--STUB
cprint("computer.beep", frequency, duration)
end
function obj.stop() -- Stops the computer. Returns true if the state changed.
--STUB
cprint("computer.stop")
end
function obj.start() -- Starts the computer. Returns true if the state changed.
--STUB
cprint("computer.start")
end
local cec = {}
return obj,cec

53
src/component/eeprom.lua Normal file
View File

@ -0,0 +1,53 @@
local address, slot, filename = ...
local code = love.filesystem.read(filename)
local data = ""
local label = "EEPROM"
-- eeprom component
local obj = {}
function obj.getData() -- Get the currently stored byte array.
cprint("eeprom.getData")
return data
end
function obj.setData(newdata) -- Overwrite the currently stored byte array.
cprint("eeprom.setData", newdata)
data = newdata -- TODO
end
function obj.getDataSize() -- Get the storage capacity of this EEPROM.
cprint("eeprom.getDataSize")
return math.huge -- STUB
end
function obj.getSize() -- Get the storage capacity of this EEPROM.
cprint("eeprom.getSize")
return math.huge -- STUB
end
function obj.getLabel() -- Get the label of the EEPROM.
cprint("eeprom.getLabel")
return label
end
function obj.setLabel(newlabel) -- Set the label of the EEPROM.
cprint("eeprom.setLabel", newlabel)
label = newlabel -- TODO
end
function obj.getChecksum() -- Get the checksum of the data on this EEPROM.
cprint("eeprom.getChecksum")
return "1badbabe" -- STUB
end
function obj.get() -- Get the currently stored byte array.
cprint("eeprom.get")
return code
end
function obj.set(newcode) -- Overwrite the currently stored byte array.
cprint("eeprom.set", newcode)
code = newcode -- TODO
end
function obj.makeReadonly(checksum) -- Make this EEPROM readonly if it isn't already. This process cannot be reversed!
--STUB
print("eeprom.makeReadonly", checksum)
end
local cec = {}
return obj,cec

View File

@ -0,0 +1,139 @@
local address, slot, directory, readonly = ...
if not love.filesystem.exists(directory) then
love.filesystem.createDirectory(directory)
end
if directory == "tmpfs" then
computer.setTempAddress(address)
end
local label = ("/" .. directory):match(".*/(.+)")
local handles = {}
local function cleanPath(path)
local path = path:gsub("\\", "/")
local tPath = {}
for part in path:gmatch("[^/]+") do
if part ~= "" and part ~= "." then
if part == ".." and #tPath > 0 and tPath[#tPath] ~= ".." then
table.remove(tPath)
else
table.insert(tPath, part:sub(1,255))
end
end
end
return table.concat(tPath, "/")
end
-- filesystem component
local obj = {}
function obj.read(handle, count) -- Reads up to the specified amount of data from an open file descriptor with the specified handle. Returns nil when EOF is reached.
--TODO
cprint("filesystem.read", handle, count)
if count == math.huge then count = nil end
local ret = { handles[handle]:read(count) }
if ret[1] ~= nil then ret[2] = nil end
if ret[1] == "" and count ~= 0 then ret[1] = nil end
return table.unpack(ret)
end
function obj.lastModified(path) -- Returns the (real world) timestamp of when the object at the specified absolute path in the file system was modified.
--STUB
cprint("filesystem.lastModified", path)
end
function obj.spaceUsed() -- The currently used capacity of the file system, in bytes.
--STUB
cprint("filesystem.spaceUsed")
end
function obj.rename(from, to) -- Renames/moves an object from the first specified absolute path in the file system to the second.
--STUB
cprint("filesystem.rename", from, to)
end
function obj.close(handle) -- Closes an open file descriptor with the specified handle.
--TODO
cprint("filesystem.close", handle)
handles[handle]:close()
handles[handle] = nil
end
function obj.write(handle, value) -- Writes the specified data to an open file descriptor with the specified handle.
--STUB
cprint("filesystem.write", handle, value)
end
function obj.remove(path) -- Removes the object at the specified absolute path in the file system.
--STUB
cprint("filesystem.remove", path)
end
function obj.size(path) -- Returns the size of the object at the specified absolute path in the file system.
--STUB
cprint("filesystem.size", path)
end
function obj.seek(handle, whence, offset) -- Seeks in an open file descriptor with the specified handle. Returns the new pointer position.
--STUB
cprint("filesystem.seek", handle, whence, offset)
end
function obj.spaceTotal() -- The overall capacity of the file system, in bytes.
--STUB
cprint("filesystem.spaceTotal")
end
function obj.getLabel() -- Get the current label of the file system.
--TODO
cprint("filesystem.getLabel")
return label
end
function obj.setLabel(value) -- Sets the label of the file system. Returns the new value, which may be truncated.
--TODO
cprint("filesystem.setLabel", value)
label = value
end
function obj.open(path, mode) -- Opens a new file descriptor and returns its handle.
--TODO
cprint("filesystem.open", path, mode)
if mode == nil then mode = "r" end
compCheckArg(1,path,"string")
compCheckArg(2,mode,"string")
local file = love.filesystem.newFile(directory .. "/" .. path, mode:sub(1,1))
if not file then return nil end
while true do
local rnddescrpt = math.random(1000000000,9999999999)
if handles[rnddescrpt] == nil then
handles[rnddescrpt] = file
return rnddescrpt
end
end
end
function obj.exists(path) -- Returns whether an object exists at the specified absolute path in the file system.
--TODO
cprint("filesystem.exists", path)
return love.filesystem.exists(directory .. "/" .. path)
end
function obj.list(path) -- Returns a list of names of objects in the directory at the specified absolute path in the file system.
--TODO
cprint("filesystem.list", path)
local list = love.filesystem.getDirectoryItems(directory .. "/" .. path)
for i = 1,#list do
if love.filesystem.isDirectory(directory .. "/" .. path .. "/" .. list[i]) then
list[i] = list[i] .. "/"
end
end
return list
end
function obj.isReadOnly() -- Returns whether the file system is read-only.
--STUB
cprint("filesystem.isReadOnly")
return readonly
end
function obj.makeDirectory(path) -- Creates a directory at the specified absolute path in the file system. Creates parent directories, if necessary.
--STUB
cprint("filesystem.makeDirectory", path)
end
function obj.isDirectory(path) -- Returns whether the object at the specified absolute path in the file system is a directory.
--STUB
cprint("filesystem.isDirectory", path)
return love.filesystem.isDirectory(directory .. "/" .. path)
end
local cec = {}
return obj,cec

202
src/component/gpu.lua Normal file
View File

@ -0,0 +1,202 @@
local address, slot, maxwidth, maxheight, maxtier = ...
local lua_utf8 = require("utf8")
local bindaddress
local depthTbl = {1,4,8}
local rdepthTbl = {1,nil,nil,2,nil,nil,nil,3}
-- gpu component
local obj = {}
function obj.bind(address) -- Binds the GPU to the screen with the specified address.
cprint("gpu.bind", address)
compCheckArg(1,address,"string")
local thing = component.exists(address)
if thing == nil then
return nil, "invalid address"
elseif thing ~= "screen" then
return nil, "not a screen"
end
bindaddress = address
end
function obj.getForeground() -- Get the current foreground color and whether it's from the palette or not.
cprint("gpu.getForeground")
if bindaddress == nil then
return nil, "no screen"
end
return component.cecinvoke(bindaddress, "getForeground")
end
function obj.setForeground(value, palette) -- Sets the foreground color to the specified value. Optionally takes an explicit palette index. Returns the old value and if it was from the palette its palette index.
cprint("gpu.setForeground", value, palette)
compCheckArg(1,value,"number")
compCheckArg(2,palette,"boolean","nil")
if bindaddress == nil then
return nil, "no screen"
end
if palette and component.cecinvoke(bindaddress, "getDepth") == 1 then
error("color palette not suppported",3)
end
if palette == true and (value < 0 or value > 15) then
error("invalid palette index",3)
end
return component.cecinvoke(bindaddress, "setForeground", value, palette)
end
function obj.getBackground() -- Get the current background color and whether it's from the palette or not.
cprint("gpu.getBackground")
if bindaddress == nil then
return nil, "no screen"
end
return component.cecinvoke(bindaddress, "getBackground")
end
function obj.setBackground(value, palette) -- Sets the background color to the specified value. Optionally takes an explicit palette index. Returns the old value and if it was from the palette its palette index.
cprint("gpu.setBackground", value, palette)
compCheckArg(1,value,"number")
compCheckArg(2,palette,"boolean","nil")
if bindaddress == nil then
return nil, "no screen"
end
if palette and component.cecinvoke(bindaddress, "getDepth") == 1 then
error("color palette not suppported",3)
end
value = math.floor(value)
if palette and (value < 0 or value > 15) then
error("invalid palette index",3)
end
return component.cecinvoke(bindaddress, "setBackground", value, palette)
end
function obj.getDepth() -- Returns the currently set color depth.
cprint("gpu.getDepth")
return depthTbl[component.cecinvoke(bindaddress, "getDepth")]
end
function obj.setDepth(depth) -- Set the color depth. Returns the previous value.
cprint("gpu.setDepth", depth)
compCheckArg(1,depth,"number")
if bindaddress == nil then
return nil, "no screen"
end
local scrmax = component.cecinvoke(bindaddress, "maxDepth")
if rdepthTbl[depth] == nil or rdepthTbl[depth] > math.max(scrmax, maxtier) then
error("unsupported depth",3)
end
return component.cecinvoke(bindaddress, "setDepth", rdepthTbl[depth])
end
function obj.maxDepth() -- Get the maximum supported color depth.
cprint("gpu.maxDepth")
return depthTbl[math.min(component.cecinvoke(bindaddress, "maxDepth"), maxtier)]
end
function obj.fill(x, y, width, height, char) -- Fills a portion of the screen at the specified position with the specified size with the specified character.
cprint("gpu.fill", x, y, width, height, char)
compCheckArg(1,x,"number")
compCheckArg(2,y,"number")
compCheckArg(3,width,"number")
compCheckArg(4,height,"number")
compCheckArg(5,char,"string")
if bindaddress == nil then
return nil, "no screen"
end
if lua_utf8.len(char) ~= 1 then
return nil, "invalid fill value"
end
return component.cecinvoke(bindaddress, "fill", x, y, width, height, char)
end
function obj.getScreen() -- Get the address of the screen the GPU is currently bound to.
cprint("gpu.getScreen")
return bindaddress
end
function obj.getResolution() -- Get the current screen resolution.
cprint("gpu.getResolution")
if bindaddress == nil then
return nil, "no screen"
end
return component.cecinvoke(bindaddress, "getResolution")
end
function obj.setResolution(width, height) -- Set the screen resolution. Returns true if the resolution changed.
cprint("gpu.setResolution", width, height)
compCheckArg(1,width,"number")
compCheckArg(2,height,"number")
if bindaddress == nil then
return nil, "no screen"
end
return component.cecinvoke(bindaddress, "setResolution", width, height)
end
function obj.maxResolution() -- Get the maximum screen resolution.
cprint("gpu.maxResolution")
if bindaddress == nil then
return nil, "no screen"
end
local smw,smh = component.cecinvoke(bindaddress, "maxResolution")
return math.min(smw, maxwidth), math.min(smh, maxheight)
end
function obj.getPaletteColor(index) -- Get the palette color at the specified palette index.
cprint("gpu.getPaletteColor", index)
compCheckArg(1,index,"number")
if bindaddress == nil then
return nil, "no screen"
end
if component.cecinvoke(bindaddress, "getDepth") == 1 then
return "palette not available"
end
index = math.floor(index)
if index < 0 or index > 15 then
error("invalid palette index",3)
end
return component.cecinvoke(bindaddress, "getPaletteColor", index)
end
function obj.setPaletteColor(index, color) -- Set the palette color at the specified palette index. Returns the previous value.
cprint("gpu.setPaletteColor", index, color)
compCheckArg(1,index,"number")
compCheckArg(2,color,"number")
if bindaddress == nil then
return nil, "no screen"
end
if component.cecinvoke(bindaddress, "getDepth") == 1 then
return "palette not available"
end
index = math.floor(index)
if index < 0 or index > 15 then
error("invalid palette index",3)
end
return component.cecinvoke(bindaddress, "setPaletteColor", index, color)
end
function obj.get(x, y) -- Get the value displayed on the screen at the specified index, as well as the foreground and background color. If the foreground or background is from the palette, returns the palette indices as fourth and fifth results, else nil, respectively.
cprint("gpu.get", x, y)
compCheckArg(1,x,"number")
compCheckArg(2,y,"number")
if bindaddress == nil then
return nil, "no screen"
end
local w,h = component.cecinvoke(bindaddress, "getResolution")
if x < 1 or x > w or y < 1 or y > h then
error("index out of bounds",3)
end
return component.cecinvoke(bindaddress, "get", x, y)
end
function obj.set(x, y, value, vertical) -- Plots a string value to the screen at the specified position. Optionally writes the string vertically.
cprint("gpu.set", x, y, value, vertical)
compCheckArg(1,x,"number")
compCheckArg(2,y,"number")
compCheckArg(3,value,"string")
compCheckArg(4,vertical,"boolean","nil")
if bindaddress == nil then
return nil, "no screen"
end
return component.cecinvoke(bindaddress, "set", x, y, value, vertical)
end
function obj.copy(x, y, width, height, tx, ty) -- Copies a portion of the screen from the specified location with the specified size by the specified translation.
cprint("gpu.copy", x, y, width, height, tx, ty)
compCheckArg(1,x,"number")
compCheckArg(2,y,"number")
compCheckArg(3,width,"number")
compCheckArg(4,height,"number")
compCheckArg(5,tx,"number")
compCheckArg(6,ty,"number")
if bindaddress == nil then
return nil, "no screen"
end
return component.cecinvoke(bindaddress, "copy", x, y, width, height, tx, ty)
end
local cec = {}
return obj,cec

View File

@ -0,0 +1,92 @@
local address = ...
local lua_utf8 = require("utf8")
-- Conversion table for Love2D keys to LWJGL key codes
local keys = {
["q"] = 16, ["w"] = 17, ["e"] = 18, ["r"] = 19,
["t"] = 20, ["y"] = 21, ["u"] = 22, ["i"] = 23,
["o"] = 24, ["p"] = 25, ["a"] = 30, ["s"] = 31,
["d"] = 32, ["f"] = 33, ["g"] = 34, ["h"] = 35,
["j"] = 36, ["k"] = 37, ["l"] = 38, ["z"] = 44,
["x"] = 45, ["c"] = 46, ["v"] = 47, ["b"] = 48,
["n"] = 49, ["m"] = 50,
["1"] = 2, ["2"] = 3, ["3"] = 4, ["4"] = 5, ["5"] = 6,
["6"] = 7, ["7"] = 8, ["8"] = 9, ["9"] = 10, ["0"] = 11,
[" "] = 57,
["'"] = 40, [","] = 51, ["-"] = 12, ["."] = 52, ["/"] = 53,
[":"] = 146, [";"] = 39, ["="] = 13, ["@"] = 145, ["["] = 26,
["\\"] = 43, ["]"] = 27, ["^"] = 144, ["_"] = 147, ["`"] = 41,
["up"] = 200,
["down"] = 208,
["right"] = 205,
["left"] = 203,
["home"] = 199,
["end"] = 207,
["pageup"] = 201,
["pagedown"] = 209,
["insert"] = 210,
["backspace"] = 14,
["tab"] = 15,
["return"] = 28,
["delete"] = 211,
["capslock"] = 58,
["numlock"] = 69,
["scrolllock"] = 70,
["f1"] = 59,
["f2"] = 60,
["f3"] = 61,
["f4"] = 62,
["f5"] = 63,
["f6"] = 64,
["f7"] = 65,
["f8"] = 66,
["f9"] = 67,
["f10"] = 68,
["f12"] = 88,
["f13"] = 100,
["f14"] = 101,
["f15"] = 102,
["f16"] = 103,
["f17"] = 104,
["f18"] = 105,
["rshift"] = 54,
["lshift"] = 42,
["rctrl"] = 157,
["lctrl"] = 29,
["ralt"] = 184,
["lalt"] = 56,
}
local code2char = {}
function love.textinput(t)
cprint("textinput",t)
kbdcodes[#kbdcodes].char = lua_utf8.codepoint(t)
code2char[kbdcodes[#kbdcodes].code] = kbdcodes[#kbdcodes].char
end
function love.keypressed(key)
cprint("keypressed",key)
table.insert(kbdcodes,{type="key_down",addr=address,code=keys[key]})
end
function love.keyreleased(key)
cprint("keyreleased",key)
table.insert(kbdcodes,{type="key_up",addr=address,code=keys[key],char=code2char[keys[key]]})
end
-- keyboard component
-- Much complex
local obj = {}
-- Such methods
local cec = {}
-- Wow
return obj,cec

245
src/component/screen.lua Normal file
View File

@ -0,0 +1,245 @@
local address, slot, maxwidth, maxheight, maxtier = ...
local lua_utf8 = require("utf8")
local width, height, tier = maxwidth, maxheight, maxtier
local scrfgc, scrfgp = 0xFFFFFF
local scrbgc, scrfgp = 0x000000
local palcol
if tier == 3 then
palcol = {}
for i = 0,15 do
palcol[i] = i * 0x111111
end
else
palcol = {[0]=0xFFFFFF,0xFFCC33,0xCC66CC,0x6699FF,0xFFFF33,0x33CC33,0xFF6699,0x333333,0xCCCCCC,0x336699,0x9933CC,0x333399,0x663300,0x336600,0xFF3333,0x000000}
end
local screen = {txt = {}, fg = {}, bg = {}, fgp = {}, bgp = {}}
for y = 1,height do
screen.txt[y] = {}
screen.fg[y] = {}
screen.bg[y] = {}
screen.fgp[y] = {}
screen.bgp[y] = {}
for x = 1,width do
screen.txt[y][x] = " "
screen.fg[y][x] = scrfgc
screen.bg[y][x] = scrbgc
screen.fgp[y][x] = scrfgp
screen.bgp[y][x] = scrbgp
end
end
function z(val)
local size = val < 0x10000 and (val < 0x800 and (val < 0x80 and 1 or 2) or 3) or 4
if size == 1 then return string.char(val) end
local b = {string.char((240*2^(4-size)%256)+(val/2^(size*6-6))%(2^(7-size)))}
for i = size*6-12,0,-6 do
b[#b+1] = string.char(128+(val/2^i)%64)
end
return table.concat(b)
end
love.window.setMode(width*8, height*16,{})
local idata = love.image.newImageData(width*8, height*16)
idata:mapPixel(function() return 0,0,0,255 end)
local image = love.graphics.newImage(idata)
function love.draw()
love.graphics.draw(image)
end
local function breakColor(color)
return math.floor(color/65536%256), math.floor(color/256%256), math.floor(color%256)
end
local function renderChar(char,x,y,fr,fg,fb,br,bg,bb)
if unifont[char] ~= nil then
char = unifont[char]
local size = #char/16
for i = 1,#char,size do
local line = tonumber(char:sub(i,i+size-1),16)
local cx = x
for j = size*4-1,0,-1 do
local bit = math.floor(line/2^j)%2
if bit == 0 then
idata:setPixel(cx,y,br,bg,bb,255)
else
idata:setPixel(cx,y,fr,fg,fb,255)
end
cx = cx + 1
end
y = y + 1
end
end
end
local function setPos(x,y,c,fr,fg,fb,br,bg,bb)
screen.txt[y][x] = z(c)
screen.fg[y][x] = scrfgc
screen.bg[y][x] = scrbgc
screen.fgp[y][x] = scrfgp
screen.bgp[y][x] = scrbgp
renderChar(c,(x-1)*8,(y-1)*16,fr,fg,fb,br,bg,bb)
end
-- screen component
local obj = {}
function obj.isTouchModeInverted() -- Whether touch mode is inverted (sneak-activate opens GUI, instead of normal activate).
--STUB
cprint("screen.isTouchModeInverted")
return false
end
function obj.setTouchModeInverted(value) -- Sets whether to invert touch mode (sneak-activate opens GUI, instead of normal activate).
--STUB
cprint("screen.setTouchModeInverted", value)
end
function obj.isPrecise() -- Returns whether the screen is in high precision mode (sub-pixel mouse event positions).
--STUB
cprint("screen.isPrecise")
return false
end
function obj.setPrecise(enabled) -- Set whether to use high precision mode (sub-pixel mouse event positions).
--STUB
cprint("screen.setPrecise", enabled)
end
function obj.turnOff() -- Turns off the screen. Returns true if it was on.
--STUB
cprint("screen.turnOff")
return false
end
function obj.turnOn() -- Turns the screen on. Returns true if it was off.
--STUB
cprint("screen.turnOn")
return false
end
function obj.isOn() -- Returns whether the screen is currently on.
--STUB
cprint("screen.isOn")
return true
end
function obj.getAspectRatio() -- The aspect ratio of the screen. For multi-block screens this is the number of blocks, horizontal and vertical.
--STUB
cprint("screen.getAspectRatio")
return 1, 1
end
function obj.getKeyboards() -- The list of keyboards attached to the screen.
--STUB
cprint("screen.getKeyboards")
return {}
end
local cec = {}
function cec.getForeground() -- Get the current foreground color and whether it's from the palette or not.
cprint("(cec) screen.getForeground")
return scrfgc, nil
end
function cec.setForeground(value, palette) -- Sets the foreground color to the specified value. Optionally takes an explicit palette index. Returns the old value and if it was from the palette its palette index.
cprint("(cec) screen.setForeground", value, palette)
local old = scrfgc
scrfgc = palette and palcol[value] or value
scrfgp = palette and value
return old, nil
end
function cec.getBackground() -- Get the current background color and whether it's from the palette or not.
cprint("(cec) screen.getBackground")
return scrbgc, nil
end
function cec.setBackground(value, palette) -- Sets the background color to the specified value. Optionally takes an explicit palette index. Returns the old value and if it was from the palette its palette index.
cprint("(cec) screen.setBackground", value, palette)
local old = scrbgc
scrbgc = palette and palcol[value] or value
scrbgp = palette and value
return old, nil
end
function cec.getDepth() -- Returns the currently set color depth.
cprint("(cec) screen.getDepth")
return tier
end
function cec.setDepth(depth) -- Set the color depth. Returns the previous value.
cprint("(cec) screen.setDepth", depth)
tier = math.min(depth, maxtier)
end
function cec.maxDepth() -- Get the maximum supported color depth.
cprint("(cec) screen.maxDepth")
return maxtier
end
function cec.fill(x1, y1, w, h, char) -- Fills a portion of the screen at the specified position with the specified size with the specified character.
--STUB
cprint("(cec) screen.fill", x1, y1, w, h, char)
if w <= 0 or h <= 0 then
return true
end
local x2 = x1+w-1
local y2 = y1+h-1
if x2 < 1 or y2 < 1 or x1 > width or y1 > height then
return true
end
local fr,fg,fb = breakColor(scrfgc)
local br,bg,bb = breakColor(scrbgc)
local code = lua_utf8.codepoint(char)
for y = y1,y2 do
for x = x1,x2 do
setPos(x,y,code,fr,fg,fb,br,bg,bb)
end
end
end
function cec.getResolution() -- Get the current screen resolution.
cprint("(cec) screen.getResolution")
return width, height
end
function cec.setResolution(newwidth, newheight) -- Set the screen resolution. Returns true if the resolution changed.
cprint("(cec) screen.setResolution", newwidth, newheight)
width, height = math.min(newwidth, maxwidth), math.min(newheight, maxheight)
end
function cec.maxResolution() -- Get the maximum screen resolution.
cprint("(cec) screen.maxResolution")
return maxwidth, maxheight
end
function cec.getPaletteColor(index) -- Get the palette color at the specified palette index.
cprint("(cec) screen.getPaletteColor", index)
return palcol[index]
end
function cec.setPaletteColor(index, color) -- Set the palette color at the specified palette index. Returns the previous value.
--TODO
cprint("(cec) screen.setPaletteColor", index, color)
palcol[index] = color
end
function cec.get(x, y) -- Get the value displayed on the screen at the specified index, as well as the foreground and background color. If the foreground or background is from the palette, returns the palette indices as fourth and fifth results, else nil, respectively.
cprint("(cec) screen.get", x, y)
return screen.txt[y][x], screen.fg[y][x], screen.bg[y][x], screen.fgp[y][x], screen.bgp[y][x]
end
function cec.set(x, y, value, vertical) -- Plots a string value to the screen at the specified position. Optionally writes the string vertically.
cprint("(cec) screen.set", x, y, value, vertical)
local fr,fg,fb = breakColor(scrfgc)
local br,bg,bb = breakColor(scrbgc)
if vertical and x >= 1 and x <= width and y <= height then
for _,c in lua_utf8.codes(value) do
if y >= 1 then
setPos(x,y,c,fr,fg,fb,br,bg,bb)
end
y = y + 1
if y > height then break end
end
image:refresh()
elseif not vertical and y >= 1 and y <= height and x <= width then
for _,c in lua_utf8.codes(value) do
if x >= 1 then
setPos(x,y,c,fr,fg,fb,br,bg,bb)
end
x = x + #unifont[c]/32
if x > width then break end
end
image:refresh()
end
return true
end
function cec.copy(x, y, width, height, tx, ty) -- Copies a portion of the screen from the specified location with the specified size by the specified translation.
--STUB
cprint("(cec) screen.copy", x, y, width, height, tx, ty)
end
return obj,cec

6
src/conf.lua Normal file
View File

@ -0,0 +1,6 @@
function love.conf(t)
t.identity = "ocemu2d"
t.window = nil
t.modules.joystick = false
t.modules.physics = false
end

272
src/main.lua Normal file
View File

@ -0,0 +1,272 @@
require("compat52.strict")
conf = {
-- Format: string:type, (string or number or nil):address, (number or nil):slot, component parameters
-- Read component files for parameter documentation
components = {
{"gpu",nil,0,160,50,3},
{"screen",nil,nil,80,25,3},
{"eeprom",nil,9,"lua/bios.lua"},
{"filesystem",nil,5,"loot/OpenOS",true},
{"filesystem",nil,nil,"tmpfs",false},
{"computer"},
{"keyboard"},
}
}
machine = {
starttime = love.timer.getTime(),
signals = {},
}
local function _checkArg(pos, n, have, ...)
have = type(have)
local function check(want, ...)
if not want then
return false
else
return have == want or check(...)
end
end
if not check(...) then
local msg = string.format("bad argument #%d (%s expected, got %s)", n, table.concat({...}, " or "), have)
error(msg, pos)
end
end
function checkArg(...)
_checkArg(4, ...)
end
function compCheckArg(...)
_checkArg(5, ...)
end
if true then
cprint = print
else
cprint = function() end
end
local env = {
_VERSION = "Lua 5.2",
assert = assert,
bit32 = {
arshift = bit32.arshift,
band = bit32.band,
bnot = bit32.bnot,
bor = bit32.bor,
btest = bit32.btest,
bxor = bit32.bxor,
extract = bit32.extract,
lrotate = bit32.lrotate,
lshift = bit32.lshift,
replace = bit32.replace,
rrotate = bit32.rrotate,
rshift = bit32.rshift,
},
collectgarbage = collectgarbage,
coroutine = {
create = coroutine.create,
resume = coroutine.resume,
running = coroutine.running,
status = coroutine.status,
wrap = coroutine.wrap,
yield = coroutine.yield,
},
debug = {
debug = debug.debug,
gethook = debug.gethook,
getinfo = debug.getinfo,
getlocal = debug.getlocal,
getmetatable = debug.getmetatable,
getregistry = debug.getregistry,
getupvalue = debug.getupvalue,
getuservalue = debug.getuservalue,
sethook = debug.sethook,
setlocal = debug.setlocal,
setmetatable = debug.setmetatable,
setupvalue = debug.setupvalue,
setuservalue = debug.setuservalue,
traceback = debug.traceback,
upvalueid = debug.upvalueid,
upvaluejoin = debug.upvaluejoin,
},
dofile = dofile,
error = error,
getmetatable = getmetatable,
io = {
close = io.close,
flush = io.flush,
input = io.input,
lines = io.lines,
open = io.open,
output = io.output,
popen = io.popen,
read = io.read,
stderr = io.stderr,
stdin = io.stdin,
stdout = io.stdout,
tmpfile = io.tmpfile,
type = io.type,
write = io.write,
},
ipairs = ipairs,
load = load,
loadfile = loadfile,
math = {
abs = math.abs,
acos = math.acos,
asin = math.asin,
atan = math.atan,
atan2 = math.atan2,
ceil = math.ceil,
cos = math.cos,
cosh = math.cosh,
deg = math.deg,
exp = math.exp,
floor = math.floor,
fmod = math.fmod,
frexp = math.frexp,
huge = math.huge,
ldexp = math.ldexp,
log = math.log,
max = math.max,
min = math.min,
modf = math.modf,
pi = 3.1415926535898,
pow = math.pow,
rad = math.rad,
random = math.random,
randomseed = math.randomseed,
sin = math.sin,
sinh = math.sinh,
sqrt = math.sqrt,
tan = math.tan,
tanh = math.tanh,
},
next = next,
os = {
clock = os.clock,
date = os.date,
difftime = os.difftime,
execute = os.execute,
exit = os.exit,
getenv = os.getenv,
remove = os.remove,
rename = os.rename,
setlocale = os.setlocale,
time = os.time,
tmpname = os.tmpname,
},
pairs = pairs,
pcall = pcall,
print = print,
rawequal = rawequal,
rawget = rawget,
rawlen = rawlen,
rawset = rawset,
require = require,
select = select,
setmetatable = setmetatable,
string = {
byte = string.byte,
char = string.char,
dump = string.dump,
find = string.find,
format = string.format,
gmatch = string.gmatch,
gsub = string.gsub,
len = string.len,
lower = string.lower,
match = string.match,
rep = string.rep,
reverse = string.reverse,
sub = string.sub,
upper = string.upper,
},
table = {
concat = table.concat,
insert = table.insert,
pack = table.pack,
remove = table.remove,
sort = table.sort,
unpack = table.unpack,
},
tonumber = tonumber,
tostring = tostring,
type = type,
xpcall = xpcall,
}
setmetatable(env,{
__index = function(_,k)
cprint("Missing environment access", "env." .. k)
end,
})
-- load unifont
unifont = {}
for line in love.filesystem.lines("unifont.hex") do
local a,b = line:match("(.+):(.*)")
unifont[tonumber(a,16)] = b
end
-- load api's into environment
love.filesystem.load("apis/computer.lua")(env)
love.filesystem.load("apis/os.lua")(env)
love.filesystem.load("apis/system.lua")(env)
love.filesystem.load("apis/unicode.lua")(env)
love.filesystem.load("apis/userdata.lua")(env)
love.filesystem.load("apis/component.lua")(env)
-- load machine.lua
local machine_data, err = love.filesystem.read("lua/machine.lua")
if machine_data == nil then
error("Failed to load machine.lua:\n\t" .. tostring(err))
end
local machine_fn, err = load(machine_data,"=kernel","t",env)
if machine_fn == nil then
error("Failed to parse machine.lua\n\t" .. tostring(err))
end
local machine_thread = coroutine.create(machine_fn)
local results = { coroutine.resume(machine_thread) }
if results[1] then
if #results ~= 1 then
error("Unexpected result during initialization:\n\t",table.concat(results,", ",2))
end
else
error("Failed to initialize machine.lua\n\t" .. tostring(results[2]))
end
cprint("Machine.lua booted ...")
local resume_thread
function resume_thread(...)
if coroutine.status(machine_thread) ~= "dead" then
cprint("resume",...)
local results = { coroutine.resume(machine_thread, ...) }
cprint("yield",table.unpack(results))
if type(results[2]) == "function" then
resume_thread(results[2]())
end
if coroutine.status(machine_thread) == "dead" and type(results[2]) ~= "function" then
cprint("machine.lua has died")
end
end
end
kbdcodes = {}
function love.update(dt)
if #kbdcodes > 0 then
local kbdcode = kbdcodes[1]
table.remove(kbdcodes,1)
table.insert(machine.signals,{kbdcode.type,kbdcode.addr,kbdcode.char or 0,kbdcode.code})
end
if #machine.signals > 0 then
signal = machine.signals[1]
table.remove(machine.signals, 1)
resume_thread(table.unpack(signal))
else
resume_thread()
end
end

126
src/utf8_simple.lua Normal file
View File

@ -0,0 +1,126 @@
-- ABNF from RFC 3629
--
-- UTF8-octets = *( UTF8-char )
-- UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
-- UTF8-1 = %x00-7F
-- UTF8-2 = %xC2-DF UTF8-tail
-- UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
-- %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
-- UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
-- %xF4 %x80-8F 2( UTF8-tail )
-- UTF8-tail = %x80-BF
-- 0xxxxxxx | 007F (127)
-- 110xxxxx 10xxxxxx | 07FF (2047)
-- 1110xxxx 10xxxxxx 10xxxxxx | FFFF (65535)
-- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx | 10FFFF (1114111)
local pattern = '[%z\1-\127\194-\244][\128-\191]*'
-- helper function
local posrelat =
function (pos, len)
if pos < 0 then
pos = len + pos + 1
end
return pos
end
local utf8 = {}
-- THE MEAT
-- maps f over s's utf8 characters f can accept args: (visual_index, utf8_character, byte_index)
utf8.map =
function (s, f, no_subs)
local i = 0
if no_subs then
for b, e in s:gmatch('()' .. pattern .. '()') do
i = i + 1
local c = e - b
f(i, c, b)
end
else
for b, c in s:gmatch('()(' .. pattern .. ')') do
i = i + 1
f(i, c, b)
end
end
end
-- THE REST
-- generator for the above -- to iterate over all utf8 chars
utf8.chars =
function (s, no_subs)
return coroutine.wrap(function () return utf8.map(s, coroutine.yield, no_subs) end)
end
-- returns the number of characters in a UTF-8 string
utf8.len =
function (s)
-- count the number of non-continuing bytes
return select(2, s:gsub('[^\128-\193]', ''))
end
-- replace all utf8 chars with mapping
utf8.replace =
function (s, map)
return s:gsub(pattern, map)
end
-- reverse a utf8 string
utf8.reverse =
function (s)
-- reverse the individual greater-than-single-byte characters
s = s:gsub(pattern, function (c) return #c > 1 and c:reverse() end)
return s:reverse()
end
-- strip non-ascii characters from a utf8 string
utf8.strip =
function (s)
return s:gsub(pattern, function (c) return #c > 1 and '' end)
end
-- like string.sub() but i, j are utf8 strings
-- a utf8-safe string.sub()
utf8.sub =
function (s, i, j)
local l = utf8.len(s)
i = posrelat(i, l)
j = j and posrelat(j, l) or l
if i < 1 then i = 1 end
if j > l then j = l end
if i > j then return '' end
local diff = j - i
local iter = utf8.chars(s, true)
-- advance up to i
for _ = 1, i - 1 do iter() end
local c, b = select(2, iter())
-- i and j are the same, single-charaacter sub
if diff == 0 then
return string.sub(s, b, b + c - 1)
end
i = b
-- advance up to j
for _ = 1, diff - 1 do iter() end
c, b = select(2, iter())
return string.sub(s, i, b + c - 1)
end
return utf8