This commit is contained in:
2025-12-08 21:42:20 -05:00
parent 67db52a25f
commit 7bc6d87322
70 changed files with 11403 additions and 58 deletions

303
Build/boot/cct/boot.lua Executable file
View File

@@ -0,0 +1,303 @@
local term = term
local os = os
-- Function to write text to the terminal with special character handling
local function write(text)
local x, y = term.getCursorPos()
local w, h = term.getSize()
for i = 1, #text do
local c = text:sub(i, i)
if c == "\n" then
y = y + 1
x = 1
elseif c == "\t" then
local tabSize = 4
local spaces = tabSize - ((x - 1) % tabSize)
term.write(string.rep(" ", spaces))
x = x + spaces
elseif c == "\b" then
if x > 1 then
x = x - 1
term.setCursorPos(x, y)
term.write(" ")
term.setCursorPos(x, y)
end
else
if x <= w and y <= h then
term.setCursorPos(x, y)
term.write(c)
x = x + 1
end
end
-- Handle wrapping if we go past right edge
if x > w then
x = 1
y = y + 1
end
-- Handle scrolling if we go past bottom
if y-1 > h then
term.scroll(1)
y = h
term.setCursorPos(x, y)
end
end
term.setCursorPos(x, y)
end
-- Function to display critical errors and halt the system
local function displaySuperBadError(err)
term.setBackgroundColor(0x1)
term.setTextColor(0x4)
term.clear()
term.setCursorPos(1, 1)
term.write("A critical error occurred while loading the system:")
term.setCursorPos(1, 3)
write(err)
while true do end
end
-- Wrap all in xpcall to catch errors
local ok, err = xpcall(function()
local apis={}
-- List of standard Lua globals
local lua = {
coroutine = true,
debug = true,
_HOST = true,
_VERSION = true,
assert = true,
collectgarbage = true,
error = true,
gcinfo = true,
getfenv = true,
getmetatable = true,
ipairs = true,
__inext = true,
load = true,
math = true,
next = true,
pairs = true,
pcall = true,
rawequal = true,
rawget = true,
rawlen = true,
rawset = true,
select = true,
setfenv = true,
setmetatable = true,
string = true,
table = true,
tonumber = true,
tostring = true,
type = true,
xpcall = true,
_G=true
}
-- Move all non-Lua standard library globals into the apis table
for i,v in pairs(_G) do
if not lua[i] or lua[i]==nil then
apis[i]=v
_G[i]=nil
end
end
-- Set up terminal default colors
apis.term.setPaletteColor(0x1, 0x000000) -- #000000
apis.term.setPaletteColor(0x2, 0xFFFFFF) -- #FFFFFF
apis.term.setPaletteColor(0x4, 0xFF0000) -- #FF0000
apis.term.setPaletteColor(0x8, 0x00FF00) -- #00FF00
apis.term.setPaletteColor(0x10, 0x0000FF) -- #0000FF
apis.term.setPaletteColor(0x20, 0x00FFFF) -- #00FFFF
apis.term.setPaletteColor(0x40, 0xFF00FF) -- #FF00FF
apis.term.setPaletteColor(0x80, 0xFFFF00) -- #FFFF00
apis.term.setPaletteColor(0x100, 0xFF6D00) -- #FF6D00
apis.term.setPaletteColor(0x200, 0x6DFF55) -- #6DFF55
apis.term.setPaletteColor(0x400, 0x24FFFF) -- #24FFFF
apis.term.setPaletteColor(0x800, 0x924900) -- #924900
apis.term.setPaletteColor(0x1000, 0x6D6D55) -- #6D6D55
apis.term.setPaletteColor(0x2000, 0xDBDBAA) -- #DBDBAA
apis.term.setPaletteColor(0x4000, 0x6D00FF) -- #6D00FF
apis.term.setPaletteColor(0x8000, 0xB6FF00) -- #B6FF00
-- Wrapper to read files
local function getFile(path)
local file = apis.fs.open(path, "r")
if not file then displaySuperBadError("Could not open file: "..path) end
local content = file.readAll()
file.close()
return content
end
-- Load kernel first if it fails, we can't continue so we display an error
local Kernel = load(getFile("/$/boot/kernel.lua"),"@Kernel")
local initFs = load(getFile("/$/boot/cct/initdisks","@Init_disks"))(apis)
local fs = load(getFile("/$/boot/initfs"),"@InitFs")()
if not Kernel then
displaySuperBadError("Could not load kernel.")
end
if not initFs then
displaySuperBadError("Could not load initdisks.")
end
if not fs then
displaySuperBadError("Could not load initfs.")
end
-- Set up event queue
local eventQueue = {}
local lkeys={}
lkeys[apis.keys.enter]="\n"
lkeys[apis.keys.backspace]="\b"
lkeys[apis.keys.tab]="\t"
-- Function to queue events
local function queueEvent(event, ...)
table.insert(eventQueue, {event, ...})
end
-- Set up computer api
local computer = {
time = function() return apis.os.epoch("utc") end,
clock = apis.os.clock,
shutdown = apis.os.shutdown,
reboot = apis.os.reboot,
getMachineEvent = function()
if #eventQueue > 0 then
return table.unpack(table.remove(eventQueue, 1))
else
return nil
end
end,
getEEPROM = function()
return getFile("/startup.lua")
end,
setEEPROM = function(_,text)
local h = apis.fs.open("/startup.lua", "w")
h.write(text)
h.close()
end
}
-- Set up terminal colors
local icolors={
[0x1] =0, -- #000000
[0x2] =1, -- #FFFFFF
[0x4] =2, -- #FF0000
[0x8] =3, -- #00FF00
[0x10] =4, -- #0000FF
[0x20] =5, -- #00FFFF
[0x40] =6, -- #FF00FF
[0x80] =7, -- #FFFF00
[0x100] =8, -- #FF6D00
[0x200] =9, -- #6DFF55
[0x400] =10, -- #24FFFF
[0x800] =11, -- #924900
[0x1000] =12, -- #6D6D55
[0x2000] =13, -- #DBDBAA
[0x4000] =14, -- #6D00FF
[0x8000] =15 -- #B6FF00
}
local colors={
[0]=0x0001, -- #000000
0x0002, -- #FFFFFF
0x0004, -- #FF0000
0x0008, -- #00FF00
0x0010, -- #0000FF
0x0020, -- #00FFFF
0x0040, -- #FF00FF
0x0080, -- #FFFF00
0x0100, -- #FF6D00
0x0200, -- #6DFF55
0x0400, -- #24FFFF
0x0800, -- #924900
0x1000, -- #6D6D55
0x2000, -- #DBDBAA
0x4000, -- #6D00FF
0x8000 -- #B6FF00
}
apis.term.setBackgroundColor(0x1)
apis.term.setTextColor(0x1000)
apis.term.clear()
apis.term.setCursorPos(1, 1)
-- Make the kernel coroutine so we can hook its execution
local kernelCoro = coroutine.create(function()
local ok, err = xpcall(Kernel, debug.traceback, apis, initFs, "cct", "/sbin/init", {
print=function(_,text) write(text.."\n") end,
printInline=function(_,text) write(text) end,
clear=function() apis.term.clear() apis.term.setCursorPos(1,1) end,
setCursorPos=function(_,x,y) apis.term.setCursorPos(x,y) end,
getCursorPos=function() return apis.term.getCursorPos() end,
getSize=function() return apis.term.getSize() end,
setBackgroundColor=function(_,color) apis.term.setBackgroundColor(colors[color]) end,
setTextColor=function(_,color) apis.term.setTextColor(colors[color]) end,
getBackgroundColor=function() return icolors[apis.term.getBackgroundColor()] end,
getTextColor=function() return icolors[apis.term.getTextColor()] end
}, computer, fs, "$")
if not ok then
displaySuperBadError(err)
end
end)
-- Diffine a coroutine.resumeWithTimeout function to avoid hanging the system, time is in milliseconds
function coroutine.resumeWithTimeout(co, timeout, ...)
local startTime = computer.time()
debug.sethook(co, function()
if computer.time() > startTime + timeout then
return coroutine.yield("timeout")
end
end, "", 1000)
local ret = {coroutine.resume(co, ...)}
if ret[1]=="timeout" then
return nil, "Coroutine timed out"
elseif ret[1]==false then
return false, ret[2]
else
debug.sethook(co)
return true, table.unpack(ret)
end
end
write("Loaded in "..tostring(apis.os.clock()).." seconds.\n")
-- Main loop to run the kernel coroutine
while true do
local status, err = coroutine.resumeWithTimeout(kernelCoro, 50)
apis.os.queueEvent("NoSleep")
local exit = false
while not exit do
local event = {coroutine.yield()}
if event[1] == "char" then
queueEvent("keyTyped", 1, event[2])
elseif event[1] == "key" then
queueEvent("keyPressed", 1, event[2])
if lkeys[event[2]] then
queueEvent("keyTyped", 1, lkeys[event[2]])
end
elseif event[1] == "key_up" then
queueEvent("keyReleased", 1, event[2])
elseif event[1] == "disk" then
queueEvent("componentAdded", "disk")
elseif event[1] == "disk_eject" then
queueEvent("componentRemoved", "disk")
elseif event[1] == "NoSleep" then
exit=true
end
end
if status == false or coroutine.status(kernelCoro)=="dead" then
displaySuperBadError("Kernel error: "..tostring(err))
coroutine.yield("key")
end
end
end, debug.traceback)
if not ok then
displaySuperBadError("Fatal error during boot: "..err)
end
while true do coroutine.yield() end

119
Build/boot/cct/eeprom Executable file
View File

@@ -0,0 +1,119 @@
-- UnBIOS by JackMacWindows
-- This will undo most of the changes/additions made in the BIOS, but some things may remain wrapped if `debug` is unavailable
-- To use, just place a `bios.lua` in the root of the drive, and run this program
-- Here's a list of things that are irreversibly changed:
-- * both `bit` and `bit32` are kept for compatibility
-- * string metatable blocking (on old versions of CC)
-- In addition, if `debug` is not available these things are also irreversibly changed:
-- * old Lua 5.1 `load` function (for loading from a function)
-- * `loadstring` prefixing (before CC:T 1.96.0)
-- * `http.request`
-- * `os.shutdown` and `os.reboot`
-- * `peripheral`
-- * `turtle.equip[Left|Right]`
-- Licensed under the MIT license
local args = {...}
if _HOST:find("UnBIOS") then return end
local keptAPIs = {keys=true, bit32 = true, bit = true, ccemux = true, config = true, coroutine = true, debug = true, fs = true, http = true, mounter = true, os = true, periphemu = true, peripheral = true, redstone = true, rs = true, term = true, utf8 = true, _HOST = true, _CC_DEFAULT_SETTINGS = true, _CC_DISABLE_LUA51_FEATURES = true, _VERSION = true, assert = true, collectgarbage = true, error = true, gcinfo = true, getfenv = true, getmetatable = true, ipairs = true, __inext = true,load = true, loadstring = true, math = true, newproxy = true, next = true, pairs = true, pcall = true, rawequal = true, rawget = true, rawlen = true, rawset = true, select = true, setfenv = true, setmetatable = true, string = true, table = true, tonumber = true, tostring = true, type = true, unpack = true, xpcall = true, turtle = true, pocket = true, commands = true, _G = true}
local t = {}
for k in pairs(_G) do if not keptAPIs[k] then table.insert(t, k) end end
for _,k in ipairs(t) do _G[k] = nil end
local native = _G.term.native()
for _, method in ipairs {"nativePaletteColor", "nativePaletteColour", "screenshot"} do native[method] = _G.term[method] end
_G.term = native
if _G.http then
_G.http.checkURL = _G.http.checkURLAsync
_G.http.websocket = _G.http.websocketAsync
end
if _G.commands then _G.commands = _G.commands.native end
if _G.turtle then _G.turtle.native, _G.turtle.craft = nil end
local delete = {os = {"version", "pullEventRaw", "pullEvent", "run", "loadAPI", "unloadAPI", "sleep"}, http = _G.http and {"get", "post", "put", "delete", "patch", "options", "head", "trace", "listen", "checkURLAsync", "websocketAsync"}, fs = {"complete", "isDriveRoot"}}
for k,v in pairs(delete) do for _,a in ipairs(v) do _G[k][a] = nil end end
_G._HOST = _G._HOST .. " (UnBIOS)"
-- Set up TLCO
-- This functions by crashing `rednet.run` by removing `os.pullEventRaw`. Normally
-- this would cause `parallel` to throw an error, but we replace `error` with an
-- empty placeholder to let it continue and return without throwing. This results
-- in the `pcall` returning successfully, preventing the error-displaying code
-- from running - essentially making it so that `os.shutdown` is called immediately
-- after the new BIOS exits.
--
-- From there, the setup code is placed in `term.native` since it's the first
-- thing called after `parallel` exits. This loads the new BIOS and prepares it
-- for execution. Finally, it overwrites `os.shutdown` with the new function to
-- allow it to be the last function called in the original BIOS, and returns.
-- From there execution continues, calling the `term.redirect` dummy, skipping
-- over the error-handling code (since `pcall` returned ok), and calling
-- `os.shutdown()`. The real `os.shutdown` is re-added, and the new BIOS is tail
-- called, which effectively makes it run as the main chunk.
local olderror = error
_G.error = function() end
_G.term.redirect = function() end
function _G.term.native()
_G.term.native = nil
_G.term.redirect = nil
_G.error = olderror
term.setBackgroundColor(32768)
term.setTextColor(1)
term.setCursorPos(1, 1)
term.setCursorBlink(true)
term.clear()
local file = fs.open("/$/boot/cct/boot.lua", "r")
if file == nil then
term.setCursorBlink(false)
term.setTextColor(16384)
term.write("Could not find /boot/cct/boot.lua. UnBIOS cannot continue.")
term.setCursorPos(1, 2)
term.write("Press any key to continue")
coroutine.yield("key")
os.shutdown()
end
local fn, err = loadstring(file.readAll(), "@bootloader")
file.close()
if fn == nil then
term.setCursorBlink(false)
term.setTextColor(16384)
term.write("Could not load /boot/cc/boot.lua. UnBIOS cannot continue.")
term.setCursorPos(1, 2)
term.write(err)
term.setCursorPos(1, 3)
term.write("Press any key to continue")
coroutine.yield("key")
os.shutdown()
end
setfenv(fn, _G)
local oldshutdown = os.shutdown
os.shutdown = function()
os.shutdown = oldshutdown
return fn(table.unpack(args))
end
end
if debug then
-- Restore functions that were overwritten in the BIOS
-- Apparently this has to be done *after* redefining term.native
local function restoreValue(tab, idx, name, hint)
local i, key, value = 1, debug.getupvalue(tab[idx], hint)
while key ~= name and key ~= nil do
key, value = debug.getupvalue(tab[idx], i)
i=i+1
end
tab[idx] = value or tab[idx]
end
restoreValue(_G, "loadstring", "nativeloadstring", 1)
restoreValue(_G, "load", "nativeload", 5)
if http then restoreValue(http, "request", "nativeHTTPRequest", 3) end
restoreValue(os, "shutdown", "nativeShutdown", 1)
restoreValue(os, "reboot", "nativeReboot", 1)
if turtle then
restoreValue(turtle, "equipLeft", "v", 1)
restoreValue(turtle, "equipRight", "v", 1)
end
do
local i, key, value = 1, debug.getupvalue(peripheral.isPresent, 2)
while key ~= "native" and key ~= nil do
key, value = debug.getupvalue(peripheral.isPresent, i)
i=i+1
end
_G.peripheral = value or peripheral
end
end

