UnBIOS: Load custom BIOSes in ComputerCraft without modifying ROM

July 8, 2025 ยท View on GitHub

-- 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 if _HOST:find("UnBIOS") then return end local keptAPIs = {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("/bios.lua", "r") if file == nil then term.setCursorBlink(false) term.setTextColor(16384) term.write("Could not find /bios.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(), "@bios.lua") file.close() if fn == nil then term.setCursorBlink(false) term.setTextColor(16384) term.write("Could not load /bios.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() 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 coroutine.yield()