diff --git a/TODO.md b/TODO.md index 72199fa..4501157 100644 --- a/TODO.md +++ b/TODO.md @@ -2,7 +2,6 @@ - make `computer` component use callbacks - finish tmpfs (rework the whole thing) -- device info - userdata support # To re-evaluate diff --git a/src/luaarch.c b/src/luaarch.c index a4f8e21..07111ba 100644 --- a/src/luaarch.c +++ b/src/luaarch.c @@ -299,6 +299,27 @@ fail: return 0; } +static int luaArch_computer_getDeviceInfo(lua_State *L) { + luaArch *arch = luaArch_from(L); + nn_Computer *C = arch->computer; + lua_createtable(L, 0, 0); + for(size_t i = 0;; i++) { + const char *devAddr = nn_deviceInfoAt(C, i); + if(devAddr == NULL) break; + size_t len; + const nn_DeviceField *fields = nn_getDeviceInfo(C, i, &len); + lua_pushstring(L, devAddr); + lua_createtable(L, 0, len); + for(size_t j = 0; j < len; j++) { + lua_pushstring(L, fields[j].name); + lua_pushstring(L, fields[j].value); + lua_settable(L, -3); + } + lua_settable(L, -3); + } + return 1; +} + static int luaArch_component_list(lua_State *L) { luaArch *arch = luaArch_from(L); lua_createtable(L, 64, 0); @@ -613,6 +634,8 @@ static void luaArch_loadEnv(lua_State *L) { lua_setfield(L, computer, "pushSignal"); lua_pushcfunction(L, luaArch_computer_popSignal); lua_setfield(L, computer, "popSignal"); + lua_pushcfunction(L, luaArch_computer_getDeviceInfo); + lua_setfield(L, computer, "getDeviceInfo"); lua_setglobal(L, "computer"); lua_createtable(L, 0, 10); int component = lua_gettop(L); diff --git a/src/main.c b/src/main.c index 5b7eb93..a7d003b 100644 --- a/src/main.c +++ b/src/main.c @@ -514,6 +514,18 @@ int main(int argc, char **argv) { nn_setTmpAddress(c, nn_getComponentAddress(tmpfs)); + nn_CommonDeviceInfo cinfo; + nn_clearCommonDeviceInfo(&cinfo); + cinfo.CLASS = NN_DEVICECLASS_SYSTEM; + cinfo.DESC = "The main computer"; + cinfo.VENDOR = "NeoNucleus Inc."; + cinfo.PRODUCT = "NeoComputer"; + cinfo.VERSION = "0.-1.0"; + cinfo.CLOCK = "1 MHz"; + cinfo.CAPACITY = "10 kJ"; + + nn_addCommonDeviceInfo(c, nn_getComputerAddress(c), cinfo); + nn_mountComponent(c, screen, -1, false); nn_mountComponent(c, ocelotCard, -1, false); nn_mountComponent(c, tmpfs, -1, false); diff --git a/src/neonucleus.c b/src/neonucleus.c index ee65ae4..bcb636b 100644 --- a/src/neonucleus.c +++ b/src/neonucleus.c @@ -1030,6 +1030,40 @@ typedef struct nn_Signal { nn_Value *values; } nn_Signal; +typedef struct nn_DeviceInfoEntry { + const char *address; + nn_Arena arena; + size_t len; + nn_DeviceField *fields; +} nn_DeviceInfoEntry; + +typedef struct nn_DeviceInfoArray { + nn_DeviceInfoEntry *entries; + size_t len; + size_t cap; + size_t limit; +} nn_DeviceInfoArray; + +nn_Exit nn_resizeDeviceInfoArray(nn_Context *ctx, nn_DeviceInfoArray *arr, size_t capNeeded) { + if(capNeeded > arr->limit) return NN_ELIMIT; + size_t newCap = arr->cap; + // enlarging + while(capNeeded > newCap) { + if(newCap == 0) newCap = 1; + else newCap *= 2; + } + // shrinking + while(capNeeded <= newCap/2 && newCap > 0) { + newCap /= 2; + } + if(newCap == arr->cap) return NN_OK; + nn_DeviceInfoEntry *newEntries = nn_realloc(ctx, arr->entries, sizeof(nn_DeviceInfoEntry) * arr->cap, sizeof(nn_DeviceInfoEntry) * newCap); + if(newEntries == NULL) return NN_ENOMEM; + arr->entries = newEntries; + arr->cap = newCap; + return NN_OK; +} + typedef struct nn_Computer { nn_ComputerState state; nn_Universe *universe; @@ -1044,6 +1078,7 @@ typedef struct nn_Computer { size_t callBudget; size_t totalCallBudget; nn_HashMap components; + nn_DeviceInfoArray deviceInfo; double totalEnergy; size_t totalMemory; double creationTimestamp; @@ -1169,6 +1204,11 @@ nn_Computer *nn_createComputer(nn_Universe *universe, void *userdata, const char return NULL; } + c->deviceInfo.entries = NULL; + c->deviceInfo.len = 0; + c->deviceInfo.cap = 0; + c->deviceInfo.limit = maxDevices; + c->totalEnergy = 500; c->env.handler = nn_default_envHandler; c->totalMemory = totalMemory; @@ -1264,6 +1304,97 @@ void nn_setComputerEnvironment(nn_Computer *computer, nn_Environment env) { computer->env = env; } +const char *nn_deviceInfoAt(nn_Computer *computer, size_t idx) { + return idx < computer->deviceInfo.len ? computer->deviceInfo.entries[idx].address : NULL; +} + +const nn_DeviceField *nn_getDeviceInfo(nn_Computer *computer, size_t idx, size_t *fieldCount) { + nn_DeviceInfoEntry entry = computer->deviceInfo.entries[idx]; + *fieldCount = entry.len; + return entry.fields; +} + +nn_Exit nn_addDeviceInfo(nn_Computer *computer, const char *addr, const nn_DeviceField *fields) { + size_t len = 0; + while(fields[len].name != NULL) len++; + return nn_addDeviceInfoL(computer, addr, fields, len); +} + +nn_Exit nn_addDeviceInfoL(nn_Computer *computer, const char *addr, const nn_DeviceField *fields, size_t fieldCount) { + // We remove NULL values for simplicity in some emulators + size_t len = 0; + for(size_t i = 0; i < fieldCount; i++) { + if(fields[i].value != NULL) len++; + } + nn_Context *ctx = &computer->universe->ctx; + + nn_Arena arena; + nn_arinit(&arena, ctx); + nn_Exit e = nn_resizeDeviceInfoArray(ctx, &computer->deviceInfo, computer->deviceInfo.len + 1); + if(e) goto fail; + + nn_DeviceInfoEntry entry; + entry.len = len; + entry.address = nn_arstrdup(&arena, addr); + if(entry.address == NULL) goto fail; + entry.fields = nn_aralloc(&arena, sizeof(nn_DeviceField) * len); + if(entry.fields == NULL) goto fail; + + size_t j = 0; + for(size_t i = 0; i < fieldCount; i++) { + nn_DeviceField f = fields[i]; + if(f.value == NULL) continue; + f.name = nn_arstrdup(&arena, f.name); + if(f.name == NULL) goto fail; + f.value = nn_arstrdup(&arena, f.value); + if(f.value == NULL) goto fail; + entry.fields[j] = f; + j++; + } + + entry.arena = arena; + computer->deviceInfo.entries[computer->deviceInfo.len] = entry; + computer->deviceInfo.len++; + return NN_OK; +fail: + nn_ardestroy(&arena); + return e; +} + +nn_Exit nn_addCommonDeviceInfo(nn_Computer *computer, const char *addr, nn_CommonDeviceInfo info) { + // NULL value is ignored by addDeviceInfoL + nn_DeviceField fields[] = { + {NN_DEVICEATTR_CLASS, info.CLASS}, + {NN_DEVICEATTR_DESC, info.DESC}, + {NN_DEVICEATTR_VENDOR, info.VENDOR}, + {NN_DEVICEATTR_PRODUCT, info.PRODUCT}, + {NN_DEVICEATTR_VERSION, info.VERSION}, + {NN_DEVICEATTR_SERIAL, info.SERIAL}, + {NN_DEVICEATTR_CAPACITY, info.CAPACITY}, + {NN_DEVICEATTR_SIZE, info.SIZE}, + {NN_DEVICEATTR_CLOCK, info.CLOCK}, + {NN_DEVICEATTR_WIDTH, info.WIDTH}, + {NULL, NULL}, + }; + return nn_addDeviceInfo(computer, addr, fields); +} + +// Sets every field to NULL. +void nn_clearCommonDeviceInfo(nn_CommonDeviceInfo *info) { + info->CLASS = NULL; + info->DESC = NULL; + info->VENDOR = NULL; + info->PRODUCT = NULL; + info->VERSION = NULL; + info->SERIAL = NULL; + info->CAPACITY = NULL; + info->SIZE = NULL; + info->CLOCK = NULL; + info->WIDTH = NULL; +} + +bool nn_removeDeviceInfo(nn_Computer *computer, const char *addr); + void nn_beepComputer(nn_Computer *computer, nn_Beep beep) { if(beep.duration < 0) beep.duration = 0; nn_EnvironmentRequest req; @@ -1289,6 +1420,12 @@ void nn_destroyComputer(nn_Computer *computer) { nn_signalComponent(c->comp, computer, NN_CSIGUNMOUNTED); nn_dropComponent(c->comp); } + + for(size_t i = 0; i < computer->deviceInfo.len; i++) { + nn_ardestroy(&computer->deviceInfo.entries[i].arena); + } + nn_free(ctx, computer->deviceInfo.entries, sizeof(nn_DeviceInfoEntry) * computer->deviceInfo.cap); + nn_destroyLock(ctx, computer->lock); nn_hashDeinit(&computer->components); if(computer->tmpaddress != NULL) nn_strfree(ctx, computer->tmpaddress); diff --git a/src/neonucleus.h b/src/neonucleus.h index e6497f1..830d476 100644 --- a/src/neonucleus.h +++ b/src/neonucleus.h @@ -455,6 +455,69 @@ void nn_forceCrashComputer(nn_Computer *computer, const char *s); bool nn_isComputerOn(nn_Computer *computer); void nn_setComputerEnvironment(nn_Computer *computer, nn_Environment env); +// Device information + +// Standard device attribute fields + +#define NN_DEVICEATTR_CLASS "class" +#define NN_DEVICEATTR_DESC "description" +#define NN_DEVICEATTR_VENDOR "vendor" +#define NN_DEVICEATTR_PRODUCT "product" +#define NN_DEVICEATTR_VERSION "version" +#define NN_DEVICEATTR_SERIAL "serial" +#define NN_DEVICEATTR_CAPACITY "capacity" +#define NN_DEVICEATTR_SIZE "size" +#define NN_DEVICEATTR_CLOCK "clock" +#define NN_DEVICEATTR_WIDTH "width" + +// Standard device classes + +#define NN_DEVICECLASS_SYSTEM "system" +#define NN_DEVICECLASS_BRIDGE "bridge" +#define NN_DEVICECLASS_MEMORY "memory" +#define NN_DEVICECLASS_PROCESSOR "processor" +#define NN_DEVICECLASS_ADDRESS "address" +#define NN_DEVICECLASS_STORAGE "storage" +#define NN_DEVICECLASS_DISK "disk" +#define NN_DEVICECLASS_TAPE "tape" +#define NN_DEVICECLASS_BUS "bus" +#define NN_DEVICECLASS_NETWORK "network" +#define NN_DEVICECLASS_DISPLAY "display" +#define NN_DEVICECLASS_INPUT "input" +#define NN_DEVICECLASS_PRINTER "printer" +#define NN_DEVICECLASS_MULTIMEDIA "multimedia" +#define NN_DEVICECLASS_COMMUNICATION "communication" +#define NN_DEVICECLASS_POWER "power" +#define NN_DEVICECLASS_VOLUME "volume" +#define NN_DEVICECLASS_GENERIC "generic" + +typedef struct nn_DeviceField { + const char *name; + const char *value; +} nn_DeviceField; + +typedef struct nn_CommonDeviceInfo { + const char *CLASS; + const char *DESC; + const char *VENDOR; + const char *PRODUCT; + const char *VERSION; + const char *SERIAL; + const char *CAPACITY; + const char *SIZE; + const char *CLOCK; + const char *WIDTH; +} nn_CommonDeviceInfo; + +const char *nn_deviceInfoAt(nn_Computer *computer, size_t idx); +const nn_DeviceField *nn_getDeviceInfo(nn_Computer *computer, size_t idx, size_t *fieldCount); +nn_Exit nn_addDeviceInfo(nn_Computer *computer, const char *addr, const nn_DeviceField *fields); +nn_Exit nn_addDeviceInfoL(nn_Computer *computer, const char *addr, const nn_DeviceField *fields, size_t fieldCount); +nn_Exit nn_addCommonDeviceInfo(nn_Computer *computer, const char *addr, nn_CommonDeviceInfo info); +// Sets every field to NULL. +void nn_clearCommonDeviceInfo(nn_CommonDeviceInfo *info); +bool nn_removeDeviceInfo(nn_Computer *computer, const char *addr); + void nn_beepComputer(nn_Computer *computer, nn_Beep beep); // get the userdata pointer