201
Build/boot/cct/initdisks Executable file
View File

@@ -0,0 +1,201 @@
local apis = ({...})[1]
local fs = apis.fs
local native = apis.peripheral
local peripheral = {}
local sides = {"top", "bottom", "left", "right", "front", "back"}
function peripheral.getType(name)
if native.isPresent(name) then
return native.getType(name)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
return native.call(side, "getTypeRemote", name)
end
end
return nil
end
function peripheral.getNames()
local names = {}
for n = 1, #sides do
local side = sides[n]
if native.isPresent(side) then
table.insert(names, side)
end
if native.hasType(side, "peripheral_hub") then
local hubSides = native.call(side, "getConnectedSides")
for _, hubSide in ipairs(hubSides) do
table.insert(names, hubSide)
end
end
end
return names
end
---------------------------------------------------------
-- STORAGE
---------------------------------------------------------
local disks = {} -- real disks
local internal = {} -- "$" disk
---------------------------------------------------------
-- HELPERS
---------------------------------------------------------
local function norm(path)
if not path or path == "" then return "/" end
return fs.combine("/", path)
end
--- Creates a disk object given a base path
local function createDisk(id, basePath, readonly, periph)
basePath = norm(basePath)
local disk = {address=id,isReadOnly=function() return readonly end}
function disk:spaceUsed()
return fs.getCapacity(basePath) - fs.getFreeSpace(basePath)
end
function disk:spaceTotal()
return fs.getCapacity(basePath)
end
function disk:readAllText(path)
local p = fs.combine(basePath, path)
if not fs.exists(p) then return nil, "file not found" end
local h = fs.open(p, "r")
local t = h.readAll()
h.close()
return t
end
function disk:writeAllText(path, text)
local p = fs.combine(basePath, path)
local h = fs.open(p, "w")
if not h then return nil, "cannot write" end
h.write(text)
h.close()
return true
end
function disk:appendAllText(path, text)
local p = fs.combine(basePath, path)
local h = fs.open(p, "a")
if not h then return nil, "cannot append" end
h.write(text)
h.close()
return true
end
function disk:list(path)
local p = fs.combine(basePath, path)
if not fs.exists(p) or not fs.isDir(p) then return nil, "not directory" end
return fs.list(p)
end
function disk:fileExists(path)
local p = fs.combine(basePath, path)
return fs.exists(p) and not fs.isDir(p)
end
function disk:directoryExists(path)
local p = fs.combine(basePath, path)
return fs.exists(p) and fs.isDir(p)
end
function disk:makeDirectory(path)
local p = fs.combine(basePath, path)
fs.makeDir(p)
return true
end
function disk:remove(path)
local p = fs.combine(basePath, path)
if fs.exists(p) then fs.delete(p) end
return true
end
function disk:setLabel(label)
periph.setLabel(label)
end
function disk:getLabel(label)
return periph.getLabel()
end
function disk:size(path)
local p = fs.combine(basePath, path)
return fs.getSize(p)
end
return disk
end
---------------------------------------------------------
-- INTERNAL DISK "$" (mapped to "/")
---------------------------------------------------------
internal["$"] = createDisk("$", "/$", false, {
setLabel=function(label)
local h = fs.open("/.label", "w")
h.write(label)
h.close()
end,
getLabel=function()
local h = fs.open("/.label", "r")
local label = h.readAll()
h.close()
return label
end
})
---------------------------------------------------------
-- SCAN REAL DISK PERIPHERALS
---------------------------------------------------------
local function refresh()
-- remove disks that no longer exist
for id, _ in pairs(disks) do
if not peripheral.getType(id) then
disks[id] = nil
end
end
-- detect new disks
for _, name in ipairs(peripheral.getNames()) do
if peripheral.getType(name) == "disk" then
if not disks[name] then
local mount = disk.getMountPath(name)
if mount then
disks[name] = createDisk(name, mount, false, disk)
end
end
end
end
end
---------------------------------------------------------
-- ITERATOR
---------------------------------------------------------
local function iter()
refresh()
-- first internal
local combined = {}
for id, obj in pairs(internal) do
combined[id] = obj
end
for id, obj in pairs(disks) do
combined[id] = obj
end
return pairs(combined)
end
---------------------------------------------------------
-- MODULE RETURN
---------------------------------------------------------
return {
refresh = refresh,
list = iter
}

69
Build/boot/initfs Executable file
View File

