diff --git a/rewrite/luaarch.c b/rewrite/luaarch.c new file mode 100644 index 0000000..d1a542a --- /dev/null +++ b/rewrite/luaarch.c @@ -0,0 +1,465 @@ +#include "neonucleus.h" +#include +#include +#include +#include +#include +#include + +// This architecture is written horrendously. +// This code is garbage, and is entirely just for testing. +// This architecture has effectively 0 sandboxing. +// The error handling in this architecture is effectively nothing. +// Also if the total memory available changes during execution, this Lua arch will NOT CARE. + +const char luaArch_machineLua[] = { +#embed "machine.lua" +,'\0' +}; + +typedef struct luaArch { + nn_Computer *computer; + lua_State *L; + size_t freeMem; +} luaArch; + +void *luaArch_alloc(void *ud, void *ptr, size_t osize, size_t nsize) { + luaArch *arch = ud; + if(nsize == 0) { + free(ptr); + arch->freeMem += osize; + return NULL; + } + if(ptr == NULL) { + if(arch->freeMem < nsize) return NULL; + void *mem = malloc(nsize); + if(mem == NULL) return NULL; + arch->freeMem -= nsize; + return mem; + } + if(arch->freeMem + osize < nsize) return NULL; + void *mem = realloc(ptr, nsize); + if(mem == NULL) return NULL; + arch->freeMem += osize; + arch->freeMem -= nsize; + return mem; +} + +static luaArch *luaArch_from(lua_State *L) { + lua_getfield(L, LUA_REGISTRYINDEX, "archPtr"); + luaArch *arch = lua_touserdata(L, -1); + lua_pop(L, 1); + return arch; +} + +// pushes an NN value from a Lua stack index +static void luaArch_luaToNN(luaArch *arch, int luaIdx) { + lua_State *L = arch->L; + nn_Computer *C = arch->computer; + + if(lua_isnoneornil(L, luaIdx)) { + nn_pushnull(C); + return; + } + if(lua_isnumber(L, luaIdx)) { + nn_pushnumber(C, lua_tonumber(L, luaIdx)); + return; + } + if(lua_isstring(L, luaIdx)) { + size_t len; + const char *s = lua_tolstring(L, luaIdx, &len); + nn_pushlstring(C, s, len); + return; + } + if(lua_isboolean(L, luaIdx)) { + nn_pushbool(C, lua_toboolean(L, luaIdx)); + return; + } + luaL_error(L, "bad Lua value: %s", luaL_typename(L, luaIdx)); +} + +// pushes a Lua value from an NN stack index +static void luaArch_nnToLua(luaArch *arch, size_t nnIdx) { + lua_State *L = arch->L; + nn_Computer *C = arch->computer; + + if(nn_isnull(C, nnIdx)) { + lua_pushnil(L); + return; + } + if(nn_isnumber(C, nnIdx)) { + lua_pushnumber(L, nn_tonumber(C, nnIdx)); + return; + } + if(nn_isstring(C, nnIdx)) { + size_t len; + const char *s = nn_tolstring(C, nnIdx, &len); + lua_pushlstring(L, s, len); + return; + } + if(nn_isboolean(C, nnIdx)) { + lua_pushboolean(L, nn_toboolean(C, nnIdx)); + return; + } + + luaL_error(L, "bad NN value: %s", nn_typenameof(C, nnIdx)); +} + +static int luaArch_computer_freeMemory(lua_State *L) { + lua_pushinteger(L, luaArch_from(L)->freeMem); + return 1; +} + +static int luaArch_computer_totalMemory(lua_State *L) { + lua_pushinteger(L, nn_getTotalMemory(luaArch_from(L)->computer)); + return 1; +} + +static int luaArch_computer_uptime(lua_State *L) { + lua_pushnumber(L, nn_getUptime(luaArch_from(L)->computer)); + return 1; +} + +static int luaArch_computer_address(lua_State *L) { + const char *addr = nn_getComputerAddress(luaArch_from(L)->computer); + lua_pushstring(L, addr); + return 1; +} + +static int luaArch_computer_tmpAddress(lua_State *L) { + const char *addr = nn_getTmpAddress(luaArch_from(L)->computer); + if(addr == NULL) lua_pushnil(L); + else lua_pushstring(L, addr); + return 1; +} + +static int luaArch_computer_users(lua_State *L) { + nn_Computer *c = luaArch_from(L)->computer; + size_t userc = 0; + while(1) { + const char *user = nn_getUser(c, userc); + if(user == NULL) break; + lua_pushstring(L, user); + userc++; + } + return userc; +} + +static int luaArch_computer_addUser(lua_State *L) { + nn_Computer *c = luaArch_from(L)->computer; + const char *user = luaL_checkstring(L, 1); + nn_Exit err = nn_addUser(c, user); + if(err) { + nn_setErrorFromExit(c, err); + lua_pushnil(L); + lua_pushstring(L, nn_getError(c)); + return 2; + } + lua_pushboolean(L, true); + return 1; +} + +static int luaArch_computer_removeUser(lua_State *L) { + nn_Computer *c = luaArch_from(L)->computer; + const char *user = luaL_checkstring(L, 1); + lua_pushboolean(L, nn_removeUser(c, user)); + return 1; +} + +static int luaArch_computer_energy(lua_State *L) { + lua_pushnumber(L, nn_getEnergy(luaArch_from(L)->computer)); + return 1; +} + +static int luaArch_computer_maxEnergy(lua_State *L) { + lua_pushnumber(L, nn_getTotalEnergy(luaArch_from(L)->computer)); + return 1; +} + +static int luaArch_computer_getArchitecture(lua_State *L) { + lua_pushstring(L, nn_getArchitecture(luaArch_from(L)->computer).name); + return 1; +} + +static int luaArch_computer_setArchitecture(lua_State *L) { + nn_Computer *c = luaArch_from(L)->computer; + const char *archname = luaL_checkstring(L, 1); + nn_Architecture arch = nn_findSupportedArchitecture(c, archname); + if(arch.name == NULL) { + lua_pushnil(L); + lua_pushstring(L, "unknown architecture"); + return 2; + } + nn_setComputerState(c, NN_CHARCH); + nn_setDesiredArchitecture(c, &arch); + return 0; +} + +static int luaArch_computer_getArchitectures(lua_State *L) { + nn_Computer *c = luaArch_from(L)->computer; + size_t len; + const nn_Architecture *arch = nn_getSupportedArchitectures(c, &len); + lua_createtable(L, len, 0); + for(size_t i = 0; i < len; i++) { + lua_pushstring(L, arch[i].name); + lua_seti(L, -2, i+1); + } + return 1; +} + +static int luaArch_computer_shutdown(lua_State *L) { + nn_Computer *c = luaArch_from(L)->computer; + bool restart = lua_toboolean(L, 1); + nn_setComputerState(c, restart ? NN_RESTART : NN_POWEROFF); + return 0; +} + +static int luaArch_component_list(lua_State *L) { + luaArch *arch = luaArch_from(L); + lua_createtable(L, 64, 0); + for(size_t i = 0; true; i++) { + const char *addr = nn_getComponentAddress(arch->computer, i); + if(addr == NULL) break; + lua_pushstring(L, nn_getComponentType(arch->computer, addr)); + lua_setfield(L, -2, addr); + } + return 1; +} + +static int luaArch_component_invoke(lua_State *L) { + luaArch *arch = luaArch_from(L); + const char *address = luaL_checkstring(L, 1); + const char *method = luaL_checkstring(L, 2); + size_t argc = lua_gettop(L); + + if(!nn_hasComponent(arch->computer, address)) { + lua_pushnil(L); + lua_pushstring(L, "no such component"); + return 2; + } + if(!nn_hasMethod(arch->computer, address, method)) { + lua_pushnil(L); + lua_pushstring(L, "no such method"); + return 2; + } + + nn_clearstack(arch->computer); + for(size_t i = 3; i <= argc; i++) { + luaArch_luaToNN(arch, i); + } + nn_Exit err = nn_call(arch->computer, address, method); + if(err != NN_OK) { + lua_pushnil(L); + lua_pushstring(L, nn_getError(arch->computer)); + return 2; + } + size_t retc = nn_getstacksize(arch->computer); + for(size_t i = 0; i < retc; i++) { + luaArch_nnToLua(arch, i); + } + nn_clearstack(arch->computer); + return retc; +} + +static int luaArch_component_type(lua_State *L) { + luaArch *arch = luaArch_from(L); + const char *address = luaL_checkstring(L, 1); + + if(!nn_hasComponent(arch->computer, address)) { + lua_pushnil(L); + lua_pushstring(L, "no such component"); + return 2; + } + lua_pushstring(L, nn_getComponentType(arch->computer, address)); + return 1; +} + +static int luaArch_component_doc(lua_State *L) { + luaArch *arch = luaArch_from(L); + const char *address = luaL_checkstring(L, 1); + const char *method = luaL_checkstring(L, 2); + + if(!nn_hasComponent(arch->computer, address)) { + lua_pushnil(L); + lua_pushstring(L, "no such component"); + return 2; + } + if(!nn_hasMethod(arch->computer, address, method)) { + lua_pushnil(L); + lua_pushstring(L, "no such method"); + return 2; + } + lua_pushstring(L, nn_getComponentDoc(arch->computer, address, method)); + return 1; +} + +static int luaArch_component_slot(lua_State *L) { + luaArch *arch = luaArch_from(L); + const char *address = luaL_checkstring(L, 1); + + if(!nn_hasComponent(arch->computer, address)) { + lua_pushnil(L); + lua_pushstring(L, "no such component"); + return 2; + } + lua_pushinteger(L, nn_getComponentSlot(arch->computer, address)); + return 1; +} + +static int luaArch_component_methods(lua_State *L) { + luaArch *arch = luaArch_from(L); + const char *address = luaL_checkstring(L, 1); + + if(!nn_hasComponent(arch->computer, address)) { + lua_pushnil(L); + lua_pushstring(L, "no such component"); + return 2; + } + size_t len; + const nn_Method *methods = nn_getComponentMethods(arch->computer, address, &len); + lua_createtable(L, 0, len); + for(size_t i = 0; i < len; i++) { + if(methods[i].flags & NN_FIELD_MASK) continue; // skip + + lua_pushboolean(L, (methods[i].flags & NN_DIRECT) != 0); + lua_setfield(L, -2, methods[i].name); + } + return 1; +} + +static int luaArch_component_fields(lua_State *L) { + luaArch *arch = luaArch_from(L); + const char *address = luaL_checkstring(L, 1); + + if(!nn_hasComponent(arch->computer, address)) { + lua_pushnil(L); + lua_pushstring(L, "no such component"); + return 2; + } + size_t len; + const nn_Method *methods = nn_getComponentMethods(arch->computer, address, &len); + lua_createtable(L, 0, len); + for(size_t i = 0; i < len; i++) { + if((methods[i].flags & NN_FIELD_MASK) == 0) continue; // skip + + lua_createtable(L, 0, 3); + lua_pushboolean(L, (methods[i].flags & NN_DIRECT) != 0); + lua_setfield(L, -2, "direct"); + lua_pushboolean(L, (methods[i].flags & NN_GETTER) != 0); + lua_setfield(L, -2, "getter"); + lua_pushboolean(L, (methods[i].flags & NN_SETTER) != 0); + lua_setfield(L, -2, "setter"); + lua_setfield(L, -2, methods[i].name); + } + return 1; +} + +static void luaArch_loadEnv(lua_State *L) { + lua_createtable(L, 0, 10); + int computer = lua_gettop(L); + lua_pushcfunction(L, luaArch_computer_freeMemory); + lua_setfield(L, computer, "freeMemory"); + lua_pushcfunction(L, luaArch_computer_totalMemory); + lua_setfield(L, computer, "totalMemory"); + lua_pushcfunction(L, luaArch_computer_uptime); + lua_setfield(L, computer, "uptime"); + lua_pushcfunction(L, luaArch_computer_address); + lua_setfield(L, computer, "address"); + lua_pushcfunction(L, luaArch_computer_tmpAddress); + lua_setfield(L, computer, "tmpAddress"); + lua_pushcfunction(L, luaArch_computer_users); + lua_setfield(L, computer, "users"); + lua_pushcfunction(L, luaArch_computer_addUser); + lua_setfield(L, computer, "addUser"); + lua_pushcfunction(L, luaArch_computer_removeUser); + lua_setfield(L, computer, "removeUser"); + lua_pushcfunction(L, luaArch_computer_energy); + lua_setfield(L, computer, "energy"); + lua_pushcfunction(L, luaArch_computer_maxEnergy); + lua_setfield(L, computer, "maxEnergy"); + lua_pushcfunction(L, luaArch_computer_getArchitecture); + lua_setfield(L, computer, "getArchitecture"); + lua_pushcfunction(L, luaArch_computer_getArchitectures); + lua_setfield(L, computer, "getArchitectures"); + lua_pushcfunction(L, luaArch_computer_setArchitecture); + lua_setfield(L, computer, "setArchitecture"); + lua_pushcfunction(L, luaArch_computer_shutdown); + lua_setfield(L, computer, "shutdown"); + lua_setglobal(L, "computer"); + lua_createtable(L, 0, 10); + int component = lua_gettop(L); + lua_pushcfunction(L, luaArch_component_list); + lua_setfield(L, component, "list"); + lua_pushcfunction(L, luaArch_component_invoke); + lua_setfield(L, component, "invoke"); + lua_pushcfunction(L, luaArch_component_doc); + lua_setfield(L, component, "doc"); + lua_pushcfunction(L, luaArch_component_type); + lua_setfield(L, component, "type"); + lua_pushcfunction(L, luaArch_component_slot); + lua_setfield(L, component, "slot"); + lua_pushcfunction(L, luaArch_component_methods); + lua_setfield(L, component, "methods"); + lua_pushcfunction(L, luaArch_component_fields); + lua_setfield(L, component, "fields"); + lua_setglobal(L, "component"); +} + +static nn_Exit luaArch_handler(nn_ArchitectureRequest *req) { + nn_Computer *computer = req->computer; + luaArch *arch = req->localState; + switch(req->action) { + case NN_ARCH_FREEMEM: + req->freeMemory = arch->freeMem; + return NN_OK; + case NN_ARCH_INIT: + // wrapped in a block to prevent L from leaking, because L is common in Lua code so it may be used by mistake + { + arch = malloc(sizeof(*arch)); + arch->freeMem = nn_getTotalMemory(computer); + arch->computer = computer; + lua_State *L = lua_newstate(luaArch_alloc, arch); + arch->L = L; + req->localState = arch; + luaL_openlibs(L); + lua_pushlightuserdata(L, arch); + lua_setfield(L, LUA_REGISTRYINDEX, "archPtr"); + + luaArch_loadEnv(L); + + lua_settop(L, 0); + luaL_loadbufferx(L, luaArch_machineLua, strlen(luaArch_machineLua), "=machine.lua", "t"); + } + return NN_OK; + case NN_ARCH_DEINIT: + lua_close(arch->L); + free(arch); + return NN_OK; + case NN_ARCH_TICK:; + lua_settop(arch->L, 1); + int ret = 0; + int res = lua_resume(arch->L, NULL, 0, &ret); + if(res == LUA_OK) { + // halted, fuck + lua_pop(arch->L, ret); + nn_setError(computer, "machine halted"); + nn_setComputerState(computer, NN_CRASHED); + return NN_OK; + } else if(res != LUA_YIELD) { + const char *s = lua_tostring(arch->L, -1); + nn_setError(computer, s); + nn_setComputerState(computer, NN_CRASHED); + return NN_OK; + } + return NN_OK; + } + return NN_OK; +} + +nn_Architecture getLuaArch() { + return (nn_Architecture) { + .name = LUA_VERSION, + .state = NULL, + .handler = luaArch_handler, + }; +} diff --git a/rewrite/machine.lua b/rewrite/machine.lua new file mode 100644 index 0000000..c19ce23 --- /dev/null +++ b/rewrite/machine.lua @@ -0,0 +1,138 @@ +-- The boot-up code of the Lua architecture. +-- Extremely bad. +-- Do not use in a serious context, you will be hacked. +-- There is no sandboxing here. + +local sysyieldobj = {} + +local function sysyield() + coroutine.yield(sysyieldobj) +end + +local resume = coroutine.resume + +function coroutine.resume(co, ...) + local t = {resume(co, ...)} + -- NOTE: user can trigger false sysyields + -- by simply yielding an object who's + -- __eql always returns true. + if t[1] and t[2] == sysyieldobj then + coroutine.yield(sysyieldobj) + else + return table.unpack(t) + end +end + +local clist = component.list + +function component.list(ctype, exact) + local list = clist() + local desired = {} + + for addr, type in pairs(list) do + if ctype then + if exact then + if ctype == type then desired[addr] = type end + else + if string.find(type, ctype) then desired[addr] = type end + end + else + desired[addr] = type + end + end + + setmetatable(desired, {__call = next}) + return desired +end + +local componentCallback = { + __call = function(self, ...) + return component.invoke(self.address, self.name, ...) + end, + __tostring = function(self) + return component.doc(self.address, self.name) or "function" + end, +} + +local componentProxy = { + __index = function(self, key) + if self.fields[key] and self.fields[key].getter then + return component.invoke(self.address, key) + end + return rawget(self, key) + end, + __newindex = function(self, key, value) + if self.fields[key] and self.fields[key].setter then + return component.invoke(self.address, key, value) + end + rawset(self, key, value) + end, + __pairs = function(self) + local reachedFields = false + local key, val + return function() + if not reachedFields then + key, val = next(self, key) + if key == nil then reachedFields = true end + end + if reachedFields then + key = next(self.fields, key) + val = self[key] + end + return key, val + end + end, +} + +local proxyCache = setmetatable({}, {__mode = "v"}) + +function component.proxy(address) + if proxyCache[address] then return proxyCache[address] end + + local t, err = component.type(address) + if not t then return nil, err end + local slot, err = component.slot(address) + if not slot then return nil, err end + + local proxy = {address = address, type = t, slot = slot} + proxy.fields, err = component.fields(address) + if not proxy.fields then return nil, err end + + local methods = component.methods(address) + for name in pairs(methods) do + proxy[name] = setmetatable({address=address,name=name}, componentCallback) + end + + setmetatable(proxy, componentProxy) + proxyCache[address] = proxy + return proxy +end + +function computer.getProgramLocations() + return {} +end + +local shutdown, setArch = computer.shutdown, computer.setArchitecture + +function computer.shutdown(...) + shutdown(...) + sysyield() +end + +function computer.setArchitecture(arch) + if arch == computer.getArchitecture() then return end + local ok, err = setArch(arch) + sysyield() + return ok, err +end + +local eeprom = component.list("eeprom", true)() +assert(eeprom, "missing firmware") + +local code = assert(component.invoke(eeprom, "get")) +local f = assert(load(code, "=bios")) + +local ok, err = xpcall(f, debug.traceback) +if not ok then + print(err) +end diff --git a/rewrite/main.c b/rewrite/main.c index ae036ad..8bff0d0 100644 --- a/rewrite/main.c +++ b/rewrite/main.c @@ -1,9 +1,18 @@ // The main file of the test emulator // This is not a serious emulator intended for practical use, // it is simply just to test stuff and showcase the API. +// Error handling has been omitted in most places. #include "neonucleus.h" #include +#include + +nn_Architecture getLuaArch(); + +static const char minBIOS[] = { +#embed "minBIOS.lua" +,'\0' +}; static nn_Exit sandbox_handler(nn_ComponentRequest *req) { nn_Computer *c = req->computer; @@ -29,24 +38,6 @@ static nn_Exit sandbox_handler(nn_ComponentRequest *req) { return NN_OK; } -static nn_Exit sandbox_arch(nn_ArchitectureRequest *req) { - nn_Computer *c = req->computer; - switch(req->action) { - case NN_ARCH_INIT: - return NN_OK; - case NN_ARCH_DEINIT: - return NN_OK; - case NN_ARCH_FREEMEM: - req->freeMemory = 0; - return NN_OK; - case NN_ARCH_TICK: - nn_pushstring(c, "Hello from component call"); - nn_call(c, "sandbox", "log"); - return NN_OK; - } - return NN_OK; -} - int main() { nn_Context ctx; nn_initContext(&ctx); @@ -54,30 +45,81 @@ int main() { // create the universe nn_Universe *u = nn_createUniverse(&ctx); - nn_Architecture arch = { - .name = "Sandbox test", - .state = NULL, - .handler = sandbox_arch, - }; + nn_Architecture arch = getLuaArch(); - nn_ComponentMethod sandboxMethods[] = { + nn_Method sandboxMethods[] = { {"log", "log(msg: string) - Log to stdout", true}, {NULL}, }; nn_ComponentType *ctype = nn_createComponentType(u, "sandbox", NULL, sandboxMethods, sandbox_handler); + nn_EEPROM eeprom = { + .size = 4 * NN_KiB, + .dataSize = NN_KiB, + .readCallCost = 1, + .readEnergyCost = 0, + .readDataCallCost = 1, + .readDataEnergyCost = 0, + .writeCallCost = 1, + .writeEnergyCost = 0, + .writeDataCallCost = 1, + .writeDataEnergyCost = 0, + .handler = NULL, + }; + nn_VEEPROM veeprom = { + .code = minBIOS, + .codelen = strlen(minBIOS), + .data = NULL, + .datalen = 0, + .label = NULL, + .labellen = 0, + .arch = NULL, + .isReadonly = false, + }; + + nn_ComponentType *etype = nn_createVEEPROM(u, &eeprom, &veeprom); + nn_Computer *c = nn_createComputer(u, NULL, "computer0", 8 * NN_MiB, 256, 256); nn_setArchitecture(c, &arch); + nn_addSupportedArchitecture(c, &arch); nn_addComponent(c, ctype, "sandbox", -1, NULL); + nn_addComponent(c, etype, "eeprom", 0, etype); + + while(true) { + nn_Exit e = nn_tick(c); + if(e != NN_OK) { + nn_setErrorFromExit(c, e); + printf("error: %s\n", nn_getError(c)); + goto cleanup; + } - printf("Component added: %s\n", nn_hasComponent(c, "sandbox") ? "TRUE" : "FALSE"); - printf("Method active: %s\n", nn_hasMethod(c, "sandbox", "log") ? "TRUE" : "FALSE"); - printf("Incorrect method active: %s\n", nn_hasMethod(c, "sandbox", "notlog") ? "TRUE" : "FALSE"); + nn_ComputerState state = nn_getComputerState(c); + if(state == NN_POWEROFF) break; + if(state == NN_CRASHED) { + printf("error: %s\n", nn_getError(c)); + goto cleanup; + } + if(state == NN_CHARCH) { + printf("new arch: %s\n", nn_getDesiredArchitecture(c).name); + goto cleanup; + } + if(state == NN_BLACKOUT) { + printf("out of energy\n"); + goto cleanup; + } + if(state == NN_RESTART) { + printf("restart requested\n"); + goto cleanup; + } + } + +cleanup:; nn_destroyComputer(c); nn_destroyComponentType(ctype); + nn_destroyComponentType(etype); // rip the universe nn_destroyUniverse(u); return 0; diff --git a/rewrite/minBIOS.lua b/rewrite/minBIOS.lua new file mode 100644 index 0000000..f17ed5a --- /dev/null +++ b/rewrite/minBIOS.lua @@ -0,0 +1,102 @@ +-- Minimum viable BIOS, effectively a blend between LuaBIOS and AdvancedLoader + +local component, computer = component, computer +local gpu = component.list("gpu")() +local screen = component.list("screen")() +local eeprom = component.list("eeprom")() + +computer.getBootAddress = function() + return component.invoke(eeprom, "getData") +end +computer.setBootAddress = function(addr) + return component.invoke(eeprom, "setData", addr) +end + +if gpu and screen then + component.invoke(gpu, "bind", screen) + local w, h = component.invoke(gpu, "maxResolution") + component.invoke(gpu, "maxResolution") + component.invoke(gpu, "setForeground", 0xFFFFFF) + component.invoke(gpu, "setBackground", 0x000000) + component.invoke(gpu, "fill", 1, 1, w, h, " ") +end + +local function romRead(addr, path) + local fs = component.proxy(addr) + local fd = fs.open(path) + if not fd then return end + local data = "" + while true do + local chunk, err = fs.read(fd, math.huge) + if err then + fs.close(fd) + return nil + end + if not chunk then break end + data = data .. chunk + end + fs.close(fd) + return data +end + +local function getBootCode(addr) + local drive = component.proxy(addr) + local sectorSize = drive.getSectorSize() + local firstSector = drive.readSector(1) + + -- Generic MBR bootcode + if firstSector:sub(-2, -1) == "\x55\xAA" then + local codeEnd = sectorSize - 67 -- no laughing!!!! + local term = string.find(firstSector, "\0", 5, true) + return load(string.sub(firstSector, 5, term and (term - 1) or codeEnd)) + end + -- TODO: whatever else NC might be testing +end + +local paths = { + "init.lua", + "OS.lua", + "boot/pipes/kernel", +} + +local prevboot = computer.getBootAddress() +if prevboot then + if component.type(prevboot) == "filesystem" then + for _, path in ipairs(paths) do + local code = romRead(prevboot, path) + if code then + assert(load(code))(prevboot) + error("halted") + end + end + end + if component.type(prevboot) == "drive" then + local f = getBootCode(prevboot) + if f then + f(prevboot) + error("halted") + end + end +end + +for addr in component.list("filesystem", true) do + for _, path in ipairs(paths) do + local code = romRead(addr, path) + if code then + computer.setBootAddress(addr) + assert(load(code))(addr) + error("halted") + end + end +end + +for addr in component.list("drive", true) do + local f = getBootCode(addr) + if f then + computer.setBootAddress(addr) + f(addr) + error("halted") + end +end + +error("no bootable medium found") diff --git a/rewrite/neonucleus.c b/rewrite/neonucleus.c index 711704e..474772c 100644 --- a/rewrite/neonucleus.c +++ b/rewrite/neonucleus.c @@ -232,6 +232,111 @@ void nn_memset(void *dest, int x, size_t len) { for(size_t i = 0; i < len; i++) out[i] = (char)x; } +// taken from https://wiki.osdev.org/CRC32 +// OSDev wiki is really useful sometimes +// TODO: maybe allow one that uses compiler intrinsics +// because CPUs are really good at CRC32 nowadays + +unsigned int nn_crc32_poly8_lookup[256] = +{ + 0, 0x77073096, 0xEE0E612C, 0x990951BA, + 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, + 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, + 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, + 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, + 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, + 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, + 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, + 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, + 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, + 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, + 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, + 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, + 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, + 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, + 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, + 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, + 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, + 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D +}; + +unsigned int nn_computeCRC32(const char *data, size_t datalen) { + unsigned int crc = 0xFFFFFFFF; + while(datalen-- > 0) { + crc = nn_crc32_poly8_lookup[(crc ^ *(data++)) & 0xFF] ^ (crc >> 8); + } + return (crc ^ 0xFFFFFFFF); +} + +void nn_crc32ChecksumBytes(unsigned int checksum, char out[8]) { + char bytes[4]; + bytes[0] = (checksum >> 0) & 0xFF; + bytes[1] = (checksum >> 8) & 0xFF; + bytes[2] = (checksum >> 16) & 0xFF; + bytes[3] = (checksum >> 24) & 0xFF; + char alpha[16] = "0123456789abcdef"; + + for(size_t i = 0; i < 4; i++) { + char byte = bytes[i]; + out[i*2] = alpha[(byte>>4) & 0xF]; + out[i*2+1] = alpha[(byte>>0) & 0xF]; + } +} + +int nn_memcmp(const char *a, const char *b, size_t len) { + for(size_t i = 0; i < len; i++) { + char c = a[i]; + char d = b[i]; + if(c != d) return (int)(unsigned char)c - (int)(unsigned char)d; + } + return 0; +} + int nn_strcmp(const char *a, const char *b) { size_t i = 0; while(1) { @@ -430,7 +535,7 @@ typedef struct nn_ComponentType { const char *name; nn_ComponentHandler *handler; // NULL-terminated - nn_ComponentMethod *methods; + nn_Method *methods; size_t methodCount; } nn_ComponentType; @@ -458,6 +563,17 @@ typedef enum nn_ValueType { NN_VAL_TABLE, } nn_ValueType; +// we don't use the enum member as a name because then all the +// switch cases would have a useless branch for it. +const char *nn_typenames[6] = { + [NN_VAL_NULL] = "null", + [NN_VAL_BOOL] = "bool", + [NN_VAL_NUM] = "number", + [NN_VAL_STR] = "string", + [NN_VAL_USERDATA] = "userdata", + [NN_VAL_TABLE] = "table", +}; + typedef struct nn_String { nn_Context ctx; size_t refc; @@ -493,6 +609,7 @@ typedef struct nn_Computer { nn_Universe *universe; void *userdata; char *address; + char *tmpaddress; void *archState; nn_Architecture arch; nn_Architecture desiredArch; @@ -511,10 +628,12 @@ typedef struct nn_Computer { size_t stackSize; size_t archCount; size_t signalCount; + size_t userCount; nn_Value callstack[NN_MAX_STACK]; char errorBuffer[NN_MAX_ERROR_SIZE]; nn_Architecture archs[NN_MAX_ARCHITECTURES]; nn_Signal signals[NN_MAX_SIGNALS]; + char *users[NN_MAX_USERS]; } nn_Computer; nn_Universe *nn_createUniverse(nn_Context *ctx) { @@ -529,7 +648,7 @@ void nn_destroyUniverse(nn_Universe *universe) { nn_free(&ctx, universe, sizeof(nn_Universe)); } -nn_ComponentType *nn_createComponentType(nn_Universe *universe, const char *name, void *userdata, const nn_ComponentMethod methods[], nn_ComponentHandler *handler) { +nn_ComponentType *nn_createComponentType(nn_Universe *universe, const char *name, void *userdata, const nn_Method methods[], nn_ComponentHandler *handler) { nn_Context *ctx = &universe->ctx; nn_ComponentType *ctype = nn_alloc(ctx, sizeof(nn_ComponentType)); @@ -548,14 +667,14 @@ nn_ComponentType *nn_createComponentType(nn_Universe *universe, const char *name size_t methodCount = 0; while(methods[methodCount].name != NULL) methodCount++; - nn_ComponentMethod *methodscpy = nn_aralloc(arena, methodCount * sizeof(nn_ComponentMethod)); + nn_Method *methodscpy = nn_aralloc(arena, methodCount * sizeof(nn_Method)); if(methodscpy == NULL) goto fail; ctype->methods = methodscpy; ctype->methodCount = methodCount; for(size_t i = 0; i < methodCount; i++) { - nn_ComponentMethod cpy; - cpy.direct = methods[i].direct; + nn_Method cpy; + cpy.flags = methods[i].flags; cpy.name = nn_arstrdup(arena, methods[i].name); if(cpy.name == NULL) goto fail; cpy.docString = nn_arstrdup(arena, methods[i].docString); @@ -605,6 +724,8 @@ nn_Computer *nn_createComputer(nn_Universe *universe, void *userdata, const char return NULL; } + c->tmpaddress = NULL; + c->arch.name = NULL; c->desiredArch.name = NULL; c->archState = NULL; @@ -637,6 +758,7 @@ nn_Computer *nn_createComputer(nn_Universe *universe, void *userdata, const char c->stackSize = 0; c->archCount = 0; c->signalCount = 0; + c->userCount = 0; // set to empty string c->errorBuffer[0] = '\0'; return c; @@ -668,9 +790,13 @@ void nn_destroyComputer(nn_Computer *computer) { for(size_t j = 0; j < s.len; j++) nn_dropValue(s.values[j]); nn_free(ctx, s.values, sizeof(nn_Value) * s.len); } + for(size_t i = 0; i < computer->userCount; i++) { + nn_strfree(ctx, computer->users[i]); + } nn_free(ctx, computer->components, sizeof(nn_Component) * computer->componentCap); nn_free(ctx, computer->deviceInfo, sizeof(nn_DeviceInfo) * computer->deviceInfoCap); + if(computer->tmpaddress != NULL) nn_strfree(ctx, computer->tmpaddress); nn_strfree(ctx, computer->address); nn_free(ctx, computer, sizeof(nn_Computer)); } @@ -683,6 +809,63 @@ const char *nn_getComputerAddress(nn_Computer *computer) { return computer->address; } +nn_Exit nn_setTmpAddress(nn_Computer *computer, const char *address) { + nn_Context ctx = computer->universe->ctx; + if(address == NULL) { + if(computer->tmpaddress != NULL) { + nn_strfree(&ctx, computer->tmpaddress); + } + computer->tmpaddress = NULL; + return NN_OK; + } + char *newTmp = nn_strdup(&ctx, address); + if(newTmp == NULL) return NN_ENOMEM; + if(computer->tmpaddress != NULL) { + nn_strfree(&ctx, computer->tmpaddress); + } + computer->tmpaddress = newTmp; + return NN_OK; +} + +const char *nn_getTmpAddress(nn_Computer *computer) { + return computer->tmpaddress; +} + +nn_Exit nn_addUser(nn_Computer *computer, const char *user) { + if(computer->userCount == NN_MAX_USERS) return NN_ELIMIT; + size_t len = nn_strlen(user); + if(len >= NN_MAX_USERNAME) return NN_ELIMIT; + + char *usercpy = nn_strdup(&computer->universe->ctx, user); + if(usercpy == NULL) return NN_ENOMEM; + computer->users[computer->userCount++] = usercpy; + return NN_OK; +} + +bool nn_removeUser(nn_Computer *computer, const char *user) { + bool removed = false; + size_t j = 0; + nn_Context ctx = computer->universe->ctx; + + for(size_t i = 0; i < computer->userCount; i++) { + char *u = computer->users[i]; + if(nn_strcmp(u, user) == 0) { + nn_strfree(&ctx, u); + removed = true; + } else { + computer->users[j] = computer->users[i]; + j++; + } + } + computer->userCount = j; + return removed; +} + +const char *nn_getUser(nn_Computer *computer, size_t idx) { + if(idx >= computer->userCount) return NULL; + return computer->users[idx]; +} + void nn_setArchitecture(nn_Computer *computer, const nn_Architecture *arch) { computer->arch = *arch; } @@ -705,7 +888,7 @@ nn_Exit nn_addSupportedArchitecture(nn_Computer *computer, const nn_Architecture return NN_OK; } -const nn_Architecture *nn_getSupportedArchitecture(nn_Computer *computer, size_t *len) { +const nn_Architecture *nn_getSupportedArchitectures(nn_Computer *computer, size_t *len) { *len = computer->archCount; return computer->archs; } @@ -796,7 +979,7 @@ nn_ComputerState nn_getComputerState(nn_Computer *computer) { return computer->state; } -static void nn_setErrorFromExit(nn_Computer *computer, nn_Exit exit) { +void nn_setErrorFromExit(nn_Computer *computer, nn_Exit exit) { switch(exit) { case NN_OK: return; // no error @@ -1007,7 +1190,7 @@ int nn_getComponentSlot(nn_Computer *computer, const char *address) { return 0; } -const nn_ComponentMethod *nn_getComponentMethods(nn_Computer *computer, const char *address, size_t *len) { +const nn_Method *nn_getComponentMethods(nn_Computer *computer, const char *address, size_t *len) { for(size_t i = 0; i < computer->componentLen; i++) { nn_Component *c = &computer->components[i]; if(nn_strcmp(c->address, address) == 0) { @@ -1024,6 +1207,25 @@ const char *nn_getComponentAddress(nn_Computer *computer, size_t idx) { return computer->components[idx].address; } +const char *nn_getComponentDoc(nn_Computer *computer, const char *address, const char *method) { + if(!nn_hasComponent(computer, address)) { + return NULL; + } + if(!nn_hasMethod(computer, address, method)) { + return NULL; + } + for(size_t i = 0; i < computer->componentLen; i++) { + nn_Component c = computer->components[i]; + if(nn_strcmp(c.address, address) != 0) continue; + + for(size_t j = 0; j < c.ctype->methodCount; j++) { + if(nn_strcmp(c.ctype->methods[j].name, method) == 0) return c.ctype->methods[j].docString; + } + return NULL; + } + return NULL; +} + static void nn_retainValue(nn_Value val) { switch(val.type) { case NN_VAL_NULL: @@ -1096,6 +1298,8 @@ nn_Exit nn_call(nn_Computer *computer, const char *address, const char *method) nn_Exit err = c.ctype->handler(&req); if(err) { if(err != NN_EBADCALL) nn_setErrorFromExit(computer, err); + // clear junk + nn_clearstack(computer); return err; } @@ -1253,35 +1457,40 @@ void nn_clearstack(nn_Computer *computer) { } bool nn_isnull(nn_Computer *computer, size_t idx) { - if(idx < computer->stackSize) return false; + if(idx >= computer->stackSize) return false; return computer->callstack[idx].type == NN_VAL_NULL; } bool nn_isboolean(nn_Computer *computer, size_t idx) { - if(idx < computer->stackSize) return false; + if(idx >= computer->stackSize) return false; return computer->callstack[idx].type == NN_VAL_BOOL; } bool nn_isnumber(nn_Computer *computer, size_t idx) { - if(idx < computer->stackSize) return false; + if(idx >= computer->stackSize) return false; return computer->callstack[idx].type == NN_VAL_NUM; } bool nn_isstring(nn_Computer *computer, size_t idx) { - if(idx < computer->stackSize) return false; + if(idx >= computer->stackSize) return false; return computer->callstack[idx].type == NN_VAL_STR; } bool nn_isuserdata(nn_Computer *computer, size_t idx) { - if(idx < computer->stackSize) return false; + if(idx >= computer->stackSize) return false; return computer->callstack[idx].type == NN_VAL_USERDATA; } bool nn_istable(nn_Computer *computer, size_t idx) { - if(idx < computer->stackSize) return false; + if(idx >= computer->stackSize) return false; return computer->callstack[idx].type == NN_VAL_TABLE; } +const char *nn_typenameof(nn_Computer *computer, size_t idx) { + if(idx >= computer->stackSize) return "none"; + return nn_typenames[computer->callstack[idx].type]; +} + bool nn_toboolean(nn_Computer *computer, size_t idx) { return computer->callstack[idx].boolean; } @@ -1430,9 +1639,23 @@ typedef struct nn_EEPROM_state { void *userdata; } nn_EEPROM_state; +typedef struct nn_VEEPROM_state { + nn_Universe *universe; + char *code; + size_t codelen; + char *data; + size_t datalen; + size_t archlen; + size_t labellen; + bool isReadonly; + char arch[NN_MAX_ARCHNAME]; + char label[NN_MAX_LABEL]; +} nn_VEEPROM_state; + nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) { nn_EEPROM_state *state = req->typeUserdata; void *instance = req->compUserdata; + // NULL for FREETYPE nn_Computer *computer = req->computer; nn_Context ctx = state->universe->ctx; @@ -1440,6 +1663,7 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) { ereq.userdata = state->userdata; ereq.instance = instance; ereq.computer = computer; + ereq.eepromConf = &state->eeprom; const char *method = req->methodCalled; @@ -1462,6 +1686,67 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) { if(nn_strcmp(method, "getDataSize") == 0) { return nn_pushnumber(computer, state->eeprom.dataSize); } + if(nn_strcmp(method, "isReadOnly") == 0) { + ereq.action = NN_EEPROM_ISREADONLY; + nn_Exit e = state->eeprom.handler(&ereq); + if(e) return e; + req->returnCount = 1; + return nn_pushbool(computer, ereq.buflen != 0); + } + if(nn_strcmp(method, "getChecksum") == 0) { + // yup, on-stack. + // Perhaps in the future we'll make it heap-allocated. + char buf[state->eeprom.size]; + ereq.action = NN_EEPROM_GET; + ereq.buf = buf; + ereq.buflen = state->eeprom.size; + nn_Exit e = state->eeprom.handler(&ereq); + if(e) return e; + unsigned int chksum = nn_computeCRC32(buf, ereq.buflen); + char encoded[8]; + nn_crc32ChecksumBytes(chksum, encoded); + req->returnCount = 1; + return nn_pushlstring(computer, encoded, 8); + } + if(nn_strcmp(method, "makeReadonly") == 0) { + // 1st argument is a string, which is the checksum we're meant to have + if(nn_getstacksize(computer) < 1) { + nn_setError(computer, "bad argument #1 (string expected)"); + return NN_EBADCALL; + } + if(!nn_isstring(computer, 0)) { + nn_setError(computer, "bad argument #1 (string expected)"); + return NN_EBADCALL; + } + size_t len; + const char *desired = nn_tolstring(computer, 0, &len); + if(len != 8) { + nn_setError(computer, "invalid checksum"); + return NN_EBADCALL; + } + // yup, on-stack. + // Perhaps in the future we'll make it heap-allocated. + char buf[state->eeprom.size]; + ereq.action = NN_EEPROM_GET; + ereq.buf = buf; + ereq.buflen = state->eeprom.size; + nn_Exit e = state->eeprom.handler(&ereq); + if(e) return e; + unsigned int chksum = nn_computeCRC32(buf, ereq.buflen); + char encoded[8]; + nn_crc32ChecksumBytes(chksum, encoded); + if(nn_memcmp(encoded, desired, sizeof(char) * 8)) { + nn_setError(computer, "incorrect checksum"); + return NN_EBADCALL; + } + + ereq.action = NN_EEPROM_MAKEREADONLY; + e = state->eeprom.handler(&ereq); + if(e) return e; + + req->returnCount = 1; + return nn_pushbool(computer, true); + } if(nn_strcmp(method, "getLabel") == 0) { char buf[NN_MAX_LABEL]; ereq.action = NN_EEPROM_GETLABEL; @@ -1518,33 +1803,106 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) { req->returnCount = 1; return nn_pushlstring(computer, buf, ereq.buflen); } + if(nn_strcmp(method, "getArchitecture") == 0) { + char buf[NN_MAX_ARCHNAME]; + ereq.action = NN_EEPROM_GETARCH; + ereq.buf = buf; + ereq.buflen = NN_MAX_ARCHNAME; + nn_Exit e = state->eeprom.handler(&ereq); + if(e) return e; + req->returnCount = 1; + return nn_pushlstring(computer, buf, ereq.buflen); + } + if(nn_strcmp(method, "set") == 0) { + if(nn_getstacksize(computer) < 1) { + nn_setError(computer, "bad argument #1 (string expected)"); + return NN_EBADCALL; + } + if(!nn_isstring(computer, 0)) { + nn_setError(computer, "bad argument #1 (string expected)"); + return NN_EBADCALL; + } + size_t len; + const char *s = nn_tolstring(computer, 0, &len); + if(len > state->eeprom.size) { + nn_setError(computer, "not enough space"); + return NN_EBADCALL; + } + ereq.action = NN_EEPROM_SET; + // DO NOT MODIFY IT!!!! + ereq.buf = (char*)s; + ereq.buflen = len; + return state->eeprom.handler(&ereq); + } + if(nn_strcmp(method, "setData") == 0) { + if(nn_getstacksize(computer) < 1) { + nn_setError(computer, "bad argument #1 (string expected)"); + return NN_EBADCALL; + } + if(!nn_isstring(computer, 0)) { + nn_setError(computer, "bad argument #1 (string expected)"); + return NN_EBADCALL; + } + size_t len; + const char *s = nn_tolstring(computer, 0, &len); + if(len > state->eeprom.dataSize) { + nn_setError(computer, "not enough space"); + return NN_EBADCALL; + } + ereq.action = NN_EEPROM_SETDATA; + // DO NOT MODIFY IT!!!! + ereq.buf = (char*)s; + ereq.buflen = len; + return state->eeprom.handler(&ereq); + } + if(nn_strcmp(method, "setArchitecture") == 0) { + if(nn_getstacksize(computer) < 1) { + nn_setError(computer, "bad argument #1 (string expected)"); + return NN_EBADCALL; + } + if(!nn_isstring(computer, 0)) { + nn_setError(computer, "bad argument #1 (string expected)"); + return NN_EBADCALL; + } + size_t len; + const char *s = nn_tolstring(computer, 0, &len); + if(len > NN_MAX_ARCHNAME) { + nn_setError(computer, "not enough space"); + return NN_EBADCALL; + } + ereq.action = NN_EEPROM_SETARCH; + // DO NOT MODIFY IT!!!! + ereq.buf = (char*)s; + ereq.buflen = len; + return state->eeprom.handler(&ereq); + } return NN_OK; } return NN_OK; } -nn_ComponentType *nn_createEEPROM(nn_Universe *universe, nn_EEPROM *eeprom, void *userdata) { +nn_ComponentType *nn_createEEPROM(nn_Universe *universe, const nn_EEPROM *eeprom, void *userdata) { nn_Context ctx = universe->ctx; nn_EEPROM_state *state = nn_alloc(&ctx, sizeof(*state)); if(state == NULL) return NULL; state->universe = universe; state->eeprom = *eeprom; state->userdata = userdata; - const nn_ComponentMethod methods[] = { - {"getSize", "getSize(): number - Get the storage capacity of the EEPROM.", true}, - {"getDataSize", "getDataSize(): number - Get the storage capacity of the EEPROM data.", true}, - {"getLabel", "getLabel(): string - Get the EEPROM label", false}, - {"setLabel", "setLabel(label: string): string - Set the EEPROM label and return what was actually set, which may be truncated.", false}, - {"get", "get(): string - Get the current EEPROM contents.", false}, - {"getData", "getData(): string - Get the current EEPROM data contents.", false}, - {"set", "set(data: string) - Set the current EEPROM contents.", false}, - {"setData", "setData(data: string) - Set the current EEPROM data contents.", false}, - {"getArchitecture", "getArchitecture(): string - Get the current EEPROM architecture intended.", false}, - {"setArchitecture", "setArchitecture(data: string) - Set the current EEPROM architecture intended.", false}, - {"isReadOnly", "isReadOnly(): boolean - Returns whether the EEPROM is read-only.", false}, - {"makeReadonly", "makeReadonly() - Makes the EEPROM read-only, this cannot be undone.", false}, - {"getChecksum", "getChecksum(): string - Returns a simple checksum of the EEPROM's contents and data.", false}, - {NULL, NULL, false}, + const nn_Method methods[] = { + {"getSize", "function(): number - Get the storage capacity of the EEPROM.", NN_DIRECT}, + {"getDataSize", "function(): number - Get the storage capacity of the EEPROM data.", NN_DIRECT}, + {"getLabel", "function(): string - Get the EEPROM label", NN_INDIRECT}, + {"setLabel", "function(label: string): string - Set the EEPROM label and return what was actually set, which may be truncated.", NN_INDIRECT}, + {"get", "function(): string - Get the current EEPROM contents.", NN_INDIRECT}, + {"getData", "function(): string - Get the current EEPROM data contents.", NN_INDIRECT}, + {"set", "function(data: string) - Set the current EEPROM contents.", NN_INDIRECT}, + {"setData", "function(data: string) - Set the current EEPROM data contents.", NN_INDIRECT}, + {"getArchitecture", "function(): string - Get the current EEPROM architecture intended.", NN_INDIRECT}, + {"setArchitecture", "function(data: string) - Set the current EEPROM architecture intended.", NN_INDIRECT}, + {"isReadOnly", "function(): boolean - Returns whether the EEPROM is read-only.", NN_INDIRECT}, + {"makeReadonly", "function(checksum: string) - Makes the EEPROM read-only, this cannot be undone.", NN_INDIRECT}, + {"getChecksum", "function(): string - Returns a simple checksum of the EEPROM's contents and data.", NN_INDIRECT}, + {NULL, NULL, NN_INDIRECT}, }; nn_ComponentType *t = nn_createComponentType(universe, "eeprom", state, methods, nn_eeprom_handler); if(t == NULL) { @@ -1553,3 +1911,100 @@ nn_ComponentType *nn_createEEPROM(nn_Universe *universe, nn_EEPROM *eeprom, void } return t; } + +static nn_Exit nn_veeprom_handler(nn_EEPROMRequest *req) { + const nn_EEPROM *conf = req->eepromConf; + nn_VEEPROM_state *state = req->userdata; + nn_Context ctx = state->universe->ctx; + switch(req->action) { + case NN_EEPROM_DROP: + nn_free(&ctx, state->code, sizeof(char) * conf->size); + nn_free(&ctx, state->data, sizeof(char) * conf->dataSize); + nn_free(&ctx, state, sizeof(*state)); + return NN_OK; + case NN_EEPROM_ISREADONLY: + req->buflen = state->isReadonly ? 1 : 0; + return NN_OK; + case NN_EEPROM_MAKEREADONLY: + state->isReadonly = true; + return NN_OK; + case NN_EEPROM_GET: + req->buflen = state->codelen; + nn_memcpy(req->buf, state->code, sizeof(char) * state->codelen); + return NN_OK; + case NN_EEPROM_GETDATA: + req->buflen = state->datalen; + nn_memcpy(req->buf, state->data, sizeof(char) * state->datalen); + return NN_OK; + case NN_EEPROM_GETARCH: + req->buflen = state->archlen; + nn_memcpy(req->buf, state->arch, sizeof(char) * state->archlen); + return NN_OK; + case NN_EEPROM_GETLABEL: + req->buflen = state->labellen; + nn_memcpy(req->buf, state->label, sizeof(char) * state->labellen); + return NN_OK; + case NN_EEPROM_SET: + state->codelen = req->buflen; + nn_memcpy(state->code, req->buf, sizeof(char) * state->codelen); + return NN_OK; + case NN_EEPROM_SETDATA: + state->datalen = req->buflen; + nn_memcpy(state->data, req->buf, sizeof(char) * state->datalen); + return NN_OK; + case NN_EEPROM_SETARCH: + state->archlen = req->buflen; + nn_memcpy(state->arch, req->buf, sizeof(char) * state->archlen); + return NN_OK; + case NN_EEPROM_SETLABEL: + state->labellen = req->buflen; + nn_memcpy(state->label, req->buf, sizeof(char) * state->labellen); + return NN_OK; + } + return NN_OK; +} + +nn_ComponentType *nn_createVEEPROM(nn_Universe *universe, const nn_EEPROM *eeprom, const nn_VEEPROM *vmem) { + nn_Context ctx = universe->ctx; + char *code = NULL; + char *data = NULL; + size_t archlen = 0; + nn_VEEPROM_state *state = nn_alloc(&ctx, sizeof(*state)); + if(state == NULL) goto fail; + state->universe = universe; + state->isReadonly = vmem->isReadonly; + + code = nn_alloc(&ctx, sizeof(char) * eeprom->size); + if(code == NULL) goto fail; + + data = nn_alloc(&ctx, sizeof(char) * eeprom->dataSize); + if(data == NULL) goto fail; + + state->code = code; + state->data = data; + + state->codelen = vmem->codelen; + state->datalen = vmem->datalen; + state->labellen = vmem->labellen; + nn_memcpy(state->code, vmem->code, sizeof(char) * state->codelen); + nn_memcpy(state->data, vmem->data, sizeof(char) * state->datalen); + nn_memcpy(state->label, vmem->label, sizeof(char) * state->labellen); + + if(vmem->arch != NULL) { + archlen = nn_strlen(vmem->arch); + } + state->archlen = archlen; + nn_memcpy(state->arch, vmem->arch, sizeof(char) * archlen); + + nn_EEPROM neeprom = *eeprom; + neeprom.handler = nn_veeprom_handler; + nn_ComponentType *ty = nn_createEEPROM(universe, &neeprom, state); + if(ty == NULL) goto fail; + return ty; +fail:; + // remember, freeing NULL is fine! + nn_free(&ctx, code, sizeof(char) * eeprom->size); + nn_free(&ctx, data, sizeof(char) * eeprom->dataSize); + nn_free(&ctx, state, sizeof(*state)); + return NULL; +} diff --git a/rewrite/neonucleus.h b/rewrite/neonucleus.h index a613143..60655ea 100644 --- a/rewrite/neonucleus.h +++ b/rewrite/neonucleus.h @@ -48,10 +48,16 @@ extern "C" { #define NN_CLOSEPORTS 0 // maximum amount of architectures one machine can support. #define NN_MAX_ARCHITECTURES 32 +// maximum size of the architecture name EEPROMs can store +#define NN_MAX_ARCHNAME 64 // maximum amount of keyboards a screen can have #define NN_MAX_KEYBOARDS 64 // the port used by tunnel cards. This port is invalid for modems. #define NN_TUNNEL_PORT 0 +// maximum amount of users a computer can have +#define NN_MAX_USERS 64 +// maximum length of a username +#define NN_MAX_USERNAME 128 // the maximum size of a UTF-8 character #define NN_MAXIMUM_UNICODE_BUFFER 4 @@ -93,18 +99,23 @@ typedef struct nn_LockRequest { nn_LockAction action; } nn_LockRequest; -// intended for a plain mutex. +// Intended for a plain mutex. // This is used for synchronization. OpenComputers achieves synchronization // between the worker threads by sending them as requests to a central thread (indirect methods). -// In NeoNucleus, we simply use a lock. This technically makes all methods direct, however -// we consider methods to be indirect if they require locks. +// In NeoNucleus, the function pointer is invoked on the calling thead. This technically makes all methods direct, +// however we consider methods to be indirect if they require synchronization of mutable state. +// Do note that locks are only used in "full" component implementations, such as the volatile storage devices. +// The interfaces do not do any automatic synchronization via locks, all synchronization is assumed +// to be handled in the implementer of the interface, because only you know how to best synchronize +// it with the outside world. typedef void nn_LockProc(void *state, nn_LockRequest *req); // The *context* NeoNucleus is operating in. // This determines: // - How memory is allocated -// - How random numbers are generated +// - How random numbers are generated and what the range is // - What the current time is +// - How locks work typedef struct nn_Context { void *state; nn_AllocProc *alloc; @@ -221,6 +232,29 @@ void nn_destroyComputer(nn_Computer *computer); void *nn_getComputerUserdata(nn_Computer *computer); const char *nn_getComputerAddress(nn_Computer *computer); +// address is copied. +// It can be NULL if you wish to have no tmp address. +// It can fail due to out-of-memory errors. +nn_Exit nn_setTmpAddress(nn_Computer *computer, const char *address); +// can return NULL if none was set +const char *nn_getTmpAddress(nn_Computer *computer); + +// Registers a user to the computer. +nn_Exit nn_addUser(nn_Computer *computer, const char *user); +// Unregisters a user from the computer. +// If they were never there, nothing is removed and all is fine. +// It returns if the user was originally there. +bool nn_removeUser(nn_Computer *computer, const char *user); +// NULL for out-of-bound users +// Can be used to iterate all users. +const char *nn_getUser(nn_Computer *computer, size_t idx); +// Helper function. +// Always returns true if 0 users are registered. +// If users are registered, it will only return true if the specified +// user is registered. +// This can be used for checking signals. +bool nn_hasUser(nn_Computer *computer, const char *user); + // Sets the computer's architecture. // The architecture determines everything from how the computer runs, to how it turns off. // Everything is limited by the architecture. @@ -239,7 +273,7 @@ nn_Architecture nn_getDesiredArchitecture(nn_Computer *computer); // The architecture is copied, it can be freed after this is called. nn_Exit nn_addSupportedArchitecture(nn_Computer *computer, const nn_Architecture *arch); // Returns the array of supported architectures, as well as the length. -const nn_Architecture *nn_getSupportedArchitecture(nn_Computer *computer, size_t *len); +const nn_Architecture *nn_getSupportedArchitectures(nn_Computer *computer, size_t *len); // Helper function for searching for an architecture using a computer which supports it and the architecture name. // If the architecture is not found, it returns one with a NULL name. nn_Architecture nn_findSupportedArchitecture(nn_Computer *computer, const char *name); @@ -262,6 +296,9 @@ double nn_getUptime(nn_Computer *computer); // copies the string into the local error buffer. The error is NULL terminated, but also capped by NN_MAX_ERROR_SIZE void nn_setError(nn_Computer *computer, const char *s); +// set a default error message from an exit. +// Does nothing for EBADCALL. +void nn_setErrorFromExit(nn_Computer *computer, nn_Exit exit); // copies the string into the local error buffer. The error is capped by NN_MAX_ERROR_SIZE-1. The -1 is there because the NULL terminator is still inserted at the end. // Do note that nn_getError() still returns a NULL-terminated string, thus NULL terminators in this error will lead to a shortened error. void nn_setLError(nn_Computer *computer, const char *s, size_t len); @@ -297,11 +334,24 @@ void nn_removeDeviceInfo(nn_Computer *computer, const char *address); // gets the device info array. const nn_DeviceInfo *nn_getDeviceInfo(nn_Computer *computer, size_t *len); -typedef struct nn_ComponentMethod { +typedef enum nn_MethodFlags { + NN_INDIRECT = 0, + NN_DIRECT = (1<<0), + // this indicates this method wraps a *field* + // getter means calling it with no arguments will return the current value, + NN_GETTER = (1<<1), + // this indicates this method wraps a *field* + // setter means calling it with 1 argument will try to set the value. + NN_SETTER = (1<<2), +} nn_MethodFlags; + +#define NN_FIELD_MASK (NN_GETTER | NN_SETTER) + +typedef struct nn_Method { const char *name; const char *docString; - bool direct; -} nn_ComponentMethod; + nn_MethodFlags flags; +} nn_Method; typedef struct nn_ComponentType nn_ComponentType; @@ -344,7 +394,7 @@ typedef struct nn_ComponentRequest { typedef nn_Exit nn_ComponentHandler(nn_ComponentRequest *req); // Creates a new component type. It is safe to free name and methods afterwards. -nn_ComponentType *nn_createComponentType(nn_Universe *universe, const char *name, void *userdata, const nn_ComponentMethod methods[], nn_ComponentHandler *handler); +nn_ComponentType *nn_createComponentType(nn_Universe *universe, const char *name, void *userdata, const nn_Method methods[], nn_ComponentHandler *handler); // NOTE: do not destroy this before destroying any components using it, or any computers with components using it. // The component type is still used one last time for the destructor of the components. void nn_destroyComponentType(nn_ComponentType *ctype); @@ -366,11 +416,13 @@ const char *nn_getComponentType(nn_Computer *computer, const char *address); // Gets the slot of a component. int nn_getComponentSlot(nn_Computer *computer, const char *address); // Returns the array of component methods. This can be used for doc strings or just listing methods. -const nn_ComponentMethod *nn_getComponentMethods(nn_Computer *computer, const char *address, size_t *len); +const nn_Method *nn_getComponentMethods(nn_Computer *computer, const char *address, size_t *len); // get the address at a certain index. // It'll return NULL for out of bounds indexes. // This can be used to iterate over all components. const char *nn_getComponentAddress(nn_Computer *computer, size_t idx); +// Returns the doc-string associated with a method. +const char *nn_getComponentDoc(nn_Computer *computer, const char *address, const char *method); // this uses the call stack. // Component calls must not call other components, it just doesn't work. @@ -468,6 +520,9 @@ bool nn_isuserdata(nn_Computer *computer, size_t idx); // Returns whether the value at [idx] is a table. // [idx] starts at 0. bool nn_istable(nn_Computer *computer, size_t idx); +// Returns the name of the type of the value at that index. +// For out of bounds indexes, "none" is returned. +const char *nn_typenameof(nn_Computer *computer, size_t idx); // NOTE: behavior of the nn_to*() functions and nn_dumptable() when the values have the wrong types or at out of bounds indexes is undefined. @@ -539,6 +594,7 @@ typedef struct nn_EEPROMRequest { void *instance; // the computer making the request nn_Computer *computer; + const struct nn_EEPROM *eepromConf; nn_EEPROMAction action; // all the get* options should set this to the length, // and its initial value is the capacity of [buf]. @@ -572,9 +628,21 @@ typedef struct nn_EEPROM { nn_Exit (*handler)(nn_EEPROMRequest *request); } nn_EEPROM; +typedef struct nn_VEEPROM { + const char *code; + size_t codelen; + const char *data; + size_t datalen; + const char *label; + size_t labellen; + const char *arch; + bool isReadonly; +} nn_VEEPROM; + // the userdata passed to the component is the userdata // in the handler -nn_ComponentType *nn_createEEPROM(nn_Universe *universe, nn_EEPROM *eeprom, void *userdata); +nn_ComponentType *nn_createEEPROM(nn_Universe *universe, const nn_EEPROM *eeprom, void *userdata); +nn_ComponentType *nn_createVEEPROM(nn_Universe *universe, const nn_EEPROM *eeprom, const nn_VEEPROM *vmem); #ifdef __cplusplus }