WIP fs impl

This commit is contained in:
IonutParau 2026-02-10 17:48:36 +01:00
parent c0d965be26
commit 9c5e4a3d5b
6 changed files with 842 additions and 83 deletions

View File

@ -73,7 +73,7 @@ local function dofile(file)
status("> " .. file) status("> " .. file)
local program, reason = raw_loadfile(file) local program, reason = raw_loadfile(file)
if program then if program then
local result = table.pack(pcall(program)) local result = table.pack(xpcall(program, debug.traceback))
if result[1] then if result[1] then
return table.unpack(result, 2, result.n) return table.unpack(result, 2, result.n)
else else

View File

@ -31,13 +31,13 @@ void *luaArch_alloc(void *ud, void *ptr, size_t osize, size_t nsize) {
return NULL; return NULL;
} }
if(ptr == NULL) { if(ptr == NULL) {
if(arch->freeMem < nsize) return NULL; //if(arch->freeMem < nsize) return NULL;
void *mem = malloc(nsize); void *mem = malloc(nsize);
if(mem == NULL) return NULL; if(mem == NULL) return NULL;
arch->freeMem -= nsize; arch->freeMem -= nsize;
return mem; return mem;
} }
if(arch->freeMem + osize < nsize) return NULL; //if(arch->freeMem + osize < nsize) return NULL;
void *mem = realloc(ptr, nsize); void *mem = realloc(ptr, nsize);
if(mem == NULL) return NULL; if(mem == NULL) return NULL;
arch->freeMem += osize; arch->freeMem += osize;
@ -101,6 +101,19 @@ static void luaArch_nnToLua(luaArch *arch, size_t nnIdx) {
lua_pushboolean(L, nn_toboolean(C, nnIdx)); lua_pushboolean(L, nn_toboolean(C, nnIdx));
return; return;
} }
if(nn_istable(C, nnIdx)) {
size_t start = nn_getstacksize(C);
size_t len;
nn_dumptable(C, nnIdx, &len);
lua_createtable(L, 0, len);
for(size_t i = 0; i < len; i++) {
luaArch_nnToLua(arch, start + i * 2);
luaArch_nnToLua(arch, start + i * 2 + 1);
lua_settable(L, -3);
}
nn_popn(C, len * 2);
return;
}
luaL_error(L, "bad NN value: %s", nn_typenameof(C, nnIdx)); luaL_error(L, "bad NN value: %s", nn_typenameof(C, nnIdx));
} }
@ -214,6 +227,12 @@ static int luaArch_computer_shutdown(lua_State *L) {
return 0; return 0;
} }
static int luaArch_computer_isOverused(lua_State *L) {
nn_Computer *c = luaArch_from(L)->computer;
lua_pushboolean(L, nn_componentsOverused(c));
return 1;
}
static int luaArch_component_list(lua_State *L) { static int luaArch_component_list(lua_State *L) {
luaArch *arch = luaArch_from(L); luaArch *arch = luaArch_from(L);
lua_createtable(L, 64, 0); lua_createtable(L, 64, 0);
@ -385,6 +404,8 @@ static void luaArch_loadEnv(lua_State *L) {
lua_setfield(L, computer, "setArchitecture"); lua_setfield(L, computer, "setArchitecture");
lua_pushcfunction(L, luaArch_computer_shutdown); lua_pushcfunction(L, luaArch_computer_shutdown);
lua_setfield(L, computer, "shutdown"); lua_setfield(L, computer, "shutdown");
lua_pushcfunction(L, luaArch_computer_isOverused);
lua_setfield(L, computer, "isOverused");
lua_setglobal(L, "computer"); lua_setglobal(L, "computer");
lua_createtable(L, 0, 10); lua_createtable(L, 0, 10);
int component = lua_gettop(L); int component = lua_gettop(L);

View File

@ -23,7 +23,8 @@ function coroutine.resume(co, ...)
end end
end end
local clist = component.list local clist, cinvoke, computer, component, print = component.list, component.invoke, computer, component, print
debug.print = print
function component.list(ctype, exact) function component.list(ctype, exact)
local list = clist() local list = clist()
@ -41,10 +42,26 @@ function component.list(ctype, exact)
end end
end end
setmetatable(desired, {__call = next}) local key = nil
setmetatable(desired, {__call = function()
local val
key, val = next(desired, key)
return key, val
end})
return desired return desired
end end
function component.invoke(address, method, ...)
local t = {pcall(cinvoke, address, method, ...)}
if computer.energy() <= 0 then sysyield() end -- out of power
if computer.isOverused() then sysyield() end -- overused
if t[1] then
return table.unpack(t, 2)
end
return nil, t[2]
end
local componentCallback = { local componentCallback = {
__call = function(self, ...) __call = function(self, ...)
return component.invoke(self.address, self.name, ...) return component.invoke(self.address, self.name, ...)
@ -126,6 +143,35 @@ function computer.setArchitecture(arch)
return ok, err return ok, err
end end
function checkArg(arg, val, ...)
local t = {...}
for i=1,#t do
if type(val) == t[i] then return end
end
error("bad argument #" .. arg .. " (" .. table.concat(t, ", ") .. ") expected", 2)
end
-- HORRENDOUS approximation
unicode = string
if os.getenv("NN_REPL") == "1" then
while true do
io.write("lua> ")
io.flush()
local l = io.read("l")
if not l then break end
local f, err = load("return " .. l, "=repl")
if f then
print(f())
else
f, err = load(l, "=repl")
if f then f() else print(err) end
end
end
io.write("\n")
print("exiting repl")
end
local eeprom = component.list("eeprom", true)() local eeprom = component.list("eeprom", true)()
assert(eeprom, "missing firmware") assert(eeprom, "missing firmware")

View File

@ -4,11 +4,44 @@
// Error handling has been omitted in most places. // Error handling has been omitted in most places.
#include "neonucleus.h" #include "neonucleus.h"
#include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
nn_Architecture getLuaArch(); nn_Architecture getLuaArch();
#if defined(NN_WINDOWS)
#define NE_PATHSEP '\\'
#include <windows.h>
#error "Windows is not supported yet"
#elif defined(NN_POSIX)
#define NE_PATHSEP '/'
#include <dirent.h>
#include <unistd.h>
typedef DIR ne_dir;
ne_dir *ne_opendir(const char *path) {
return opendir(path);
}
void ne_closedir(ne_dir *dir) {
closedir(dir);
}
bool ne_readdir(ne_dir *dir, char path[NN_MAX_PATH]) {
struct dirent *ent = readdir(dir);
if(ent == NULL) return true;
strncpy(path, ent->d_name, NN_MAX_PATH-1);
return false;
}
bool ne_exists(const char *path) {
return access(path, F_OK) == 0;
}
#endif
static const char minBIOS[] = { static const char minBIOS[] = {
#embed "minBIOS.lua" #embed "minBIOS.lua"
,'\0' ,'\0'
@ -38,6 +71,161 @@ static nn_Exit sandbox_handler(nn_ComponentRequest *req) {
return NN_OK; return NN_OK;
} }
typedef struct ne_FsState {
char path[NN_MAX_PATH];
bool isReadonly;
FILE *files[NN_MAX_OPENFILES];
ne_dir *dir;
} ne_FsState;
void ne_fsState_truepath(ne_FsState *state, char truepath[NN_MAX_PATH], const char *path) {
snprintf(truepath, sizeof(char) * NN_MAX_PATH, "%s%c%s", state->path, NE_PATHSEP, path);
for(size_t i = 0; truepath[i] != 0; i++) {
if(truepath[i] == '/') truepath[i] = NE_PATHSEP;
}
}
nn_Exit ne_fsState_handler(nn_FilesystemRequest *req) {
nn_Computer *C = req->computer;
ne_FsState *state = req->instance;
FILE *f;
char truepath[NN_MAX_PATH];
switch(req->action) {
case NN_FS_DROP:
for(size_t i = 0; i < NN_MAX_OPENFILES; i++) {
if(state->files[i] != NULL) fclose(state->files[i]);
}
if(state->dir != NULL) {
ne_closedir(state->dir);
}
free(state);
return NN_OK;
case NN_FS_SPACEUSED:
req->size = 0;
return NN_OK;
case NN_FS_GETLABEL:
req->strarg1 = NULL;
return NN_OK;
case NN_FS_SETLABEL:
req->strarg1 = NULL;
return NN_OK;
case NN_FS_OPEN:;
req->fd = NN_MAX_OPENFILES;
for(size_t i = 0; i < NN_MAX_OPENFILES; i++) {
if(state->files[i] == NULL) {
req->fd = i;
break;
}
}
if(req->fd == NN_MAX_OPENFILES) {
nn_setError(C, "too many open handles");
return NN_EBADCALL;
}
const char *path = req->strarg1;
const char *mode = req->strarg2;
switch(mode[0]) {
case 'r':
mode = "rb";
case 'w':
mode = "wb";
case 'a':
mode = "ab";
default:
mode = "rb";
}
ne_fsState_truepath(state, truepath, path);
f = fopen(truepath, mode);
if(f == NULL) {
nn_setError(C, strerror(errno));
return NN_EBADCALL;
}
state->files[req->fd] = f;
return NN_OK;
case NN_FS_CLOSE:
if(req->fd < 0 || req->fd >= NN_MAX_OPENFILES) {
nn_setError(C, "bad file descriptor");
return NN_EBADCALL;
}
f = state->files[req->fd];
if(f == NULL) {
nn_setError(C, "bad file descriptor");
return NN_EBADCALL;
}
fclose(f);
state->files[req->fd] = NULL;
return NN_OK;
case NN_FS_READ:
if(req->fd < 0 || req->fd >= NN_MAX_OPENFILES) {
nn_setError(C, "bad file descriptor");
return NN_EBADCALL;
}
f = state->files[req->fd];
if(f == NULL) {
nn_setError(C, "bad file descriptor");
return NN_EBADCALL;
}
if(feof(f)) {
req->strarg1 = NULL;
} else {
req->strarg1len = fread(req->strarg1, sizeof(char), req->strarg1len, f);
}
return NN_OK;
case NN_FS_WRITE:
if(req->fd < 0 || req->fd >= NN_MAX_OPENFILES) {
nn_setError(C, "bad file descriptor");
return NN_EBADCALL;
}
f = state->files[req->fd];
if(f == NULL) {
nn_setError(C, "bad file descriptor");
return NN_EBADCALL;
}
fwrite(req->strarg1, sizeof(char), req->strarg1len, f);
return NN_OK;
case NN_FS_OPENDIR:
ne_fsState_truepath(state, truepath, req->strarg1);
state->dir = ne_opendir(truepath);
if(state->dir == NULL) {
nn_setError(C, strerror(errno));
return NN_EBADCALL;
}
return NN_OK;
case NN_FS_READDIR:;
char ent[NN_MAX_PATH];
if(ne_readdir(state->dir, ent)) {
req->strarg1 = NULL;
return NN_OK;
}
strcpy(req->strarg1, ent);
req->strarg1len = strlen(ent);
return NN_OK;
case NN_FS_CLOSEDIR:
ne_closedir(state->dir);
state->dir = NULL;
return NN_OK;
case NN_FS_EXISTS:
ne_fsState_truepath(state, truepath, req->strarg1);
req->size = ne_exists(truepath) ? 1 : 0;
return NN_OK;
}
return NN_OK;
}
ne_FsState *ne_newFS(const char *path, bool readonly) {
ne_FsState *fs = malloc(sizeof(*fs));
for(size_t i = 0; i < NN_MAX_OPENFILES; i++) {
fs->files[i] = NULL;
}
sprintf(fs->path, "data%c%s", NE_PATHSEP, path);
fs->isReadonly = readonly;
return fs;
}
int main() { int main() {
nn_Context ctx; nn_Context ctx;
nn_initContext(&ctx); nn_initContext(&ctx);
@ -53,19 +241,6 @@ int main() {
}; };
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 = { nn_VEEPROM veeprom = {
.code = minBIOS, .code = minBIOS,
.codelen = strlen(minBIOS), .codelen = strlen(minBIOS),
@ -77,7 +252,12 @@ int main() {
.isReadonly = false, .isReadonly = false,
}; };
nn_ComponentType *etype = nn_createVEEPROM(u, &eeprom, &veeprom); nn_ComponentType *etype = nn_createVEEPROM(u, &nn_defaultEEPROM, &veeprom);
nn_ComponentType *fstype[5];
fstype[0] = nn_createFilesystem(u, &nn_defaultFloppy, ne_fsState_handler, NULL);
for(size_t i = 1; i < 5; i++) {
fstype[i] = nn_createFilesystem(u, &nn_defaultFilesystems[i-1], ne_fsState_handler, NULL);
}
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);
@ -87,9 +267,12 @@ int main() {
nn_addComponent(c, ctype, "sandbox", -1, NULL); nn_addComponent(c, ctype, "sandbox", -1, NULL);
nn_addComponent(c, etype, "eeprom", 0, etype); nn_addComponent(c, etype, "eeprom", 0, etype);
ne_FsState *mainFS = ne_newFS("OpenOS", false);
nn_addComponent(c, fstype[1], "mainFS", 2, mainFS);
while(true) { while(true) {
nn_Exit e = nn_tick(c); nn_Exit e = nn_tick(c);
if(e != NN_OK && e != NN_EBUSY) { if(e != NN_OK) {
nn_setErrorFromExit(c, e); nn_setErrorFromExit(c, e);
printf("error: %s\n", nn_getError(c)); printf("error: %s\n", nn_getError(c));
goto cleanup; goto cleanup;
@ -120,6 +303,7 @@ cleanup:;
nn_destroyComputer(c); nn_destroyComputer(c);
nn_destroyComponentType(ctype); nn_destroyComponentType(ctype);
nn_destroyComponentType(etype); nn_destroyComponentType(etype);
for(size_t i = 0; i < 5; i++) nn_destroyComponentType(fstype[i]);
// rip the universe // rip the universe
nn_destroyUniverse(u); nn_destroyUniverse(u);
return 0; return 0;

View File

@ -12,6 +12,9 @@
// we need the header. // we need the header.
#include "neonucleus.h" #include "neonucleus.h"
// to use the numerical accuracy better
#define NN_COMPONENT_CALLBUDGET 10000
#ifdef NN_ATOMIC_NONE #ifdef NN_ATOMIC_NONE
typedef size_t nn_refc_t; typedef size_t nn_refc_t;
@ -41,25 +44,6 @@ bool nn_decRef(nn_refc_t *refc) {
} }
#endif #endif
// 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__)
//define something for Windows (32-bit and 64-bit, this part is common)
#define NN_WINDOWS
#elif __APPLE__
#define NN_MACOS
#elif __linux__
#define NN_LINUX
#endif
#if __unix__ // all unices not caught above
// Unix
#define NN_UNIX
#define NN_POSIX
#elif defined(_POSIX_VERSION)
// POSIX
#define NN_POSIX
#endif
typedef struct nn_Lock nn_Lock; typedef struct nn_Lock nn_Lock;
// the special includes // the special includes
@ -328,6 +312,43 @@ void nn_crc32ChecksumBytes(unsigned int checksum, char out[8]) {
} }
} }
bool nn_simplifyPath(const char original[NN_MAX_PATH], char simplified[NN_MAX_PATH]) {
// pass 1: check for valid characters, and \ becomes /
for(size_t i = 0; true; i++) {
if(original[i] == '\\') simplified[i] = '/';
else simplified[i] = original[i];
if(original[i] == '\0') break;
}
// get rid of //, starting / and ending /
{
while(simplified[0] == '/') {
for(size_t i = 1; true; i++) {
simplified[i-1] = simplified[i];
if(simplified[i] == '\0') break;
}
}
size_t j = 0;
for(size_t i = 0; simplified[i] != '\0'; i++) {
if(simplified[i] == '/' && simplified[i+1] == '/') {
// simply discard it
continue;
} else {
simplified[j] = simplified[i];
j++;
}
}
simplified[j] = '\0';
while(simplified[j-1] == '/') {
j--;
simplified[j] = '\0';
}
}
// TODO: handle ..
// valid
return true;
}
int nn_memcmp(const char *a, const char *b, size_t len) { int nn_memcmp(const char *a, const char *b, size_t len) {
for(size_t i = 0; i < len; i++) { for(size_t i = 0; i < len; i++) {
char c = a[i]; char c = a[i];
@ -549,6 +570,7 @@ typedef struct nn_Component {
char *address; char *address;
nn_ComponentType *ctype; nn_ComponentType *ctype;
int slot; int slot;
float budgetUsed;
void *userdata; void *userdata;
void *state; void *state;
} nn_Component; } nn_Component;
@ -1024,6 +1046,7 @@ nn_Exit nn_tick(nn_Computer *computer) {
return NN_EBADSTATE; return NN_EBADSTATE;
} }
nn_resetCallBudget(computer); nn_resetCallBudget(computer);
nn_resetComponentBudgets(computer);
computer->state = NN_RUNNING; computer->state = NN_RUNNING;
nn_ArchitectureRequest req; nn_ArchitectureRequest req;
req.computer = computer; req.computer = computer;
@ -1031,8 +1054,6 @@ nn_Exit nn_tick(nn_Computer *computer) {
req.localState = computer->archState; req.localState = computer->archState;
req.action = NN_ARCH_TICK; req.action = NN_ARCH_TICK;
err = computer->arch.handler(&req); err = computer->arch.handler(&req);
// dont crash on EBUSY because it just means go again
if(err == NN_EBUSY) return err;
if(err) { if(err) {
computer->state = NN_CRASHED; computer->state = NN_CRASHED;
nn_setErrorFromExit(computer, err); nn_setErrorFromExit(computer, err);
@ -1284,7 +1305,7 @@ nn_Exit nn_call(nn_Computer *computer, const char *address, const char *method)
if(nn_strcmp(c.address, address) != 0) continue; if(nn_strcmp(c.address, address) != 0) continue;
// minimum cost of a component call // minimum cost of a component call
nn_callCost(computer, 1); if(computer->callBudget > 0) computer->callBudget--;
nn_ComponentRequest req; nn_ComponentRequest req;
req.typeUserdata = c.ctype->userdata; req.typeUserdata = c.ctype->userdata;
@ -1340,10 +1361,32 @@ void nn_resetCallBudget(nn_Computer *computer) {
} }
bool nn_componentsOverused(nn_Computer *computer) { bool nn_componentsOverused(nn_Computer *computer) {
for(size_t i = 0; i < computer->componentLen; i++) {
if(computer->components[i].budgetUsed >= NN_COMPONENT_CALLBUDGET) return true;
}
if(computer->totalCallBudget == 0) return false; if(computer->totalCallBudget == 0) return false;
return computer->callBudget == 0; return computer->callBudget == 0;
} }
void nn_resetComponentBudgets(nn_Computer *computer) {
for(size_t i = 0; i < computer->componentLen; i++) {
computer->components[i].budgetUsed = 0;
}
}
bool nn_costComponent(nn_Computer *computer, const char *address, double perTick) {
return nn_costComponentN(computer, address, 1, perTick);
}
bool nn_costComponentN(nn_Computer *computer, const char *address, double amount, double perTick) {
for(size_t i = 0; i < computer->componentLen; i++) {
nn_Component *c = &computer->components[i];
if(nn_strcmp(c->address, address) != 0) continue;
c->budgetUsed += (NN_COMPONENT_CALLBUDGET * amount) / perTick;
return c->budgetUsed >= NN_COMPONENT_CALLBUDGET;
}
return false;
}
bool nn_checkstack(nn_Computer *computer, size_t amount) { bool nn_checkstack(nn_Computer *computer, size_t amount) {
return computer->stackSize + amount <= NN_MAX_STACK; return computer->stackSize + amount <= NN_MAX_STACK;
} }
@ -1647,6 +1690,7 @@ typedef struct nn_EEPROM_state {
nn_Universe *universe; nn_Universe *universe;
nn_EEPROM eeprom; nn_EEPROM eeprom;
void *userdata; void *userdata;
nn_EEPROMHandler *handler;
} nn_EEPROM_state; } nn_EEPROM_state;
typedef struct nn_VEEPROM_state { typedef struct nn_VEEPROM_state {
@ -1685,7 +1729,7 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
return NN_OK; return NN_OK;
case NN_COMP_DEINIT: case NN_COMP_DEINIT:
ereq.action = NN_EEPROM_DROP; ereq.action = NN_EEPROM_DROP;
return state->eeprom.handler(&ereq); return state->handler(&ereq);
case NN_COMP_ENABLED: case NN_COMP_ENABLED:
req->methodEnabled = true; req->methodEnabled = true;
return NN_OK; return NN_OK;
@ -1698,19 +1742,20 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
} }
if(nn_strcmp(method, "isReadOnly") == 0) { if(nn_strcmp(method, "isReadOnly") == 0) {
ereq.action = NN_EEPROM_ISREADONLY; ereq.action = NN_EEPROM_ISREADONLY;
nn_Exit e = state->eeprom.handler(&ereq); nn_Exit e = state->handler(&ereq);
if(e) return e; if(e) return e;
req->returnCount = 1; req->returnCount = 1;
return nn_pushbool(computer, ereq.buflen != 0); return nn_pushbool(computer, ereq.buflen != 0);
} }
if(nn_strcmp(method, "getChecksum") == 0) { if(nn_strcmp(method, "getChecksum") == 0) {
nn_costComponent(computer, req->compAddress, 1);
// yup, on-stack. // yup, on-stack.
// Perhaps in the future we'll make it heap-allocated. // Perhaps in the future we'll make it heap-allocated.
char buf[state->eeprom.size]; char buf[state->eeprom.size];
ereq.action = NN_EEPROM_GET; ereq.action = NN_EEPROM_GET;
ereq.buf = buf; ereq.buf = buf;
ereq.buflen = state->eeprom.size; ereq.buflen = state->eeprom.size;
nn_Exit e = state->eeprom.handler(&ereq); nn_Exit e = state->handler(&ereq);
if(e) return e; if(e) return e;
unsigned int chksum = nn_computeCRC32(buf, ereq.buflen); unsigned int chksum = nn_computeCRC32(buf, ereq.buflen);
char encoded[8]; char encoded[8];
@ -1719,6 +1764,7 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
return nn_pushlstring(computer, encoded, 8); return nn_pushlstring(computer, encoded, 8);
} }
if(nn_strcmp(method, "makeReadonly") == 0) { if(nn_strcmp(method, "makeReadonly") == 0) {
nn_costComponent(computer, req->compAddress, 1);
// 1st argument is a string, which is the checksum we're meant to have // 1st argument is a string, which is the checksum we're meant to have
if(nn_getstacksize(computer) < 1) { if(nn_getstacksize(computer) < 1) {
nn_setError(computer, "bad argument #1 (string expected)"); nn_setError(computer, "bad argument #1 (string expected)");
@ -1740,7 +1786,7 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
ereq.action = NN_EEPROM_GET; ereq.action = NN_EEPROM_GET;
ereq.buf = buf; ereq.buf = buf;
ereq.buflen = state->eeprom.size; ereq.buflen = state->eeprom.size;
nn_Exit e = state->eeprom.handler(&ereq); nn_Exit e = state->handler(&ereq);
if(e) return e; if(e) return e;
unsigned int chksum = nn_computeCRC32(buf, ereq.buflen); unsigned int chksum = nn_computeCRC32(buf, ereq.buflen);
char encoded[8]; char encoded[8];
@ -1751,23 +1797,26 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
} }
ereq.action = NN_EEPROM_MAKEREADONLY; ereq.action = NN_EEPROM_MAKEREADONLY;
e = state->eeprom.handler(&ereq); e = state->handler(&ereq);
if(e) return e; if(e) return e;
req->returnCount = 1; req->returnCount = 1;
return nn_pushbool(computer, true); return nn_pushbool(computer, true);
} }
if(nn_strcmp(method, "getLabel") == 0) { if(nn_strcmp(method, "getLabel") == 0) {
nn_costComponent(computer, req->compAddress, 1);
char buf[NN_MAX_LABEL]; char buf[NN_MAX_LABEL];
ereq.action = NN_EEPROM_GETLABEL; ereq.action = NN_EEPROM_GETLABEL;
ereq.buf = buf; ereq.buf = buf;
ereq.buflen = NN_MAX_LABEL; ereq.buflen = NN_MAX_LABEL;
nn_Exit e = state->eeprom.handler(&ereq); nn_Exit e = state->handler(&ereq);
if(e) return e; if(e) return e;
req->returnCount = 1; req->returnCount = 1;
if(ereq.buf == NULL) return nn_pushnull(computer);
return nn_pushlstring(computer, buf, ereq.buflen); return nn_pushlstring(computer, buf, ereq.buflen);
} }
if(nn_strcmp(method, "setLabel") == 0) { if(nn_strcmp(method, "setLabel") == 0) {
nn_costComponent(computer, req->compAddress, 1);
if(nn_getstacksize(computer) < 1) { if(nn_getstacksize(computer) < 1) {
nn_setError(computer, "bad argument #1 (string expected)"); nn_setError(computer, "bad argument #1 (string expected)");
return NN_EBADCALL; return NN_EBADCALL;
@ -1784,46 +1833,50 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
ereq.action = NN_EEPROM_SETLABEL; ereq.action = NN_EEPROM_SETLABEL;
ereq.buf = buf; ereq.buf = buf;
ereq.buflen = len; ereq.buflen = len;
nn_Exit e = state->eeprom.handler(&ereq); nn_Exit e = state->handler(&ereq);
if(e) return e; if(e) return e;
req->returnCount = 1; req->returnCount = 1;
return nn_pushlstring(computer, buf, ereq.buflen); return nn_pushlstring(computer, buf, ereq.buflen);
} }
if(nn_strcmp(method, "get") == 0) { if(nn_strcmp(method, "get") == 0) {
nn_costComponent(computer, req->compAddress, 1);
// yup, on-stack. // yup, on-stack.
// Perhaps in the future we'll make it heap-allocated. // Perhaps in the future we'll make it heap-allocated.
char buf[state->eeprom.size]; char buf[state->eeprom.size];
ereq.action = NN_EEPROM_GET; ereq.action = NN_EEPROM_GET;
ereq.buf = buf; ereq.buf = buf;
ereq.buflen = state->eeprom.size; ereq.buflen = state->eeprom.size;
nn_Exit e = state->eeprom.handler(&ereq); nn_Exit e = state->handler(&ereq);
if(e) return e; if(e) return e;
req->returnCount = 1; req->returnCount = 1;
return nn_pushlstring(computer, buf, ereq.buflen); return nn_pushlstring(computer, buf, ereq.buflen);
} }
if(nn_strcmp(method, "getData") == 0) { if(nn_strcmp(method, "getData") == 0) {
nn_costComponent(computer, req->compAddress, 1);
// yup, on-stack. // yup, on-stack.
// Perhaps in the future we'll make it heap-allocated. // Perhaps in the future we'll make it heap-allocated.
char buf[state->eeprom.dataSize]; char buf[state->eeprom.dataSize];
ereq.action = NN_EEPROM_GETDATA; ereq.action = NN_EEPROM_GETDATA;
ereq.buf = buf; ereq.buf = buf;
ereq.buflen = state->eeprom.dataSize; ereq.buflen = state->eeprom.dataSize;
nn_Exit e = state->eeprom.handler(&ereq); nn_Exit e = state->handler(&ereq);
if(e) return e; if(e) return e;
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) { if(nn_strcmp(method, "getArchitecture") == 0) {
nn_costComponent(computer, req->compAddress, 1);
char buf[NN_MAX_ARCHNAME]; char buf[NN_MAX_ARCHNAME];
ereq.action = NN_EEPROM_GETARCH; ereq.action = NN_EEPROM_GETARCH;
ereq.buf = buf; ereq.buf = buf;
ereq.buflen = NN_MAX_ARCHNAME; ereq.buflen = NN_MAX_ARCHNAME;
nn_Exit e = state->eeprom.handler(&ereq); nn_Exit e = state->handler(&ereq);
if(e) return e; if(e) return e;
req->returnCount = 1; req->returnCount = 1;
return nn_pushlstring(computer, buf, ereq.buflen); return nn_pushlstring(computer, buf, ereq.buflen);
} }
if(nn_strcmp(method, "set") == 0) { if(nn_strcmp(method, "set") == 0) {
nn_costComponent(computer, req->compAddress, 1);
if(nn_getstacksize(computer) < 1) { if(nn_getstacksize(computer) < 1) {
nn_setError(computer, "bad argument #1 (string expected)"); nn_setError(computer, "bad argument #1 (string expected)");
return NN_EBADCALL; return NN_EBADCALL;
@ -1842,9 +1895,10 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
// DO NOT MODIFY IT!!!! // DO NOT MODIFY IT!!!!
ereq.buf = (char*)s; ereq.buf = (char*)s;
ereq.buflen = len; ereq.buflen = len;
return state->eeprom.handler(&ereq); return state->handler(&ereq);
} }
if(nn_strcmp(method, "setData") == 0) { if(nn_strcmp(method, "setData") == 0) {
nn_costComponent(computer, req->compAddress, 1);
if(nn_getstacksize(computer) < 1) { if(nn_getstacksize(computer) < 1) {
nn_setError(computer, "bad argument #1 (string expected)"); nn_setError(computer, "bad argument #1 (string expected)");
return NN_EBADCALL; return NN_EBADCALL;
@ -1863,9 +1917,10 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
// DO NOT MODIFY IT!!!! // DO NOT MODIFY IT!!!!
ereq.buf = (char*)s; ereq.buf = (char*)s;
ereq.buflen = len; ereq.buflen = len;
return state->eeprom.handler(&ereq); return state->handler(&ereq);
} }
if(nn_strcmp(method, "setArchitecture") == 0) { if(nn_strcmp(method, "setArchitecture") == 0) {
nn_costComponent(computer, req->compAddress, 1);
if(nn_getstacksize(computer) < 1) { if(nn_getstacksize(computer) < 1) {
nn_setError(computer, "bad argument #1 (string expected)"); nn_setError(computer, "bad argument #1 (string expected)");
return NN_EBADCALL; return NN_EBADCALL;
@ -1884,20 +1939,30 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
// DO NOT MODIFY IT!!!! // DO NOT MODIFY IT!!!!
ereq.buf = (char*)s; ereq.buf = (char*)s;
ereq.buflen = len; ereq.buflen = len;
return state->eeprom.handler(&ereq); return state->handler(&ereq);
} }
return NN_OK; return NN_OK;
} }
return NN_OK; return NN_OK;
} }
nn_ComponentType *nn_createEEPROM(nn_Universe *universe, const nn_EEPROM *eeprom, void *userdata) { nn_EEPROM nn_defaultEEPROM = (nn_EEPROM) {
.size = 4 * NN_KiB,
.dataSize = 256,
.readEnergyCost = 10,
.writeEnergyCost = 100,
.readDataEnergyCost = 10,
.writeDataEnergyCost = 50,
};
nn_ComponentType *nn_createEEPROM(nn_Universe *universe, const nn_EEPROM *eeprom, nn_EEPROMHandler *handler, 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;
state->handler = handler;
const nn_Method methods[] = { const nn_Method methods[] = {
{"getSize", "function(): number - Get the storage capacity of the EEPROM.", NN_DIRECT}, {"getSize", "function(): number - Get the storage capacity of the EEPROM.", NN_DIRECT},
{"getDataSize", "function(): number - Get the storage capacity of the EEPROM data.", NN_DIRECT}, {"getDataSize", "function(): number - Get the storage capacity of the EEPROM data.", NN_DIRECT},
@ -2006,9 +2071,7 @@ nn_ComponentType *nn_createVEEPROM(nn_Universe *universe, const nn_EEPROM *eepro
state->archlen = archlen; state->archlen = archlen;
nn_memcpy(state->arch, vmem->arch, sizeof(char) * archlen); nn_memcpy(state->arch, vmem->arch, sizeof(char) * archlen);
nn_EEPROM neeprom = *eeprom; nn_ComponentType *ty = nn_createEEPROM(universe, eeprom, nn_veeprom_handler, state);
neeprom.handler = nn_veeprom_handler;
nn_ComponentType *ty = nn_createEEPROM(universe, &neeprom, state);
if(ty == NULL) goto fail; if(ty == NULL) goto fail;
return ty; return ty;
fail:; fail:;
@ -2018,3 +2081,395 @@ fail:;
nn_free(&ctx, state, sizeof(*state)); nn_free(&ctx, state, sizeof(*state));
return NULL; return NULL;
} }
typedef struct nn_Filesystem_state {
nn_Universe *universe;
void *userdata;
nn_FilesystemHandler *handler;
nn_Filesystem fs;
} nn_Filesystem_state;
nn_Filesystem nn_defaultFilesystems[4] = {
(nn_Filesystem) {
.spaceTotal = 1 * NN_MiB,
.readsPerTick = 4,
.writesPerTick = 2,
.dataEnergyCost = 256.0 / NN_MiB,
},
(nn_Filesystem) {
.spaceTotal = 2 * NN_MiB,
.readsPerTick = 4,
.writesPerTick = 2,
.dataEnergyCost = 512.0 / NN_MiB,
},
(nn_Filesystem) {
.spaceTotal = 4 * NN_MiB,
.readsPerTick = 7,
.writesPerTick = 3,
.dataEnergyCost = 1024.0 / NN_MiB,
},
(nn_Filesystem) {
.spaceTotal = 8 * NN_MiB,
.readsPerTick = 13,
.writesPerTick = 5,
.dataEnergyCost = 2048.0 / NN_MiB,
},
};
nn_Filesystem nn_defaultFloppy = (nn_Filesystem) {
.spaceTotal = 512 * NN_KiB,
.readsPerTick = 1,
.writesPerTick = 1,
.dataEnergyCost = 8.0 / NN_MiB,
};
nn_Exit nn_filesystem_handler(nn_ComponentRequest *req) {
nn_Filesystem_state *state = req->typeUserdata;
void *instance = req->compUserdata;
// NULL for FREETYPE
nn_Computer *computer = req->computer;
nn_Context ctx = state->universe->ctx;
nn_FilesystemRequest fsreq;
fsreq.userdata = state->userdata;
fsreq.instance = instance;
fsreq.computer = computer;
fsreq.fsConf = &state->fs;
const char *method = req->methodCalled;
nn_Exit err;
switch(req->action) {
case NN_COMP_FREETYPE:
nn_free(&ctx, state, sizeof(*state));
break;
case NN_COMP_INIT:
return NN_OK;
case NN_COMP_DEINIT:
fsreq.action = NN_FS_DROP;
return state->handler(&fsreq);
case NN_COMP_ENABLED:
req->methodEnabled = true;
return NN_OK;
case NN_COMP_CALL:
if(nn_strcmp(method, "spaceTotal") == 0) {
req->returnCount = 1;
return nn_pushnumber(computer, state->fs.spaceTotal);
}
if(nn_strcmp(method, "spaceUsed") == 0) {
fsreq.action = NN_FS_SPACEUSED;
err = state->handler(&fsreq);
if(err) return err;
req->returnCount = 1;
return nn_pushnumber(computer, fsreq.size);
}
if(nn_strcmp(method, "isReadOnly") == 0) {
fsreq.action = NN_FS_ISREADONLY;
err = state->handler(&fsreq);
if(err) return err;
req->returnCount = 1;
return nn_pushbool(computer, fsreq.size != 0);
}
if(nn_strcmp(method, "getLabel") == 0) {
char buf[NN_MAX_LABEL];
fsreq.action = NN_FS_GETLABEL;
fsreq.strarg1 = buf;
fsreq.strarg1len = NN_MAX_LABEL;
err = state->handler(&fsreq);
if(err) return err;
req->returnCount = 1;
if(fsreq.strarg1 == NULL) return nn_pushnull(computer);
return nn_pushlstring(computer, buf, fsreq.strarg1len);
}
if(nn_strcmp(method, "setLabel") == 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;
}
fsreq.action = NN_FS_SETLABEL;
// DO NOT MODIFY THE BUFFER!!!
fsreq.strarg1 = (char *)nn_tolstring(computer, 0, &fsreq.strarg1len);
err = state->handler(&fsreq);
if(err) return err;
req->returnCount = 1;
if(fsreq.strarg1 == NULL) return nn_pushnull(computer);
return nn_pushlstring(computer, fsreq.strarg1, fsreq.strarg1len);
}
if(nn_strcmp(method, "open") == 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;
}
if(nn_getstacksize(computer) < 2) {
err = nn_pushstring(computer, "r");
if(err) return err;
}
if(!nn_isstring(computer, 1)) {
nn_setError(computer, "bad argument #2 (string expected)");
return NN_EBADCALL;
}
size_t pathlen;
const char *path = nn_tolstring(computer, 0, &pathlen);
if(pathlen >= NN_MAX_PATH) {
nn_setError(computer, "path too long");
return NN_EBADCALL;
}
char truepath[NN_MAX_PATH];
nn_simplifyPath(path, truepath);
size_t modelen;
const char *mode = nn_tolstring(computer, 1, &modelen);
fsreq.action = NN_FS_OPEN;
fsreq.strarg1 = truepath;
fsreq.strarg1len = nn_strlen(truepath);
fsreq.strarg2 = (char *)mode;
fsreq.strarg2len = modelen;
err = state->handler(&fsreq);
if(err) return err;
req->returnCount = 1;
return nn_pushnumber(computer, fsreq.fd);
}
if(nn_strcmp(method, "close") == 0) {
if(nn_getstacksize(computer) < 1) {
nn_setError(computer, "bad argument #1 (integer expected)");
return NN_EBADCALL;
}
if(!nn_isnumber(computer, 0)) {
nn_setError(computer, "bad argument #1 (integer expected)");
return NN_EBADCALL;
}
fsreq.fd = nn_tonumber(computer, 0);
fsreq.action = NN_FS_CLOSE;
return state->handler(&fsreq);
}
if(nn_strcmp(method, "read") == 0) {
if(nn_getstacksize(computer) < 1) {
nn_setError(computer, "bad argument #1 (integer expected)");
return NN_EBADCALL;
}
if(!nn_isnumber(computer, 0)) {
nn_setError(computer, "bad argument #1 (integer expected)");
return NN_EBADCALL;
}
if(nn_getstacksize(computer) < 2) {
err = nn_pushnumber(computer, NN_MAX_READ);
if(err) return err;
}
if(!nn_isnumber(computer, 1)) {
nn_setError(computer, "bad argument #2 (integer expected)");
return NN_EBADCALL;
}
fsreq.action = NN_FS_READ;
fsreq.fd = nn_tonumber(computer, 0);
size_t requested = nn_tonumber(computer, 1);
if(requested > NN_MAX_READ) requested = NN_MAX_READ;
char buf[requested];
fsreq.strarg1 = buf;
fsreq.strarg1len = requested;
err = state->handler(&fsreq);
if(err) return err;
req->returnCount = 1;
if(fsreq.strarg1 == NULL) return nn_pushnull(computer);
return nn_pushlstring(computer, fsreq.strarg1, fsreq.strarg1len);
}
if(nn_strcmp(method, "write") == 0) {
if(nn_getstacksize(computer) < 1) {
nn_setError(computer, "bad argument #1 (integer expected)");
return NN_EBADCALL;
}
if(!nn_isnumber(computer, 0)) {
nn_setError(computer, "bad argument #1 (integer expected)");
return NN_EBADCALL;
}
if(nn_getstacksize(computer) < 2) {
nn_setError(computer, "bad argument #2 (string expected)");
return NN_EBADCALL;
}
if(!nn_isnumber(computer, 1)) {
nn_setError(computer, "bad argument #2 (string expected)");
return NN_EBADCALL;
}
fsreq.action = NN_FS_WRITE;
fsreq.fd = nn_tonumber(computer, 0);
fsreq.strarg1 = (char *)nn_tolstring(computer, 1, &fsreq.strarg1len);
err = state->handler(&fsreq);
if(err) return err;
req->returnCount = 1;
return nn_pushbool(computer, true);
}
if(nn_strcmp(method, "seek") == 0) {
if(nn_getstacksize(computer) < 1) {
nn_setError(computer, "bad argument #1 (integer expected)");
return NN_EBADCALL;
}
if(!nn_isnumber(computer, 0)) {
nn_setError(computer, "bad argument #1 (integer expected)");
return NN_EBADCALL;
}
if(nn_getstacksize(computer) < 2) {
err = nn_pushstring(computer, "cur");
if(err) return err;
}
if(!nn_isstring(computer, 1)) {
nn_setError(computer, "bad argument #2 (string expected)");
return NN_EBADCALL;
}
if(nn_getstacksize(computer) < 2) {
err = nn_pushnumber(computer, 0);
if(err) return err;
}
if(!nn_isnumber(computer, 2)) {
nn_setError(computer, "bad argument #2 (string expected)");
return NN_EBADCALL;
}
fsreq.action = NN_FS_SEEK;
fsreq.fd = nn_tonumber(computer, 0);
const char *whence = nn_tostring(computer, 1);
fsreq.off = nn_tonumber(computer, 2);
if(nn_strcmp(whence, "set") == 0) {
fsreq.whence = NN_SEEK_SET;
} else if(nn_strcmp(whence, "cur") == 0) {
fsreq.whence = NN_SEEK_CUR;
} else if(nn_strcmp(whence, "end") == 0) {
fsreq.whence = NN_SEEK_END;
} else {
nn_setError(computer, "bad seek whence");
return NN_EBADCALL;
}
err = state->handler(&fsreq);
return nn_pushnumber(computer, fsreq.off);
}
if(nn_strcmp(method, "list") == 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;
}
char truepath[NN_MAX_PATH];
size_t pathlen;
const char *path = nn_tolstring(computer, 0, &pathlen);
if(pathlen >= NN_MAX_PATH) {
nn_setError(computer, "path too long");
return NN_EBADCALL;
}
nn_simplifyPath(path, truepath);
int dirfd;
fsreq.action = NN_FS_OPENDIR;
fsreq.strarg1 = truepath;
fsreq.strarg1len = nn_strlen(truepath);
err = state->handler(&fsreq);
if(err) return err;
dirfd = fsreq.fd;
// this sucks hard
size_t entryCount = 0;
while(1) {
char entry[NN_MAX_PATH];
fsreq.action = NN_FS_READDIR;
fsreq.fd = dirfd;
fsreq.strarg1 = entry;
fsreq.strarg1len = NN_MAX_PATH;
err = state->handler(&fsreq);
if(err) goto list_fail;
if(fsreq.strarg1 == NULL) break;
if(fsreq.strarg1len == 1 && entry[0] == '.') continue;
if(fsreq.strarg1len == 2 && entry[0] == '.' && entry[1] == '.') continue;
err = nn_pushlstring(computer, entry, fsreq.strarg1len);
if(err) goto list_fail;
entryCount++;
}
err = nn_pusharraytable(computer, entryCount);
if(err) goto list_fail;
req->returnCount = 1;
fsreq.action = NN_FS_CLOSEDIR;
fsreq.fd = dirfd;
state->handler(&fsreq);
return NN_OK;
list_fail:
fsreq.action = NN_FS_CLOSEDIR;
fsreq.fd = dirfd;
state->handler(&fsreq);
return err;
}
if(nn_strcmp(method, "exists") == 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;
}
char truepath[NN_MAX_PATH];
size_t pathlen;
const char *path = nn_tolstring(computer, 0, &pathlen);
if(pathlen >= NN_MAX_PATH) {
nn_setError(computer, "path too long");
return NN_EBADCALL;
}
nn_simplifyPath(path, truepath);
fsreq.action = NN_FS_EXISTS;
fsreq.strarg1 = truepath;
fsreq.strarg1len = nn_strlen(truepath);
err = state->handler(&fsreq);
if(err) return err;
req->returnCount = 1;
return nn_pushbool(computer, fsreq.size != 0);
}
}
return NN_OK;
}
nn_ComponentType *nn_createFilesystem(nn_Universe *universe, const nn_Filesystem *filesystem, nn_FilesystemHandler *handler, void *userdata) {
nn_Context ctx = universe->ctx;
nn_Filesystem_state *state = nn_alloc(&ctx, sizeof(*state));
if(state == NULL) return NULL;
state->universe = universe;
state->userdata = userdata;
state->handler = handler;
state->fs = *filesystem;
const nn_Method methods[] = {
{"spaceTotal", "function(): number - Get the storage capacity of the filesystem.", NN_DIRECT},
{"spaceUsed", "function(): number - Get the space used by the files on the drive.", NN_DIRECT},
{"isReadOnly", "function(): boolean - Returns whether the drive is read-only.", NN_DIRECT},
{"getLabel", "function(): string - Get the filesystem label.", NN_INDIRECT},
{"setLabel", "function(label: string): string - Set the filesystem label and return what was actually set, which may be truncated.", NN_INDIRECT},
{"open", "function(path: string, mode?: string = 'r'): number - Open a file. Valid modes are 'r' (read-only), 'w' (write-only) and 'a' (append-only).", NN_INDIRECT},
{"close", "function(fd: number) - Closes a file.", NN_INDIRECT},
{"read", "function(fd: number, count?: number): string? - Reads part of a file. If there is no more data, nothing is returned.", NN_INDIRECT},
{"write", "function(fd: number, data: string): boolean - Writes the data to a file. Returns true on success.", NN_INDIRECT},
{"seek", "function(fd: number, whence: string, offset: number): number - Seeks a file. Valid whences are 'set' (relative to start), 'cur' (relative to current), 'end' (relative to EoF, backwards). Returns the new position.", NN_INDIRECT},
{"list", "function(path: string): string[] - Returns the names of the entries inside of the directory. Directories get a / appended to their names.", NN_INDIRECT},
{"exists", "function(path: string): boolean - Checks if there exists an entry at the specified path.", NN_INDIRECT},
{"size", "function(path: string) - Gets the size of the entry at the specified path.", NN_INDIRECT},
{"remove", "function(path: string): boolean - Removes the entry at the specified path.", NN_INDIRECT},
{"rename", "function(from: string, to: string): boolean - Renames or moves an entry to a new location.", NN_INDIRECT},
{"isDirectory", "function(path: string): boolean - Checks if the entry at the specified path is a directory.", NN_INDIRECT},
{"lastModified", "function(path: string): number - Returns the UNIX timestamp of the time the entry was last modified. This is stored in milliseconds, but is always a multiple of 1000.", NN_INDIRECT},
{"makeDirectory", "function(path: string): boolean - Creates a directory. Creates parent directories if necessary.", NN_INDIRECT},
{NULL, NULL, NN_INDIRECT},
};
nn_ComponentType *t = nn_createComponentType(universe, "filesystem", state, methods, nn_filesystem_handler);
if(t == NULL) {
nn_free(&ctx, state, sizeof(*state));
return NULL;
}
return t;
}

View File

@ -5,6 +5,27 @@
extern "C" { extern "C" {
#endif #endif
// Platform checking support, to help out users.
// Used internally as well.
// 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__)
//define something for Windows (32-bit and 64-bit, this part is common)
#define NN_WINDOWS
#elif __APPLE__
#define NN_MACOS
#elif __linux__
#define NN_LINUX
#endif
#if __unix__ // all unices not caught above
// Unix
#define NN_UNIX
#define NN_POSIX
#elif defined(_POSIX_VERSION)
// POSIX
#define NN_POSIX
#endif
// every C standard header we depend on, conveniently put here // every C standard header we depend on, conveniently put here
#include <stddef.h> // for NULL, #include <stddef.h> // for NULL,
#include <stdint.h> // for intptr_t #include <stdint.h> // for intptr_t
@ -25,7 +46,7 @@ extern "C" {
#define NN_ALLOC_ALIGN 16 #define NN_ALLOC_ALIGN 16
// the maximum amount of items the callstack can have. // the maximum amount of items the callstack can have.
#define NN_MAX_STACK 256 #define NN_MAX_STACK 256
// the maximum size a path is allowed to have // the maximum size a path is allowed to have, including the NULL terminator!
#define NN_MAX_PATH 256 #define NN_MAX_PATH 256
// the maximum amount of bytes which can be read from a file. // the maximum amount of bytes which can be read from a file.
// You are given a buffer you are meant to fill at least partially, this is simply the limit of that buffer's size. // You are given a buffer you are meant to fill at least partially, this is simply the limit of that buffer's size.
@ -206,8 +227,6 @@ typedef enum nn_Exit {
NN_EBADCALL, NN_EBADCALL,
// bad state, the function was called at the wrong time // bad state, the function was called at the wrong time
NN_EBADSTATE, NN_EBADSTATE,
// resource busy. If the result of nn_call, you should call it again later.
NN_EBUSY,
} nn_Exit; } nn_Exit;
// This stores necessary data between computers // This stores necessary data between computers
@ -369,6 +388,7 @@ void nn_setComputerState(nn_Computer *computer, nn_ComputerState state);
nn_ComputerState nn_getComputerState(nn_Computer *computer); nn_ComputerState nn_getComputerState(nn_Computer *computer);
// runs a tick of the computer. Make sure to check the state as well! // runs a tick of the computer. Make sure to check the state as well!
// This automatically resets the component budgets and call budget.
nn_Exit nn_tick(nn_Computer *computer); nn_Exit nn_tick(nn_Computer *computer);
typedef struct nn_DeviceInfoEntry { typedef struct nn_DeviceInfoEntry {
@ -493,10 +513,6 @@ void nn_setCallBudget(nn_Computer *computer, size_t budget);
// gets the total call budget // gets the total call budget
size_t nn_getCallBudget(nn_Computer *computer); size_t nn_getCallBudget(nn_Computer *computer);
// subtracts from the call budget.
// This cannot underflow, it's clamped to 0.
void nn_callCost(nn_Computer *computer, size_t callIntensity);
// returns the remaining call budget // returns the remaining call budget
size_t nn_callBudgetRemaining(nn_Computer *computer); size_t nn_callBudgetRemaining(nn_Computer *computer);
@ -507,6 +523,19 @@ void nn_resetCallBudget(nn_Computer *computer);
// At this point, the architecture should exit with a yield. // At this point, the architecture should exit with a yield.
bool nn_componentsOverused(nn_Computer *computer); bool nn_componentsOverused(nn_Computer *computer);
void nn_resetComponentBudgets(nn_Computer *computer);
// Uses 1/perTick to the component budget.
// Upon a full component budget being used for that component, it returns true.
// nn_componentsOverused() will also return true.
// This indicates the architecture should yield, to throttle the computer for overuse.
bool nn_costComponent(nn_Computer *computer, const char *address, double perTick);
// Uses amount/perTick to the component budget.
// Upon a full component budget being used for that component, it returns true.
// nn_componentsOverused() will also return true.
// This indicates the architecture should yield, to throttle the computer for overuse.
bool nn_costComponentN(nn_Computer *computer, const char *address, double amount, double perTick);
// call stack operations. // call stack operations.
// The type system and API are inspired by Lua, as Lua remains the most popular architecture for OpenComputers. // The type system and API are inspired by Lua, as Lua remains the most popular architecture for OpenComputers.
// This does support other languages, however it may make some APIs clunky due to the usage of tables and 1-based indexing. // This does support other languages, however it may make some APIs clunky due to the usage of tables and 1-based indexing.
@ -664,30 +693,24 @@ typedef struct nn_EEPROMRequest {
char *buf; char *buf;
} nn_EEPROMRequest; } nn_EEPROMRequest;
// reads and writes are always 1/1
typedef struct nn_EEPROM { typedef struct nn_EEPROM {
// the maximum capacity of the EEPROM // the maximum capacity of the EEPROM
size_t size; size_t size;
// the maximum capacity of the EEPROM's associated data // the maximum capacity of the EEPROM's associated data
size_t dataSize; size_t dataSize;
// the call cost of reading an EEPROM
size_t readCallCost;
// the energy cost of reading an EEPROM // the energy cost of reading an EEPROM
double readEnergyCost; double readEnergyCost;
// the call cost of reading an EEPROM's associated data
size_t readDataCallCost;
// the energy cost of reading an EEPROM's associated data // the energy cost of reading an EEPROM's associated data
double readDataEnergyCost; double readDataEnergyCost;
// the call cost of writing to an EEPROM
size_t writeCallCost;
// the energy cost of writing to an EEPROM // the energy cost of writing to an EEPROM
double writeEnergyCost; double writeEnergyCost;
// the call cost of writing to an EEPROM's associated data
size_t writeDataCallCost;
// the energy cost of writing to an EEPROM's associated data // the energy cost of writing to an EEPROM's associated data
double writeDataEnergyCost; double writeDataEnergyCost;
nn_Exit (*handler)(nn_EEPROMRequest *request);
} nn_EEPROM; } nn_EEPROM;
extern nn_EEPROM nn_defaultEEPROM;
typedef struct nn_VEEPROM { typedef struct nn_VEEPROM {
const char *code; const char *code;
size_t codelen; size_t codelen;
@ -699,9 +722,11 @@ typedef struct nn_VEEPROM {
bool isReadonly; bool isReadonly;
} nn_VEEPROM; } nn_VEEPROM;
typedef nn_Exit nn_EEPROMHandler(nn_EEPROMRequest *request);
// 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, const nn_EEPROM *eeprom, void *userdata); nn_ComponentType *nn_createEEPROM(nn_Universe *universe, const nn_EEPROM *eeprom, nn_EEPROMHandler *handler, void *userdata);
nn_ComponentType *nn_createVEEPROM(nn_Universe *universe, const nn_EEPROM *eeprom, const nn_VEEPROM *vmem); nn_ComponentType *nn_createVEEPROM(nn_Universe *universe, const nn_EEPROM *eeprom, const nn_VEEPROM *vmem);
// Note on paths: // Note on paths:
@ -751,13 +776,14 @@ typedef enum nn_FilesystemAction {
// The entry should be stored in strarg2, and strarg2len is the capacity of the buffer. // The entry should be stored in strarg2, and strarg2len is the capacity of the buffer.
// If the buffer is too short, truncate the result. // If the buffer is too short, truncate the result.
// Set strarg2len to the length of the entry. // Set strarg2len to the length of the entry.
// If there are no more entries, set strarg2 to NULL.
// Do note that directories should have / appended at the end of their entries. // Do note that directories should have / appended at the end of their entries.
// Directory file descriptors are not exposed to Lua, // Directory file descriptors are not exposed to the architecture,
// thus they can only come from NN_FS_OPENDIR. // thus they can only come from NN_FS_OPENDIR.
// This means you may not need to validate these file descriptors. // This means you may not need to validate these file descriptors.
NN_FS_READDIR, NN_FS_READDIR,
// close a directory file descriptor, stored in fd. // close a directory file descriptor, stored in fd.
// Directory file descriptors are not exposed to Lua, // Directory file descriptors are not exposed to the architecture,
// thus they can only come from NN_FS_OPENDIR. // thus they can only come from NN_FS_OPENDIR.
// This means you may not need to validate these file descriptors. // This means you may not need to validate these file descriptors.
NN_FS_CLOSEDIR, NN_FS_CLOSEDIR,
@ -767,11 +793,12 @@ typedef enum nn_FilesystemAction {
// as needed. // as needed.
NN_FS_MKDIR, NN_FS_MKDIR,
// Return the lastmodified timestamp. // Return the lastmodified timestamp.
// This number is stored in milliseconds, but aligned to seconds. // This number is stored in seconds.
// DO NOT RETURN A NUMBER NOT DIVISIBLE BY 1000, OpenOS WILL BREAK
// DUE TO BAD CODE.
// The timestamp should be stored in size, it may not make // The timestamp should be stored in size, it may not make
// sense but it is a field and it is there. // sense but it is a field and it is there.
// Do note that the lastModified() method returns it in milliseconds,
// however it must be a multiple of 1000 due to OpenOS depending
// on that behavior.
NN_FS_LASTMODIFIED, NN_FS_LASTMODIFIED,
// Checks if a path, stored in strarg1, is a directory. // Checks if a path, stored in strarg1, is a directory.
// If it is, size should be set to 1. // If it is, size should be set to 1.
@ -781,8 +808,6 @@ typedef enum nn_FilesystemAction {
// If it is, size should be set to 1. // If it is, size should be set to 1.
// If it is not, size should be set to 0. // If it is not, size should be set to 0.
NN_FS_ISREADONLY, NN_FS_ISREADONLY,
// Makes a file-system read-only.
NN_FS_MAKEREADONLY,
// Checks if a path, stored in strarg1, exists on the filesystem. // Checks if a path, stored in strarg1, exists on the filesystem.
// If it is, size should be set to 1. // If it is, size should be set to 1.
// If it is not, size should be set to 0. // If it is not, size should be set to 0.
@ -824,6 +849,7 @@ typedef struct nn_FilesystemRequest {
void *userdata; void *userdata;
void *instance; void *instance;
nn_Computer *computer; nn_Computer *computer;
struct nn_Filesystem *fsConf;
nn_FilesystemAction action; nn_FilesystemAction action;
int fd; int fd;
nn_FilesystemWhence whence; nn_FilesystemWhence whence;
@ -835,6 +861,33 @@ typedef struct nn_FilesystemRequest {
size_t size; size_t size;
} nn_FilesystemRequest; } nn_FilesystemRequest;
typedef struct nn_Filesystem {
// the maximum capacity of the filesystem
size_t spaceTotal;
// how many read calls can be done per tick
// list, exists, isDirectory, seek also count as reads.
double readsPerTick;
// how many write calls can be done per tick
// makeDirectory, open, remove and rename also count as writes.
double writesPerTick;
// The energy cost of an actual read/write.
// It is per-byte, so if a read returns 4096 bytes, then this cost is multiplied by 4096.
double dataEnergyCost;
} nn_Filesystem;
// 4 Tiers.
// 0 - Tier 1 equivalent
// 1 - Tier 2 equivalent
// 2 - Tier 3 equivalent
// 3 - Tier 4, a better version of Tier 3.
extern nn_Filesystem nn_defaultFilesystems[4];
// a basic floppy
extern nn_Filesystem nn_defaultFloppy;
typedef nn_Exit nn_FilesystemHandler(nn_FilesystemRequest *request);
nn_ComponentType *nn_createFilesystem(nn_Universe *universe, const nn_Filesystem *filesystem, nn_FilesystemHandler *handler, void *userdata);
typedef enum nn_ScreenAction { typedef enum nn_ScreenAction {
// instance dropped // instance dropped
NN_SCR_DROP, NN_SCR_DROP,