we got Lua running

This commit is contained in:
IonutParau 2026-02-06 20:35:12 +01:00
parent e33a011549
commit 5936c6952d
6 changed files with 1338 additions and 68 deletions

465
rewrite/luaarch.c Normal file
View File

@ -0,0 +1,465 @@
#include "neonucleus.h"
#include <lualib.h>
#include <stdlib.h>
#include <stdio.h>
#include <lua.h>
#include <lauxlib.h>
#include <string.h>
// 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,
};
}

138
rewrite/machine.lua Normal file
View File

@ -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

View File

@ -1,9 +1,18 @@
// The main file of the test emulator // The main file of the test emulator
// This is not a serious emulator intended for practical use, // This is not a serious emulator intended for practical use,
// it is simply just to test stuff and showcase the API. // it is simply just to test stuff and showcase the API.
// Error handling has been omitted in most places.
#include "neonucleus.h" #include "neonucleus.h"
#include <stdio.h> #include <stdio.h>
#include <string.h>
nn_Architecture getLuaArch();
static const char minBIOS[] = {
#embed "minBIOS.lua"
,'\0'
};
static nn_Exit sandbox_handler(nn_ComponentRequest *req) { static nn_Exit sandbox_handler(nn_ComponentRequest *req) {
nn_Computer *c = req->computer; nn_Computer *c = req->computer;
@ -29,24 +38,6 @@ static nn_Exit sandbox_handler(nn_ComponentRequest *req) {
return NN_OK; 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() { int main() {
nn_Context ctx; nn_Context ctx;
nn_initContext(&ctx); nn_initContext(&ctx);
@ -54,30 +45,81 @@ int main() {
// create the universe // create the universe
nn_Universe *u = nn_createUniverse(&ctx); nn_Universe *u = nn_createUniverse(&ctx);
nn_Architecture arch = { nn_Architecture arch = getLuaArch();
.name = "Sandbox test",
.state = NULL,
.handler = sandbox_arch,
};
nn_ComponentMethod sandboxMethods[] = { nn_Method sandboxMethods[] = {
{"log", "log(msg: string) - Log to stdout", true}, {"log", "log(msg: string) - Log to stdout", true},
{NULL}, {NULL},
}; };
nn_ComponentType *ctype = nn_createComponentType(u, "sandbox", NULL, sandboxMethods, sandbox_handler); 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_Computer *c = nn_createComputer(u, NULL, "computer0", 8 * NN_MiB, 256, 256);
nn_setArchitecture(c, &arch); nn_setArchitecture(c, &arch);
nn_addSupportedArchitecture(c, &arch);
nn_addComponent(c, ctype, "sandbox", -1, NULL); nn_addComponent(c, ctype, "sandbox", -1, NULL);
nn_addComponent(c, etype, "eeprom", 0, etype);
printf("Component added: %s\n", nn_hasComponent(c, "sandbox") ? "TRUE" : "FALSE"); while(true) {
printf("Method active: %s\n", nn_hasMethod(c, "sandbox", "log") ? "TRUE" : "FALSE"); nn_Exit e = nn_tick(c);
printf("Incorrect method active: %s\n", nn_hasMethod(c, "sandbox", "notlog") ? "TRUE" : "FALSE"); if(e != NN_OK) {
nn_setErrorFromExit(c, e);
printf("error: %s\n", nn_getError(c));
goto cleanup;
}
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_destroyComputer(c);
nn_destroyComponentType(ctype); nn_destroyComponentType(ctype);
nn_destroyComponentType(etype);
// rip the universe // rip the universe
nn_destroyUniverse(u); nn_destroyUniverse(u);
return 0; return 0;

102
rewrite/minBIOS.lua Normal file
View File

@ -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")

View File

@ -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; 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) { int nn_strcmp(const char *a, const char *b) {
size_t i = 0; size_t i = 0;
while(1) { while(1) {
@ -430,7 +535,7 @@ typedef struct nn_ComponentType {
const char *name; const char *name;
nn_ComponentHandler *handler; nn_ComponentHandler *handler;
// NULL-terminated // NULL-terminated
nn_ComponentMethod *methods; nn_Method *methods;
size_t methodCount; size_t methodCount;
} nn_ComponentType; } nn_ComponentType;
@ -458,6 +563,17 @@ typedef enum nn_ValueType {
NN_VAL_TABLE, NN_VAL_TABLE,
} nn_ValueType; } 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 { typedef struct nn_String {
nn_Context ctx; nn_Context ctx;
size_t refc; size_t refc;
@ -493,6 +609,7 @@ typedef struct nn_Computer {
nn_Universe *universe; nn_Universe *universe;
void *userdata; void *userdata;
char *address; char *address;
char *tmpaddress;
void *archState; void *archState;
nn_Architecture arch; nn_Architecture arch;
nn_Architecture desiredArch; nn_Architecture desiredArch;
@ -511,10 +628,12 @@ typedef struct nn_Computer {
size_t stackSize; size_t stackSize;
size_t archCount; size_t archCount;
size_t signalCount; size_t signalCount;
size_t userCount;
nn_Value callstack[NN_MAX_STACK]; nn_Value callstack[NN_MAX_STACK];
char errorBuffer[NN_MAX_ERROR_SIZE]; char errorBuffer[NN_MAX_ERROR_SIZE];
nn_Architecture archs[NN_MAX_ARCHITECTURES]; nn_Architecture archs[NN_MAX_ARCHITECTURES];
nn_Signal signals[NN_MAX_SIGNALS]; nn_Signal signals[NN_MAX_SIGNALS];
char *users[NN_MAX_USERS];
} nn_Computer; } nn_Computer;
nn_Universe *nn_createUniverse(nn_Context *ctx) { 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_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_Context *ctx = &universe->ctx;
nn_ComponentType *ctype = nn_alloc(ctx, sizeof(nn_ComponentType)); 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; size_t methodCount = 0;
while(methods[methodCount].name != NULL) methodCount++; 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; if(methodscpy == NULL) goto fail;
ctype->methods = methodscpy; ctype->methods = methodscpy;
ctype->methodCount = methodCount; ctype->methodCount = methodCount;
for(size_t i = 0; i < methodCount; i++) { for(size_t i = 0; i < methodCount; i++) {
nn_ComponentMethod cpy; nn_Method cpy;
cpy.direct = methods[i].direct; cpy.flags = methods[i].flags;
cpy.name = nn_arstrdup(arena, methods[i].name); cpy.name = nn_arstrdup(arena, methods[i].name);
if(cpy.name == NULL) goto fail; if(cpy.name == NULL) goto fail;
cpy.docString = nn_arstrdup(arena, methods[i].docString); 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; return NULL;
} }
c->tmpaddress = NULL;
c->arch.name = NULL; c->arch.name = NULL;
c->desiredArch.name = NULL; c->desiredArch.name = NULL;
c->archState = NULL; c->archState = NULL;
@ -637,6 +758,7 @@ nn_Computer *nn_createComputer(nn_Universe *universe, void *userdata, const char
c->stackSize = 0; c->stackSize = 0;
c->archCount = 0; c->archCount = 0;
c->signalCount = 0; c->signalCount = 0;
c->userCount = 0;
// set to empty string // set to empty string
c->errorBuffer[0] = '\0'; c->errorBuffer[0] = '\0';
return c; 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]); for(size_t j = 0; j < s.len; j++) nn_dropValue(s.values[j]);
nn_free(ctx, s.values, sizeof(nn_Value) * s.len); 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->components, sizeof(nn_Component) * computer->componentCap);
nn_free(ctx, computer->deviceInfo, sizeof(nn_DeviceInfo) * computer->deviceInfoCap); 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_strfree(ctx, computer->address);
nn_free(ctx, computer, sizeof(nn_Computer)); nn_free(ctx, computer, sizeof(nn_Computer));
} }
@ -683,6 +809,63 @@ const char *nn_getComputerAddress(nn_Computer *computer) {
return computer->address; 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) { void nn_setArchitecture(nn_Computer *computer, const nn_Architecture *arch) {
computer->arch = *arch; computer->arch = *arch;
} }
@ -705,7 +888,7 @@ nn_Exit nn_addSupportedArchitecture(nn_Computer *computer, const nn_Architecture
return NN_OK; 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; *len = computer->archCount;
return computer->archs; return computer->archs;
} }
@ -796,7 +979,7 @@ nn_ComputerState nn_getComputerState(nn_Computer *computer) {
return computer->state; return computer->state;
} }
static void nn_setErrorFromExit(nn_Computer *computer, nn_Exit exit) { void nn_setErrorFromExit(nn_Computer *computer, nn_Exit exit) {
switch(exit) { switch(exit) {
case NN_OK: case NN_OK:
return; // no error return; // no error
@ -1007,7 +1190,7 @@ int nn_getComponentSlot(nn_Computer *computer, const char *address) {
return 0; 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++) { for(size_t i = 0; i < computer->componentLen; i++) {
nn_Component *c = &computer->components[i]; nn_Component *c = &computer->components[i];
if(nn_strcmp(c->address, address) == 0) { 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; 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) { static void nn_retainValue(nn_Value val) {
switch(val.type) { switch(val.type) {
case NN_VAL_NULL: 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); nn_Exit err = c.ctype->handler(&req);
if(err) { if(err) {
if(err != NN_EBADCALL) nn_setErrorFromExit(computer, err); if(err != NN_EBADCALL) nn_setErrorFromExit(computer, err);
// clear junk
nn_clearstack(computer);
return err; return err;
} }
@ -1253,35 +1457,40 @@ void nn_clearstack(nn_Computer *computer) {
} }
bool nn_isnull(nn_Computer *computer, size_t idx) { 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; return computer->callstack[idx].type == NN_VAL_NULL;
} }
bool nn_isboolean(nn_Computer *computer, size_t idx) { 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; return computer->callstack[idx].type == NN_VAL_BOOL;
} }
bool nn_isnumber(nn_Computer *computer, size_t idx) { 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; return computer->callstack[idx].type == NN_VAL_NUM;
} }
bool nn_isstring(nn_Computer *computer, size_t idx) { 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; return computer->callstack[idx].type == NN_VAL_STR;
} }
bool nn_isuserdata(nn_Computer *computer, size_t idx) { 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; return computer->callstack[idx].type == NN_VAL_USERDATA;
} }
bool nn_istable(nn_Computer *computer, size_t idx) { 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; 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) { bool nn_toboolean(nn_Computer *computer, size_t idx) {
return computer->callstack[idx].boolean; return computer->callstack[idx].boolean;
} }
@ -1430,9 +1639,23 @@ typedef struct nn_EEPROM_state {
void *userdata; void *userdata;
} nn_EEPROM_state; } 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_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
nn_EEPROM_state *state = req->typeUserdata; nn_EEPROM_state *state = req->typeUserdata;
void *instance = req->compUserdata; void *instance = req->compUserdata;
// NULL for FREETYPE
nn_Computer *computer = req->computer; nn_Computer *computer = req->computer;
nn_Context ctx = state->universe->ctx; nn_Context ctx = state->universe->ctx;
@ -1440,6 +1663,7 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
ereq.userdata = state->userdata; ereq.userdata = state->userdata;
ereq.instance = instance; ereq.instance = instance;
ereq.computer = computer; ereq.computer = computer;
ereq.eepromConf = &state->eeprom;
const char *method = req->methodCalled; const char *method = req->methodCalled;
@ -1462,6 +1686,67 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
if(nn_strcmp(method, "getDataSize") == 0) { if(nn_strcmp(method, "getDataSize") == 0) {
return nn_pushnumber(computer, state->eeprom.dataSize); 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) { if(nn_strcmp(method, "getLabel") == 0) {
char buf[NN_MAX_LABEL]; char buf[NN_MAX_LABEL];
ereq.action = NN_EEPROM_GETLABEL; ereq.action = NN_EEPROM_GETLABEL;
@ -1518,33 +1803,106 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
req->returnCount = 1; req->returnCount = 1;
return nn_pushlstring(computer, buf, ereq.buflen); 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;
} }
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_Context ctx = universe->ctx;
nn_EEPROM_state *state = nn_alloc(&ctx, sizeof(*state)); nn_EEPROM_state *state = nn_alloc(&ctx, sizeof(*state));
if(state == NULL) return NULL; if(state == NULL) return NULL;
state->universe = universe; state->universe = universe;
state->eeprom = *eeprom; state->eeprom = *eeprom;
state->userdata = userdata; state->userdata = userdata;
const nn_ComponentMethod methods[] = { const nn_Method methods[] = {
{"getSize", "getSize(): number - Get the storage capacity of the EEPROM.", true}, {"getSize", "function(): number - Get the storage capacity of the EEPROM.", NN_DIRECT},
{"getDataSize", "getDataSize(): number - Get the storage capacity of the EEPROM data.", true}, {"getDataSize", "function(): number - Get the storage capacity of the EEPROM data.", NN_DIRECT},
{"getLabel", "getLabel(): string - Get the EEPROM label", false}, {"getLabel", "function(): string - Get the EEPROM label", NN_INDIRECT},
{"setLabel", "setLabel(label: string): string - Set the EEPROM label and return what was actually set, which may be truncated.", false}, {"setLabel", "function(label: string): string - Set the EEPROM label and return what was actually set, which may be truncated.", NN_INDIRECT},
{"get", "get(): string - Get the current EEPROM contents.", false}, {"get", "function(): string - Get the current EEPROM contents.", NN_INDIRECT},
{"getData", "getData(): string - Get the current EEPROM data contents.", false}, {"getData", "function(): string - Get the current EEPROM data contents.", NN_INDIRECT},
{"set", "set(data: string) - Set the current EEPROM contents.", false}, {"set", "function(data: string) - Set the current EEPROM contents.", NN_INDIRECT},
{"setData", "setData(data: string) - Set the current EEPROM data contents.", false}, {"setData", "function(data: string) - Set the current EEPROM data contents.", NN_INDIRECT},
{"getArchitecture", "getArchitecture(): string - Get the current EEPROM architecture intended.", false}, {"getArchitecture", "function(): string - Get the current EEPROM architecture intended.", NN_INDIRECT},
{"setArchitecture", "setArchitecture(data: string) - Set the current EEPROM architecture intended.", false}, {"setArchitecture", "function(data: string) - Set the current EEPROM architecture intended.", NN_INDIRECT},
{"isReadOnly", "isReadOnly(): boolean - Returns whether the EEPROM is read-only.", false}, {"isReadOnly", "function(): boolean - Returns whether the EEPROM is read-only.", NN_INDIRECT},
{"makeReadonly", "makeReadonly() - Makes the EEPROM read-only, this cannot be undone.", false}, {"makeReadonly", "function(checksum: string) - Makes the EEPROM read-only, this cannot be undone.", NN_INDIRECT},
{"getChecksum", "getChecksum(): string - Returns a simple checksum of the EEPROM's contents and data.", false}, {"getChecksum", "function(): string - Returns a simple checksum of the EEPROM's contents and data.", NN_INDIRECT},
{NULL, NULL, false}, {NULL, NULL, NN_INDIRECT},
}; };
nn_ComponentType *t = nn_createComponentType(universe, "eeprom", state, methods, nn_eeprom_handler); nn_ComponentType *t = nn_createComponentType(universe, "eeprom", state, methods, nn_eeprom_handler);
if(t == NULL) { if(t == NULL) {
@ -1553,3 +1911,100 @@ nn_ComponentType *nn_createEEPROM(nn_Universe *universe, nn_EEPROM *eeprom, void
} }
return t; 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;
}

View File

@ -48,10 +48,16 @@ extern "C" {
#define NN_CLOSEPORTS 0 #define NN_CLOSEPORTS 0
// maximum amount of architectures one machine can support. // maximum amount of architectures one machine can support.
#define NN_MAX_ARCHITECTURES 32 #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 // maximum amount of keyboards a screen can have
#define NN_MAX_KEYBOARDS 64 #define NN_MAX_KEYBOARDS 64
// the port used by tunnel cards. This port is invalid for modems. // the port used by tunnel cards. This port is invalid for modems.
#define NN_TUNNEL_PORT 0 #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 // the maximum size of a UTF-8 character
#define NN_MAXIMUM_UNICODE_BUFFER 4 #define NN_MAXIMUM_UNICODE_BUFFER 4
@ -93,18 +99,23 @@ typedef struct nn_LockRequest {
nn_LockAction action; nn_LockAction action;
} nn_LockRequest; } nn_LockRequest;
// intended for a plain mutex. // Intended for a plain mutex.
// This is used for synchronization. OpenComputers achieves synchronization // This is used for synchronization. OpenComputers achieves synchronization
// between the worker threads by sending them as requests to a central thread (indirect methods). // 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 // In NeoNucleus, the function pointer is invoked on the calling thead. This technically makes all methods direct,
// we consider methods to be indirect if they require locks. // 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); typedef void nn_LockProc(void *state, nn_LockRequest *req);
// The *context* NeoNucleus is operating in. // The *context* NeoNucleus is operating in.
// This determines: // This determines:
// - How memory is allocated // - How memory is allocated
// - How random numbers are generated // - How random numbers are generated and what the range is
// - What the current time is // - What the current time is
// - How locks work
typedef struct nn_Context { typedef struct nn_Context {
void *state; void *state;
nn_AllocProc *alloc; nn_AllocProc *alloc;
@ -221,6 +232,29 @@ void nn_destroyComputer(nn_Computer *computer);
void *nn_getComputerUserdata(nn_Computer *computer); void *nn_getComputerUserdata(nn_Computer *computer);
const char *nn_getComputerAddress(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. // Sets the computer's architecture.
// The architecture determines everything from how the computer runs, to how it turns off. // The architecture determines everything from how the computer runs, to how it turns off.
// Everything is limited by the architecture. // 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. // The architecture is copied, it can be freed after this is called.
nn_Exit nn_addSupportedArchitecture(nn_Computer *computer, const nn_Architecture *arch); nn_Exit nn_addSupportedArchitecture(nn_Computer *computer, const nn_Architecture *arch);
// Returns the array of supported architectures, as well as the length. // 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. // 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. // If the architecture is not found, it returns one with a NULL name.
nn_Architecture nn_findSupportedArchitecture(nn_Computer *computer, const char *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 // 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); 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. // 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. // 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); 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. // gets the device info array.
const nn_DeviceInfo *nn_getDeviceInfo(nn_Computer *computer, size_t *len); 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 *name;
const char *docString; const char *docString;
bool direct; nn_MethodFlags flags;
} nn_ComponentMethod; } nn_Method;
typedef struct nn_ComponentType nn_ComponentType; typedef struct nn_ComponentType nn_ComponentType;
@ -344,7 +394,7 @@ typedef struct nn_ComponentRequest {
typedef nn_Exit nn_ComponentHandler(nn_ComponentRequest *req); typedef nn_Exit nn_ComponentHandler(nn_ComponentRequest *req);
// Creates a new component type. It is safe to free name and methods afterwards. // 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. // 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. // The component type is still used one last time for the destructor of the components.
void nn_destroyComponentType(nn_ComponentType *ctype); 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. // Gets the slot of a component.
int nn_getComponentSlot(nn_Computer *computer, const char *address); 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. // 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. // get the address at a certain index.
// It'll return NULL for out of bounds indexes. // It'll return NULL for out of bounds indexes.
// This can be used to iterate over all components. // This can be used to iterate over all components.
const char *nn_getComponentAddress(nn_Computer *computer, size_t idx); 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. // this uses the call stack.
// Component calls must not call other components, it just doesn't work. // 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. // Returns whether the value at [idx] is a table.
// [idx] starts at 0. // [idx] starts at 0.
bool nn_istable(nn_Computer *computer, size_t idx); 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. // 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; void *instance;
// the computer making the request // the computer making the request
nn_Computer *computer; nn_Computer *computer;
const struct nn_EEPROM *eepromConf;
nn_EEPROMAction action; nn_EEPROMAction action;
// all the get* options should set this to the length, // all the get* options should set this to the length,
// and its initial value is the capacity of [buf]. // and its initial value is the capacity of [buf].
@ -572,9 +628,21 @@ typedef struct nn_EEPROM {
nn_Exit (*handler)(nn_EEPROMRequest *request); nn_Exit (*handler)(nn_EEPROMRequest *request);
} nn_EEPROM; } 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 // the userdata passed to the component is the userdata
// in the handler // 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 #ifdef __cplusplus
} }