@@ -0,0 +1,69 @@
local fs = {}
local disks = {}
local mounts = {}
local function resolve(path)
local mountPoint = "/"
for mount, disk in pairs(mounts) do
if path:sub(1, #mount) == mount then
if not mountPoint or #mount > #mountPoint then
mountPoint = mount
end
end
end
local newPath = path:sub(#mountPoint + 1)
return disks[mounts[mountPoint]], newPath
end
---------------------------------------------------------
function fs.update(initdisks)
disks = {}
for k, v in initdisks.list() do
disks[k] = v
end
end
function fs.exists(path)
local disk, newPath = resolve(path)
return disk:directoryExists(newPath) or disk:fileExists(newPath)
end
function fs.isFile(path)
local disk, newPath = resolve(path)
return disk:fileExists(newPath)
end
function fs.isDir(path)
local disk, newPath = resolve(path)
return disk:directoryExists(newPath)
end
function fs.list(path)
local disk, newPath = resolve(path)
return disk:list(newPath)
end
function fs.makeDir(path)
local disk, newPath = resolve(path)
return disk:makeDirectory(newPath)
end
function fs.remove(path)
local disk, newPath = resolve(path)
return disk:remove(newPath)
end
function fs.readAllText(path)
local disk, newPath = resolve(path)
return disk:readAllText(newPath)
end
function fs.writeAllText(path, text)
local disk, newPath = resolve(path)
return disk:writeAllText(newPath, text)
end
function fs.appendAllText(path, text)
local disk, newPath = resolve(path)
return disk:appendAllText(newPath, text)
end
function fs.load(path)
return load(fs.readAllText(path), path)
end
function fs.mount(disk, mountPoint)
if not disks[disk] then return end
mounts[mountPoint] = disk
end
return fs

166
Build/boot/kernel.lua Executable file
View File

@@ -0,0 +1,166 @@
local args = {...}
local apis = args[1]
local disks = args[2]
local arch = args[3]
local initPath = args[4]
local screen = args[5]
local computer = args[6]
local ifs = args[7]
local LOG_Text = ""
local kernel = {}
kernel.process = "Kernel"
kernel.user = "root"
kernel.group = "root"
kernel.groups = {0}
kernel.uid = 0
kernel.gid = 0
kernel.stage = "start"
kernel.key = {}
kernel.chache = {}
kernel.chache.preload = {}
local windowsExp = false
function kernel.log(msg, level)
LOG_Text = LOG_Text..tostring(computer.time()).." "..kernel.user.." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n"
if kernel.stage == "start" then
screen:print(tostring(computer.time()).." "..kernel.user.." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg)
end
end
function kernel.PANIC(msg)
kernel.log("PANIC: "..msg, "PANIC")
pcall(kernel["saveLog"])
screen:setTextColor(2)
screen:setBackgroundColor(0)
screen:clear()
screen:setCursorPos(1,1)
screen:print(LOG_Text)
screen:print("KERNEL PANIC!\n"..msg.."\nSystem halted.")
screen:print("Press any key to continue...")
while true do
local event={computer:getMachineEvent()}
if event[1]=="keyPressed" then
break
end
end
computer:reboot()
end
kernel.panic=kernel.PANIC
if windowsExp then
screen:setTextColor(1)
screen:setBackgroundColor(4)
screen:clear()
local w,h = screen:getSize()
screen:setCursorPos(3,5)
screen:print(":(")
screen:setCursorPos(3,7)
screen:print("Your PC ran into a problem and needs to restart. We're just collecting some error")
screen:setCursorPos(3,8)
screen:print("info, and then we'll restart for you.\n")
screen:setCursorPos(3,h-5)
screen:print("Stop code: average windows experience")
screen:setCursorPos(1,h)
screen:print("Press any key to continue... jk reboot it yourself lazy")
while true do end
end
kernel.log("Kernel loaded.", "INFO")
kernel.log("Mounting disks...", "INFO")
disks.refresh()
ifs.update(disks)
ifs.mount("$", "/")
function kernel.saveLog()
ifs.writeAllText("/var/log/syslog.log", LOG_Text)
end
local fstab=ifs.readAllText("/etc/fstab")
local split = function(str, delim, maxResultCountOrNil)
assert(#delim == 1, "only delim len 1 supported for now")
maxResultCountOrNil = (maxResultCountOrNil or 0)-1
local rv = {}
local buf = ""
for i = 1, #str do
local c = string.sub(str,i,i)
if #rv ~= maxResultCountOrNil and c == delim then
table.insert(rv, buf)
buf = ""
else
buf = buf..c
end
end
table.insert(rv, buf)
return rv
end
for i,v in ipairs(split(fstab,"\n")) do
if v:sub(1,1)=="U" then
local id=""
for i=3,#v do
if v:sub(i,i)==";" then
if i==3 then kernel.log("Invalid fstab line... Skipping.","WARN") goto endline end
id=v:sub(3,i-1)
end
end
local path=v:sub(#id+4)
kernel.log("Mounted "..id.." to "..path)
ifs.mount(id,path)
::endline::
end
end
ifs.remove("/tmp")
ifs.makeDir("/tmp")
local drivers={}
drivers.raw={}
drivers.processes={}
drivers.prior={}
drivers.type={}
for i=0, 99 do
drivers.prior[i]={}
end
function drivers.register(prior,object)
drivers.raw[#drivers.raw+1]=object
if object.main then drivers.processes[#drivers.processes+1]=object.main end
if drivers.prior[prior]==nil then drivers.prior[prior]={} end
drivers.prior[prior][#drivers.prior[prior]+1]=object
if drivers.type[object.type]==nil then drivers.type[object.type]={} end
drivers.type[object.type][#drivers.type[object.type]+1]=object
end
local modules={}
for i=0, 99 do
modules[i]={}
end
for i,v in ipairs(ifs.list("/lib/modules/Hyperion/")) do
local prior=tonumber(v:sub(1,2))
modules[prior][#modules[prior]+1]="/lib/modules/Hyperion/"..v
end
kernel.drivers=drivers
kernel.ifs=ifs
kernel.apis=apis
kernel.computer=computer
kernel.initPath=initPath
kernel.arch=arch
kernel.initdisks=initdisks
kernel.screen=screen
for _,p in ipairs(modules) do
for _,v in ipairs(p) do
local code=ifs.readAllText(v)
local func,err=load(code,"@"..v)
if not func then kernel.log("ModuLoadErr: "..tostring(err)) goto skip end
local status, err = xpcall(func,debug.traceback,kernel)
if not status then goto skip end
if not err then goto skip end
if not err.init then goto skip end
local ok, err = xpcall(status.main,debug.traceback)
if not ok then kernel.log("ModuInitErr: "..tostring(err)) end
::skip::
end
end
kernel.PANIC("Execution complete")

40
Build/boot/oc/boot.lua Executable file
View File

@@ -0,0 +1,40 @@
local lua = {
coroutine = true,
debug = true,
_HOST = true,
_VERSION = true,
assert = true,
collectgarbage = true,
error = true,
gcinfo = true,
getfenv = true,
getmetatable = true,
ipairs = true,
__inext = true,
load = true,
math = true,
next = true,
pairs = true,
pcall = true,
rawequal = true,
rawget = true,
rawlen = true,
rawset = true,
select = true,
setfenv = true,
setmetatable = true,
string = true,
table = true,
tonumber = true,
tostring = true,
type = true,
xpcall = true,
_G=true
}
for i,v in pairs(_G) do
if not lua[i] or lua[i]==nil then
apis[i]=v
_G[i]=nil
end
end

18
Build/boot/oc/eeprom Executable file
View File

@@ -0,0 +1,18 @@
checkArg=nil
local oldcomputer=computer
_G.computer=nil
local os=os
_G.os=nil
function component.wrap(address)
local methods=oldcomponent.methods(address)
local object={}
for _,method in ipairs(methods) do
object[method]=function(_,...)
return oldcomponent.invoke(address,method,...)
end
end
return object
end
local

1
Build/boot/oc/initfs.lua Executable file
View File

@@ -0,0 +1 @@
local fs={}

2
Build/etc/fstab Executable file
View File

@@ -0,0 +1,2 @@
U $;/
U devfs0000;/dev/

3543
Build/lib/LibDeflate Executable file

File diff suppressed because it is too large Load Diff

9
Build/lib/deflate Executable file
View File

@@ -0,0 +1,9 @@
local deflate=require("LibDeflate")
local lib={}
lib.compress=function(data)
return deflate:CompressDeflate(data)
end
lib.decompress=function(data)
return deflate:DecompressDeflate(data)
end
return lib

0
docs/api/kernel.txt → Build/lib/globals/string Normal file → Executable file
View File

0
Build/lib/globals/table Executable file
View File

View File

@@ -0,0 +1,137 @@
function string.hasSuffix(str, suffix)
return string.sub(str, #suffix+1) == suffix
end
function string.hasPrefix(str, prefix)
return string.sub(str, 1, #prefix) == prefix
end
function string.getSuffix(str, prefix)
return string.sub(str, #prefix+1)
end
function string.getPrefix(str, suffix)
return string.sub(str, 1, #suffix)
end
function string.join(str, ...)
return table.concat(table.pack(str, ...))
end
function string.delim(str, ...)
return table.concat(table.pack(...), str)
end
function string.split(str, delim, maxResultCountOrNil)
assert(#delim == 1, "only delim len 1 supported for now")
maxResultCountOrNil = (maxResultCountOrNil or 0)-1
local rv = {}
local buf = ""
for i = 1, #str do
local c = string.sub(str,i,i)
if #rv ~= maxResultCountOrNil and c == delim then
table.insert(rv, buf)
buf = ""
else
buf = buf..c
end
end
table.insert(rv, buf)
return rv
end
function string.replace(str, search, replacement)
local rv = ""
local consumedLen = 1
local i = 1
while i<#str do
if string.sub(str, i, i+#search-1) == search then
rv = rv .. string.sub(str, consumedLen, i-1) .. replacement
i=i+#search
consumedLen = i
end
i=i+1
end
return rv .. string.sub(str, consumedLen)
end
function table.deepcopy(orig, copies)
copies = copies or {}
if type(orig) ~= 'table' then
return orig
elseif copies[orig] then
return copies[orig]
end
local copy = {}
copies[orig] = copy
for k, v in next, orig, nil do
local copied_key = table.deepcopy(k, copies)
local copied_val = table.deepcopy(v, copies)
copy[copied_key] = copied_val
end
return copy
end
function table.hasKey(tabl, query)
for i,v in pairs(tabl) do
if i==query then
return true
end
end
return false
end
function table.hasVal(tabl, query)
for i,v in pairs(tabl) do
if v==query then
return true
end
end
return false
end
local function serialize(table)
local output = "{"
for i,v in pairs(table) do
local coma=true
if type(i) == "string" then
output=output.."[\""..i.."\"]="
end
if type(v) == "table" then
if v == table then
output=string.sub(output,1,#output-(#i+1))
coma=false
else
output=output..serialize(v)
end
elseif type(v) == "string" then
output=output.."[=["..v.."]=]"
elseif type(v) == "number" then
output=output..tostring(v)
elseif type(v) == "boolean" then
if v == true then
output=output.."true"
else
output=output.."false"
end
elseif type(v) == "function" then
output=output.."function() end"
else
error("serialization of type \""..type(v).."\" is not supported")
end
if coma then
output=output..","
end
end
if #table>0 or string.sub(output,#output,#output) == "," then
output=string.sub(output,1,#output-1)
end
output=output.."}"
return output
end
table.serialize=serialize

View File

@@ -0,0 +1,217 @@
local args = {...}
local kernel = args[1]
local fs = {}
local disks = {}
local mounts = {["/"]="$"}
-- path normaliing
local function normalizePath(path)
if not path or path == "" then return "/" end
if path:sub(1,1) ~= "/" then path = "/" .. path end
local parts = {}
for part in path:gmatch("[^/]+") do
if part == ".." then
if #parts > 0 then table.remove(parts) end
elseif part ~= "." and part ~= "" then
parts[#parts+1] = part
end
end
return "/" .. table.concat(parts, "/")
end
-- disk resolution
local function resolvePath(path)
local abs = normalizePath(path)
local best = "/"
for mount, diskAddr in pairs(mounts) do
if abs:sub(1, #mount) == mount and #mount > #best then
best = mount
end
end
local newPath = abs:sub(#best + 1)
if newPath == "" then newPath = "/" end
return disks[mounts[best]], newPath
end
-- Symlinks
-- Format: #!@SYMLINK[target]
local SYMLINK_PREFIX = "#!@SYMLINK["
local SYMLINK_SUFFIX = "]"
local SYMLINK_MAX_DEPTH = 64
local function isSymlink(disk, path)
if not disk:fileExists(path) then return false end
local text = disk:readAllText(path)
if not text then return false end
return text:sub(1, #SYMLINK_PREFIX) == SYMLINK_PREFIX
end
local function readSymlink(disk, path)
local text = disk:readAllText(path)
if not text then return nil end
if text:sub(1, #SYMLINK_PREFIX) ~= SYMLINK_PREFIX then return nil end
local target = text:sub(#SYMLINK_PREFIX + 1)
if target:sub(-1) == SYMLINK_SUFFIX then
target = target:sub(1, -2)
end
return target
end
-- Path resolution
local function splitPath(p)
local parts = {}
for part in p:gmatch("[^/]+") do parts[#parts+1] = part end
return parts
end
local function resolveSymlink(path)
path = normalizePath(path)
local symlinkDepth = 0
local parts = splitPath(path)
local resolved = {}
local i = 1
while i <= #parts do
local comp = parts[i]
if comp == "." then
elseif comp == ".." then
if #resolved > 0 then table.remove(resolved) end
else
local currentPath = "/" .. table.concat(resolved, "/")
local disk, p = resolvePath(currentPath .. "/" .. comp)
if isSymlink(disk, p) then
symlinkDepth = symlinkDepth + 1
if symlinkDepth > SYMLINK_MAX_DEPTH then
error("Too many levels of symbolic links: " .. path)
end
local target = readSymlink(disk, p)
if not target then
resolved[#resolved+1] = comp
else
if target:sub(1,1) == "/" then
resolved = splitPath(normalizePath(target))
else
local base = resolved
resolved = {}
for _, seg in ipairs(base) do resolved[#resolved+1]=seg end
for seg in target:gmatch("[^/]+") do
if seg == ".." then
if #resolved>0 then table.remove(resolved) end
elseif seg ~= "." then
resolved[#resolved+1]=seg
end
end
end
local remaining = {}
for j=i+1,#parts do remaining[#remaining+1]=parts[j] end
parts = remaining
i = 0
end
else
resolved[#resolved+1]=comp
end
end
i=i+1
end
local finalPath="/"..table.concat(resolved,"/")
local disk,diskPath=resolvePath(finalPath)
return disk,diskPath
end
-- PUBLIC API: MOUNTING
function fs.mount(disk, mountPoint)
if kernel.uid ~= 0 then error("Permission Denied") end
mounts[mountPoint]=disk
end
function fs.unmount(mountPoint)
if kernel.uid ~= 0 then error("Permission Denied") end
mounts[mountPoint]=nil
end
function fs.virtDisk(diskObj)
if kernel.uid ~= 0 then error("Permission Denied") end
if disks[diskObj.address] then error("Disk exists") end
disks[diskObj.address]=diskObj
end
function fs.eject(addr)
if kernel.uid ~= 0 then error("Permission Denied") end
disks[addr]=nil
end
-- SYMLINK API
function fs.symlink(target, linkPath)
local disk, p = resolvePath(linkPath)
return disk:writeAllText(p, SYMLINK_PREFIX..target..SYMLINK_SUFFIX)
end
function fs.isLink(path)
local disk, p = resolvePath(path)
return isSymlink(disk,p)
end
function fs.readLink(path)
local disk, p = resolvePath(path)
return readSymlink(disk,p)
end
-- FILE OPERATIONS
function fs.exists(path)
local disk, p = resolveSymlink(path)
return disk:fileExists(p) or disk:directoryExists(p)
end
function fs.isFile(path)
local disk, p = resolveSymlink(path)
if isSymlink(disk, p) then return false end
return disk:fileExists(p)
end
function fs.isDir(path)
local disk, p = resolveSymlink(path)
if isSymlink(disk, p) then return false end
return disk:directoryExists(p)
end
function fs.list(path)
local disk, p = resolveSymlink(path)
return disk:list(p)
end
function fs.makeDir(path)
local disk, p = resolveSymlink(path)
return disk:makeDirectory(p)
end
function fs.remove(path)
local disk, p = resolvePath(path)
if isSymlink(disk, p) then
return disk:remove(p)
end
local d2, p2 = resolveSymlink(path)
return d2:remove(p2)
end
function fs.readAllText(path)
local disk, p = resolveSymlink(path)
return disk:readAllText(p)
end
function fs.writeAllText(path, text)
local disk, p = resolveSymlink(path)
return disk:writeAllText(p, text)
end
function fs.appendAllText(path, text)
local disk, p = resolveSymlink(path)
return disk:appendAllText(p, text)
end
function fs.getSize(path)
local disk, p = resolveSymlink(path)
return disk:getSize(p)
end
-- INIT
for _,v in kernel.initdisks.list() do fs.virtDisk(v) end
kernel.fs=fs
kernel.chache.preload.fs=fs
kernel.chache.preload.filesystem=fs

View File

@@ -0,0 +1,22 @@
local args={...}
local kernel=args[1]
local ifs=kernel.ifs
local initdisks=kernel.initdisks
local fstab=ifs.readAllText("/etc/fstab")
kernel.fs.update(initdisks)
for i,v in ipairs(string.split(fstab,"\n")) do
if v:sub(1,1)=="U" then
local id=""
for i=3,#v do
if v:sub(i,i)==";" then
if i==3 then kernel.log("Invalid fstab line... Skipping.","WARN") goto endline end
id=v:sub(3,i-1)
end
end
local path=v:sub(#id+4)
kernel.log("Mounted "..id.." to "..path)
kernel.fs.mount(id,path)
::endline::
end
end

View File

@@ -0,0 +1,182 @@
local args={...}
local kernel=args[1]
function kernel.fs.mkvirtfs(address, readOnly, label, autoRegister)
if kernel.uid ~= 0 then error("Permission Denied") end
local disk = {}
disk.address = address
local isRO = readOnly or false
local backend = {
["/"] = { __dir = true }
}
-----------------------------------------------------------------
-- Helpers
-----------------------------------------------------------------
local function norm(path)
if not path or path == "" then return "/" end
if path:sub(1,1) ~= "/" then path = "/" .. path end
path = path:gsub("/+", "/")
return path
end
local function split(path)
local parts = {}
for p in path:gmatch("[^/]+") do table.insert(parts, p) end
return parts
end
local function getNode(path)
path = norm(path)
if path == "/" then return backend["/"] end
local node = backend["/"]
for _, part in ipairs(split(path)) do
node = node[part]
if not node then return nil end
end
return node
end
local function getParent(path)
local parts = split(norm(path))
local name = table.remove(parts)
local parentPath = "/" .. table.concat(parts, "/")
return getNode(parentPath), name
end
local function ensureWrite()
if isRO then return false, "read-only" end
return true
end
-----------------------------------------------------------------
-- File IO (supports string or file-object with read/write funcs)
-----------------------------------------------------------------
local function fileRead(node)
if type(node) == "string" then
return node
elseif type(node) == "table" and node.__file and node.read then
return node.read()
end
return nil
end
local function fileWrite(parent, name, text)
local node = parent[name]
if type(node) == "string" then
parent[name] = text
return true
elseif type(node) == "table" and node.__file and node.write then
return node.write(text)
end
parent[name] = text
return true
end
local function fileAppend(parent, name, text)
local node = parent[name]
if type(node) == "string" then
parent[name] = node .. text
return true
elseif type(node) == "table" and node.__file and node.write then
return node.write((node.read() or "") .. text)
end
parent[name] = text
return true
end
-----------------------------------------------------------------
-- API
-----------------------------------------------------------------
function disk:isReadOnly() return isRO end
function disk:spaceUsed() return 0 end
function disk:spaceTotal() return 0 end
function disk:readAllText(path)
local node = getNode(path)
if not node then return nil, "file not found" end
return fileRead(node)
end
function disk:writeAllText(path, text)
local ok, err = ensureWrite(); if not ok then return false, err end
local parent, name = getParent(path)
if not parent or not parent.__dir then return false, "invalid directory" end
return fileWrite(parent, name, tostring(text))
end
function disk:appendAllText(path, text)
local ok, err = ensureWrite(); if not ok then return false, err end
local parent, name = getParent(path)
if not parent or not parent.__dir then return false, "invalid directory" end
return fileAppend(parent, name, tostring(text))
end
function disk:list(path)
local node = getNode(path)
if not node or not node.__dir then return nil, "not a directory" end
local out = {}
for k, _ in pairs(node) do if k ~= "__dir" then table.insert(out, k) end end
return out
end
function disk:fileExists(path)
local node = getNode(path)
return type(node) == "string" or (type(node) == "table" and node.__file)
end
function disk:directoryExists(path)
local node = getNode(path)
return node and type(node) == "table" and node.__dir or false
end
function disk:makeDirectory(path)
local ok, err = ensureWrite(); if not ok then return false, err end
local parent, name = getParent(path)
if not parent or not parent.__dir then return false, "invalid parent directory" end
parent[name] = { __dir = true }
return true
end
function disk:remove(path)
local ok, err = ensureWrite(); if not ok then return false, err end
local parent, name = getParent(path)
if not parent or not parent[name] then return false, "not found" end
parent[name] = nil
return true
end
function disk:setLabel(new)
local ok, err = ensureWrite(); if not ok then return false, err end
label = tostring(new)
end
function disk:getLabel()
return label
end
function disk:size(path)
local node = getNode(path)
if type(node) == "string" then return #node end
if type(node) == "table" and node.__file and node.read then
local v = node.read()
return v and #v or 0
end
return 0
end
-----------------------------------------------------------------
-- Auto-register with kernel and return backend
-----------------------------------------------------------------
if kernel and kernel.fs and kernel.fs.virtDisk and autoRegister then
kernel.fs.virtDisk(disk)
end
return backend, disk
end

View File

@@ -0,0 +1,56 @@
local args = {...}
local kernel = args[1]
-- List of search paths
local paths = {
"/lib/?",
"/usr/lib/?",
"/usr/local/lib/?"
}
-- Custom require implementation
function require(module)
-- Return cached module if it already exists
if kernel.cache.preload[module] then
return kernel.cache.preload[module]
end
local err = {}
for _, path in ipairs(paths) do
-- Replace "?" with module name
local filePath = string.replace(path, "?", module)
-- Try to open file
local file = kernel.fs.isFile(filePath)
if file then
local content = kernel.fs.readAllText(filePath)
-- Load the module as Lua code
local chunk, loadErr = load(content, filePath)
if not chunk then
table.insert(err, "Error loading: " .. filePath .. ": " .. loadErr)
else
-- Execute the module. If the module returns a value, cache it.
local ok, result = xpcall(chunk, debug.traceback)
if not ok then
table.insert(err, "Error executing: "..filePath..": "..tostring(result))
else
if result ~= nil then
kernel.cache.preload[module] = result
return result
end
-- If module doesn't return anything, cache `true` like Lua does
kernel.cache.preload[module] = true
return true
end
end
else
table.insert(err, "Module not found: " .. filePath)
end
end
-- If nothing worked, raise an error with all reasons
error("Unable to require module '" .. module .. "':\n" .. table.concat(err, "\n"))
end

View File

@@ -0,0 +1,17 @@
local args={...}
local kernel = args[1]
local data = kernel.fs.mkvirtfs("devfs0000", true, "devfs", false)
data["/"]["OSVERSION"]="Hyperion 1.0.0"
data["/"]["eeprom"]={
__file=true,
read=function()
return kernel.computer:getEEPROM()
end,
write=function(text)
if kernel.uid ~= 0 then error("Permission Denied") end
kernel.computer:setEEPROM(text)
end
}

View File

@@ -0,0 +1,12 @@
local args={...}
local kernel=args[1]
kernel.drivers.processes[#kernel.drivers.processes+1]=function()
while true do
local event={kernel.computer:getMachineEvent()}
while event[1]~=nil do
kernel.hpv.sendSig("all", table.unpack(event))
event={kernel.computer:getMachineEvent()}
end
coroutine.yield()
end
end

View File

@@ -0,0 +1,19 @@
local args={...}
local kernel=args[2]
for _,subf in ipairs(kernel.fs.list("/lib/modules/")) do
if subf~="Hyperion" then
for _,driver in ipairs(kernel.fs.list("/lib/modules/"..subf)) do
local code = kernel.fs.readAllText("/lib/modules/"..subf.."/"..driver)
local func, err = load(code, "@"..driver)
if not func then
kernel.log("DriverLoadErr: "..tostring(err), "ERROR")
else
local ok, err = xpcall(func, debug.traceback)
if not ok then
kernel.log("DriverLoadErr: "..tostring(err), "ERROR")
end
end
end
end
end

View File

@@ -0,0 +1,11 @@
local args={...}
local kernel=args[1]
for _,l in ipairs(kernel.drivers.prior) do
for _,d in ipairs(l) do
if d.init then
local ok,err = xpcall(d.init, debug.traceback)
if not ok then kernel.log("DriverInitErr: "..tostring(err)) end
end
end
end

View File

View File

@@ -0,0 +1,167 @@
local args={...}
local kernel = args[1]
local tasks={}
local currentTask={}
local signals={}
local tid=1
local gid=1
local sys={}
function sys.hookSig(sig, func)
if not signals[tostring(currentTask.pid)][sig] then
signals[tostring(currentTask.pid)][sig]={}
end
signals[tostring(currentTask.pid)][sig][#signals[tostring(currentTask.pid)][sig]+1]=func
end
function sys.clearSigHooks(typ)
if not typ or typ == "all" then
signals[tostring(currentTask.pid)]={}
currentTask.signal=signals[tostring(currentTask.pid)]
else
if currentTask.signal[typ] then
currentTask.signal[typ]={}
end
end
end
function sys.sendSig(pid, signal, ...)
if pid=="all" then
for i,v in pairs(tasks) do
v.sigQ[#v.sigQ+1]={signal, ...}
end
return
end
if not tasks[tostring(pid)] then return false end
tasks[tostring(pid)].sigQ[#tasks[tostring(pid)].sigQ+1]={signal, ...}
return true
end
function sys.flushSigs()
local ret = {}
for i=1, #currentTask.sigQ do
if currentTask.signal[currentTask.sigQ[i][1]] then
for _,v in ipairs(currentTask.signal[currentTask.sigQ[1][1]]) do
coroutine.resumeWithTimeout(coroutine.create(function()
local ok, err = xpcall(v, debug.traceback, table.unpack(table.remove(currentTask.sigQ, 1)))
if not ok then
table.insert(ret, err)
end
end), 10)
end
else
for _,v in ipairs(currentTask.signal["unhandledEvent"]) do
coroutine.resumeWithTimeout(coroutine.create(function()
local ok, err = xpcall(v, debug.traceback, table.unpack(table.remove(currentTask.sigQ, 1)))
if not ok then
table.insert(ret, err)
end
end), 10)
end
end
end
end
function sys.spawn(func, name, evars, args)
local id=tid
tid=tid+1
name=name or tostring(id)
signals[tostring(id)]={}
tasks[tostring(id)]={
coro=coroutine.create(function()
local ret = {xpcall(func, debug.traceback, table.unpack(args))}
if not ret[1] then
sys.sendSig(currentTask.ppid, "ChildTaskError", id, err)
else
sys.sendSig(currentTask.ppid, "ChildTaskExit", id, table.unpack(ret, 2))
end
currentTask.status="Z"
end),
name=name,
pid=id,
ppid=currentTask.pid,
tgid=currentTask.tgid,
user=kernel.user,
uid=kernel.uid,
evars=evars,
args=args,
vy=0,
ivy=0,
status="R",
signal=signals[tostring(id)],
parent=currentTask,
children={},
sibling=currentTask.children,
sigQ={}
}
end
local function collectZombieProc()
local ret = {}
for _,v in pairs(tasks) do
if v.status=="Z" then
local pid = v.pid
for _,c in ipairs(v.children) do
c.parent=tasks["1"]
c.sibling=tasks["1"].children
c.ppid=1
c.tgid=1
end
signals[tostring(pid)]=nil
tasks[tostring(pid)]=nil
table.insert(ret, pid)
end
end
return ret
end
signals["1"]={}
tasks["1"]={
coro=coroutine.create(function()
local ret = {xpcall(kernel.fs.load(kernel.initPath), debug.traceback)}
if not ok then
kernel.panic(err)
else
kernel.panic("Attempted to kill init!")
end
end),
name="SysInit",
pid=1,
ppid=0,
tgid=1,
user=kernel.user,
uid=kernel.uid,
evars={},
args={kernel},
vy=0,
ivy=0,
status="R",
signal=signals["1"],
parent={name="Hyprkrnl",pid=0},
children={},
sibling={},
sigQ={}
}
kernel.chache.preload.sys=sys
kernel.chache.preload.system=sys
kernel.hpv=sys
kernel.saveLog()
while true do
for _,v in pairs(tasks) do
currentTask=v
kernel.user=currentTask.user
kernel.uid=currentTask.uid
sys.flushSigs()
local status = coroutine.resumeWithTimeout(currentTask.coro, 50)
if status then
currentTask.vy=currentTask.vy+1
else
currentTask.ivy=currentTask.ivy+1
end
end
collectZombieProc()
end
kernel.panic("Exited pid 0")

109
Build/lib/modules/cct-tto.kmod Executable file
View File

@@ -0,0 +1,109 @@
local args={...}
local apis=args[1]
local drivers=args[2]
local driver={}
driver.name="CCT TTO Module"
driver.version="0.1.0"
driver.description="CCT TTO Module Kernel Module"
driver.arch="cct"
driver.author="HyperionOS Dev Team"
driver.license="MIT"
driver.api={}
colors={
0x000000, -- #000000
0xFFFFFF, -- #FFFFFF
0xFF0000, -- #FF0000
0x00FF00, -- #00FF00
0x0000FF, -- #0000FF
0x00FFFF, -- #00FFFF
0xFF00FF, -- #FF00FF
0xFFFF00, -- #FFFF00
0xFF6D00, -- #FF6D00
0x6DFF55, -- #6DFF55
0x24FFFF, -- #24FFFF
0x924900, -- #924900
0x6D6D55, -- #6D6D55
0xDBDBAA, -- #DBDBAA
0x6D00FF, -- #6D00FF
0xB6FF00 -- #B6FF00
}
icolors={
[0x1] =0, -- #000000
[0x2] =1, -- #FFFFFF
[0x4] =2, -- #FF0000
[0x8] =3, -- #00FF00
[0x10] =4, -- #0000FF
[0x20] =5, -- #00FFFF
[0x40] =6, -- #FF00FF
[0x80] =7, -- #FFFF00
[0x100] =8, -- #FF6D00
[0x200] =9, -- #6DFF55
[0x400] =10, -- #24FFFF
[0x800] =11, -- #924900
[0x1000] =12, -- #6D6D55
[0x2000] =13, -- #DBDBAA
[0x4000] =14, -- #6D00FF
[0x8000] =15 -- #B6FF00
}
function driver.load()
end
function driver.unload()
-- Nothing to unload
end
function driver.reload()
-- Nothing to reload
end
function driver.main()
-- Nothing to run
end
function driver.api.print(text)
apis.term.write(text.."\n")
end
function driver.api.printInline(text)
apis.term.write(text)
end
function driver.api.clear()
apis.term.clear()
apis.term.setCursorPos(1,1)
end
function driver.api.getSize()
return apis.term.getSize()
end
function driver.api.setBackgroundColor(index)
apis.term.setBackgroundColor(colors[index] or 0)
end
function driver.api.setForegroundColor(index)
apis.term.setTextColor(colors[index] or 0)
end
function driver.api.setCursorPos(x, y)
apis.term.setCursorPos(x, y)
end
function driver.api.getCursorPos()
return apis.term.getCursorPos()
end
function driver.api.getBackgroundColor()
return icolors[apis.term.getBackgroundColor()]
end
function driver.api.getForegroundColor()
return icolors[apis.term.getTextColor()]
end
drivers.register(driver)

8
Build/sbin/init Executable file
View File

@@ -0,0 +1,8 @@
local args={...}
local kernel=args[1]
kernel.panic("text")
for i,v in ipairs(kernel.fs.list("/dev")) do
kernl.log(v)
end

0
LICENSE Normal file → Executable file
View File

0
README.md Normal file → Executable file
View File

177
Src/iso.lua Executable file
View File

@@ -0,0 +1,177 @@
-- Minimal ISO9660 parser
-- Provides: ISO.open(path) -> iso object
-- Methods: :close(), :readPVD(), :listRoot(), :readFile(path)
local M = {}
local SECTOR_SIZE = 2048
local function readBytes(f, pos, len)
f:seek("set", pos)
return f:read(len)
end
local function u8(s, i)
return string.byte(s, i)
end
local function u16le(s, i)
local b1 = string.byte(s, i)
local b2 = string.byte(s, i+1)
return b1 + b2 * 256
end
local function u32le(s, i)
local b1 = string.byte(s, i)
local b2 = string.byte(s, i+1)
local b3 = string.byte(s, i+2)
local b4 = string.byte(s, i+3)
return b1 + b2*256 + b3*65536 + b4*16777216
end
local function trimVersion(name)
-- remove ;1 version and trailing dots
local n = name:gsub(";1$", "")
n = n:gsub("%.$", "")
return n
end
local ISO = {}
ISO.__index = ISO
function M.open(path)
local f, err = io.open(path, "rb")
if not f then return nil, err end
local self = setmetatable({ f = f, path = path }, ISO)
return self
end
function ISO:close()
if self.f then self.f:close(); self.f = nil end
end
function ISO:readSector(n)
local pos = n * SECTOR_SIZE
return readBytes(self.f, pos, SECTOR_SIZE)
end
function ISO:readPVD()
-- Primary Volume Descriptor at sector 16 (index 16)
local pvd = self:readSector(16)
if not pvd or #pvd < SECTOR_SIZE then return nil, "cannot read PVD" end
local typecode = u8(pvd,1)
local id = pvd:sub(2,6)
if typecode ~= 1 or id ~= "CD001" then return nil, "no primary volume descriptor" end
local volumeIdentifier = pvd:sub(41,72):gsub('%z+$','')
-- root directory record starts at offset 157 (1-based index)
local rdOffset = 157
local rdLen = u8(pvd, rdOffset)
local extent = u32le(pvd, rdOffset + 2)
local dataLen = u32le(pvd, rdOffset + 10)
self.pvd = {
volumeIdentifier = volumeIdentifier,
root = { extent = extent, size = dataLen }
}
return self.pvd
end
local function parseDirRecords(buffer, bytesToRead)
local records = {}
local offset = 1
local total = bytesToRead or #buffer
while offset <= total do
local len = string.byte(buffer, offset)
if not len or len == 0 then
-- padding to sector boundary, move to next byte
offset = offset + 1
else
local rec = {}
rec.len = len
rec.extAttrLen = string.byte(buffer, offset+1)
rec.extent = u32le(buffer, offset+2)
rec.size = u32le(buffer, offset+10)
rec.recordingDate = buffer:sub(offset+18, offset+24)
rec.flags = string.byte(buffer, offset+25)
rec.fileUnitSize = string.byte(buffer, offset+26)
rec.interleaveGapSize = string.byte(buffer, offset+27)
rec.volumeSeq = u16le(buffer, offset+29)
local fiLen = string.byte(buffer, offset+32)
local idStart = offset + 33
local idEnd = idStart + fiLen - 1
local fileId = buffer:sub(idStart, idEnd)
-- strip trailing ;1 and dots
rec.fileIdentifier = fileId
rec.fileName = trimVersion(fileId)
rec.isDirectory = (rec.flags & 0x02) ~= 0
table.insert(records, rec)
offset = offset + len
end
end
return records
end
function ISO:listRoot()
if not self.pvd then self:readPVD() end
local root = self.pvd.root
local extent = root.extent
local size = root.size
local pos = extent * SECTOR_SIZE
local buffer = readBytes(self.f, pos, size)
local records = parseDirRecords(buffer, size)
-- filter out '.' and '..' and entries with empty name
local out = {}
for _, r in ipairs(records) do
local name = r.fileName
if name and name ~= "" and name ~= "\0" and name ~= "\1" and name ~= "." and name ~= ".." then
table.insert(out, { name = name, isDirectory = r.isDirectory, extent = r.extent, size = r.size })
end
end
return out
end
local function splitPath(path)
local parts = {}
for part in path:gmatch('[^/\\]+') do parts[#parts+1]=part end
return parts
end
function ISO:findEntryByPath(path)
if not self.pvd then self:readPVD() end
local parts = splitPath(path)
local curExtent = self.pvd.root.extent
local curSize = self.pvd.root.size
for i, part in ipairs(parts) do
-- read directory entries for curExtent
local buffer = readBytes(self.f, curExtent * SECTOR_SIZE, curSize)
local records = parseDirRecords(buffer, curSize)
local found = nil
for _, r in ipairs(records) do
local name = trimVersion(r.fileIdentifier)
if name:lower() == part:lower() then
found = r; break
end
end
if not found then return nil, "path not found: " .. part end
if i < #parts then
if not found.isDirectory then return nil, "not a directory: "..part end
curExtent = found.extent
curSize = found.size
else
return found
end
end
return nil, "empty path"
end
function ISO:readFile(path)
local rec, err = self:findEntryByPath(path)
if not rec then return nil, err end
if rec.isDirectory then return nil, "path is a directory" end
local pos = rec.extent * SECTOR_SIZE
return readBytes(self.f, pos, rec.size)
end
-- return module
return M

10
Test/Hyperion-core-v0.1.0.pkg Executable file
View File

@@ -0,0 +1,10 @@
{
"Name": "Hyperion core",
"Version": "0.1.0",
"Publishers": ["HyperionOS Dev Team"],
"Description": "Core system files and librarys for HyperionOS",
"Dependencies": [
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-kernel-v0.1.0.pkg"
],
"Tar": "https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-core-v0.1.0.tar"
}

View File

@@ -0,0 +1,2 @@
U $;/
U devfs0000;/dev/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
local deflate=require("LibDeflate")
local lib={}
lib.compress=function(data)
return deflate:CompressDeflate(data)
end
lib.decompress=function(data)
return deflate:DecompressDeflate(data)
end
return lib

View File

View File

View File

@@ -0,0 +1,8 @@
local args={...}
local kernel=args[1]
kernel.panic("text")
for i,v in ipairs(kernel.fs.list("/dev")) do
kernl.log(v)
end

View File

@@ -0,0 +1,11 @@
{
"Name": "Hyperion CCT Firmware",
"Version": "0.1.0",
"Publishers": ["HyperionOS Dev Team"],
"Description": "The ComputerCraftTweaked firmware package for HyperionOS.",
"Dependencies": [
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-kernel-v0.1.0.pkg"
],
"Tar": "https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-firmware-cct-v0.1.0.tar",
"BuildCommand": "sudo cp boot/cct/eeprom /dev/eeprom"
}

View File

@@ -0,0 +1,303 @@
local term = term
local os = os
-- Function to write text to the terminal with special character handling
local function write(text)
local x, y = term.getCursorPos()
local w, h = term.getSize()
for i = 1, #text do
local c = text:sub(i, i)
if c == "\n" then
y = y + 1
x = 1
elseif c == "\t" then
local tabSize = 4
local spaces = tabSize - ((x - 1) % tabSize)
term.write(string.rep(" ", spaces))
x = x + spaces
elseif c == "\b" then
if x > 1 then
x = x - 1
term.setCursorPos(x, y)
term.write(" ")
term.setCursorPos(x, y)
end
else
if x <= w and y <= h then
term.setCursorPos(x, y)
term.write(c)
x = x + 1
end
end
-- Handle wrapping if we go past right edge
if x > w then
x = 1
y = y + 1
end
-- Handle scrolling if we go past bottom
if y-1 > h then
term.scroll(1)
y = h
term.setCursorPos(x, y)
end
end
term.setCursorPos(x, y)
end
-- Function to display critical errors and halt the system
local function displaySuperBadError(err)
term.setBackgroundColor(0x1)
term.setTextColor(0x4)
term.clear()
term.setCursorPos(1, 1)
term.write("A critical error occurred while loading the system:")
term.setCursorPos(1, 3)
write(err)
while true do end
end
-- Wrap all in xpcall to catch errors
local ok, err = xpcall(function()
local apis={}
-- List of standard Lua globals
local lua = {
coroutine = true,
debug = true,
_HOST = true,
_VERSION = true,
assert = true,
collectgarbage = true,
error = true,
gcinfo = true,
getfenv = true,
getmetatable = true,
ipairs = true,
__inext = true,
load = true,
math = true,
next = true,
pairs = true,
pcall = true,
rawequal = true,
rawget = true,
rawlen = true,
rawset = true,
select = true,
setfenv = true,
setmetatable = true,
string = true,
table = true,
tonumber = true,
tostring = true,
type = true,
xpcall = true,
_G=true
}
-- Move all non-Lua standard library globals into the apis table
for i,v in pairs(_G) do
if not lua[i] or lua[i]==nil then
apis[i]=v
_G[i]=nil
end
end
-- Set up terminal default colors
apis.term.setPaletteColor(0x1, 0x000000) -- #000000
apis.term.setPaletteColor(0x2, 0xFFFFFF) -- #FFFFFF
apis.term.setPaletteColor(0x4, 0xFF0000) -- #FF0000
apis.term.setPaletteColor(0x8, 0x00FF00) -- #00FF00
apis.term.setPaletteColor(0x10, 0x0000FF) -- #0000FF
apis.term.setPaletteColor(0x20, 0x00FFFF) -- #00FFFF
apis.term.setPaletteColor(0x40, 0xFF00FF) -- #FF00FF
apis.term.setPaletteColor(0x80, 0xFFFF00) -- #FFFF00
apis.term.setPaletteColor(0x100, 0xFF6D00) -- #FF6D00
apis.term.setPaletteColor(0x200, 0x6DFF55) -- #6DFF55
apis.term.setPaletteColor(0x400, 0x24FFFF) -- #24FFFF
apis.term.setPaletteColor(0x800, 0x924900) -- #924900
apis.term.setPaletteColor(0x1000, 0x6D6D55) -- #6D6D55
apis.term.setPaletteColor(0x2000, 0xDBDBAA) -- #DBDBAA
apis.term.setPaletteColor(0x4000, 0x6D00FF) -- #6D00FF
apis.term.setPaletteColor(0x8000, 0xB6FF00) -- #B6FF00
-- Wrapper to read files
local function getFile(path)
local file = apis.fs.open(path, "r")
if not file then displaySuperBadError("Could not open file: "..path) end
local content = file.readAll()
file.close()
return content
end
-- Load kernel first if it fails, we can't continue so we display an error
local Kernel = load(getFile("/$/boot/kernel.lua"),"@Kernel")
local initFs = load(getFile("/$/boot/cct/initdisks","@Init_disks"))(apis)
local fs = load(getFile("/$/boot/initfs"),"@InitFs")()
if not Kernel then
displaySuperBadError("Could not load kernel.")
end
if not initFs then
displaySuperBadError("Could not load initdisks.")
end
if not fs then
displaySuperBadError("Could not load initfs.")
end
-- Set up event queue
local eventQueue = {}
local lkeys={}
lkeys[apis.keys.enter]="\n"
lkeys[apis.keys.backspace]="\b"
lkeys[apis.keys.tab]="\t"
-- Function to queue events
local function queueEvent(event, ...)
table.insert(eventQueue, {event, ...})
end
-- Set up computer api
local computer = {
time = function() return apis.os.epoch("utc") end,
clock = apis.os.clock,
shutdown = apis.os.shutdown,
reboot = apis.os.reboot,
getMachineEvent = function()
if #eventQueue > 0 then
return table.unpack(table.remove(eventQueue, 1))
else
return nil
end
end,
getEEPROM = function()
return getFile("/startup.lua")
end,
setEEPROM = function(_,text)
local h = apis.fs.open("/startup.lua", "w")
h.write(text)
h.close()
end
}
-- Set up terminal colors
local icolors={
[0x1] =0, -- #000000
[0x2] =1, -- #FFFFFF
[0x4] =2, -- #FF0000
[0x8] =3, -- #00FF00
[0x10] =4, -- #0000FF
[0x20] =5, -- #00FFFF
[0x40] =6, -- #FF00FF
[0x80] =7, -- #FFFF00
[0x100] =8, -- #FF6D00
[0x200] =9, -- #6DFF55
[0x400] =10, -- #24FFFF
[0x800] =11, -- #924900
[0x1000] =12, -- #6D6D55
[0x2000] =13, -- #DBDBAA
[0x4000] =14, -- #6D00FF
[0x8000] =15 -- #B6FF00
}
local colors={
[0]=0x0001, -- #000000
0x0002, -- #FFFFFF
0x0004, -- #FF0000
0x0008, -- #00FF00
0x0010, -- #0000FF
0x0020, -- #00FFFF
0x0040, -- #FF00FF
0x0080, -- #FFFF00
0x0100, -- #FF6D00
0x0200, -- #6DFF55
0x0400, -- #24FFFF
0x0800, -- #924900
0x1000, -- #6D6D55
0x2000, -- #DBDBAA
0x4000, -- #6D00FF
0x8000 -- #B6FF00
}
apis.term.setBackgroundColor(0x1)
apis.term.setTextColor(0x1000)
apis.term.clear()
apis.term.setCursorPos(1, 1)
-- Make the kernel coroutine so we can hook its execution
local kernelCoro = coroutine.create(function()
local ok, err = xpcall(Kernel, debug.traceback, apis, initFs, "cct", "/sbin/init", {
print=function(_,text) write(text.."\n") end,
printInline=function(_,text) write(text) end,
clear=function() apis.term.clear() apis.term.setCursorPos(1,1) end,
setCursorPos=function(_,x,y) apis.term.setCursorPos(x,y) end,
getCursorPos=function() return apis.term.getCursorPos() end,
getSize=function() return apis.term.getSize() end,
setBackgroundColor=function(_,color) apis.term.setBackgroundColor(colors[color]) end,
setTextColor=function(_,color) apis.term.setTextColor(colors[color]) end,
getBackgroundColor=function() return icolors[apis.term.getBackgroundColor()] end,
getTextColor=function() return icolors[apis.term.getTextColor()] end
}, computer, fs, "$")
if not ok then
displaySuperBadError(err)
end
end)
-- Diffine a coroutine.resumeWithTimeout function to avoid hanging the system, time is in milliseconds
function coroutine.resumeWithTimeout(co, timeout, ...)
local startTime = computer.time()
debug.sethook(co, function()
if computer.time() > startTime + timeout then
return coroutine.yield("timeout")
end
end, "", 1000)
local ret = {coroutine.resume(co, ...)}
if ret[1]=="timeout" then
return nil, "Coroutine timed out"
elseif ret[1]==false then
return false, ret[2]
else
debug.sethook(co)
return true, table.unpack(ret)
end
end
write("Loaded in "..tostring(apis.os.clock()).." seconds.\n")
-- Main loop to run the kernel coroutine
while true do
local status, err = coroutine.resumeWithTimeout(kernelCoro, 50)
apis.os.queueEvent("NoSleep")
local exit = false
while not exit do
local event = {coroutine.yield()}
if event[1] == "char" then
queueEvent("keyTyped", 1, event[2])
elseif event[1] == "key" then
queueEvent("keyPressed", 1, event[2])
if lkeys[event[2]] then
queueEvent("keyTyped", 1, lkeys[event[2]])
end
elseif event[1] == "key_up" then
queueEvent("keyReleased", 1, event[2])
elseif event[1] == "disk" then
queueEvent("componentAdded", "disk")
elseif event[1] == "disk_eject" then
queueEvent("componentRemoved", "disk")
elseif event[1] == "NoSleep" then
exit=true
end
end
if status == false or coroutine.status(kernelCoro)=="dead" then
displaySuperBadError("Kernel error: "..tostring(err))
coroutine.yield("key")
end
end
end, debug.traceback)
if not ok then
displaySuperBadError("Fatal error during boot: "..err)
end
while true do coroutine.yield() end

View File

@@ -0,0 +1,119 @@
-- UnBIOS by JackMacWindows
-- This will undo most of the changes/additions made in the BIOS, but some things may remain wrapped if `debug` is unavailable
-- To use, just place a `bios.lua` in the root of the drive, and run this program
-- Here's a list of things that are irreversibly changed:
-- * both `bit` and `bit32` are kept for compatibility
-- * string metatable blocking (on old versions of CC)
-- In addition, if `debug` is not available these things are also irreversibly changed:
-- * old Lua 5.1 `load` function (for loading from a function)
-- * `loadstring` prefixing (before CC:T 1.96.0)
-- * `http.request`
-- * `os.shutdown` and `os.reboot`
-- * `peripheral`
-- * `turtle.equip[Left|Right]`
-- Licensed under the MIT license
local args = {...}
if _HOST:find("UnBIOS") then return end
local keptAPIs = {keys=true, bit32 = true, bit = true, ccemux = true, config = true, coroutine = true, debug = true, fs = true, http = true, mounter = true, os = true, periphemu = true, peripheral = true, redstone = true, rs = true, term = true, utf8 = true, _HOST = true, _CC_DEFAULT_SETTINGS = true, _CC_DISABLE_LUA51_FEATURES = true, _VERSION = true, assert = true, collectgarbage = true, error = true, gcinfo = true, getfenv = true, getmetatable = true, ipairs = true, __inext = true,load = true, loadstring = true, math = true, newproxy = true, next = true, pairs = true, pcall = true, rawequal = true, rawget = true, rawlen = true, rawset = true, select = true, setfenv = true, setmetatable = true, string = true, table = true, tonumber = true, tostring = true, type = true, unpack = true, xpcall = true, turtle = true, pocket = true, commands = true, _G = true}
local t = {}
for k in pairs(_G) do if not keptAPIs[k] then table.insert(t, k) end end
for _,k in ipairs(t) do _G[k] = nil end
local native = _G.term.native()
for _, method in ipairs {"nativePaletteColor", "nativePaletteColour", "screenshot"} do native[method] = _G.term[method] end
_G.term = native
if _G.http then
_G.http.checkURL = _G.http.checkURLAsync
_G.http.websocket = _G.http.websocketAsync
end
if _G.commands then _G.commands = _G.commands.native end
if _G.turtle then _G.turtle.native, _G.turtle.craft = nil end
local delete = {os = {"version", "pullEventRaw", "pullEvent", "run", "loadAPI", "unloadAPI", "sleep"}, http = _G.http and {"get", "post", "put", "delete", "patch", "options", "head", "trace", "listen", "checkURLAsync", "websocketAsync"}, fs = {"complete", "isDriveRoot"}}
for k,v in pairs(delete) do for _,a in ipairs(v) do _G[k][a] = nil end end
_G._HOST = _G._HOST .. " (UnBIOS)"
-- Set up TLCO
-- This functions by crashing `rednet.run` by removing `os.pullEventRaw`. Normally
-- this would cause `parallel` to throw an error, but we replace `error` with an
-- empty placeholder to let it continue and return without throwing. This results
-- in the `pcall` returning successfully, preventing the error-displaying code
-- from running - essentially making it so that `os.shutdown` is called immediately
-- after the new BIOS exits.
--
-- From there, the setup code is placed in `term.native` since it's the first
-- thing called after `parallel` exits. This loads the new BIOS and prepares it
-- for execution. Finally, it overwrites `os.shutdown` with the new function to
-- allow it to be the last function called in the original BIOS, and returns.
-- From there execution continues, calling the `term.redirect` dummy, skipping
-- over the error-handling code (since `pcall` returned ok), and calling
-- `os.shutdown()`. The real `os.shutdown` is re-added, and the new BIOS is tail
-- called, which effectively makes it run as the main chunk.
local olderror = error
_G.error = function() end
_G.term.redirect = function() end
function _G.term.native()
_G.term.native = nil
_G.term.redirect = nil
_G.error = olderror
term.setBackgroundColor(32768)
term.setTextColor(1)
term.setCursorPos(1, 1)
term.setCursorBlink(true)
term.clear()
local file = fs.open("/$/boot/cct/boot.lua", "r")
if file == nil then
term.setCursorBlink(false)
term.setTextColor(16384)
term.write("Could not find /boot/cct/boot.lua. UnBIOS cannot continue.")
term.setCursorPos(1, 2)
term.write("Press any key to continue")
coroutine.yield("key")
os.shutdown()
end
local fn, err = loadstring(file.readAll(), "@bootloader")
file.close()
if fn == nil then
term.setCursorBlink(false)
term.setTextColor(16384)
term.write("Could not load /boot/cc/boot.lua. UnBIOS cannot continue.")
term.setCursorPos(1, 2)
term.write(err)
term.setCursorPos(1, 3)
term.write("Press any key to continue")
coroutine.yield("key")
os.shutdown()
end
setfenv(fn, _G)
local oldshutdown = os.shutdown
os.shutdown = function()
os.shutdown = oldshutdown
return fn(table.unpack(args))
end
end
if debug then
-- Restore functions that were overwritten in the BIOS
-- Apparently this has to be done *after* redefining term.native
local function restoreValue(tab, idx, name, hint)
local i, key, value = 1, debug.getupvalue(tab[idx], hint)
while key ~= name and key ~= nil do
key, value = debug.getupvalue(tab[idx], i)
i=i+1
end
tab[idx] = value or tab[idx]
end
restoreValue(_G, "loadstring", "nativeloadstring", 1)
restoreValue(_G, "load", "nativeload", 5)
if http then restoreValue(http, "request", "nativeHTTPRequest", 3) end
restoreValue(os, "shutdown", "nativeShutdown", 1)
restoreValue(os, "reboot", "nativeReboot", 1)
if turtle then
restoreValue(turtle, "equipLeft", "v", 1)
restoreValue(turtle, "equipRight", "v", 1)
end
do
local i, key, value = 1, debug.getupvalue(peripheral.isPresent, 2)
while key ~= "native" and key ~= nil do
key, value = debug.getupvalue(peripheral.isPresent, i)
i=i+1
end
_G.peripheral = value or peripheral
end
end

View File

@@ -0,0 +1,201 @@
local apis = ({...})[1]
local fs = apis.fs
local native = apis.peripheral
local peripheral = {}
local sides = {"top", "bottom", "left", "right", "front", "back"}
function peripheral.getType(name)
if native.isPresent(name) then
return native.getType(name)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
return native.call(side, "getTypeRemote", name)
end
end
return nil
end
function peripheral.getNames()
local names = {}
for n = 1, #sides do
local side = sides[n]
if native.isPresent(side) then
table.insert(names, side)
end
if native.hasType(side, "peripheral_hub") then
local hubSides = native.call(side, "getConnectedSides")
for _, hubSide in ipairs(hubSides) do
table.insert(names, hubSide)
end
end
end
return names
end
---------------------------------------------------------
-- STORAGE
---------------------------------------------------------
local disks = {} -- real disks
local internal = {} -- "$" disk
---------------------------------------------------------
-- HELPERS
---------------------------------------------------------
local function norm(path)
if not path or path == "" then return "/" end
return fs.combine("/", path)
end
--- Creates a disk object given a base path
local function createDisk(id, basePath, readonly, periph)
basePath = norm(basePath)
local disk = {address=id,isReadOnly=function() return readonly end}
function disk:spaceUsed()
return fs.getCapacity(basePath) - fs.getFreeSpace(basePath)
end
function disk:spaceTotal()
return fs.getCapacity(basePath)
end
function disk:readAllText(path)
local p = fs.combine(basePath, path)
if not fs.exists(p) then return nil, "file not found" end
local h = fs.open(p, "r")
local t = h.readAll()
h.close()
return t
end
function disk:writeAllText(path, text)
local p = fs.combine(basePath, path)
local h = fs.open(p, "w")
if not h then return nil, "cannot write" end
h.write(text)
h.close()
return true
end
function disk:appendAllText(path, text)
local p = fs.combine(basePath, path)
local h = fs.open(p, "a")
if not h then return nil, "cannot append" end
h.write(text)
h.close()
return true
end
function disk:list(path)
local p = fs.combine(basePath, path)
if not fs.exists(p) or not fs.isDir(p) then return nil, "not directory" end
return fs.list(p)
end
function disk:fileExists(path)
local p = fs.combine(basePath, path)
return fs.exists(p) and not fs.isDir(p)
end
function disk:directoryExists(path)
local p = fs.combine(basePath, path)
return fs.exists(p) and fs.isDir(p)
end
function disk:makeDirectory(path)
local p = fs.combine(basePath, path)
fs.makeDir(p)
return true
end
function disk:remove(path)
local p = fs.combine(basePath, path)
if fs.exists(p) then fs.delete(p) end
return true
end
function disk:setLabel(label)
periph.setLabel(label)
end
function disk:getLabel(label)
return periph.getLabel()
end
function disk:size(path)
local p = fs.combine(basePath, path)
return fs.getSize(p)
end
return disk
end
---------------------------------------------------------
-- INTERNAL DISK "$" (mapped to "/")
---------------------------------------------------------
internal["$"] = createDisk("$", "/$", false, {
setLabel=function(label)
local h = fs.open("/.label", "w")
h.write(label)
h.close()
end,
getLabel=function()
local h = fs.open("/.label", "r")
local label = h.readAll()
h.close()
return label
end
})
---------------------------------------------------------
-- SCAN REAL DISK PERIPHERALS
---------------------------------------------------------
local function refresh()
-- remove disks that no longer exist
for id, _ in pairs(disks) do
if not peripheral.getType(id) then
disks[id] = nil
end
end
-- detect new disks
for _, name in ipairs(peripheral.getNames()) do
if peripheral.getType(name) == "disk" then
if not disks[name] then
local mount = disk.getMountPath(name)
if mount then
disks[name] = createDisk(name, mount, false, disk)
end
end
end
end
end
---------------------------------------------------------
-- ITERATOR
---------------------------------------------------------
local function iter()
refresh()
-- first internal
local combined = {}
for id, obj in pairs(internal) do
combined[id] = obj
end
for id, obj in pairs(disks) do
combined[id] = obj
end
return pairs(combined)
end
---------------------------------------------------------
-- MODULE RETURN
---------------------------------------------------------
return {
refresh = refresh,
list = iter
}

View File

@@ -0,0 +1,109 @@
local args={...}
local apis=args[1]
local drivers=args[2]
local driver={}
driver.name="CCT TTO Module"
driver.version="0.1.0"
driver.description="CCT TTO Module Kernel Module"
driver.arch="cct"
driver.author="HyperionOS Dev Team"
driver.license="MIT"
driver.api={}
colors={
0x000000, -- #000000
0xFFFFFF, -- #FFFFFF
0xFF0000, -- #FF0000
0x00FF00, -- #00FF00
0x0000FF, -- #0000FF
0x00FFFF, -- #00FFFF
0xFF00FF, -- #FF00FF
0xFFFF00, -- #FFFF00
0xFF6D00, -- #FF6D00
0x6DFF55, -- #6DFF55
0x24FFFF, -- #24FFFF
0x924900, -- #924900
0x6D6D55, -- #6D6D55
0xDBDBAA, -- #DBDBAA
0x6D00FF, -- #6D00FF
0xB6FF00 -- #B6FF00
}
icolors={
[0x1] =0, -- #000000
[0x2] =1, -- #FFFFFF
[0x4] =2, -- #FF0000
[0x8] =3, -- #00FF00
[0x10] =4, -- #0000FF
[0x20] =5, -- #00FFFF
[0x40] =6, -- #FF00FF
[0x80] =7, -- #FFFF00
[0x100] =8, -- #FF6D00
[0x200] =9, -- #6DFF55
[0x400] =10, -- #24FFFF
[0x800] =11, -- #924900
[0x1000] =12, -- #6D6D55
[0x2000] =13, -- #DBDBAA
[0x4000] =14, -- #6D00FF
[0x8000] =15 -- #B6FF00
}
function driver.load()
end
function driver.unload()
-- Nothing to unload
end
function driver.reload()
-- Nothing to reload
end
function driver.main()
-- Nothing to run
end
function driver.api.print(text)
apis.term.write(text.."\n")
end
function driver.api.printInline(text)
apis.term.write(text)
end
function driver.api.clear()
apis.term.clear()
apis.term.setCursorPos(1,1)
end
function driver.api.getSize()
return apis.term.getSize()
end
function driver.api.setBackgroundColor(index)
apis.term.setBackgroundColor(colors[index] or 0)
end
function driver.api.setForegroundColor(index)
apis.term.setTextColor(colors[index] or 0)
end
function driver.api.setCursorPos(x, y)
apis.term.setCursorPos(x, y)
end
function driver.api.getCursorPos()
return apis.term.getCursorPos()
end
function driver.api.getBackgroundColor()
return icolors[apis.term.getBackgroundColor()]
end
function driver.api.getForegroundColor()
return icolors[apis.term.getTextColor()]
end
drivers.register(driver)

View File

@@ -0,0 +1,11 @@
{
"Name": "Hyperion OC Firmware",
"Version": "0.1.0",
"Publishers": ["HyperionOS Dev Team"],
"Description": "The OpenComputers firmware package for HyperionOS.",
"Dependencies": [
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-kernel-v0.1.0.pkg"
],
"Tar": "https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-firmware-oc-v0.1.0.tar",
"BuildCommand": "cp boot/oc/eeprom /dev/eeprom"
}

View File

@@ -0,0 +1,40 @@
local lua = {
coroutine = true,
debug = true,
_HOST = true,
_VERSION = true,
assert = true,
collectgarbage = true,
error = true,
gcinfo = true,
getfenv = true,
getmetatable = true,
ipairs = true,
__inext = true,
load = true,
math = true,
next = true,
pairs = true,
pcall = true,
rawequal = true,
rawget = true,
rawlen = true,
rawset = true,
select = true,
setfenv = true,
setmetatable = true,
string = true,
table = true,
tonumber = true,
tostring = true,
type = true,
xpcall = true,
_G=true
}
for i,v in pairs(_G) do
if not lua[i] or lua[i]==nil then
apis[i]=v
_G[i]=nil
end
end

View File

@@ -0,0 +1,18 @@
checkArg=nil
local oldcomputer=computer
_G.computer=nil
local os=os
_G.os=nil
function component.wrap(address)
local methods=oldcomponent.methods(address)
local object={}
for _,method in ipairs(methods) do
object[method]=function(_,...)
return oldcomponent.invoke(address,method,...)
end
end
return object
end
local

View File

@@ -0,0 +1 @@
local fs={}

View File

@@ -0,0 +1,8 @@
{
"Name": "Hyperion Kernel",
"Version": "1.0.0",
"Publishers": ["HyperionOS Dev Team"],
"Description": "The kernel package for HyperionOS.",
"Dependencies": [],
"Tar": "https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-kernel-v0.1.0.tar"
}

View File

@@ -0,0 +1,69 @@
local fs = {}
local disks = {}
local mounts = {}
local function resolve(path)
local mountPoint = "/"
for mount, disk in pairs(mounts) do
if path:sub(1, #mount) == mount then
if not mountPoint or #mount > #mountPoint then
mountPoint = mount
end
end
end
local newPath = path:sub(#mountPoint + 1)
return disks[mounts[mountPoint]], newPath
end
---------------------------------------------------------
function fs.update(initdisks)
disks = {}
for k, v in initdisks.list() do
disks[k] = v
end
end
function fs.exists(path)
local disk, newPath = resolve(path)
return disk:directoryExists(newPath) or disk:fileExists(newPath)
end
function fs.isFile(path)
local disk, newPath = resolve(path)
return disk:fileExists(newPath)
end
function fs.isDir(path)
local disk, newPath = resolve(path)
return disk:directoryExists(newPath)
end
function fs.list(path)
local disk, newPath = resolve(path)
return disk:list(newPath)
end
function fs.makeDir(path)
local disk, newPath = resolve(path)
return disk:makeDirectory(newPath)
end
function fs.remove(path)
local disk, newPath = resolve(path)
return disk:remove(newPath)
end
function fs.readAllText(path)
local disk, newPath = resolve(path)
return disk:readAllText(newPath)
end
function fs.writeAllText(path, text)
local disk, newPath = resolve(path)
return disk:writeAllText(newPath, text)
end
function fs.appendAllText(path, text)
local disk, newPath = resolve(path)
return disk:appendAllText(newPath, text)
end
function fs.load(path)
return load(fs.readAllText(path), path)
end
function fs.mount(disk, mountPoint)
if not disks[disk] then return end
mounts[mountPoint] = disk
end
return fs

View File

@@ -0,0 +1,166 @@
local args = {...}
local apis = args[1]
local disks = args[2]
local arch = args[3]
local initPath = args[4]
local screen = args[5]
local computer = args[6]
local ifs = args[7]
local LOG_Text = ""
local kernel = {}
kernel.process = "Kernel"
kernel.user = "root"
kernel.group = "root"
kernel.groups = {0}
kernel.uid = 0
kernel.gid = 0
kernel.stage = "start"
kernel.key = {}
kernel.chache = {}
kernel.chache.preload = {}
local windowsExp = false
function kernel.log(msg, level)
LOG_Text = LOG_Text..tostring(computer.time()).." "..kernel.user.." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n"
if kernel.stage == "start" then
screen:print(tostring(computer.time()).." "..kernel.user.." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg)
end
end
function kernel.PANIC(msg)
kernel.log("PANIC: "..msg, "PANIC")
pcall(kernel["saveLog"])
screen:setTextColor(2)
screen:setBackgroundColor(0)
screen:clear()
screen:setCursorPos(1,1)
screen:print(LOG_Text)
screen:print("KERNEL PANIC!\n"..msg.."\nSystem halted.")
screen:print("Press any key to continue...")
while true do
local event={computer:getMachineEvent()}
if event[1]=="keyPressed" then
break
end
end
computer:reboot()
end
kernel.panic=kernel.PANIC
if windowsExp then
screen:setTextColor(1)
screen:setBackgroundColor(4)
screen:clear()
local w,h = screen:getSize()
screen:setCursorPos(3,5)
screen:print(":(")
screen:setCursorPos(3,7)
screen:print("Your PC ran into a problem and needs to restart. We're just collecting some error")
screen:setCursorPos(3,8)
screen:print("info, and then we'll restart for you.\n")
screen:setCursorPos(3,h-5)
screen:print("Stop code: average windows experience")
screen:setCursorPos(1,h)
screen:print("Press any key to continue... jk reboot it yourself lazy")
while true do end
end
kernel.log("Kernel loaded.", "INFO")
kernel.log("Mounting disks...", "INFO")
disks.refresh()
ifs.update(disks)
ifs.mount("$", "/")
function kernel.saveLog()
ifs.writeAllText("/var/log/syslog.log", LOG_Text)
end
local fstab=ifs.readAllText("/etc/fstab")
local split = function(str, delim, maxResultCountOrNil)
assert(#delim == 1, "only delim len 1 supported for now")
maxResultCountOrNil = (maxResultCountOrNil or 0)-1
local rv = {}
local buf = ""
for i = 1, #str do
local c = string.sub(str,i,i)
if #rv ~= maxResultCountOrNil and c == delim then
table.insert(rv, buf)
buf = ""
else
buf = buf..c
end
end
table.insert(rv, buf)
return rv
end
for i,v in ipairs(split(fstab,"\n")) do
if v:sub(1,1)=="U" then
local id=""
for i=3,#v do
if v:sub(i,i)==";" then
if i==3 then kernel.log("Invalid fstab line... Skipping.","WARN") goto endline end
id=v:sub(3,i-1)
end
end
local path=v:sub(#id+4)
kernel.log("Mounted "..id.." to "..path)
ifs.mount(id,path)
::endline::
end
end
ifs.remove("/tmp")
ifs.makeDir("/tmp")
local drivers={}
drivers.raw={}
drivers.processes={}
drivers.prior={}
drivers.type={}
for i=0, 99 do
drivers.prior[i]={}
end
function drivers.register(prior,object)
drivers.raw[#drivers.raw+1]=object
if object.main then drivers.processes[#drivers.processes+1]=object.main end
if drivers.prior[prior]==nil then drivers.prior[prior]={} end
drivers.prior[prior][#drivers.prior[prior]+1]=object
if drivers.type[object.type]==nil then drivers.type[object.type]={} end
drivers.type[object.type][#drivers.type[object.type]+1]=object
end
local modules={}
for i=0, 99 do
modules[i]={}
end
for i,v in ipairs(ifs.list("/lib/modules/Hyperion/")) do
local prior=tonumber(v:sub(1,2))
modules[prior][#modules[prior]+1]="/lib/modules/Hyperion/"..v
end
kernel.drivers=drivers
kernel.ifs=ifs
kernel.apis=apis
kernel.computer=computer
kernel.initPath=initPath
kernel.arch=arch
kernel.initdisks=initdisks
kernel.screen=screen
for _,p in ipairs(modules) do
for _,v in ipairs(p) do
local code=ifs.readAllText(v)
local func,err=load(code,"@"..v)
if not func then kernel.log("ModuLoadErr: "..tostring(err)) goto skip end
local status, err = xpcall(func,debug.traceback,kernel)
if not status then goto skip end
if not err then goto skip end
if not err.init then goto skip end
local ok, err = xpcall(status.main,debug.traceback)
if not ok then kernel.log("ModuInitErr: "..tostring(err)) end
::skip::
end
end
kernel.PANIC("Execution complete")

View File

@@ -0,0 +1,137 @@
function string.hasSuffix(str, suffix)
return string.sub(str, #suffix+1) == suffix
end
function string.hasPrefix(str, prefix)
return string.sub(str, 1, #prefix) == prefix
end
function string.getSuffix(str, prefix)
return string.sub(str, #prefix+1)
end
function string.getPrefix(str, suffix)
return string.sub(str, 1, #suffix)
end
function string.join(str, ...)
return table.concat(table.pack(str, ...))
end
function string.delim(str, ...)
return table.concat(table.pack(...), str)
end
function string.split(str, delim, maxResultCountOrNil)
assert(#delim == 1, "only delim len 1 supported for now")
maxResultCountOrNil = (maxResultCountOrNil or 0)-1
local rv = {}
local buf = ""
for i = 1, #str do
local c = string.sub(str,i,i)
if #rv ~= maxResultCountOrNil and c == delim then
table.insert(rv, buf)
buf = ""
else
buf = buf..c
end
end
table.insert(rv, buf)
return rv
end
function string.replace(str, search, replacement)
local rv = ""
local consumedLen = 1
local i = 1
while i<#str do
if string.sub(str, i, i+#search-1) == search then
rv = rv .. string.sub(str, consumedLen, i-1) .. replacement
i=i+#search
consumedLen = i
end
i=i+1
end
return rv .. string.sub(str, consumedLen)
end
function table.deepcopy(orig, copies)
copies = copies or {}
if type(orig) ~= 'table' then
return orig
elseif copies[orig] then
return copies[orig]
end
local copy = {}
copies[orig] = copy
for k, v in next, orig, nil do
local copied_key = table.deepcopy(k, copies)
local copied_val = table.deepcopy(v, copies)
copy[copied_key] = copied_val
end
return copy
end
function table.hasKey(tabl, query)
for i,v in pairs(tabl) do
if i==query then
return true
end
end
return false
end
function table.hasVal(tabl, query)
for i,v in pairs(tabl) do
if v==query then
return true
end
end
return false
end
local function serialize(table)
local output = "{"
for i,v in pairs(table) do
local coma=true
if type(i) == "string" then
output=output.."[\""..i.."\"]="
end
if type(v) == "table" then
if v == table then
output=string.sub(output,1,#output-(#i+1))
coma=false
else
output=output..serialize(v)
end
elseif type(v) == "string" then
output=output.."[=["..v.."]=]"
elseif type(v) == "number" then
output=output..tostring(v)
elseif type(v) == "boolean" then
if v == true then
output=output.."true"
else
output=output.."false"
end
elseif type(v) == "function" then
output=output.."function() end"
else
error("serialization of type \""..type(v).."\" is not supported")
end
if coma then
output=output..","
end
end
if #table>0 or string.sub(output,#output,#output) == "," then
output=string.sub(output,1,#output-1)
end
output=output.."}"
return output
end
table.serialize=serialize

View File

@@ -0,0 +1,217 @@
local args = {...}
local kernel = args[1]
local fs = {}
local disks = {}
local mounts = {["/"]="$"}
-- path normaliing
local function normalizePath(path)
if not path or path == "" then return "/" end
if path:sub(1,1) ~= "/" then path = "/" .. path end
local parts = {}
for part in path:gmatch("[^/]+") do
if part == ".." then
if #parts > 0 then table.remove(parts) end
elseif part ~= "." and part ~= "" then
parts[#parts+1] = part
end
end
return "/" .. table.concat(parts, "/")
end
-- disk resolution
local function resolvePath(path)
local abs = normalizePath(path)
local best = "/"
for mount, diskAddr in pairs(mounts) do
if abs:sub(1, #mount) == mount and #mount > #best then
best = mount
end
end
local newPath = abs:sub(#best + 1)
if newPath == "" then newPath = "/" end
return disks[mounts[best]], newPath
end
-- Symlinks
-- Format: #!@SYMLINK[target]
local SYMLINK_PREFIX = "#!@SYMLINK["
local SYMLINK_SUFFIX = "]"
local SYMLINK_MAX_DEPTH = 64
local function isSymlink(disk, path)
if not disk:fileExists(path) then return false end
local text = disk:readAllText(path)
if not text then return false end
return text:sub(1, #SYMLINK_PREFIX) == SYMLINK_PREFIX
end
local function readSymlink(disk, path)
local text = disk:readAllText(path)
if not text then return nil end
if text:sub(1, #SYMLINK_PREFIX) ~= SYMLINK_PREFIX then return nil end
local target = text:sub(#SYMLINK_PREFIX + 1)
if target:sub(-1) == SYMLINK_SUFFIX then
target = target:sub(1, -2)
end
return target
end
-- Path resolution
local function splitPath(p)
local parts = {}
for part in p:gmatch("[^/]+") do parts[#parts+1] = part end
return parts
end
local function resolveSymlink(path)
path = normalizePath(path)
local symlinkDepth = 0
local parts = splitPath(path)
local resolved = {}
local i = 1
while i <= #parts do
local comp = parts[i]
if comp == "." then
elseif comp == ".." then
if #resolved > 0 then table.remove(resolved) end
else
local currentPath = "/" .. table.concat(resolved, "/")
local disk, p = resolvePath(currentPath .. "/" .. comp)
if isSymlink(disk, p) then
symlinkDepth = symlinkDepth + 1
if symlinkDepth > SYMLINK_MAX_DEPTH then
error("Too many levels of symbolic links: " .. path)
end
local target = readSymlink(disk, p)
if not target then
resolved[#resolved+1] = comp
else
if target:sub(1,1) == "/" then
resolved = splitPath(normalizePath(target))
else
local base = resolved
resolved = {}
for _, seg in ipairs(base) do resolved[#resolved+1]=seg end
for seg in target:gmatch("[^/]+") do
if seg == ".." then
if #resolved>0 then table.remove(resolved) end
elseif seg ~= "." then
resolved[#resolved+1]=seg
end
end
end
local remaining = {}
for j=i+1,#parts do remaining[#remaining+1]=parts[j] end
parts = remaining
i = 0
end
else
resolved[#resolved+1]=comp
end
end
i=i+1
end
local finalPath="/"..table.concat(resolved,"/")
local disk,diskPath=resolvePath(finalPath)
return disk,diskPath
end
-- PUBLIC API: MOUNTING
function fs.mount(disk, mountPoint)
if kernel.uid ~= 0 then error("Permission Denied") end
mounts[mountPoint]=disk
end
function fs.unmount(mountPoint)
if kernel.uid ~= 0 then error("Permission Denied") end
mounts[mountPoint]=nil
end
function fs.virtDisk(diskObj)
if kernel.uid ~= 0 then error("Permission Denied") end
if disks[diskObj.address] then error("Disk exists") end
disks[diskObj.address]=diskObj
end
function fs.eject(addr)
if kernel.uid ~= 0 then error("Permission Denied") end
disks[addr]=nil
end
-- SYMLINK API
function fs.symlink(target, linkPath)
local disk, p = resolvePath(linkPath)
return disk:writeAllText(p, SYMLINK_PREFIX..target..SYMLINK_SUFFIX)
end
function fs.isLink(path)
local disk, p = resolvePath(path)
return isSymlink(disk,p)
end
function fs.readLink(path)
local disk, p = resolvePath(path)
return readSymlink(disk,p)
end
-- FILE OPERATIONS
function fs.exists(path)
local disk, p = resolveSymlink(path)
return disk:fileExists(p) or disk:directoryExists(p)
end
function fs.isFile(path)
local disk, p = resolveSymlink(path)
if isSymlink(disk, p) then return false end
return disk:fileExists(p)
end
function fs.isDir(path)
local disk, p = resolveSymlink(path)
if isSymlink(disk, p) then return false end
return disk:directoryExists(p)
end
function fs.list(path)
local disk, p = resolveSymlink(path)
return disk:list(p)
end
function fs.makeDir(path)
local disk, p = resolveSymlink(path)
return disk:makeDirectory(p)
end
function fs.remove(path)
local disk, p = resolvePath(path)
if isSymlink(disk, p) then
return disk:remove(p)
end
local d2, p2 = resolveSymlink(path)
return d2:remove(p2)
end
function fs.readAllText(path)
local disk, p = resolveSymlink(path)
return disk:readAllText(p)
end
function fs.writeAllText(path, text)
local disk, p = resolveSymlink(path)
return disk:writeAllText(p, text)
end
function fs.appendAllText(path, text)
local disk, p = resolveSymlink(path)
return disk:appendAllText(p, text)
end
function fs.getSize(path)
local disk, p = resolveSymlink(path)
return disk:getSize(p)
end
-- INIT
for _,v in kernel.initdisks.list() do fs.virtDisk(v) end
kernel.fs=fs
kernel.chache.preload.fs=fs
kernel.chache.preload.filesystem=fs

View File

@@ -0,0 +1,22 @@
local args={...}
local kernel=args[1]
local ifs=kernel.ifs
local initdisks=kernel.initdisks
local fstab=ifs.readAllText("/etc/fstab")
kernel.fs.update(initdisks)
for i,v in ipairs(string.split(fstab,"\n")) do
if v:sub(1,1)=="U" then
local id=""
for i=3,#v do
if v:sub(i,i)==";" then
if i==3 then kernel.log("Invalid fstab line... Skipping.","WARN") goto endline end
id=v:sub(3,i-1)
end
end
local path=v:sub(#id+4)
kernel.log("Mounted "..id.." to "..path)
kernel.fs.mount(id,path)
::endline::
end
end

View File

@@ -0,0 +1,182 @@
local args={...}
local kernel=args[1]
function kernel.fs.mkvirtfs(address, readOnly, label, autoRegister)
if kernel.uid ~= 0 then error("Permission Denied") end
local disk = {}
disk.address = address
local isRO = readOnly or false
local backend = {
["/"] = { __dir = true }
}
-----------------------------------------------------------------
-- Helpers
-----------------------------------------------------------------
local function norm(path)
if not path or path == "" then return "/" end
if path:sub(1,1) ~= "/" then path = "/" .. path end
path = path:gsub("/+", "/")
return path
end
local function split(path)
local parts = {}
for p in path:gmatch("[^/]+") do table.insert(parts, p) end
return parts
end
local function getNode(path)
path = norm(path)
if path == "/" then return backend["/"] end
local node = backend["/"]
for _, part in ipairs(split(path)) do
node = node[part]
if not node then return nil end
end
return node
end
local function getParent(path)
local parts = split(norm(path))
local name = table.remove(parts)
local parentPath = "/" .. table.concat(parts, "/")
return getNode(parentPath), name
end
local function ensureWrite()
if isRO then return false, "read-only" end
return true
end
-----------------------------------------------------------------
-- File IO (supports string or file-object with read/write funcs)
-----------------------------------------------------------------
local function fileRead(node)
if type(node) == "string" then
return node
elseif type(node) == "table" and node.__file and node.read then
return node.read()
end
return nil
end
local function fileWrite(parent, name, text)
local node = parent[name]
if type(node) == "string" then
parent[name] = text
return true
elseif type(node) == "table" and node.__file and node.write then
return node.write(text)
end
parent[name] = text
return true
end
local function fileAppend(parent, name, text)
local node = parent[name]
if type(node) == "string" then
parent[name] = node .. text
return true
elseif type(node) == "table" and node.__file and node.write then
return node.write((node.read() or "") .. text)
end
parent[name] = text
return true
end
-----------------------------------------------------------------
-- API
-----------------------------------------------------------------
function disk:isReadOnly() return isRO end
function disk:spaceUsed() return 0 end
function disk:spaceTotal() return 0 end
function disk:readAllText(path)
local node = getNode(path)
if not node then return nil, "file not found" end
return fileRead(node)
end
function disk:writeAllText(path, text)
local ok, err = ensureWrite(); if not ok then return false, err end
local parent, name = getParent(path)
if not parent or not parent.__dir then return false, "invalid directory" end
return fileWrite(parent, name, tostring(text))
end
function disk:appendAllText(path, text)
local ok, err = ensureWrite(); if not ok then return false, err end
local parent, name = getParent(path)
if not parent or not parent.__dir then return false, "invalid directory" end
return fileAppend(parent, name, tostring(text))
end
function disk:list(path)
local node = getNode(path)
if not node or not node.__dir then return nil, "not a directory" end
local out = {}
for k, _ in pairs(node) do if k ~= "__dir" then table.insert(out, k) end end
return out
end
function disk:fileExists(path)
local node = getNode(path)
return type(node) == "string" or (type(node) == "table" and node.__file)
end
function disk:directoryExists(path)
local node = getNode(path)
return node and type(node) == "table" and node.__dir or false
end
function disk:makeDirectory(path)
local ok, err = ensureWrite(); if not ok then return false, err end
local parent, name = getParent(path)
if not parent or not parent.__dir then return false, "invalid parent directory" end
parent[name] = { __dir = true }
return true
end
function disk:remove(path)
local ok, err = ensureWrite(); if not ok then return false, err end
local parent, name = getParent(path)
if not parent or not parent[name] then return false, "not found" end
parent[name] = nil
return true
end
function disk:setLabel(new)
local ok, err = ensureWrite(); if not ok then return false, err end
label = tostring(new)
end
function disk:getLabel()
return label
end
function disk:size(path)
local node = getNode(path)
if type(node) == "string" then return #node end
if type(node) == "table" and node.__file and node.read then
local v = node.read()
return v and #v or 0
end
return 0
end
-----------------------------------------------------------------
-- Auto-register with kernel and return backend
-----------------------------------------------------------------
if kernel and kernel.fs and kernel.fs.virtDisk and autoRegister then
kernel.fs.virtDisk(disk)
end
return backend, disk
end

View File

@@ -0,0 +1,56 @@
local args = {...}
local kernel = args[1]
-- List of search paths
local paths = {
"/lib/?",
"/usr/lib/?",
"/usr/local/lib/?"
}
-- Custom require implementation
function require(module)
-- Return cached module if it already exists
if kernel.cache.preload[module] then
return kernel.cache.preload[module]
end
local err = {}
for _, path in ipairs(paths) do
-- Replace "?" with module name
local filePath = string.replace(path, "?", module)
-- Try to open file
local file = kernel.fs.isFile(filePath)
if file then
local content = kernel.fs.readAllText(filePath)
-- Load the module as Lua code
local chunk, loadErr = load(content, filePath)
if not chunk then
table.insert(err, "Error loading: " .. filePath .. ": " .. loadErr)
else
-- Execute the module. If the module returns a value, cache it.
local ok, result = xpcall(chunk, debug.traceback)
if not ok then
table.insert(err, "Error executing: "..filePath..": "..tostring(result))
else
if result ~= nil then
kernel.cache.preload[module] = result
return result
end
-- If module doesn't return anything, cache `true` like Lua does
kernel.cache.preload[module] = true
return true
end
end
else
table.insert(err, "Module not found: " .. filePath)
end
end
-- If nothing worked, raise an error with all reasons
error("Unable to require module '" .. module .. "':\n" .. table.concat(err, "\n"))
end

View File

@@ -0,0 +1,17 @@
local args={...}
local kernel = args[1]
local data = kernel.fs.mkvirtfs("devfs0000", true, "devfs", false)
data["/"]["OSVERSION"]="Hyperion 1.0.0"
data["/"]["eeprom"]={
__file=true,
read=function()
return kernel.computer:getEEPROM()
end,
write=function(text)
if kernel.uid ~= 0 then error("Permission Denied") end
kernel.computer:setEEPROM(text)
end
}

View File

@@ -0,0 +1,12 @@
local args={...}
local kernel=args[1]
kernel.drivers.processes[#kernel.drivers.processes+1]=function()
while true do
local event={kernel.computer:getMachineEvent()}
while event[1]~=nil do
kernel.hpv.sendSig("all", table.unpack(event))
event={kernel.computer:getMachineEvent()}
end
coroutine.yield()
end
end

View File

@@ -0,0 +1,19 @@
local args={...}
local kernel=args[2]
for _,subf in ipairs(kernel.fs.list("/lib/modules/")) do
if subf~="Hyperion" then
for _,driver in ipairs(kernel.fs.list("/lib/modules/"..subf)) do
local code = kernel.fs.readAllText("/lib/modules/"..subf.."/"..driver)
local func, err = load(code, "@"..driver)
if not func then
kernel.log("DriverLoadErr: "..tostring(err), "ERROR")
else
local ok, err = xpcall(func, debug.traceback)
if not ok then
kernel.log("DriverLoadErr: "..tostring(err), "ERROR")
end
end
end
end
end

View File

@@ -0,0 +1,11 @@
local args={...}
local kernel=args[1]
for _,l in ipairs(kernel.drivers.prior) do
for _,d in ipairs(l) do
if d.init then
local ok,err = xpcall(d.init, debug.traceback)
if not ok then kernel.log("DriverInitErr: "..tostring(err)) end
end
end
end

View File

@@ -0,0 +1,167 @@
local args={...}
local kernel = args[1]
local tasks={}
local currentTask={}
local signals={}
local tid=1
local gid=1
local sys={}
function sys.hookSig(sig, func)
if not signals[tostring(currentTask.pid)][sig] then
signals[tostring(currentTask.pid)][sig]={}
end
signals[tostring(currentTask.pid)][sig][#signals[tostring(currentTask.pid)][sig]+1]=func
end
function sys.clearSigHooks(typ)
if not typ or typ == "all" then
signals[tostring(currentTask.pid)]={}
currentTask.signal=signals[tostring(currentTask.pid)]
else
if currentTask.signal[typ] then
currentTask.signal[typ]={}
end
end
end
function sys.sendSig(pid, signal, ...)
if pid=="all" then
for i,v in pairs(tasks) do
v.sigQ[#v.sigQ+1]={signal, ...}
end
return
end
if not tasks[tostring(pid)] then return false end
tasks[tostring(pid)].sigQ[#tasks[tostring(pid)].sigQ+1]={signal, ...}
return true
end
function sys.flushSigs()
local ret = {}
for i=1, #currentTask.sigQ do
if currentTask.signal[currentTask.sigQ[i][1]] then
for _,v in ipairs(currentTask.signal[currentTask.sigQ[1][1]]) do
coroutine.resumeWithTimeout(coroutine.create(function()
local ok, err = xpcall(v, debug.traceback, table.unpack(table.remove(currentTask.sigQ, 1)))
if not ok then
table.insert(ret, err)
end
end), 10)
end
else
for _,v in ipairs(currentTask.signal["unhandledEvent"]) do
coroutine.resumeWithTimeout(coroutine.create(function()
local ok, err = xpcall(v, debug.traceback, table.unpack(table.remove(currentTask.sigQ, 1)))
if not ok then
table.insert(ret, err)
end
end), 10)
end
end
end
end
function sys.spawn(func, name, evars, args)
local id=tid
tid=tid+1
name=name or tostring(id)
signals[tostring(id)]={}
tasks[tostring(id)]={
coro=coroutine.create(function()
local ret = {xpcall(func, debug.traceback, table.unpack(args))}
if not ret[1] then
sys.sendSig(currentTask.ppid, "ChildTaskError", id, err)
else
sys.sendSig(currentTask.ppid, "ChildTaskExit", id, table.unpack(ret, 2))
end
currentTask.status="Z"
end),
name=name,
pid=id,
ppid=currentTask.pid,
tgid=currentTask.tgid,
user=kernel.user,
uid=kernel.uid,
evars=evars,
args=args,
vy=0,
ivy=0,
status="R",
signal=signals[tostring(id)],
parent=currentTask,
children={},
sibling=currentTask.children,
sigQ={}
}
end
local function collectZombieProc()
local ret = {}
for _,v in pairs(tasks) do
if v.status=="Z" then
local pid = v.pid
for _,c in ipairs(v.children) do
c.parent=tasks["1"]
c.sibling=tasks["1"].children
c.ppid=1
c.tgid=1
end
signals[tostring(pid)]=nil
tasks[tostring(pid)]=nil
table.insert(ret, pid)
end
end
return ret
end
signals["1"]={}
tasks["1"]={
coro=coroutine.create(function()
local ret = {xpcall(kernel.fs.load(kernel.initPath), debug.traceback)}
if not ok then
kernel.panic(err)
else
kernel.panic("Attempted to kill init!")
end
end),
name="SysInit",
pid=1,
ppid=0,
tgid=1,
user=kernel.user,
uid=kernel.uid,
evars={},
args={kernel},
vy=0,
ivy=0,
status="R",
signal=signals["1"],
parent={name="Hyprkrnl",pid=0},
children={},
sibling={},
sigQ={}
}
kernel.chache.preload.sys=sys
kernel.chache.preload.system=sys
kernel.hpv=sys
kernel.saveLog()
while true do
for _,v in pairs(tasks) do
currentTask=v
kernel.user=currentTask.user
kernel.uid=currentTask.uid
sys.flushSigs()
local status = coroutine.resumeWithTimeout(currentTask.coro, 50)
if status then
currentTask.vy=currentTask.vy+1
else
currentTask.ivy=currentTask.ivy+1
end
end
collectZombieProc()
end
kernel.panic("Exited pid 0")

14
Test/HyperionOS.pkg Executable file
View File

@@ -0,0 +1,14 @@
{
"Name": "HyperionOS",
"Version": "0.1.0",
"Publishers": ["HyperionOS Dev Team"],
"Description": "A sample package for HyperionOS project.",
"Dependencies": [
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-core-v0.1.0.pkg",
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-kernel-v0.1.0.pkg",
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-ui-v0.1.0.pkg",
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-network-v0.1.0.pkg",
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-security-v0.1.0.pkg",
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-filesystem-v0.1.0.pkg"
]
}

16
Test/palette_16.txt Executable file
View File

@@ -0,0 +1,16 @@
0: #000000
1: #FFFFFF
2: #FF0000
3: #00FF00
4: #0000FF
5: #00FFFF
6: #FF00FF
7: #FFFF00
8: #FF6D00
9: #6DFF55
10: #24FFFF
11: #924900
12: #6D6D55
13: #DBDBAA
14: #6D00FF
15: #B6FF00

View File

@@ -1,34 +0,0 @@
readAllText(dir) Returns string:contents
reads all text from directory on disk
writeAllText(dir, content) Returns nil
writes all content to file
appendAllText(dir, content) Returns nil
appends all content to file
getAtributes(dir) Returns table:atributes
returns atributes of a file or directory
list(dir) Returns table:files
returns contents of directory
mkdir(dir) Returns nil
makes specified directory
mkfile(dir) Returns nil
makes specified file
-------------------------------------------------------
Atributes table
number:size
size of file in bytes
number:owner
owner UUID
number:group
group UUID
number:perms
file perms 0-21

View File

@@ -1,16 +0,0 @@
# drivers
---
## driver types
---
```
http - internet
lan - local networks
fs - filesystems
disk - normal disks
udisk - byte array disks
terminal - screens that only support text
```
### Driver APIS

View File

@@ -1,8 +0,0 @@
print(text) Returns nil
Prints text to the terminal with a trailing \n
printInline(text) Returns nil
Same as print without the trailing \n
clear() Returns nil
Clears screen

View File

@@ -0,0 +1,26 @@
# Drivers
---
Hyperion OS supports many driver types to allow it to run on any hardware
```
Driver types
tto - Supports basic text terminal output
gpio - Supports things like redstone
runner - Kernel level programs (no api)
timer - Timers and time related
periph - Basic peripheral info
gfx - PixelScreens
```
Hyperion also has a base driver api
```
Driver API
name - Name of driver+
type - Type of driver
init - Ran before init
main - Ran as a process and has normal behavior (used for checking network like things)
api - api difined by type
arch - architecture difined in bootloader (EX: cct, oc, ac, cc, ccpc)
description - discription
author - author of driver
prior - priority (low first)
```

35
docs/kernel/drivers/api/tto.md Executable file
View File

@@ -0,0 +1,35 @@
# tty driver
---
tty (Teletypewriter) is a driver class made for basic text output (ASCII only) used for primitive ui.
```
API Signature
print(String: text):Nil
Prints text to the screen with a following \n
printInline(String: text):Nil
Prints text to the screen without following \n
clear():Nil
Clears screen sets x,y of cursor to 0,0
setBackgroundColor(Number: index):Nil
Sets background color to index of pallete
getBackgroundColor():Number
Returns current background index of screen
setForegroundColor(Number: index):Nil
Sets foreground color to index of pallete
getForegroundColor():Number
Returns current foreground index of screen
setCursorPos(Number: x, Number: y):Nil
Sets x,y position of cursor
getCursorPos():Number, Number
Gets x,y position of cursor
```

1
tools/boot.lua Executable file
View File

@@ -0,0 +1 @@
dofile("/$/boot/cct/eeprom")

47
tools/listtar.lua Executable file
View File

@@ -0,0 +1,47 @@
-- tar_list.lua
-- List all paths stored in a TAR file (no extraction)
local function octal_to_number(str)
str = str:gsub("%z", ""):match("^%s*(.-)%s*$")
return tonumber(str, 8) or 0
end
local file = ({...})[1]
if not file then
print("Usage: tar_list <tarfile>")
return
end
local f = fs.open(file, "r")
local tar = f.readAll()
f.close()
local i = 1
local len = #tar
while i + 512 <= len do
local header = tar:sub(i, i + 511)
if header:match("^\0+$") then break end
-- Extract raw name & prefix
local name = header:sub(1, 100):gsub("%z.*","")
local prefix = header:sub(346, 500):gsub("%z.*","")
local size = octal_to_number(header:sub(125, 136))
i = i + 512
-- Skip file contents
local pad = (512 - (size % 512)) % 512
i = i + size + pad
local full
if prefix ~= "" then
full = prefix .. "/" .. name
else
full = name
end
-- Print exactly what Windows Explorer stored
print(full)
end

186
tools/unpack.lua Executable file
View File

@@ -0,0 +1,186 @@
-- unpack_tar.lua
-- Fully working TAR unpacker for ComputerCraft
-- Handles Windows Explorer TAR bug (duplicated path segments)
local function octal_to_number(str)
str = str:gsub("%z", ""):match("^%s*(.-)%s*$")
return tonumber(str, 8) or 0
end
---------------------------------------------------------------------
-- Fix Windows Explorer TAR bug: repeated prefix segments in name
---------------------------------------------------------------------
local function dedupe_path(path)
-- Example:
-- Build/boot/Build/boot/cct/kernel.lua
-- -> Build/boot/cct/kernel.lua
local parts = {}
for p in path:gmatch("[^/]+") do table.insert(parts, p) end
-- Try all possible prefix lengths
for prefix_len = 1, math.floor(#parts / 2) do
local ok = true
for i = 1, prefix_len do
if parts[i] ~= parts[i + prefix_len] then
ok = false
break
end
end
if ok then
-- Remove one repeated prefix
local cleaned = {}
for i = 1, #parts - prefix_len do
cleaned[#cleaned + 1] = parts[i + prefix_len]
end
return table.concat(cleaned, "/")
end
end
return path
end
---------------------------------------------------------------------
-- Directory tree helpers
---------------------------------------------------------------------
local function make_dirs(root, path)
local cur = root
for part in path:gmatch("([^/]+)/") do
if not cur[part] then
cur[part] = { __type = "dir", __entries = {} }
end
cur = cur[part].__entries
end
return cur
end
local function flatten(node, prefix)
local out = {}
prefix = prefix or ""
for name, obj in pairs(node) do
local full = prefix .. name
if obj.__type == "file" then
out[#out+1] = {
name = full,
type = "file",
contents = obj.__contents
}
elseif obj.__type == "dir" then
out[#out+1] = {
name = full .. "/",
type = "dir",
contents = flatten(obj.__entries, full .. "/")
}
end
end
return out
end
---------------------------------------------------------------------
-- TAR Unpacker
---------------------------------------------------------------------
local function unpack_tar(tarstr)
local i = 1
local len = #tarstr
local root = {}
while i + 512 <= len do
local header = tarstr:sub(i, i + 511)
-- End of archive (null block)
if header:match("^\0+$") then break end
-- Read name + ustar prefix
local name_raw = header:sub(1, 100):gsub("%z.*", "")
local prefix_raw = header:sub(346, 500):gsub("%z.*", "")
local name
if prefix_raw ~= "" then
name = prefix_raw .. "/" .. name_raw
else
name = name_raw
end
-- Normalize path
name = name:gsub("^%./", ""):gsub("/+", "/")
-- Fix Windows Explorer TAR bug
name = dedupe_path(name)
local size = octal_to_number(header:sub(125,136))
local typeflag = header:sub(157,157)
i = i + 512
local contents = tarstr:sub(i, i + size - 1)
local pad = (512 - (size % 512)) % 512
i = i + size + pad
if name == "" then goto continue end
-- Determine if its a directory
local is_dir = typeflag == "5" or name:sub(-1) == "/"
-- Remove trailing slash for hierarchical processing
local clean_name = name:gsub("/$", "")
if clean_name == "" then goto continue end
local parent_path = clean_name:match("(.+)/")
local fname = clean_name:match("([^/]+)$")
if not fname then goto continue end
local parent = root
if parent_path then
parent = make_dirs(root, parent_path .. "/")
end
if is_dir then
parent[fname] = parent[fname] or { __type = "dir", __entries = {} }
else
parent[fname] = { __type = "file", __contents = contents }
end
::continue::
end
return flatten(root)
end
---------------------------------------------------------------------
-- FS output (ComputerCraft)
---------------------------------------------------------------------
local function write_directory(prefix, items)
for _, v in ipairs(items) do
if v.type == "dir" then
fs.makeDir(prefix..v.name)
write_directory(prefix, v.contents)
elseif v.type == "file" then
local file = fs.open(prefix..v.name, "w")
file.write(v.contents)
file.close()
end
end
end
---------------------------------------------------------------------
-- Entry point
---------------------------------------------------------------------
local in_tar = ({...})[1]
local out_dir = ({...})[2]
if not in_tar or not out_dir then
print("Usage: unpack_tar <tarfile> <output_dir>")
return
end
local f = fs.open(in_tar, "r")
local tarstr = f.readAll()
f.close()
local list = unpack_tar(tarstr)
write_directory(out_dir, list)
print("TAR extracted into: " .. out_dir)

5
tools/update.lua Executable file
View File

@@ -0,0 +1,5 @@
shell.run("unpack Build.tar /")
shell.run("rm $")
shell.run("cp Build $")
shell.run("rm Build")
shell.run("rm Build.tar")