diff --git a/build.zig b/build.zig index cd7809d..75d2544 100644 --- a/build.zig +++ b/build.zig @@ -12,6 +12,8 @@ fn addEngineSources(c: *std.Build.Step.Compile) void { "src/component.c", "src/computer.c", "src/universe.c", + // components + "src/components/eeprom.c", }, }); } diff --git a/src/components/eeprom.c b/src/components/eeprom.c new file mode 100644 index 0000000..16d83f5 --- /dev/null +++ b/src/components/eeprom.c @@ -0,0 +1,135 @@ +#include "../neonucleus.h" + +void nn_eeprom_destroy(void *_, nn_component *component, nn_eeprom *eeprom) { + if(atomic_fetch_sub(&eeprom->refc, 1) > 1) return; + + if(eeprom->deinit == NULL) { + eeprom->deinit(component, eeprom); + } +} + +void nn_eeprom_getSize(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) { + nn_return(computer, nn_values_integer(eeprom->getSize(component, eeprom->userdata))); +} + +void nn_eeprom_getDataSize(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) { + nn_return(computer, nn_values_integer(eeprom->getDataSize(component, eeprom->userdata))); +} + +void nn_eeprom_getLabel(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) { + char buf[NN_LABEL_SIZE]; + size_t l = NN_LABEL_SIZE; + eeprom->getLabel(component, eeprom->userdata, buf, &l); + if(l == 0) { + nn_return(computer, nn_values_nil()); + } else { + nn_return(computer, nn_values_string(buf, l)); + } +} + +void nn_eeprom_setLabel(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) { + size_t l = 0; + nn_value label = nn_getArgument(computer, 0); + const char *buf = nn_toString(label, &l); + if(buf == NULL) { + nn_setCError(computer, "bad label (string expected)"); + return; + } + l = eeprom->setLabel(component, eeprom->userdata, buf, l); + nn_return(computer, nn_values_string(buf, l)); +} + +void nn_eeprom_get(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) { + size_t cap = eeprom->getSize(component, eeprom->userdata); + char *buf = nn_malloc(cap); + if(buf == NULL) { + nn_setCError(computer, "out of memory"); + return; + } + size_t len = eeprom->get(component, eeprom->userdata, buf); + nn_return(computer, nn_values_string(buf, len)); + nn_free(buf); +} + +void nn_eeprom_set(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) { + nn_value data = nn_getArgument(computer, 0); + size_t len; + const char *buf = nn_toString(data, &len); + if(len > eeprom->getSize(component, eeprom->userdata)) { + + } + eeprom->set(component, eeprom->userdata, buf, len); +} + +void nn_eeprom_getData(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) { + size_t cap = eeprom->getDataSize(component, eeprom->userdata); + char *buf = nn_malloc(cap); + if(buf == NULL) { + nn_setCError(computer, "out of memory"); + return; + } + int len = eeprom->getData(component, eeprom->userdata, buf); + if(len < 0) { + nn_return(computer, nn_values_nil()); + } else { + nn_return(computer, nn_values_string(buf, len)); + } + nn_free(buf); +} + +void nn_eeprom_setData(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) { + nn_value data = nn_getArgument(computer, 0); + size_t len; + const char *buf = nn_toString(data, &len); + eeprom->setData(component, eeprom->userdata, buf, len); +} + +void nn_eeprom_isReadOnly(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) { + nn_return(computer, nn_values_boolean(eeprom->isReadonly(component, eeprom->userdata))); +} + +void nn_eeprom_makeReadonly(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) { + eeprom->makeReadonly(component, eeprom->userdata); +} + +// TODO: make good +void nn_eeprom_getChecksum(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) { + size_t cap = eeprom->getDataSize(component, eeprom->userdata); + char *buf = nn_malloc(cap); + if(buf == NULL) { + nn_setCError(computer, "out of memory"); + return; + } + size_t len = eeprom->getData(component, eeprom->userdata, buf); + size_t sum = 0; + for(size_t i = 0; i < len; i++) { + sum += buf[i]; + } + nn_free(buf); + + nn_return(computer, nn_values_string((void *)&sum, sizeof(sum))); +} + +void nn_loadEepromTable(nn_universe *universe) { + nn_componentTable *eepromTable = nn_newComponentTable("eeprom", NULL, NULL, (void *)nn_eeprom_destroy); + nn_storeUserdata(universe, "NN:EEPROM", eepromTable); + + nn_defineMethod(eepromTable, "getSize", true, (void *)nn_eeprom_getSize, NULL, "getSize(): integer - Returns the maximum code capacity of the EEPROM."); + nn_defineMethod(eepromTable, "getDataSize", true, (void *)nn_eeprom_getDataSize, NULL, "getDataSize(): integer - Returns the maximum data capacity of the EEPROM."); + nn_defineMethod(eepromTable, "getLabel", false, (void *)nn_eeprom_getLabel, NULL, "getLabel(): string - Returns the current label."); + nn_defineMethod(eepromTable, "setLabel", false, (void *)nn_eeprom_setLabel, NULL, "setLabel(label: string): string - Sets the new label. Returns the actual label set to, which may be truncated."); + nn_defineMethod(eepromTable, "get", false, (void *)nn_eeprom_get, NULL, "get(): string - Reads the current code contents."); + nn_defineMethod(eepromTable, "set", false, (void *)nn_eeprom_set, NULL, "set(data: string) - Sets the current code contents."); + nn_defineMethod(eepromTable, "getData", false, (void *)nn_eeprom_getData, NULL, "getData(): string - Reads the current data contents."); + nn_defineMethod(eepromTable, "setData", false, (void *)nn_eeprom_setData, NULL, "setData(data: string) - Sets the current data contents."); + nn_defineMethod(eepromTable, "isReadOnly", false, (void *)nn_eeprom_isReadOnly, NULL, "isReadOnly(): boolean - Returns whether this EEPROM is read-only."); + nn_defineMethod(eepromTable, "makeReadOnly", false, (void *)nn_eeprom_makeReadonly, NULL, "makeReadOnly() - Makes the current EEPROM read-only. Normally, this cannot be undone."); + nn_defineMethod(eepromTable, "makeReadonly", false, (void *)nn_eeprom_makeReadonly, NULL, "makeReadonly() - Legacy alias to makeReadOnly()"); + nn_defineMethod(eepromTable, "getChecksum", false, (void *)nn_eeprom_getChecksum, NULL, "getChecksum(): string - Returns a checksum of the data on the EEPROM."); +} + +nn_component *nn_addEeprom(nn_computer *computer, nn_address address, int slot, nn_eeprom *eeprom) { + nn_componentTable *eepromTable = nn_queryUserdata(nn_getUniverse(computer), "NN:EEPROM"); + + return nn_newComponent(computer, address, slot, eepromTable, eeprom); +} diff --git a/src/computer.c b/src/computer.c index 06e48dd..7313faf 100644 --- a/src/computer.c +++ b/src/computer.c @@ -44,6 +44,8 @@ nn_computer *nn_newComputer(nn_universe *universe, nn_address address, nn_archit c->temperature = 30; c->roomTemperature = 30; c->temperatureCoefficient = 1; + c->callCost = 0; + c->callBudget = 256; // Setup Architecture c->archState = c->arch->setup(c, c->arch->userdata); @@ -58,6 +60,10 @@ nn_computer *nn_newComputer(nn_universe *universe, nn_address address, nn_archit return c; } +nn_universe *nn_getUniverse(nn_computer *computer) { + return computer->universe; +} + void nn_setTmpAddress(nn_computer *computer, nn_address tmp) { nn_free(computer->tmpAddress); computer->tmpAddress = nn_strdup(tmp); @@ -72,6 +78,7 @@ nn_address nn_getTmpAddress(nn_computer *computer) { } int nn_tickComputer(nn_computer *computer) { + computer->callCost = 0; computer->state = NN_STATE_RUNNING; nn_clearError(computer); computer->arch->tick(computer, computer->archState, computer->arch->userdata); @@ -206,6 +213,26 @@ bool nn_isUser(nn_computer *computer, const char *name) { return false; } +void nn_setCallBudget(nn_computer *computer, size_t callBudget) { + computer->callBudget = callBudget; +} + +size_t nn_getCallBudget(nn_computer *computer) { + return computer->callBudget; +} + +void nn_callCost(nn_computer *computer, size_t cost) { + computer->callCost += cost; +} + +size_t nn_getCallCost(nn_computer *computer) { + return computer->callCost; +} + +bool nn_isOverworked(nn_computer *computer) { + return computer->callCost >= computer->callBudget; +} + int nn_getState(nn_computer *computer) { return computer->state; } @@ -335,9 +362,9 @@ nn_component *nn_newComponent(nn_computer *computer, nn_address address, int slo c->slot = slot; c->computer = computer; if(table->constructor == NULL) { - c->statePtr = NULL; + c->statePtr = userdata; } else { - c->statePtr = table->constructor(table->userdata); + c->statePtr = table->constructor(table->userdata, userdata); } return c; } @@ -353,7 +380,7 @@ void nn_removeComponent(nn_computer *computer, nn_address address) { void nn_destroyComponent(nn_component *component) { nn_free(component->address); if(component->table->destructor != NULL) { - component->table->destructor(component->table->userdata, component->statePtr); + component->table->destructor(component->table->userdata, component, component->statePtr); } component->address = NULL; // marks component as freed } diff --git a/src/computer.h b/src/computer.h index 1e4ed8b..60fa5ba 100644 --- a/src/computer.h +++ b/src/computer.h @@ -40,6 +40,8 @@ typedef struct nn_computer { double temperature; double temperatureCoefficient; double roomTemperature; + size_t callCost; + size_t callBudget; } nn_computer; #endif diff --git a/src/emulator.c b/src/emulator.c index ee06bee..1ed238f 100644 --- a/src/emulator.c +++ b/src/emulator.c @@ -4,17 +4,61 @@ #include #include "neonucleus.h" #include "testLuaArch.h" - -void emulator_debugPrint(void *componentUserdata, void *methodUserdata, nn_component *component, nn_computer *computer) { - nn_value msg = nn_getArgument(computer, 0); - const char *m = nn_toCString(msg); - printf("[DEBUG] %s\n", m); - nn_return(computer, nn_values_integer(strlen(m))); + +size_t ne_eeprom_getSize(nn_component *component, void *_) { + return 4096; } +size_t ne_eeprom_getDataSize(nn_component *component, void *_) { + return 1024; +} + +void ne_eeprom_getLabel(nn_component *component, void *_, char *buf, size_t *buflen) { + *buflen = 0; +} + +size_t ne_eeprom_setLabel(nn_component *component, void *_, const char *buf, size_t buflen) { + return 0; +} + +const char *ne_eeprom_location(nn_address address) { + static char buffer[256]; + snprintf(buffer, 256, "data/%s", address); + return buffer; +} + +size_t ne_eeprom_get(nn_component *component, void *_, char *buf) { + FILE *f = fopen(ne_eeprom_location(nn_getComponentAddress(component)), "rb"); + fseek(f, 0, SEEK_END); + size_t len = ftell(f); + fseek(f, 0, SEEK_SET); + fread(buf, sizeof(char), len, f); + fclose(f); + return len; +} + +void ne_eeprom_set(nn_component *component, void *_, const char *buf, size_t len) { + FILE *f = fopen(ne_eeprom_location(nn_getComponentAddress(component)), "wb"); + fwrite(buf, sizeof(char), len, f); + fclose(f); +} + +int ne_eeprom_getData(nn_component *component, void *_, char *buf) { + return -1; +} + +void ne_eeprom_setData(nn_component *component, void *_, const char *buf, size_t len) {} + +bool ne_eeprom_isReadonly(nn_component *component, void *userdata) { + return false; +} + +void ne_eeprom_makeReadonly(nn_component *component, void *userdata) {} + int main() { printf("Setting up universe\n"); nn_universe *universe = nn_newUniverse(); + nn_loadCoreComponentTables(universe); nn_architecture *arch = testLuaArch_getArchitecture("src/sandbox.lua"); assert(arch != NULL && "Loading architecture failed"); @@ -24,10 +68,23 @@ int main() { nn_setEnergyInfo(computer, 5000, 5000); nn_addSupportedArchitecture(computer, arch); - nn_componentTable *t = nn_newComponentTable("debugPrint", NULL, NULL, NULL); - nn_defineMethod(t, "log", false, emulator_debugPrint, NULL, "logs stuff"); + nn_eeprom genericEEPROM = { + .userdata = NULL, + .refc = 1, + .deinit = NULL, + .getSize = ne_eeprom_getSize, + .getDataSize = ne_eeprom_getDataSize, + .getLabel = ne_eeprom_getLabel, + .setLabel = ne_eeprom_setLabel, + .get = ne_eeprom_get, + .set = ne_eeprom_set, + .getData = ne_eeprom_getData, + .setData = ne_eeprom_setData, + .isReadonly = ne_eeprom_isReadonly, + .makeReadonly = ne_eeprom_makeReadonly, + }; - nn_newComponent(computer, "debugPrint", -1, t, NULL); + nn_addEeprom(computer, "luaBios.lua", 0, &genericEEPROM); double lastTime = nn_realTime(); while(true) { diff --git a/src/lock.c b/src/lock.c index 6fc672d..c0985e1 100644 --- a/src/lock.c +++ b/src/lock.c @@ -13,13 +13,16 @@ nn_guard *nn_newGuard() { } void nn_lock(nn_guard *guard) { + if(guard == NULL) return; mtx_lock(&guard->m); } void nn_unlock(nn_guard *guard) { + if(guard == NULL) return; mtx_unlock(&guard->m); } void nn_deleteGuard(nn_guard *guard) { + if(guard == NULL) return; mtx_destroy(&guard->m); } diff --git a/src/neonucleus.h b/src/neonucleus.h index bb1a195..39b8718 100644 --- a/src/neonucleus.h +++ b/src/neonucleus.h @@ -4,6 +4,7 @@ #include #include #include +#include // Based off https://stackoverflow.com/questions/5919996/how-to-detect-reliably-mac-os-x-ios-linux-windows-in-c-preprocessor #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) @@ -58,6 +59,9 @@ #define NN_MAX_USER_SIZE 128 #define NN_MAX_SIGNAL_SIZE 8192 #define NN_OVERHEAT_MIN 100 +#define NN_CALL_HEAT 0.05 +#define NN_CALL_COST 1 +#define NN_LABEL_SIZE 128 typedef struct nn_guard nn_guard; typedef struct nn_universe nn_universe; @@ -151,6 +155,7 @@ void nn_setClock(nn_universe *universe, nn_clock_t *clock, void *userdata); double nn_getTime(nn_universe *universe); nn_computer *nn_newComputer(nn_universe *universe, nn_address address, nn_architecture *arch, void *userdata, size_t memoryLimit, size_t componentLimit); +nn_universe *nn_getUniverse(nn_computer *computer); int nn_tickComputer(nn_computer *computer); double nn_getUptime(nn_computer *computer); size_t nn_getComputerMemoryUsed(nn_computer *computer); @@ -170,6 +175,11 @@ const char *nn_addUser(nn_computer *computer, const char *name); void nn_deleteUser(nn_computer *computer, const char *name); const char *nn_indexUser(nn_computer *computer, size_t idx); bool nn_isUser(nn_computer *computer, const char *name); +void nn_setCallBudget(nn_computer *computer, size_t callBudget); +size_t nn_getCallBudget(nn_computer *computer); +void nn_callCost(nn_computer *computer, size_t cost); +size_t nn_getCallCost(nn_computer *computer); +bool nn_isOverworked(nn_computer *computer); /* The memory returned can be freed with nn_free() */ char *nn_serializeProgram(nn_computer *computer, size_t *len); @@ -259,8 +269,8 @@ nn_component **nn_listComponent(nn_computer *computer, size_t *len); // Component VTable stuff -typedef void *nn_componentConstructor(void *userdata); -typedef void *nn_componentDestructor(void *tableUserdata, void *componentUserdata); +typedef void *nn_componentConstructor(void *tableUserdata, void *componentUserdata); +typedef void *nn_componentDestructor(void *tableUserdata, nn_component *component, void *componentUserdata); typedef void nn_componentMethod(void *componentUserdata, void *methodUserdata, nn_component *component, nn_computer *computer); nn_componentTable *nn_newComponentTable(const char *typeName, void *userdata, nn_componentConstructor *constructor, nn_componentDestructor *destructor); @@ -314,4 +324,34 @@ const char *nn_toString(nn_value val, size_t *len); */ size_t nn_measurePacketSize(nn_value *vals, size_t len); +// COMPONENTS + +/* Loads the vtables for the default implementations of those components */ +void nn_loadCoreComponentTables(nn_universe *universe); + +// loading each component +void nn_loadEepromTable(nn_universe *universe); + +// the helpers + +// EEPROM +typedef struct nn_eeprom { + void *userdata; + atomic_size_t refc; + void (*deinit)(nn_component *component, void *userdata); + + // methods + size_t (*getSize)(nn_component *component, void *userdata); + size_t (*getDataSize)(nn_component *component, void *userdata); + void (*getLabel)(nn_component *component, void *userdata, char *buf, size_t *buflen); + size_t (*setLabel)(nn_component *component, void *userdata, const char *buf, size_t buflen); + size_t (*get)(nn_component *component, void *userdata, char *buf); + void (*set)(nn_component *component, void *userdata, const char *buf, size_t len); + int (*getData)(nn_component *component, void *userdata, char *buf); + void (*setData)(nn_component *component, void *userdata, const char *buf, size_t len); + bool (*isReadonly)(nn_component *component, void *userdata); + void (*makeReadonly)(nn_component *component, void *userdata); +} nn_eeprom; +nn_component *nn_addEeprom(nn_computer *computer, nn_address address, int slot, nn_eeprom *eeprom); + #endif diff --git a/src/sandbox.lua b/src/sandbox.lua index 3eb1946..5dc6f79 100644 --- a/src/sandbox.lua +++ b/src/sandbox.lua @@ -1,11 +1,35 @@ -print(component.doc("debugPrint", "log")) -print(component.invoke("debugPrint", "log", "Absolute cinema")) +-- sandbox stuff -computer.pushSignal("stuff", 123, "a", false, nil) -computer.pushSignal("stuf2", 456, "b", true, "shit") -computer.pushSignal("stuf3", 789, "c", false, -13) +local clist = component.list +function component.list(type, exact) + local m = clist() + if not type then return m end + local t = {} + for addr, kind in pairs(m) do + if exact then + if type == kind then + t[addr] = kind + end + else + if string.match(kind, type) then + t[addr] = kind + end + end + end + setmetatable(t, { + __call = function() + return next(t) + end, + }) + return t +end -print(computer.popSignal()) -print(computer.popSignal()) -print(computer.popSignal()) -print(computer.popSignal()) +for addr, kind in pairs(component.list()) do + if kind == "eeprom" then + local data = component.invoke(addr, "get") + assert(load(data, "=eeprom"))() + return + end +end + +error("no bios") diff --git a/src/testLuaArch.c b/src/testLuaArch.c index 914bef1..9a83239 100644 --- a/src/testLuaArch.c +++ b/src/testLuaArch.c @@ -254,10 +254,27 @@ static int testLuaArch_computer_popSignal(lua_State *L) { return retc; } +static int testLuaArch_computer_users(lua_State *L) { + nn_computer *c = testLuaArch_getComputer(L); + size_t i = 0; + while(true) { + const char *name = nn_indexUser(c, i); + if(name == NULL) break; + lua_pushstring(L, name); + i++; + } + return i; +} + static int testLuaArch_component_list(lua_State *L) { nn_computer *c = testLuaArch_getComputer(L); size_t len = 0; nn_component **components = nn_listComponent(c, &len); + if(components == NULL) { + lua_pushnil(L); + lua_pushstring(L, "out of memory"); + return 2; + } lua_createtable(L, 0, len); int list = lua_gettop(L); for(size_t i = 0; i < len; i++) { @@ -292,6 +309,62 @@ static int testLuaArch_component_doc(lua_State *L) { return 1; } +static int testLuaArch_component_fields(lua_State *L) { + lua_createtable(L, 0, 0); + return 1; +} + +static int testLuaArch_component_methods(lua_State *L) { + nn_computer *c = testLuaArch_getComputer(L); + const char *addr = luaL_checkstring(L, 1); + nn_component *component = nn_findComponent(c, (char *)addr); + if(component == NULL) { + lua_pushnil(L); + lua_pushstring(L, "no such component"); + return 2; + } + nn_componentTable *table = nn_getComponentTable(component); + lua_createtable(L, 0, 0); + int methods = lua_gettop(L); + + size_t i = 0; + while(true) { + bool direct = false; + const char *name = nn_getTableMethod(table, i, &direct); + if(name == NULL) break; + i++; + lua_pushboolean(L, direct); + lua_setfield(L, methods, name); + } + + return 1; +} + +static int testLuaArch_component_slot(lua_State *L) { + nn_computer *c = testLuaArch_getComputer(L); + const char *addr = luaL_checkstring(L, 1); + nn_component *component = nn_findComponent(c, (char *)addr); + if(component == NULL) { + lua_pushnil(L); + lua_pushstring(L, "no such component"); + return 2; + } + lua_pushinteger(L, nn_getComponentSlot(component)); + return 1; +} + +static int testLuaArch_component_type(lua_State *L) { + nn_computer *c = testLuaArch_getComputer(L); + const char *addr = luaL_checkstring(L, 1); + nn_component *component = nn_findComponent(c, (char *)addr); + if(component == NULL) { + lua_pushnil(L); + lua_pushstring(L, "no such component"); + return 2; + } + lua_pushstring(L, nn_getComponentType(nn_getComponentTable(component))); + return 1; +} static int testLuaArch_component_invoke(lua_State *L) { nn_computer *c = testLuaArch_getComputer(L); @@ -314,6 +387,10 @@ static int testLuaArch_component_invoke(lua_State *L) { lua_pushstring(L, "no such method"); return 2; } + if(nn_getError(c) != NULL) { + nn_resetCall(c); + luaL_error(L, "%s", nn_getError(c)); + } size_t retc = nn_getReturnCount(c); for(size_t i = 0; i < retc; i++) { testLuaArch_pushValue(L, nn_getReturn(c, i)); @@ -359,6 +436,8 @@ void testLuaArch_loadEnv(lua_State *L) { lua_setfield(L, computer, "pushSignal"); lua_pushcfunction(L, testLuaArch_computer_popSignal); lua_setfield(L, computer, "popSignal"); + lua_pushcfunction(L, testLuaArch_computer_users); + lua_setfield(L, computer, "users"); lua_setglobal(L, "computer"); lua_createtable(L, 0, 10); @@ -367,8 +446,16 @@ void testLuaArch_loadEnv(lua_State *L) { lua_setfield(L, component, "list"); lua_pushcfunction(L, testLuaArch_component_doc); lua_setfield(L, component, "doc"); + lua_pushcfunction(L, testLuaArch_component_fields); + lua_setfield(L, component, "fields"); + lua_pushcfunction(L, testLuaArch_component_methods); + lua_setfield(L, component, "methods"); lua_pushcfunction(L, testLuaArch_component_invoke); lua_setfield(L, component, "invoke"); + lua_pushcfunction(L, testLuaArch_component_slot); + lua_setfield(L, component, "slot"); + lua_pushcfunction(L, testLuaArch_component_type); + lua_setfield(L, component, "type"); lua_setglobal(L, "component"); } diff --git a/src/universe.c b/src/universe.c index 12384de..d8cfb4b 100644 --- a/src/universe.c +++ b/src/universe.c @@ -43,3 +43,7 @@ void nn_storeUserdata(nn_universe *universe, const char *name, void *data) { double nn_getTime(nn_universe *universe) { return universe->currentClock(universe->clockUserdata); } + +void nn_loadCoreComponentTables(nn_universe *universe) { + nn_loadEepromTable(universe); +}