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 function sleep(time) local stoptime = apis.os.clock() + (time*1000) while stoptime > apis.os.clock() do 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 true, "Coroutine timed out" elseif ret[1]==false then return false, ret[2] else debug.sethook(co) return computer.time()-startTime, 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