huge progress on ncomplib

This commit is contained in:
2026-03-29 19:50:16 +02:00
parent f722108806
commit 37cc73a21c
6 changed files with 1228 additions and 43 deletions

View File

@@ -23,7 +23,7 @@ fn addEngineSources(b: *std.Build, opts: LibBuildOpts) *std.Build.Module {
dataMod.addCSourceFiles(.{ dataMod.addCSourceFiles(.{
.files = &[_][]const u8{ .files = &[_][]const u8{
"src/neonucleus.c", "src/neonucleus.c",
"src/ncomplib.h", "src/ncomplib.c",
}, },
.flags = &.{ .flags = &.{
if (opts.baremetal) "-DNN_BAREMETAL" else "", if (opts.baremetal) "-DNN_BAREMETAL" else "",

View File

@@ -4,6 +4,7 @@
// Error handling has been omitted in most places. // Error handling has been omitted in most places.
#include "neonucleus.h" #include "neonucleus.h"
#include "ncomplib.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -376,34 +377,14 @@ int main(int argc, char **argv) {
.isReadonly = false, .isReadonly = false,
}; };
printf("%zu bytes logically used by OpenOS\n", ncl_spaceUsedIn(ncl_defaultFS, "data/OpenOS"));
printf("%zu bytes physically used by OpenOS\n", ncl_spaceUsedBy(ncl_defaultFS, "data/OpenOS"));
nn_Component *eepromCard = nn_createVEEPROM(u, "eeprom", &veeprom, &nn_defaultEEPROMs[3]); nn_Component *eepromCard = nn_createVEEPROM(u, "eeprom", &veeprom, &nn_defaultEEPROMs[3]);
size_t ramTotal = 0; size_t ramTotal = 0;
ramTotal += nn_ramSizes[5]; ramTotal += nn_ramSizes[5];
nn_Computer *c = nn_createComputer(u, NULL, "computer0", ramTotal, 256, 256);
if(showStats) {
// collects stats
nn_setEnergyHandler(c, NULL, ne_energy_accumulator);
}
// default for 64-bit, we just assume we're 64-bit.
nn_setMemoryScale(c, 1.8);
nn_setArchitecture(c, &arch);
nn_addSupportedArchitecture(c, &arch);
nn_mountComponent(c, ocelotCard, -1);
nn_mountComponent(c, eepromCard, 0);
const char *driveData = "error('unmanaged drive')";
nn_VDrive vdrive = {
.data = driveData,
.datalen = strlen(driveData),
.label = "",
.labellen = 0,
};
SetExitKey(KEY_NULL); SetExitKey(KEY_NULL);
Font font = LoadFont("unscii-16-full.ttf"); Font font = LoadFont("unscii-16-full.ttf");
@@ -413,11 +394,38 @@ int main(int argc, char **argv) {
tickDelay = atof(getenv("NN_TICKDELAY")); tickDelay = atof(getenv("NN_TICKDELAY"));
} }
const char *driveData = "error('unmanaged drive')";
nn_VDrive vdrive = {
.data = driveData,
.datalen = strlen(driveData),
.label = "",
.labellen = 0,
};
struct {int key; nn_codepoint unicode;} keybuf[512]; struct {int key; nn_codepoint unicode;} keybuf[512];
memset(keybuf, 0, sizeof(keybuf)); memset(keybuf, 0, sizeof(keybuf));
size_t keycap = sizeof(keybuf) / sizeof(keybuf[0]); size_t keycap = sizeof(keybuf) / sizeof(keybuf[0]);
double nextTick = 0; double nextTick = 0;
double nextSecond = 0;
double wattage = 0;
restart:;
nn_Computer *c = nn_createComputer(u, NULL, "computer0", ramTotal, 256, 256);
if(showStats) {
// collects stats
nn_setEnergyHandler(c, NULL, ne_energy_accumulator);
}
// default for 64-bit
if(sizeof(void *) > 4) nn_setMemoryScale(c, 1.8);
nn_setArchitecture(c, &arch);
nn_addSupportedArchitecture(c, &arch);
nn_mountComponent(c, ocelotCard, -1);
nn_mountComponent(c, eepromCard, 0);
while(true) { while(true) {
if(WindowShouldClose()) break; if(WindowShouldClose()) break;
@@ -431,8 +439,6 @@ int main(int argc, char **argv) {
statY += 20; statY += 20;
} }
if(showStats) { if(showStats) {
double wattage = accumulatedEnergyCost;
if(tickDelay > 0) wattage /= tickDelay;
double memUsagePercent = (double)nn_getUsedMemory(c) * 100 / nn_getTotalMemory(c); double memUsagePercent = (double)nn_getUsedMemory(c) * 100 / nn_getTotalMemory(c);
DrawText(TextFormat("power usage: %.2f W", wattage), 10, statY, 20, GREEN); DrawText(TextFormat("power usage: %.2f W", wattage), 10, statY, 20, GREEN);
statY += 20; statY += 20;
@@ -481,6 +487,12 @@ int main(int argc, char **argv) {
double tickNow = GetTime(); double tickNow = GetTime();
if(tickNow >= nextSecond) {
nextSecond = tickNow + 1;
wattage = accumulatedEnergyCost;
accumulatedEnergyCost = 0;
}
if(tickNow >= nextTick) { if(tickNow >= nextTick) {
accumulatedEnergyCost = 0; accumulatedEnergyCost = 0;
nextTick = tickNow + tickDelay; nextTick = tickNow + tickDelay;
@@ -511,7 +523,8 @@ int main(int argc, char **argv) {
} }
if(state == NN_RESTART) { if(state == NN_RESTART) {
printf("restart requested\n"); printf("restart requested\n");
goto cleanup; nn_destroyComputer(c);
goto restart;
} }
} }
} }

View File

@@ -1,8 +1,226 @@
#include "neonucleus.h" #include "neonucleus.h"
#include "ncomplib.h" #include "ncomplib.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <stdatomic.h>
static bool ncl_defaultHandler(ncl_VFSRequest *request);
#ifdef NN_BAREMETAL
bool ncl_defaultHandler(ncl_VFSRequest *request) {
return false; // all fails
}
#else
#include <stdio.h> #include <stdio.h>
#ifdef NN_POSIX
#include <dirent.h>
#include <sys/stat.h>
#elif defined(NN_WINDOWS)
#error "Windows is not supported yet"
#endif
bool ncl_defaultHandler(ncl_VFSRequest *request) {
if(request->action == NCL_VFS_OPEN) {
char mode[3] = "rb";
mode[0] = request->open.mode[0];
FILE *f = fopen(request->open.path, mode);
request->open.file = f;
return f != NULL;
}
if(request->action == NCL_VFS_CLOSE) {
fclose(request->close);
return true;
}
if(request->action == NCL_VFS_READ) {
FILE *f = request->read.file;
if(feof(f)) {
request->read.buf = NULL;
return true;
}
request->read.len = fread(request->read.buf, sizeof(char), request->read.len, f);
return true;
}
if(request->action == NCL_VFS_WRITE) {
FILE *f = request->write.file;
size_t written = fwrite(request->write.buf, sizeof(char), request->write.len, f);
return written == request->write.len;
}
if(request->action == NCL_VFS_SEEK) {
FILE *f = request->seek.file;
nn_FSWhence wanted = request->seek.whence;
int whence = SEEK_SET;
if(wanted == NN_SEEK_SET) whence = SEEK_SET;
if(wanted == NN_SEEK_CUR) whence = SEEK_CUR;
if(wanted == NN_SEEK_END) whence = SEEK_END;
if(fseek(f, whence, request->seek.off) < 0) return false;
request->seek.off = ftell(f);
return true;
}
if(request->action == NCL_VFS_REMOVE) {
return remove(request->remove) == 0;
}
#ifdef NN_POSIX
if(request->action == NCL_VFS_OPENDIR) {
DIR *d = opendir(request->opendir.path);
request->opendir.dir = d;
return d != NULL;
}
if(request->action == NCL_VFS_CLOSEDIR) {
DIR *d = request->closedir;
closedir(d);
return true;
}
if(request->action == NCL_VFS_READDIR) {
DIR *d = request->readdir.dir;
struct dirent *ent = readdir(d);
if(ent == NULL) {
request->readdir.name = NULL;
} else {
strncpy(request->readdir.name, ent->d_name, NN_MAX_PATH-1);
}
return true;
}
if(request->action == NCL_VFS_STAT) {
struct stat s;
if(stat(request->stat.path, &s) != 0) {
request->stat.path = NULL;
return false;
}
ncl_Stat *stat = request->stat.stat;
stat->isDirectory = S_ISDIR(s.st_mode);
stat->diskSize = s.st_blocks * 512;
stat->size = 0;
if(!stat->isDirectory) {
FILE *f = fopen(request->stat.path, "r");
if(f == NULL) {
// horribly off but don't care atp
stat->size = s.st_size;
} else {
fseek(f, SEEK_END, 0);
stat->size = ftell(f);
fclose(f);
}
}
stat->lastModified = s.st_mtime;
return true;
}
#endif
return false; // not supported
}
#endif
ncl_VFS ncl_defaultFS = (ncl_VFS) {
.state = NULL,
.handler = ncl_defaultHandler,
#ifdef NN_WINDOWS
.pathsep = '\\',
#else
.pathsep = '/',
#endif
.fileCost = 512,
};
void *ncl_openfile(ncl_VFS vfs, const char *path, const char *mode) {
ncl_VFSRequest req;
req.state = vfs.state;
req.action = NCL_VFS_OPEN;
req.open.path = path;
req.open.mode = mode;
if(!vfs.handler(&req)) return NULL;
return req.open.file;
}
void ncl_closefile(ncl_VFS vfs, void *file) {
ncl_VFSRequest req;
req.state = vfs.state;
req.action = NCL_VFS_CLOSE;
req.close = file;
vfs.handler(&req);
}
bool ncl_readfile(ncl_VFS vfs, void *file, char *buf, size_t *len);
bool ncl_writefile(ncl_VFS vfs, void *file, const char *data, size_t len);
bool ncl_seekfile(ncl_VFS vfs, void *file, nn_FSWhence whence, int *off);
bool ncl_stat(ncl_VFS vfs, const char *path, ncl_Stat *stat) {
ncl_VFSRequest req;
req.state = vfs.state;
req.action = NCL_VFS_STAT;
req.stat.path = path;
req.stat.stat = stat;
if(!vfs.handler(&req)) return false;
if(req.stat.path == NULL) return false;
return true;
}
void *ncl_opendir(ncl_VFS vfs, const char *path) {
ncl_VFSRequest req;
req.state = vfs.state;
req.action = NCL_VFS_OPENDIR;
req.opendir.path = path;
if(!vfs.handler(&req)) return NULL;
return req.opendir.dir;
}
void ncl_closedir(ncl_VFS vfs, void *dir) {
ncl_VFSRequest req;
req.state = vfs.state;
req.action = NCL_VFS_CLOSEDIR;
req.closedir = dir;
vfs.handler(&req);
}
bool ncl_readdir(ncl_VFS vfs, void *dir, char name[NN_MAX_PATH]) {
ncl_VFSRequest req;
req.state = vfs.state;
req.action = NCL_VFS_READDIR;
req.readdir.dir = dir;
req.readdir.name = name;
if(!vfs.handler(&req)) return false;
if(req.readdir.name == NULL) return false;
return true;
}
size_t ncl_spaceUsedIn(ncl_VFS vfs, const char *path) {
ncl_Stat s;
if(!ncl_stat(vfs, path, &s)) return 0;
if(!s.isDirectory) return vfs.fileCost + s.size;
size_t spaceUsed = vfs.fileCost;
void *dir = ncl_opendir(vfs, path);
if(dir == NULL) return spaceUsed;
char name[NN_MAX_PATH];
while(ncl_readdir(vfs, dir, name)) {
if(strcmp(name, ".") != 0 && strcmp(name, "..") != 0) {
char subpath[NN_MAX_PATH];
snprintf(subpath, sizeof(subpath), "%s%c%s", path, vfs.pathsep, name);
spaceUsed += ncl_spaceUsedIn(vfs, subpath);
}
}
ncl_closedir(vfs, dir);
return spaceUsed;
}
size_t ncl_spaceUsedBy(ncl_VFS vfs, const char *path) {
ncl_Stat s;
if(!ncl_stat(vfs, path, &s)) return 0;
size_t spaceUsed = s.diskSize;
if(!s.isDirectory) return s.diskSize;
void *dir = ncl_opendir(vfs, path);
if(dir == NULL) return spaceUsed;
char name[NN_MAX_PATH];
while(ncl_readdir(vfs, dir, name)) {
if(strcmp(name, ".") != 0 && strcmp(name, "..") != 0) {
char subpath[NN_MAX_PATH];
snprintf(subpath, sizeof(subpath), "%s%c%s", path, vfs.pathsep, name);
spaceUsed += ncl_spaceUsedBy(vfs, subpath);
}
}
ncl_closedir(vfs, dir);
return spaceUsed;
}
typedef struct ncl_ScreenPixel { typedef struct ncl_ScreenPixel {
nn_codepoint codepoint; nn_codepoint codepoint;
int storedFg; int storedFg;
@@ -24,11 +242,151 @@ typedef struct ncl_ScreenState {
int *palette; int *palette;
int *resolvedPalette; int *resolvedPalette;
ncl_ScreenPixel *pixels; ncl_ScreenPixel *pixels;
ncl_ScreenFlags flags;
} ncl_ScreenState; } ncl_ScreenState;
nn_Component *ncl_createFilesystem(nn_Universe *universe, const char *address, const char *path, const nn_Filesystem *fs); typedef struct nn_VRAMBuf {
nn_Component *ncl_createDrive(nn_Universe *universe, const char *address, const char *path, const nn_Drive *drive); int width;
nn_Component *ncl_createEEPROM(nn_Universe *universe, const char *address, const char *codepath, const char *datapath); int height;
ncl_ScreenPixel pixels[];
} ncl_VRAMBuf;
typedef struct ncl_GPUState {
nn_Context *ctx;
nn_Lock *lock;
nn_GPU conf;
size_t vramFree;
ncl_VRAMBuf *vram[NCL_MAX_VRAMBUF];
char *screenAddress;
int currentFg;
int currentBg;
int activeBuffer;
bool isFgPalette;
bool isBgPalette;
} ncl_GPUState;
static void ncl_freeVRAM(nn_Context *ctx, ncl_VRAMBuf *buf) {
nn_free(ctx, buf, sizeof(ncl_VRAMBuf) + sizeof(ncl_ScreenPixel) * buf->width * buf->height);
}
static ncl_VRAMBuf *ncl_allocVRAM(nn_Context *ctx, int width, int height) {
ncl_VRAMBuf *buf = nn_alloc(ctx, sizeof(ncl_VRAMBuf) + sizeof(ncl_ScreenPixel) * width * height);
if(buf == NULL) return NULL;
buf->width = width;
buf->height = height;
for(int i = 0; i < width*height; i++) {
buf->pixels[i] = (ncl_ScreenPixel) {
.codepoint = ' ',
.storedFg = 0xFFFFFF,
.storedBg = 0x000000,
.realFg = 0xFFFFFF,
.realBg = 0x000000,
};
}
return buf;
}
typedef struct ncl_FSState {
nn_Context *ctx;
nn_Lock *lock;
nn_Filesystem conf;
char *path;
ncl_VFS vfs;
// if 0, needs to be recomputed
size_t spaceUsed;
// if 0, needs to be recomputed
size_t realSpaceUsed;
atomic_size_t usage;
bool isReadonly;
// all the arrays
FILE *fds[NN_MAX_OPENFILES];
void *dirs[NN_MAX_OPENFILES];
char label[NN_MAX_LABEL];
size_t labellen;
} ncl_FSState;
typedef struct ncl_DriveState {
nn_Context *ctx;
nn_Lock *lock;
nn_Drive conf;
bool isReadonly;
atomic_size_t usage;
size_t lastSector;
char *path;
FILE *file;
char label[NN_MAX_LABEL];
size_t labellen;
} ncl_DriveState;
typedef struct ncl_EEState {
nn_Context *ctx;
nn_Lock *lock;
nn_EEPROM conf;
bool isReadonly;
atomic_size_t usage;
char *codepath;
char *datapath;
char label[NN_MAX_LABEL];
size_t labellen;
} ncl_EEState;
static void ncl_fixPath(ncl_VFS vfs, const char *path, char buf[NN_MAX_PATH]) {
size_t len = 0;
while(path[len]) {
if(len == NN_MAX_PATH) break;
if(path[len] == '/') buf[len] = vfs.pathsep;
else buf[len] = path[len];
len++;
}
buf[len] = '\0';
}
// assumes locked
static size_t ncl_fsGetUsage(ncl_FSState *fs) {
if(fs->spaceUsed == 0) {
fs->spaceUsed = ncl_spaceUsedIn(fs->vfs, fs->path);
}
if(fs->spaceUsed > fs->conf.spaceTotal) fs->spaceUsed = fs->conf.spaceTotal;
return fs->spaceUsed;
}
// assumes locked
static size_t ncl_fsGetRealUsage(ncl_FSState *fs) {
if(fs->realSpaceUsed == 0) {
fs->realSpaceUsed = ncl_spaceUsedBy(fs->vfs, fs->path);
}
return fs->realSpaceUsed;
}
static nn_Exit ncl_fsHandler(nn_FSRequest *req) {
ncl_FSState *state = req->state;
nn_Context *ctx = req->ctx;
nn_Computer *C = req->computer;
const nn_Filesystem *fs = req->fs;
if(req->action == NN_FS_DROP) {
for(size_t i = 0; i < NN_MAX_OPENFILES; i++) {
if(state->fds[i] != NULL) ncl_closefile(state->vfs, state->fds[i]);
if(state->dirs[i] != NULL) ncl_closedir(state->vfs, state->dirs[i]);
}
nn_strfree(ctx, state->path);
nn_free(ctx, state, sizeof(*state));
return NN_OK;
}
if(req->action == NN_FS_SPACEUSED) {
nn_lock(ctx, state->lock);
req->spaceUsed = ncl_fsGetUsage(state);
nn_unlock(ctx, state->lock);
return NN_OK;
}
if(C) nn_setError(C, "not implemented yet");
return NN_EBADCALL;
}
nn_Component *ncl_createFilesystem(nn_Universe *universe, const char *address, const char *path, const nn_Filesystem *fs, bool isReadonly);
nn_Component *ncl_createDrive(nn_Universe *universe, const char *address, const char *path, const nn_Drive *drive, bool isReadonly);
nn_Component *ncl_createEEPROM(nn_Universe *universe, const char *address, const char *codepath, const char *datapath, bool isReadonly);
static ncl_ScreenPixel ncl_getRealScreenPixel(const ncl_ScreenState *state, int x, int y) { static ncl_ScreenPixel ncl_getRealScreenPixel(const ncl_ScreenState *state, int x, int y) {
if(x < 1 || y < 1 || x >= state->width || y >= state->height) { if(x < 1 || y < 1 || x >= state->width || y >= state->height) {
@@ -60,7 +418,7 @@ static ncl_ScreenPixel *ncl_getRealScreenPixelPointer(const ncl_ScreenState *sta
return &state->pixels[x + y * state->conf.maxWidth]; return &state->pixels[x + y * state->conf.maxWidth];
} }
static void ncl_setRealScreenPixel(const ncl_ScreenState *state, int x, int y, ncl_ScreenPixel pixel) { static void ncl_setRealScreenPixel(ncl_ScreenState *state, int x, int y, ncl_ScreenPixel pixel) {
if(x < 1 || y < 1 || x >= state->width || y >= state->height) return; if(x < 1 || y < 1 || x >= state->width || y >= state->height) return;
x--; x--;
y--; y--;
@@ -90,6 +448,28 @@ static void ncl_recomputeScreen(const ncl_ScreenState *state) {
nn_Component *ncl_createScreen(nn_Universe *universe, const char *address, const nn_ScreenConfig *config); nn_Component *ncl_createScreen(nn_Universe *universe, const char *address, const nn_ScreenConfig *config);
nn_Component *ncl_createGPU(nn_Universe *universe, const char *address, const nn_GPU *gpu); nn_Component *ncl_createGPU(nn_Universe *universe, const char *address, const nn_GPU *gpu);
void ncl_resetScreen(ncl_ScreenState *state) {
state->width = state->conf.maxWidth;
state->height = state->conf.maxHeight;
state->viewportWidth = state->conf.maxWidth;
state->viewportHeight = state->conf.maxHeight;
for(int y = 1; y <= state->height; y++) {
for(int x = 1; x <= state->width; x++) {
ncl_setRealScreenPixel(state, x, y, (ncl_ScreenPixel) {
.codepoint = ' ',
.storedFg = 0xFFFFFF,
.storedBg = 0x000000,
.realFg = 0xFFFFFF,
.realBg = 0x000000,
});
}
}
memcpy(state->palette, state->conf.defaultPalette, sizeof(int) * state->conf.paletteColors);
ncl_recomputeScreen(state);
}
void ncl_getScreenResolution(const ncl_ScreenState *state, size_t *width, size_t *height) { void ncl_getScreenResolution(const ncl_ScreenState *state, size_t *width, size_t *height) {
*width = state->width; *width = state->width;
*height = state->height; *height = state->height;
@@ -109,8 +489,96 @@ ncl_Pixel ncl_getScreenPixel(const ncl_ScreenState *state, int x, int y) {
}; };
} }
ncl_ScreenFlags ncl_getScreenFlags(const ncl_ScreenState *state) {
return state->flags;
}
void ncl_setScreenFlags(ncl_ScreenState *state, ncl_ScreenFlags flags) {
state->flags = flags;
}
// general stuff
void ncl_statComponent(nn_Component *component, ncl_ComponentStat *stat) {
stat->labellen = 0;
stat->isReadonly = false;
const char *ty = nn_getComponentTypeID(component);
void *state = nn_getComponentState(component);
if(strcmp(ty, NCL_FS) == 0) {
ncl_FSState *fs = state;
nn_lock(fs->ctx, fs->lock);
stat->isReadonly = fs->isReadonly;
stat->usageCounter = fs->usage;
stat->labellen = fs->labellen;
memcpy(stat->label, fs->label, stat->labellen);
stat->fs.spaceUsed = ncl_fsGetUsage(fs);
stat->fs.realDiskUsage = ncl_fsGetRealUsage(fs);
stat->fs.conf = &fs->conf;
stat->fs.path = fs->path;
stat->fs.filesOpen = 0;
for(size_t i = 0; i < NN_MAX_OPENFILES; i++) {
if(fs->fds[i] != NULL) stat->fs.filesOpen++;
}
nn_unlock(fs->ctx, fs->lock);
return;
}
if(strcmp(ty, NCL_DRIVE) == 0) {
ncl_DriveState *drv = state;
nn_lock(drv->ctx, drv->lock);
stat->isReadonly = drv->isReadonly;
stat->usageCounter = drv->usage;
stat->labellen = drv->labellen;
memcpy(stat->label, drv->label, stat->labellen);
stat->drive.path = drv->path;
stat->drive.lastSector = drv->lastSector;
stat->drive.conf = &drv->conf;
nn_unlock(drv->ctx, drv->lock);
return;
}
if(strcmp(ty, NCL_EEPROM) == 0) {
ncl_EEState *ee = state;
nn_lock(ee->ctx, ee->lock);
stat->isReadonly = ee->isReadonly;
stat->usageCounter = ee->usage;
stat->labellen = ee->labellen;
memcpy(stat->label, ee->label, stat->labellen);
stat->eeprom.conf = &ee->conf;
stat->eeprom.codepath = ee->codepath;
stat->eeprom.datapath = ee->datapath;
nn_unlock(ee->ctx, ee->lock);
return;
}
}
// For EEPROMs, filesystems, drives
// Returns whether it was successful or not.
bool ncl_makeReadonly(nn_Component *component) {
const char *ty = nn_getComponentTypeID(component);
void *state = nn_getComponentState(component);
if(strcmp(ty, NCL_FS) == 0) {
ncl_FSState *fs = state;
fs->isReadonly = true;
fs->usage++;
return true;
}
if(strcmp(ty, NCL_DRIVE) == 0) {
ncl_DriveState *drv = state;
drv->isReadonly = true;
drv->usage++;
return true;
}
if(strcmp(ty, NCL_EEPROM) == 0) {
ncl_EEState *ee = state;
ee->isReadonly = true;
ee->usage++;
return true;
}
return false;
}
// all of these are encoding states // all of these are encoding states
nn_Exit ncl_encodeComponentState(nn_Universe *universe, nn_Component *comp, ncl_EncodedState *state); nn_Exit ncl_encodeComponentState(nn_Universe *universe, nn_Component *comp, ncl_EncodedState *state);
void ncl_freeEncodedState(nn_Universe *universe, ncl_EncodedState *state); void ncl_freeEncodedState(nn_Universe *universe, ncl_EncodedState *state);
nn_Exit ncl_loadComponentState(nn_Component *comp, const ncl_EncodedState *state); nn_Exit ncl_loadComponentState(nn_Component *comp, const ncl_EncodedState *state);

View File

@@ -3,6 +3,144 @@
#include "neonucleus.h" #include "neonucleus.h"
#define NCL_EEPROM "ncl-eeprom"
#define NCL_FS "ncl-filesystem"
#define NCL_DRIVE "ncl-drive"
#define NCL_GPU "ncl-gpu"
#define NCL_SCREEN "ncl-screen"
#define NCL_MAX_VRAMBUF 128
// very low-level actions
// some environment have VFSes so
// we support wrapping those
typedef struct ncl_Stat {
// whether the entry is a directory
bool isDirectory;
// the logical size of the entry
// as in, for files it is how many bytes are in there.
// For directories, it should be 0.
// Every entry has a base cost, and thus fear not,
// it will not lead to infinite disk usage.
// Instead, make their size representative of the
// size on disk / number of entries.
size_t size;
// the real size.
// This is for realSpaceUsed, and is a safety mechanism
// against disk-hogging.
size_t diskSize;
// The UNIX timestamp of the last modified date
// of the entry.
size_t lastModified;
} ncl_Stat;
typedef enum ncl_VFSAction {
NCL_VFS_OPEN,
NCL_VFS_CLOSE,
NCL_VFS_READ,
NCL_VFS_SEEK,
NCL_VFS_WRITE,
NCL_VFS_OPENDIR,
NCL_VFS_CLOSEDIR,
NCL_VFS_READDIR,
// non-recursively remove entry
NCL_VFS_REMOVE,
NCL_VFS_STAT,
} ncl_VFSAction;
typedef struct ncl_VFSRequest {
void *state;
ncl_VFSAction action;
union {
struct {
const char *path;
// same r, w and a modes as regular filesystem component
const char *mode;
void *file;
} open;
struct {
// set to NULL for EoF
char *buf;
size_t len;
void *file;
} read;
struct {
const char *buf;
size_t len;
void *file;
} write;
struct {
nn_FSWhence whence;
int off;
void *file;
} seek;
void *close;
struct {
const char *path;
void *dir;
} opendir;
struct {
// set to NULL for EoF
// buffer size is NN_MAX_PATH.
// Remember to account for terminator
char *name;
void *dir;
} readdir;
void *closedir;
const char *remove;
struct {
// set to NULL if missing
const char *path;
ncl_Stat *stat;
} stat;
struct {
// path of directory that / represents
const char *path;
// get the estimated amount of space
// used up by an empty entry.
// Used for enforcing capacity.
size_t size;
} entrysize;
};
} ncl_VFSRequest;
typedef struct ncl_VFS {
// the internal state
void *state;
// the handler.
// True on success, false on failure.
bool (*handler)(ncl_VFSRequest *request);
// the path separator
char pathsep;
// the assumed cost of a file in spaceUsedIn.
size_t fileCost;
} ncl_VFS;
extern ncl_VFS ncl_defaultFS;
void *ncl_openfile(ncl_VFS vfs, const char *path, const char *mode);
void ncl_closefile(ncl_VFS vfs, void *file);
// returns false on EoF
bool ncl_readfile(ncl_VFS vfs, void *file, char *buf, size_t *len);
bool ncl_writefile(ncl_VFS vfs, void *file, const char *data, size_t len);
bool ncl_seekfile(ncl_VFS vfs, void *file, nn_FSWhence whence, int *off);
bool ncl_stat(ncl_VFS vfs, const char *path, ncl_Stat *stat);
void *ncl_opendir(ncl_VFS vfs, const char *path);
void ncl_closedir(ncl_VFS vfs, void *dir);
// returns false on EoF
bool ncl_readdir(ncl_VFS vfs, void *dir, char name[NN_MAX_PATH]);
size_t ncl_spaceUsedIn(ncl_VFS vfs, const char *path);
// gets the real space used
size_t ncl_spaceUsedBy(ncl_VFS vfs, const char *path);
bool ncl_remove(ncl_VFS vfs, const char *path);
bool ncl_removeRecursive(ncl_VFS vfs, const char *path);
typedef struct ncl_EncodedState { typedef struct ncl_EncodedState {
char *buf; char *buf;
size_t len; size_t len;
@@ -12,11 +150,17 @@ nn_Exit ncl_encodeComponentState(nn_Universe *universe, nn_Component *comp, ncl_
void ncl_freeEncodedState(nn_Universe *universe, ncl_EncodedState *state); void ncl_freeEncodedState(nn_Universe *universe, ncl_EncodedState *state);
nn_Exit ncl_loadComponentState(nn_Component *comp, const ncl_EncodedState *state); nn_Exit ncl_loadComponentState(nn_Component *comp, const ncl_EncodedState *state);
nn_Component *ncl_createFilesystem(nn_Universe *universe, const char *address, const char *path, const nn_Filesystem *fs); size_t ncl_getLabel(nn_Component *c, char buf[NN_MAX_LABEL]);
nn_Component *ncl_createDrive(nn_Universe *universe, const char *address, const char *path, const nn_Drive *drive); size_t ncl_setLabel(nn_Component *c, const char *label, size_t len);
nn_Component *ncl_createEEPROM(nn_Universe *universe, const char *address, const char *codepath, const char *datapath);
nn_Component *ncl_createScreen(nn_Universe *universe, const char *address, const nn_ScreenConfig *config); nn_Component *ncl_createFilesystem(nn_Universe *universe, const char *address, const char *path, const nn_Filesystem *fs, bool isReadonly);
nn_Component *ncl_createGPU(nn_Universe *universe, const char *address, const nn_GPU *gpu); nn_Component *ncl_createDrive(nn_Universe *universe, const char *address, const char *path, const nn_Drive *drive, bool isReadonly);
nn_Component *ncl_createEEPROM(nn_Universe *universe, const char *address, const char *codepath, const char *datapath, bool isReadonly);
// Sets the VFS bound to a filesystem, drive or eeprom.
// This determines the filesystem the operations are run in.
// Returns the old VFS.
ncl_VFS ncl_setVFS(nn_Component *component, ncl_VFS vfs);
// TODO, stuff we could implement: // TODO, stuff we could implement:
// redstone, hologram, oled, ipu, vt, led, tape_drive, cd_drive, serial, colorful_lamp // redstone, hologram, oled, ipu, vt, led, tape_drive, cd_drive, serial, colorful_lamp
@@ -32,8 +176,71 @@ typedef struct ncl_Pixel {
typedef struct ncl_ScreenState ncl_ScreenState; typedef struct ncl_ScreenState ncl_ScreenState;
typedef enum ncl_ScreenFlags {
NCL_SCREEN_ON = 1<<0,
NCL_SCREEN_PRECISE = 1<<1,
NCL_SCREEN_TOUCHINVERTED = 1<<2,
} ncl_ScreenFlags;
nn_Component *ncl_createScreen(nn_Universe *universe, const char *address, const nn_ScreenConfig *config);
nn_Component *ncl_createGPU(nn_Universe *universe, const char *address, const nn_GPU *gpu);
typedef struct ncl_ComponentStat {
// common ones
char label[NN_MAX_LABEL];
size_t labellen;
// used for indicating usage. If higher than last time, something happened.
// This can be used to show a light or play a sound.
size_t usageCounter;
bool isReadonly;
// specific properties
union {
struct {
const nn_EEPROM *conf;
size_t codeUsed;
size_t dataUsed;
const char *codepath;
const char *datapath;
} eeprom;
struct {
const nn_Filesystem *conf;
size_t spaceUsed;
size_t realDiskUsage;
size_t filesOpen;
const char *path;
} fs;
struct {
const nn_Drive *conf;
size_t lastSector;
const char *path;
} drive;
struct {
const nn_GPU *conf;
size_t vramFree;
size_t bufferCount;
// can be NULL if there is none
const char *boundScreen;
} gpu;
struct {
const nn_ScreenConfig *conf;
ncl_ScreenState *state;
ncl_ScreenFlags flags;
int viewportWidth;
int viewportHeight;
} screen;
};
} ncl_ComponentStat;
void ncl_statComponent(nn_Component *component, ncl_ComponentStat *stat);
// For EEPROMs, filesystems, drives
// Returns whether it was successful or not.
bool ncl_makeReadonly(nn_Component *component);
void ncl_resetScreen(ncl_ScreenState *state);
void ncl_getScreenResolution(const ncl_ScreenState *state, size_t *width, size_t *height); void ncl_getScreenResolution(const ncl_ScreenState *state, size_t *width, size_t *height);
void ncl_getScreenViewport(const ncl_ScreenState *state, size_t *width, size_t *height); void ncl_getScreenViewport(const ncl_ScreenState *state, size_t *width, size_t *height);
ncl_Pixel ncl_getScreenPixel(const ncl_ScreenState *state, int x, int y); ncl_Pixel ncl_getScreenPixel(const ncl_ScreenState *state, int x, int y);
ncl_ScreenFlags ncl_getScreenFlags(const ncl_ScreenState *state);
void ncl_setScreenFlags(ncl_ScreenState *state, ncl_ScreenFlags flags);
#endif #endif

View File

@@ -1688,6 +1688,11 @@ nn_Exit nn_invokeComponent(nn_Computer *computer, const char *compAddress, const
} }
nn_MethodEntry *m = nn_getComponentMethodEntry(c, method); nn_MethodEntry *m = nn_getComponentMethodEntry(c, method);
while(nn_getstacksize(computer) > 0) {
if(!nn_isnull(computer, nn_getstacksize(computer) - 1)) break;
nn_pop(computer);
}
nn_ComponentRequest req; nn_ComponentRequest req;
req.ctx = &c->universe->ctx; req.ctx = &c->universe->ctx;
req.computer = computer; req.computer = computer;
@@ -1696,7 +1701,10 @@ nn_Exit nn_invokeComponent(nn_Computer *computer, const char *compAddress, const
req.methodIdx = m->idx; req.methodIdx = m->idx;
req.returnCount = 0; req.returnCount = 0;
nn_Exit e = c->handler(&req); nn_Exit e = c->handler(&req);
if(e) return e; if(e) {
nn_clearstack(computer);
return e;
}
size_t endOfTrim = computer->stackSize - req.returnCount; size_t endOfTrim = computer->stackSize - req.returnCount;
for(size_t i = 0; i < endOfTrim; i++) { for(size_t i = 0; i < endOfTrim; i++) {
@@ -3190,7 +3198,8 @@ static nn_Exit nn_eepromHandler(nn_ComponentRequest *req) {
if(ereq.buflen == 0) return nn_pushnull(C); if(ereq.buflen == 0) return nn_pushnull(C);
return nn_pushlstring(C, ereq.buf, ereq.buflen); return nn_pushlstring(C, ereq.buf, ereq.buflen);
} }
return NN_OK; nn_setError(C, "not implemented yet");
return NN_EBADCALL;
} }
nn_Component *nn_createEEPROM(nn_Universe *universe, const char *address, const nn_EEPROM *eeprom, void *state, nn_EEPROMHandler *handler) { nn_Component *nn_createEEPROM(nn_Universe *universe, const char *address, const nn_EEPROM *eeprom, void *state, nn_EEPROMHandler *handler) {
@@ -3303,3 +3312,374 @@ fail:
nn_free(ctx, state, sizeof(*state)); nn_free(ctx, state, sizeof(*state));
return NULL; return NULL;
} }
typedef enum nn_FSNum {
// drive stuff
NN_FSNUM_SPACETOTAL,
NN_FSNUM_SPACEUSED,
NN_FSNUM_GETLABEL,
NN_FSNUM_SETLABEL,
NN_FSNUM_ISRO,
// file I/O
NN_FSNUM_OPEN,
NN_FSNUM_READ,
NN_FSNUM_WRITE,
NN_FSNUM_SEEK,
NN_FSNUM_CLOSE,
// metadata
NN_FSNUM_LIST,
NN_FSNUM_EXISTS,
NN_FSNUM_ISDIR,
NN_FSNUM_SIZE,
NN_FSNUM_LASTMODIFIED,
// exotic
NN_FSNUM_MKDIR,
NN_FSNUM_REMOVE,
NN_FSNUM_RENAME,
NN_FSNUM_COUNT,
} nn_FSNum;
typedef struct nn_FSState {
nn_Context *ctx;
nn_Filesystem fs;
void *state;
nn_FSHandler *handler;
} nn_FSState;
static nn_Exit nn_fsPathCheck(nn_Computer *C, char buf[NN_MAX_PATH], const char *path) {
size_t l = nn_strlen(path);
if(l >= NN_MAX_PATH) {
nn_setError(C, "path too long");
return NN_EBADCALL;
}
nn_simplifyPath(path, buf);
return NN_OK;
}
static nn_Exit nn_fsHandler(nn_ComponentRequest *req) {
if(req->action == NN_COMP_SIGNAL) return NN_OK;
if(req->action == NN_COMP_CHECKMETHOD) return NN_OK;
nn_FSState *state = req->state;
nn_FSRequest freq;
freq.ctx = req->ctx;
freq.computer = req->computer;
freq.state = state->state;
freq.fs = &state->fs;
if(req->action == NN_COMP_DROP) {
freq.action = NN_FS_DROP;
state->handler(&freq);
nn_free(req->ctx, state, sizeof(*state));
return NN_OK;
}
nn_Computer *C = req->computer;
nn_FSNum method = req->methodIdx;
nn_Exit e = NN_OK;
if(method == NN_FSNUM_SPACETOTAL) {
req->returnCount = 1;
return nn_pushinteger(C, state->fs.spaceTotal);
}
if(method == NN_FSNUM_SPACEUSED) {
freq.action = NN_FS_SPACEUSED;
freq.spaceUsed = 0;
e = state->handler(&freq);
if(e) return e;
req->returnCount = 1;
return nn_pushinteger(C, freq.spaceUsed);
}
if(method == NN_FSNUM_GETLABEL) {
char buf[NN_MAX_LABEL];
freq.action = NN_FS_GETLABEL;
freq.getlabel.buf = buf;
freq.getlabel.len = NN_MAX_LABEL;
e = state->handler(&freq);
if(e) return e;
req->returnCount = 1;
if(freq.getlabel.len == 0) return nn_pushnull(C);
return nn_pushlstring(C, freq.getlabel.buf, freq.getlabel.len);
}
if(method == NN_FSNUM_SETLABEL) {
e = nn_defaultstring(C, 0, "");
if(e) return e;
if(nn_checkstring(C, 0, "bad argument #1 (label expected)")) return NN_EBADCALL;
freq.action = NN_FS_SETLABEL;
freq.setlabel.buf = nn_tolstring(C, 0, &freq.setlabel.len);
e = state->handler(&freq);
if(e) return e;
req->returnCount = 1;
if(freq.setlabel.len == 0) return nn_pushnull(C);
return nn_pushlstring(C, freq.setlabel.buf, freq.setlabel.len);
}
if(method == NN_FSNUM_ISRO) {
freq.action = NN_FS_ISRO;
e = state->handler(&freq);
if(e) return e;
req->returnCount = 1;
return nn_pushbool(C, freq.isReadonly);
}
if(method == NN_FSNUM_OPEN) {
if(nn_checkstring(C, 0, "bad argument #1 (path expected)")) return NN_EBADCALL;
e = nn_defaultstring(C, 1, "r");
if(e) return e;
if(nn_checkstring(C, 1, "bad argument #2 (mode expected)")) return NN_EBADCALL;
char truepath[NN_MAX_PATH];
e = nn_fsPathCheck(C, truepath, nn_tostring(C, 0));
if(e) return e;
freq.action = NN_FS_OPEN;
freq.open.path = truepath;
freq.open.mode = nn_tostring(C, 1);
e = state->handler(&freq);
if(e) return e;
req->returnCount = 1;
return nn_pushinteger(C, freq.fd);
}
if(method == NN_FSNUM_READ) {
if(nn_checkinteger(C, 0, "bad argument #1 (fd expected)")) return NN_EBADCALL;
e = nn_defaultinteger(C, 1, NN_MAX_READ);
if(e) return e;
if(nn_checknumber(C, 1, "bad argument #2 (number expected)")) return NN_EBADCALL;
double requested = nn_tonumber(C, 1);
if(requested > NN_MAX_READ) requested = NN_MAX_READ;
freq.action = NN_FS_CLOSE;
freq.fd = nn_tointeger(C, 0);
char buf[NN_MAX_READ];
freq.read.buf = buf;
freq.read.len = requested;
e = state->handler(&freq);
if(e) return e;
if(freq.read.buf == NULL) return NN_OK;
req->returnCount = 1;
return nn_pushbool(C, true);
}
if(method == NN_FSNUM_WRITE) {
if(nn_checkinteger(C, 0, "bad argument #1 (fd expected)")) return NN_EBADCALL;
if(nn_checkstring(C, 1, "bad argument #2 (string expected)")) return NN_EBADCALL;
freq.action = NN_FS_WRITE;
freq.fd = nn_tointeger(C, 0);
freq.write.buf = nn_tolstring(C, 1, &freq.write.len);
e = state->handler(&freq);
if(e) return e;
req->returnCount = 1;
return nn_pushbool(C, true);
}
if(method == NN_FSNUM_SEEK) {
if(nn_checkinteger(C, 0, "bad argument #1 (fd expected)")) return NN_EBADCALL;
e = nn_defaultstring(C, 1, "cur");
if(e) return e;
if(nn_checkinteger(C, 1, "bad argument #2 (whence expected)")) return NN_EBADCALL;
e = nn_defaultinteger(C, 2, 0);
if(e) return e;
if(nn_checkinteger(C, 2, "bad argument #3 (integer expected)")) return NN_EBADCALL;
const char *whence = nn_tostring(C, 1);
nn_FSWhence seek = NN_SEEK_SET;
if(nn_strcmp(whence, "set") == 0) {
seek = NN_SEEK_SET;
}
if(nn_strcmp(whence, "cur") == 0) {
seek = NN_SEEK_CUR;
}
if(nn_strcmp(whence, "end") == 0) {
seek = NN_SEEK_END;
}
freq.action = NN_FS_CLOSE;
freq.fd = nn_tointeger(C, 0);
freq.seek.whence = seek;
freq.seek.off = nn_tointeger(C, 2);
e = state->handler(&freq);
if(e) return e;
req->returnCount = 1;
return nn_pushbool(C, true);
}
if(method == NN_FSNUM_CLOSE) {
if(nn_checkinteger(C, 0, "bad argument #1 (fd expected)")) return NN_EBADCALL;
freq.action = NN_FS_CLOSE;
freq.fd = nn_tointeger(C, 0);
e = state->handler(&freq);
if(e) return e;
req->returnCount = 1;
return nn_pushbool(C, true);
}
if(method == NN_FSNUM_LIST) {
if(nn_checkstring(C, 0, "bad argument #1 (path expected)")) return NN_EBADCALL;
char truepath[NN_MAX_PATH];
e = nn_fsPathCheck(C, truepath, nn_tostring(C, 0));
if(e) return e;
freq.action = NN_FS_OPENDIR;
freq.opendir = truepath;
e = state->handler(&freq);
if(e) return e;
int dirfd = freq.fd;
size_t entCount = 0;
while(true) {
char name[NN_MAX_PATH];
freq.action = NN_FS_READDIR;
freq.fd = dirfd;
freq.readdir.buf = name;
freq.readdir.len = NN_MAX_PATH;
e = state->handler(&freq);
if(e) goto done;
if(freq.readdir.buf == NULL) break;
if(nn_isLiterallyJust(freq.readdir.buf, freq.readdir.len, '.')) continue;
e = nn_pushlstring(C, freq.readdir.buf, freq.readdir.len);
if(e) goto done;
entCount++;
}
done:;
freq.action = NN_FS_CLOSEDIR;
freq.fd = dirfd;
state->handler(&freq);
if(e) return e;
req->returnCount = 1;
return nn_pusharraytable(C, entCount);
}
if(method == NN_FSNUM_EXISTS) {
if(nn_checkstring(C, 0, "bad argument #1 (path expected)")) return NN_EBADCALL;
char truepath[NN_MAX_PATH];
e = nn_fsPathCheck(C, truepath, nn_tostring(C, 0));
if(e) return e;
freq.action = NN_FS_STAT;
freq.stat.path = truepath;
e = state->handler(&freq);
if(e) return e;
req->returnCount = 1;
return nn_pushbool(C, freq.stat.path != NULL);
}
if(method == NN_FSNUM_ISDIR) {
if(nn_checkstring(C, 0, "bad argument #1 (path expected)")) return NN_EBADCALL;
char truepath[NN_MAX_PATH];
e = nn_fsPathCheck(C, truepath, nn_tostring(C, 0));
if(e) return e;
freq.action = NN_FS_STAT;
freq.stat.path = truepath;
e = state->handler(&freq);
if(e) return e;
if(freq.stat.path == NULL) {
nn_setError(C, "no such file or directory");
return NN_EBADCALL;
}
req->returnCount = 1;
return nn_pushbool(C, freq.stat.isDirectory);
}
if(method == NN_FSNUM_SIZE) {
if(nn_checkstring(C, 0, "bad argument #1 (path expected)")) return NN_EBADCALL;
char truepath[NN_MAX_PATH];
e = nn_fsPathCheck(C, truepath, nn_tostring(C, 0));
if(e) return e;
freq.action = NN_FS_STAT;
freq.stat.path = truepath;
e = state->handler(&freq);
if(e) return e;
if(freq.stat.path == NULL) {
nn_setError(C, "no such file or directory");
return NN_EBADCALL;
}
req->returnCount = 1;
return nn_pushinteger(C, freq.stat.size);
}
if(method == NN_FSNUM_LASTMODIFIED) {
if(nn_checkstring(C, 0, "bad argument #1 (path expected)")) return NN_EBADCALL;
char truepath[NN_MAX_PATH];
e = nn_fsPathCheck(C, truepath, nn_tostring(C, 0));
if(e) return e;
freq.action = NN_FS_STAT;
freq.stat.path = truepath;
e = state->handler(&freq);
if(e) return e;
if(freq.stat.path == NULL) {
nn_setError(C, "no such file or directory");
return NN_EBADCALL;
}
req->returnCount = 1;
return nn_pushinteger(C, freq.stat.lastModified * 1000);
}
if(method == NN_FSNUM_MKDIR) {
if(nn_checkstring(C, 0, "bad argument #1 (path expected)")) return NN_EBADCALL;
char truepath[NN_MAX_PATH];
e = nn_fsPathCheck(C, truepath, nn_tostring(C, 0));
if(e) return e;
freq.action = NN_FS_MKDIR;
freq.mkdir = truepath;
e = state->handler(&freq);
if(e) return e;
req->returnCount = 1;
return nn_pushbool(C, true);
}
if(method == NN_FSNUM_REMOVE) {
if(nn_checkstring(C, 0, "bad argument #1 (path expected)")) return NN_EBADCALL;
char truepath[NN_MAX_PATH];
e = nn_fsPathCheck(C, truepath, nn_tostring(C, 0));
if(e) return e;
freq.action = NN_FS_RENAME;
freq.rename.from = truepath;
freq.rename.to = NULL;
e = state->handler(&freq);
if(e) return e;
req->returnCount = 1;
return nn_pushbool(C, true);
}
if(method == NN_FSNUM_RENAME) {
if(nn_checkstring(C, 0, "bad argument #1 (path expected)")) return NN_EBADCALL;
if(nn_checkstring(C, 1, "bad argument #2 (path expected)")) return NN_EBADCALL;
char truefrom[NN_MAX_PATH];
e = nn_fsPathCheck(C, truefrom, nn_tostring(C, 0));
if(e) return e;
char trueto[NN_MAX_PATH];
e = nn_fsPathCheck(C, trueto, nn_tostring(C, 1));
if(e) return e;
freq.action = NN_FS_RENAME;
freq.rename.from = truefrom;
freq.rename.to = trueto;
e = state->handler(&freq);
if(e) return e;
req->returnCount = 1;
return nn_pushbool(C, true);
}
nn_setError(C, "not implemented yet");
return NN_EBADCALL;
}
nn_Component *nn_createFilesystem(nn_Universe *universe, const char *address, const nn_Filesystem *fs, void *state, nn_FSHandler *handler) {
nn_Component *c = nn_createComponent(universe, address, "filesystem");
if(c == NULL) return NULL;
const nn_Method methods[NN_FSNUM_COUNT] = {
[NN_FSNUM_SPACETOTAL] = {"spaceTotal", "function(): integer - Capacity of the drive", NN_DIRECT},
[NN_FSNUM_SPACEUSED] = {"spaceUsed", "function(): integer - Amount of space used", NN_DIRECT},
[NN_FSNUM_GETLABEL] = {"getLabel", "function(): string? - Gets the label of the drive, if any", NN_DIRECT},
[NN_FSNUM_SETLABEL] = {"setLabel", "function(label?: string): string - Sets the label of the drive. Returns the new label, which may be truncated", NN_DIRECT},
[NN_FSNUM_ISRO] = {"isReadOnly", "function(): boolean - Returns whether the drive is read-only", NN_DIRECT},
[NN_FSNUM_OPEN] = {"open", "function(path: string, mode?: 'r'|'w'|'a'): integer - Open a file", NN_DIRECT},
[NN_FSNUM_READ] = {"read", "function(fd: integer, len?: integer): string? - Read from a file, returns nothing on EoF", NN_DIRECT},
[NN_FSNUM_WRITE] = {"write", "function(fd: integer, data: string): boolean - Writes to a file, returns whether the operation succeeded", NN_DIRECT},
[NN_FSNUM_SEEK] = {"seek", "function(fd: integer, whence?: 'set'|'cur'|'end', off?: integer): integer - Seeks a file, returns new position", NN_DIRECT},
[NN_FSNUM_CLOSE] = {"close", "function(fd: integer): boolean - Close a file", NN_DIRECT},
[NN_FSNUM_LIST] = {"list", "function(path: string): string[] - Returns the entries in a directory", NN_DIRECT},
[NN_FSNUM_EXISTS] = {"exists", "function(path: string): boolean - Returns whether an entry exists", NN_DIRECT},
[NN_FSNUM_ISDIR] = {"isDirectory", "function(path: string): boolean - Returns whether an entry is a directory", NN_DIRECT},
[NN_FSNUM_SIZE] = {"size", "function(path: string): integer - Returns the size of an entry", NN_DIRECT},
[NN_FSNUM_LASTMODIFIED] = {"lastModified", "function(path: string): integer - Returns the UNIX timestamp of the last modified time", NN_DIRECT},
[NN_FSNUM_MKDIR] = {"makeDirectory", "function(path: string): boolean - Create a directory, recursively. Does not fail if directory already exists", NN_DIRECT},
[NN_FSNUM_REMOVE] = {"remove", "function(path: string): boolean - Recursively deletes an entry", NN_DIRECT},
[NN_FSNUM_RENAME] = {"rename", "function(from: string, to: string): boolean - Renames/moves an entry", NN_DIRECT},
};
nn_Exit e = nn_setComponentMethodsArray(c, methods, NN_FSNUM_COUNT);
if(e) {
nn_dropComponent(c);
return NULL;
}
nn_Context *ctx = &universe->ctx;
nn_FSState *fsstate = nn_alloc(ctx, sizeof(*fsstate));
if(fsstate == NULL) {
nn_dropComponent(c);
return NULL;
}
fsstate->ctx = ctx;
fsstate->fs = *fs;
fsstate->state = state;
fsstate->handler = handler;
nn_setComponentState(c, fsstate);
nn_setComponentHandler(c, nn_fsHandler);
return c;
}

View File

@@ -50,13 +50,13 @@ extern "C" {
#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.
#define NN_MAX_READ 65536 #define NN_MAX_READ 8192
// the maximum size of a label // the maximum size of a label
#define NN_MAX_LABEL 256 #define NN_MAX_LABEL 256
// maximum size of a wakeup message // maximum size of a wakeup message
#define NN_MAX_WAKEUPMSG 2048 #define NN_MAX_WAKEUPMSG 2048
// the maximum amount of file descriptors that can be open simultaneously // the maximum amount of file descriptors that can be open simultaneously
#define NN_MAX_OPENFILES 128 #define NN_MAX_OPENFILES 16
// the maximum amount of userdata that can be sent simultaneously. // the maximum amount of userdata that can be sent simultaneously.
#define NN_MAX_USERDATA 64 #define NN_MAX_USERDATA 64
// maximum size of a signal, computed the same as modem packet costs. // maximum size of a signal, computed the same as modem packet costs.
@@ -227,6 +227,22 @@ void *nn_alloc(nn_Context *ctx, size_t size);
void nn_free(nn_Context *ctx, void *memory, size_t size); void nn_free(nn_Context *ctx, void *memory, size_t size);
void *nn_realloc(nn_Context *ctx, void *memory, size_t oldSize, size_t newSize); void *nn_realloc(nn_Context *ctx, void *memory, size_t oldSize, size_t newSize);
char *nn_strdup(nn_Context *ctx, const char *s);
void nn_strfree(nn_Context *ctx, char *s);
typedef struct nn_Lock nn_Lock;
nn_Lock *nn_createLock(nn_Context *ctx);
void nn_destroyLock(nn_Context *ctx, nn_Lock *lock);
void nn_lock(nn_Context *ctx, nn_Lock *lock);
void nn_unlock(nn_Context *ctx, nn_Lock *lock);
double nn_currentTime(nn_Context *ctx);
size_t nn_rand(nn_Context *ctx);
double nn_randf(nn_Context *ctx);
double nn_randfi(nn_Context *ctx);
typedef char nn_uuid[37]; typedef char nn_uuid[37];
void nn_randomUUID(nn_Context *ctx, nn_uuid uuid); void nn_randomUUID(nn_Context *ctx, nn_uuid uuid);
@@ -895,16 +911,114 @@ typedef struct nn_Filesystem {
// the maximum capacity of the filesystem // the maximum capacity of the filesystem
size_t spaceTotal; size_t spaceTotal;
// how many read calls can be done per tick // how many read calls can be done per tick
// list, exists, size, lastModified, isDirectory, seek also count as reads. // seek also count as reads.
double readsPerTick; double readsPerTick;
// how many write calls can be done per tick // how many write calls can be done per tick
// makeDirectory, open, remove and rename also count as writes.
double writesPerTick; double writesPerTick;
// The energy cost of an actual read/write. // 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. // It is per-byte, so if a read returns 4096 bytes, then this cost is multiplied by 4096.
double dataEnergyCost; double dataEnergyCost;
} nn_Filesystem; } nn_Filesystem;
typedef enum nn_FSAction {
NN_FS_DROP,
// drive metadata
NN_FS_SPACEUSED,
NN_FS_GETLABEL,
NN_FS_SETLABEL,
NN_FS_ISRO,
// for file I/O
NN_FS_OPEN,
NN_FS_READ,
NN_FS_WRITE,
NN_FS_SEEK,
NN_FS_CLOSE,
// for list
NN_FS_OPENDIR,
NN_FS_READDIR,
NN_FS_CLOSEDIR,
// checking metadata
NN_FS_STAT,
// make directory, recursively
NN_FS_MKDIR,
// rename, if renamed to NULL then remove
NN_FS_RENAME,
} nn_FSAction;
typedef enum nn_FSWhence {
NN_SEEK_SET,
NN_SEEK_CUR,
NN_SEEK_END,
} nn_FSWhence;
typedef struct nn_FSRequest {
nn_Context *ctx;
nn_Computer *computer;
void *state;
const nn_Filesystem *fs;
nn_FSAction action;
int fd;
union {
struct {
const char *path;
const char *mode;
} open;
struct {
char *buf;
size_t len;
} read;
struct {
const char *buf;
size_t len;
} write;
struct {
nn_FSWhence whence;
// set to new offset
int off;
} seek;
const char *opendir;
struct {
char *buf;
// set to length of entry name
size_t len;
} readdir;
struct {
// set to NULL if missing
const char *path;
// whether it is a directory
bool isDirectory;
// in seconds. Result will be multiplied by 1000.
// This is because OpenOS code is garbage.
size_t lastModified;
// size. 0 for directories.
size_t size;
} stat;
struct {
const char *from;
// if NULL, delete from, recursively.
const char *to;
} rename;
const char *mkdir;
struct {
const char *buf;
size_t len;
} setlabel;
struct {
char *buf;
size_t len;
} getlabel;
bool isReadonly;
size_t spaceUsed;
};
} nn_FSRequest;
typedef nn_Exit (nn_FSHandler)(nn_FSRequest *request);
// 4 Tiers. // 4 Tiers.
// 0 - Tier 1 equivalent // 0 - Tier 1 equivalent
// 1 - Tier 2 equivalent // 1 - Tier 2 equivalent
@@ -950,6 +1064,9 @@ typedef struct nn_VFilesystem {
nn_VFileNode *image; nn_VFileNode *image;
} nn_VFilesystem; } nn_VFilesystem;
nn_Component *nn_createFilesystem(nn_Universe *universe, const char *address, const nn_Filesystem *fs, void *state, nn_FSHandler *handler);
nn_Component *nn_createVFilesystem(nn_Universe *universe, const char *address, const nn_VFilesystem *vfs, const nn_Filesystem *fs);
// Drive class // Drive class
typedef struct nn_Drive { typedef struct nn_Drive {