drives, rebalancing, and a broken tmpfs impl

that tmpfs impl needs to be reworked to heck and back!
This commit is contained in:
2026-04-03 23:58:23 +02:00
parent e6cf5990bb
commit 32f94cf02b
9 changed files with 1291 additions and 236 deletions

11
TODO.md
View File

@@ -1,10 +1,8 @@
# For MVP functionality # For MVP functionality
- `gpu`, `screen` class implementations
- write a tester OS, basically a menu with tests to run - write a tester OS, basically a menu with tests to run
- move veeprom into NCL as tmpeeprom
- tmpdrive - tmpdrive
- tmpfs - tmpfs (reork the whole thing)
- device info - device info
- userdata support - userdata support
@@ -53,6 +51,7 @@ Not everything OC has (as a few of them are really MC-centered) but most of it.
- `serial` component, for serial communications with other devices (USB?) - `serial` component, for serial communications with other devices (USB?)
- `iron_noteblock` component - `iron_noteblock` component
- `colorful_lamp` component - `colorful_lamp` component
- OpenSolidState flash storage
# To make it good # To make it good
@@ -67,3 +66,9 @@ NOTE: we're mostly bottlenecked by the architecture (typically a Lua VM) and the
- make signals use a circular buffer instead of a simple array - make signals use a circular buffer instead of a simple array
- use more arenas if possible - use more arenas if possible
# Drive costs
The drive has platters just like in OC, as well as a cache line.
The read cost is only factored in if `cachelineOf(lastSector) != cachelineOf(newSector)`.
Seek cost is also in whole cache lines.

View File

@@ -195,6 +195,7 @@ pub fn build(b: *std.Build) !void {
.files = &.{ .files = &.{
"src/luaarch.c", "src/luaarch.c",
"src/main.c", "src/main.c",
"src/glyphcache.c",
}, },
.flags = &.{ .flags = &.{
if (opts.baremetal) "-DNN_BAREMETAL" else "", if (opts.baremetal) "-DNN_BAREMETAL" else "",

View File

@@ -3,6 +3,8 @@
-- Do not use in a serious context, you will be hacked. -- Do not use in a serious context, you will be hacked.
-- There is no sandboxing here. -- There is no sandboxing here.
os.exit = nil
os.execute = nil
local sysyieldobj = {} local sysyieldobj = {}
local coroutine = coroutine local coroutine = coroutine

View File

@@ -5,12 +5,12 @@
#include "neonucleus.h" #include "neonucleus.h"
#include "ncomplib.h" #include "ncomplib.h"
#include "glyphcache.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <raylib.h> #include <raylib.h>
#include <errno.h>
#ifdef NN_WINDOWS #ifdef NN_WINDOWS
#include <setjmp.h> #include <setjmp.h>
@@ -364,31 +364,44 @@ int main(int argc, char **argv) {
{"log", "log(msg: string) - Log to stdout", NN_DIRECT}, {"log", "log(msg: string) - Log to stdout", NN_DIRECT},
{NULL}, {NULL},
}; };
nn_Component *ocelotCard = nn_createComponent(u, "ocelot", "ocelot"); nn_Component *ocelotCard = nn_createComponent(u, NULL, "ocelot");
nn_setComponentMethods(ocelotCard, sandboxMethods); nn_setComponentMethods(ocelotCard, sandboxMethods);
nn_setComponentHandler(ocelotCard, sandbox_handler); nn_setComponentHandler(ocelotCard, sandbox_handler);
const nn_VEEPROM veeprom = { nn_Component *eepromCard = ncl_createEEPROM(u, NULL, &nn_defaultEEPROMs[3], minBIOS, strlen(minBIOS), false);
.code = minBIOS,
.codelen = strlen(minBIOS),
.data = NULL,
.datalen = 0,
.label = NULL,
.labellen = 0,
.arch = NULL,
.isReadonly = false,
};
nn_Component *eepromCard = nn_createVEEPROM(u, "eeprom", &veeprom, &nn_defaultEEPROMs[3]); char mainfspath[NN_MAX_PATH];
snprintf(mainfspath, NN_MAX_PATH, "data/%s", mainDir);
nn_Component *managedfs = ncl_createFilesystem(u, NULL, mainfspath, &nn_defaultFilesystems[3], true);
nn_Component *tmpfs = ncl_createTmpFS(u, NULL, &nn_defaultTmpFS, NCL_FILECOST_DEFAULT, false);
nn_Component *testingfs = ncl_createTmpFS(u, NULL, &nn_defaultFilesystems[3], NCL_FILECOST_DEFAULT, false);
nn_Component *managedfs = ncl_createFilesystem(u, "mainFS", "data/openos", &nn_defaultFilesystems[3], true); const char * const testDriveData =
"local g, s, d = component.list('gpu')(), component.list('screen')(), component.list('drive')()\n"
"component.invoke(g, 'bind', s, true)\n"
"component.invoke(g, 'set', 1, 1, 'starting...')\n"
"local start = computer.uptime()\n"
"local bc = component.invoke(d, 'getCapacity') / component.invoke(d, 'getSectorSize')\n"
"for i=1,bc do component.invoke(d, 'readSector', i) end\n"
"local now = computer.uptime()\n"
"component.invoke(g, 'set', 1, 2, 'took ' .. (now - start) .. 's')\n"
"while computer.uptime() < now + 3 do computer.pullSignal(0.05) end\n"
"computer.shutdown(true)\n"
;
nn_Component *testDrive = ncl_createDrive(u, NULL, &nn_floppyDrive, testDriveData, strlen(testDriveData), false);
ncl_setCLabel(managedfs, "Main Filesystem");
ncl_setCLabel(testingfs, "Secondary Filesystem");
ncl_setCLabel(testDrive, "Unmanaged Storage");
size_t ramTotal = 0; size_t ramTotal = 0;
ramTotal += nn_ramSizes[5]; ramTotal += nn_ramSizes[5];
SetExitKey(KEY_NULL); SetExitKey(KEY_NULL);
Font font = LoadFont("unscii-16-full.ttf"); const char *fontPath = getenv("NN_FONT");
if(fontPath == NULL) fontPath = "unscii-16-full.ttf";
ncl_GlyphCache *gc = ncl_createGlyphCache(fontPath, 20);
double tickDelay = 0.05; double tickDelay = 0.05;
if(getenv("NN_TICKDELAY") != NULL) { if(getenv("NN_TICKDELAY") != NULL) {
@@ -404,7 +417,7 @@ int main(int argc, char **argv) {
double wattage = 0; double wattage = 0;
nn_Component *screen = ncl_createScreen(u, NULL, &nn_defaultScreens[3]); nn_Component *screen = ncl_createScreen(u, NULL, &nn_defaultScreens[3]);
nn_Component *gpuCard = ncl_createGPU(u, NULL, &nn_defaultGPUs[2]); nn_Component *gpuCard = ncl_createGPU(u, NULL, &nn_defaultGPUs[3]);
nn_Component *keyboard = nn_createComponent( nn_Component *keyboard = nn_createComponent(
u, "mainKB", "keyboard"); u, "mainKB", "keyboard");
@@ -420,7 +433,6 @@ int main(int argc, char **argv) {
} }
} }
restart:; restart:;
nn_Computer *c = nn_createComputer(u, NULL, "computer0", ramTotal, 256, 256); nn_Computer *c = nn_createComputer(u, NULL, "computer0", ramTotal, 256, 256);
if(showStats) { if(showStats) {
@@ -434,12 +446,17 @@ restart:;
nn_setArchitecture(c, &arch); nn_setArchitecture(c, &arch);
nn_addSupportedArchitecture(c, &arch); nn_addSupportedArchitecture(c, &arch);
//nn_setTmpAddress(c, nn_getComponentAddress(tmpfs));
nn_mountComponent(c, screen, -1); nn_mountComponent(c, screen, -1);
nn_mountComponent(c, ocelotCard, -1); nn_mountComponent(c, ocelotCard, -1);
//nn_mountComponent(c, tmpfs, -1);
nn_mountComponent(c, keyboard, -1);
nn_mountComponent(c, eepromCard, 0); nn_mountComponent(c, eepromCard, 0);
nn_mountComponent(c, managedfs, 1); nn_mountComponent(c, managedfs, 1);
nn_mountComponent(c, gpuCard, 2); nn_mountComponent(c, gpuCard, 2);
nn_mountComponent(c, keyboard, -1); nn_mountComponent(c, testingfs, 3);
nn_mountComponent(c, testDrive, 4);
while(true) { while(true) {
if(WindowShouldClose()) break; if(WindowShouldClose()) break;
@@ -448,15 +465,20 @@ restart:;
// drawing the screen // drawing the screen
{ {
int offX = 0;
int offY = 0;
int cheight = 20;
int cwidth = MeasureText("A", cheight);
ncl_ScreenState *scrbuf = nn_getComponentState(screen); ncl_ScreenState *scrbuf = nn_getComponentState(screen);
ncl_lockScreen(scrbuf); ncl_lockScreen(scrbuf);
size_t scrw, scrh; size_t scrw, scrh;
ncl_getScreenResolution(scrbuf, &scrw, &scrh); ncl_getScreenViewport(scrbuf, &scrw, &scrh);
int cheight = GetScreenHeight() / scrh;
if(cheight != ncl_cellHeight(gc)) {
ncl_destroyGlyphCache(gc);
gc = ncl_createGlyphCache(fontPath, cheight);
}
int cwidth = ncl_cellWidth(gc);
int offX = (GetScreenWidth() - cwidth * scrw) / 2;
int offY = (GetScreenHeight() - cheight * scrh) / 2;
for(int y = 1; y <= scrh; y++) { for(int y = 1; y <= scrh; y++) {
for(int x = 1; x <= scrw; x++) { for(int x = 1; x <= scrw; x++) {
ncl_Pixel p = ncl_getScreenPixel(scrbuf, x, y); ncl_Pixel p = ncl_getScreenPixel(scrbuf, x, y);
@@ -464,11 +486,16 @@ restart:;
offX + (x - 1) * cwidth, offX + (x - 1) * cwidth,
offY + (y - 1) * cheight, offY + (y - 1) * cheight,
}; };
ncl_needGlyph(gc, p.codepoint);
DrawRectangle(pos.x, pos.y, cwidth, cheight, ne_processColor(p.bgColor)); DrawRectangle(pos.x, pos.y, cwidth, cheight, ne_processColor(p.bgColor));
DrawTextCodepoint(font, p.codepoint, pos, cheight, ne_processColor(p.fgColor)); if(p.codepoint != 0) {
ncl_drawGlyph(gc, p.codepoint, pos, cheight, ne_processColor(p.fgColor));
} }
} }
}
DrawRectangleLines(offX, offY, cwidth * scrw, cheight * scrh, WHITE);
ncl_unlockScreen(scrbuf); ncl_unlockScreen(scrbuf);
ncl_flushGlyphs(gc);
} }
int statY = 10; int statY = 10;
@@ -492,7 +519,8 @@ restart:;
// 1: clipboard // 1: clipboard
if(IsMouseButtonPressed(MOUSE_BUTTON_MIDDLE)) { if(IsMouseButtonPressed(MOUSE_BUTTON_MIDDLE)) {
nn_pushClipboard(c, "mainKB", GetClipboardText(), player); const char *t = GetClipboardText();
if(t != NULL) nn_pushClipboard(c, "mainKB", t, player);
} }
while(1) { while(1) {
@@ -571,12 +599,15 @@ cleanup:;
nn_dropComponent(ocelotCard); nn_dropComponent(ocelotCard);
nn_dropComponent(eepromCard); nn_dropComponent(eepromCard);
nn_dropComponent(managedfs); nn_dropComponent(managedfs);
nn_dropComponent(tmpfs);
nn_dropComponent(testingfs);
nn_dropComponent(testDrive);
nn_dropComponent(screen); nn_dropComponent(screen);
nn_dropComponent(gpuCard); nn_dropComponent(gpuCard);
nn_dropComponent(keyboard); nn_dropComponent(keyboard);
// rip the universe // rip the universe
nn_destroyUniverse(u); nn_destroyUniverse(u);
UnloadFont(font); ncl_destroyGlyphCache(gc);
CloseWindow(); CloseWindow();
free(sand.buf); free(sand.buf);
return 0; return 0;

File diff suppressed because it is too large Load Diff

View File

@@ -11,9 +11,7 @@
#define NCL_GPU "ncl-gpu" #define NCL_GPU "ncl-gpu"
#define NCL_SCREEN "ncl-screen" #define NCL_SCREEN "ncl-screen"
#define NCL_TMPEEPROM "ncl-tmpeeprom"
#define NCL_TMPFS "ncl-tmpfs" #define NCL_TMPFS "ncl-tmpfs"
#define NCL_TMPDRIVE "ncl-tmpdrive"
// Default file cost. // Default file cost.
// This is for a normal HDD/floppy. // This is for a normal HDD/floppy.
@@ -194,34 +192,30 @@ nn_Exit ncl_loadComponentState(nn_Component *comp, const ncl_EncodedState *state
size_t ncl_getLabel(nn_Component *c, char buf[NN_MAX_LABEL]); size_t ncl_getLabel(nn_Component *c, char buf[NN_MAX_LABEL]);
size_t ncl_setLabel(nn_Component *c, const char *label, size_t len); size_t ncl_setLabel(nn_Component *c, const char *label, size_t len);
size_t ncl_setCLabel(nn_Component *c, const char *label);
nn_Component *ncl_createFilesystem(nn_Universe *universe, const char *address, const char *path, const nn_Filesystem *fs, bool isReadonly); nn_Component *ncl_createFilesystem(nn_Universe *universe, const char *address, const char *path, const nn_Filesystem *fs, bool isReadonly);
#define NCL_VFS_NAMEMAX 32
// Creates a tmpfs. // Creates a tmpfs.
// This component is mostly treated like a normal filesystem, // This component is mostly treated like a normal filesystem,
// except you obviously cannot bind a tmpfs to it. // except you obviously cannot bind a vfs to it.
// Do note, it is illegal to mix encoded state between normal filesystem // Do note, it is illegal to mix encoded state between normal filesystem
// and tmpfs. // and tmpfs.
nn_Component *ncl_createTmpFS(nn_Universe *universe, const nn_Filesystem *fs); nn_Component *ncl_createTmpFS(nn_Universe *universe, const char *address, const nn_Filesystem *fs, size_t fileCost, bool isReadonly);
nn_Component *ncl_createDrive(nn_Universe *universe, const char *address, const char *path, const nn_Drive *drive, bool isReadonly); // this drive has its data in RAM.
// However, the data is not encoded in its state.
// creates a temporary drive, with some initial data // Remember to read the entire drive and save it somewhere before dropping it.
nn_Component *ncl_createTmpDrive(nn_Universe *universe, const nn_EEPROM *eeprom, const char *data, size_t datalen); nn_Component *ncl_createDrive(nn_Universe *universe, const char *address, const nn_Drive *drive, const char *data, size_t len, bool isReadonly);
// data is stored interally // data is stored interally
nn_Component *ncl_createEEPROM(nn_Universe *universe, const char *address, const char *path, bool isReadonly); nn_Component *ncl_createEEPROM(nn_Universe *universe, const char *address, const nn_EEPROM *eeprom, const char *code, size_t codelen, bool isReadonly);
// creates a temporary EEPROM, with some initial code
// the data is stored internally
nn_Component *ncl_createTmpEEPROM(nn_Universe *universe, const nn_EEPROM *eeprom, const char *code, size_t codelen);
// Gets the VFS bound to a filesystem, drive or eeprom. // Gets the VFS bound to a filesystem or drive.
// Returns the default FS if the component is not recognized. // Returns the default FS if the component is not recognized.
ncl_VFS ncl_getVFS(nn_Component *component); ncl_VFS ncl_getVFS(nn_Component *component);
// Sets the VFS bound to a filesystem, drive or eeprom. // Sets the VFS bound to a filesystem or drive.
// This determines the filesystem the operations are run in. // This determines the filesystem the operations are run in.
// Returns the old VFS. // Returns the old VFS.
ncl_VFS ncl_setVFS(nn_Component *component, ncl_VFS vfs); ncl_VFS ncl_setVFS(nn_Component *component, ncl_VFS vfs);
@@ -263,7 +257,6 @@ typedef struct ncl_ComponentStat {
const nn_EEPROM *conf; const nn_EEPROM *conf;
size_t codeUsed; size_t codeUsed;
size_t dataUsed; size_t dataUsed;
const char *codepath;
} eeprom; } eeprom;
struct { struct {
const nn_Filesystem *conf; const nn_Filesystem *conf;
@@ -275,7 +268,6 @@ typedef struct ncl_ComponentStat {
struct { struct {
const nn_Drive *conf; const nn_Drive *conf;
size_t lastSector; size_t lastSector;
const char *path;
} drive; } drive;
struct { struct {
const nn_GPU *conf; const nn_GPU *conf;
@@ -308,6 +300,25 @@ bool ncl_makeReadonly(nn_Component *component);
size_t ncl_getEEPROMData(nn_Component *component, char *buf); size_t ncl_getEEPROMData(nn_Component *component, char *buf);
void ncl_setEEPROMData(nn_Component *component, const char *data, size_t len); void ncl_setEEPROMData(nn_Component *component, const char *data, size_t len);
// Returns the amount of data written.
// The capacity MUST be at least the size of the EEPROM.
size_t ncl_getEEPROMCode(nn_Component *component, char *buf);
void ncl_setEEPROMCode(nn_Component *component, const char *data, size_t len);
size_t ncl_getEEPROMArch(nn_Component *component, char buf[NN_MAX_ARCHNAME]);
void ncl_setEEPROMArch(nn_Component *component, const char *arch, size_t len);
// Reads part of a drive.
// Off is 0-indexed.
size_t ncl_readDrive(nn_Component *component, size_t offset, char *buf, size_t len);
// Writes to part of a drive.
// Off is 0-indexed.
void ncl_writeDrive(nn_Component *component, size_t offset, const char *buf, size_t len);
// Returns the internal memory buffer with the drive data,
// and if len is not NULL, will also write the capacity.
// This is in case you do not want to risk OOM while saving the state.
char *ncl_getDriveBuffer(nn_Component *component, size_t *len);
void ncl_lockScreen(ncl_ScreenState *state); void ncl_lockScreen(ncl_ScreenState *state);
void ncl_unlockScreen(ncl_ScreenState *state); void ncl_unlockScreen(ncl_ScreenState *state);
void ncl_resetScreen(ncl_ScreenState *state); void ncl_resetScreen(ncl_ScreenState *state);

View File

@@ -1708,6 +1708,10 @@ const char *nn_getComponentTypeID(nn_Component *c) {
return c->internalID; return c->internalID;
} }
const char *nn_getComponentAddress(nn_Component *c) {
return c->address;
}
static nn_Exit nn_pushComponentAdded(nn_Computer *c, const char *address, const char *type) { static nn_Exit nn_pushComponentAdded(nn_Computer *c, const char *address, const char *type) {
nn_Exit e = nn_pushstring(c, "component_added"); nn_Exit e = nn_pushstring(c, "component_added");
if(e) return e; if(e) return e;
@@ -2381,24 +2385,28 @@ const nn_Filesystem nn_defaultFilesystems[4] = {
.readsPerTick = 4, .readsPerTick = 4,
.writesPerTick = 2, .writesPerTick = 2,
.dataEnergyCost = 256.0 / NN_MiB, .dataEnergyCost = 256.0 / NN_MiB,
.maxReadSize = 4096,
}, },
NN_INIT(nn_Filesystem) { NN_INIT(nn_Filesystem) {
.spaceTotal = 2 * NN_MiB, .spaceTotal = 2 * NN_MiB,
.readsPerTick = 4, .readsPerTick = 4,
.writesPerTick = 2, .writesPerTick = 2,
.dataEnergyCost = 512.0 / NN_MiB, .dataEnergyCost = 512.0 / NN_MiB,
.maxReadSize = 8192,
}, },
NN_INIT(nn_Filesystem) { NN_INIT(nn_Filesystem) {
.spaceTotal = 4 * NN_MiB, .spaceTotal = 4 * NN_MiB,
.readsPerTick = 7, .readsPerTick = 7,
.writesPerTick = 3, .writesPerTick = 3,
.dataEnergyCost = 1024.0 / NN_MiB, .dataEnergyCost = 1024.0 / NN_MiB,
.maxReadSize = 16384,
}, },
NN_INIT(nn_Filesystem) { NN_INIT(nn_Filesystem) {
.spaceTotal = 8 * NN_MiB, .spaceTotal = 8 * NN_MiB,
.readsPerTick = 13, .readsPerTick = 13,
.writesPerTick = 5, .writesPerTick = 5,
.dataEnergyCost = 2048.0 / NN_MiB, .dataEnergyCost = 2048.0 / NN_MiB,
.maxReadSize = 32768,
}, },
}; };
@@ -2408,6 +2416,7 @@ const nn_Filesystem nn_defaultFloppy = NN_INIT(nn_Filesystem) {
.readsPerTick = 1, .readsPerTick = 1,
.writesPerTick = 1, .writesPerTick = 1,
.dataEnergyCost = 8.0 / NN_MiB, .dataEnergyCost = 8.0 / NN_MiB,
.maxReadSize = 2048,
}; };
const nn_Filesystem nn_defaultTmpFS = NN_INIT(nn_Filesystem) { const nn_Filesystem nn_defaultTmpFS = NN_INIT(nn_Filesystem) {
@@ -2422,9 +2431,10 @@ const nn_Drive nn_defaultDrives[4] = {
.capacity = 1 * NN_MiB, .capacity = 1 * NN_MiB,
.sectorSize = 512, .sectorSize = 512,
.platterCount = 2, .platterCount = 2,
.readsPerTick = 10, .cacheLineSize = 2,
.writesPerTick = 5, .readsPerTick = 20,
.rpm = 1800, .writesPerTick = 10,
.rpm = 3600,
.onlySpinForwards = false, .onlySpinForwards = false,
.dataEnergyCost = 256.0 / NN_MiB, .dataEnergyCost = 256.0 / NN_MiB,
}, },
@@ -2432,9 +2442,10 @@ const nn_Drive nn_defaultDrives[4] = {
.capacity = 2 * NN_MiB, .capacity = 2 * NN_MiB,
.sectorSize = 512, .sectorSize = 512,
.platterCount = 4, .platterCount = 4,
.readsPerTick = 20, .cacheLineSize = 4,
.writesPerTick = 10, .readsPerTick = 30,
.rpm = 1800, .writesPerTick = 15,
.rpm = 5400,
.onlySpinForwards = false, .onlySpinForwards = false,
.dataEnergyCost = 512.0 / NN_MiB, .dataEnergyCost = 512.0 / NN_MiB,
}, },
@@ -2442,9 +2453,10 @@ const nn_Drive nn_defaultDrives[4] = {
.capacity = 4 * NN_MiB, .capacity = 4 * NN_MiB,
.sectorSize = 512, .sectorSize = 512,
.platterCount = 8, .platterCount = 8,
.readsPerTick = 30, .cacheLineSize = 4,
.writesPerTick = 15, .readsPerTick = 40,
.rpm = 1800, .writesPerTick = 20,
.rpm = 7200,
.onlySpinForwards = false, .onlySpinForwards = false,
.dataEnergyCost = 1024.0 / NN_MiB, .dataEnergyCost = 1024.0 / NN_MiB,
}, },
@@ -2452,9 +2464,10 @@ const nn_Drive nn_defaultDrives[4] = {
.capacity = 8 * NN_MiB, .capacity = 8 * NN_MiB,
.sectorSize = 512, .sectorSize = 512,
.platterCount = 16, .platterCount = 16,
.cacheLineSize = 8,
.readsPerTick = 40, .readsPerTick = 40,
.writesPerTick = 20, .writesPerTick = 20,
.rpm = 1800, .rpm = 7200,
.onlySpinForwards = false, .onlySpinForwards = false,
.dataEnergyCost = 2048.0 / NN_MiB, .dataEnergyCost = 2048.0 / NN_MiB,
}, },
@@ -2464,8 +2477,9 @@ const nn_Drive nn_floppyDrive = {
.capacity = 512 * NN_KiB, .capacity = 512 * NN_KiB,
.sectorSize = 512, .sectorSize = 512,
.platterCount = 1, .platterCount = 1,
.readsPerTick = 5, .cacheLineSize = 2,
.writesPerTick = 2, .readsPerTick = 10,
.writesPerTick = 5,
.rpm = 1800, .rpm = 1800,
.onlySpinForwards = true, .onlySpinForwards = true,
.dataEnergyCost = 128.0 / NN_MiB, .dataEnergyCost = 128.0 / NN_MiB,
@@ -2477,7 +2491,7 @@ const nn_ScreenConfig nn_defaultScreens[4] = {
.maxWidth = 50, .maxWidth = 50,
.maxHeight = 16, .maxHeight = 16,
.maxDepth = 1, .maxDepth = 1,
.defaultPalette = NULL, .defaultPalette = nn_ocpalette4,
.paletteColors = 0, .paletteColors = 0,
.editableColors = 0, .editableColors = 0,
.features = NN_SCRF_NONE, .features = NN_SCRF_NONE,
@@ -3396,118 +3410,11 @@ nn_Component *nn_createEEPROM(nn_Universe *universe, const char *address, const
return c; return c;
} }
typedef struct nn_VEEState {
char *code;
size_t codelen;
char *data;
size_t datalen;
char label[NN_MAX_LABEL];
size_t labellen;
char arch[NN_MAX_ARCHNAME];
size_t archlen;
} nn_VEEState;
static nn_Exit nn_veepromHandler(nn_EEPROMRequest *request) {
nn_VEEState *state = request->state;
nn_Computer *C = request->computer;
const nn_EEPROM *eeprom = request->eeprom;
nn_Context *ctx = request->ctx;
if(request->action == NN_EEPROM_DROP) {
nn_free(ctx, state->code, eeprom->size);
nn_free(ctx, state->data, eeprom->dataSize);
nn_free(ctx, state, sizeof(*state));
return NN_OK;
}
if(request->action == NN_EEPROM_GET) {
nn_memcpy(request->buf, state->code, state->codelen);
request->buflen = state->codelen;
return NN_OK;
}
if(request->action == NN_EEPROM_GETDATA) {
nn_memcpy(request->buf, state->data, state->datalen);
request->buflen = state->datalen;
return NN_OK;
}
if(request->action == NN_EEPROM_GETLABEL) {
nn_memcpy(request->buf, state->label, state->labellen);
request->buflen = state->labellen;
return NN_OK;
}
if(request->action == NN_EEPROM_GETARCH) {
nn_memcpy(request->buf, state->arch, state->archlen);
request->buflen = state->archlen;
return NN_OK;
}
if(request->action == NN_EEPROM_SET) {
state->codelen = request->buflen;
nn_memcpy(state->code, request->robuf, state->codelen);
return NN_OK;
}
if(request->action == NN_EEPROM_SETDATA) {
state->datalen = request->buflen;
nn_memcpy(state->data, request->robuf, state->datalen);
return NN_OK;
}
if(request->action == NN_EEPROM_SETLABEL) {
if(request->buflen > NN_MAX_LABEL) request->buflen = NN_MAX_LABEL;
state->labellen = request->buflen;
nn_memcpy(state->label, request->robuf, state->labellen);
return NN_OK;
}
if(request->action == NN_EEPROM_SETARCH) {
if(request->buflen > NN_MAX_ARCHNAME) request->buflen = NN_MAX_ARCHNAME;
state->archlen = request->buflen;
nn_memcpy(state->arch, request->robuf, state->archlen);
return NN_OK;
}
nn_setError(C, "veeprom: not implemented yet");
return NN_EBADCALL;
}
nn_Component *nn_createVEEPROM(nn_Universe *universe, const char *address, const nn_VEEPROM *veeprom, const nn_EEPROM *eeprom) {
nn_Context *ctx = &universe->ctx;
char *code = NULL;
char *data = NULL;
nn_VEEState *state = NULL;
code = nn_alloc(ctx, eeprom->size);
if(code == NULL) goto fail;
data = nn_alloc(ctx, eeprom->dataSize);
if(data == NULL) goto fail;
state = nn_alloc(ctx, sizeof(*state));
if(state == NULL) goto fail;
state->code = code;
nn_memcpy(code, veeprom->code, veeprom->codelen);
state->codelen = veeprom->codelen;
state->data = data;
nn_memcpy(data, veeprom->data, veeprom->datalen);
state->datalen = veeprom->datalen;
nn_memcpy(state->label, veeprom->label, veeprom->labellen);
state->labellen = veeprom->labellen;
if(veeprom->arch == NULL) {
state->archlen = 0;
} else {
state->archlen = nn_strlen(veeprom->arch);
}
nn_memcpy(state->arch, veeprom->arch, state->archlen);
nn_Component *c = nn_createEEPROM(universe, address, eeprom, state, nn_veepromHandler);
if(c == NULL) goto fail;
return c;
fail:
nn_free(ctx, code, eeprom->size);
nn_free(ctx, data, eeprom->dataSize);
nn_free(ctx, state, sizeof(*state));
return NULL;
}
typedef enum nn_FSNum { typedef enum nn_FSNum {
// drive stuff // drive stuff
NN_FSNUM_SPACETOTAL, NN_FSNUM_SPACETOTAL,
NN_FSNUM_SPACEUSED, NN_FSNUM_SPACEUSED,
NN_FSNUM_GETMAXREAD,
NN_FSNUM_GETLABEL, NN_FSNUM_GETLABEL,
NN_FSNUM_SETLABEL, NN_FSNUM_SETLABEL,
NN_FSNUM_ISRO, NN_FSNUM_ISRO,
@@ -3553,6 +3460,7 @@ static nn_Exit nn_fsPathCheck(nn_Computer *C, char buf[NN_MAX_PATH], const char
static nn_Exit nn_fsHandler(nn_ComponentRequest *req) { static nn_Exit nn_fsHandler(nn_ComponentRequest *req) {
if(req->action == NN_COMP_SIGNAL) return NN_OK; if(req->action == NN_COMP_SIGNAL) return NN_OK;
if(req->action == NN_COMP_CHECKMETHOD) return NN_OK; if(req->action == NN_COMP_CHECKMETHOD) return NN_OK;
nn_Context *ctx = req->ctx;
nn_FSState *state = req->classState; nn_FSState *state = req->classState;
nn_FSRequest freq; nn_FSRequest freq;
freq.ctx = req->ctx; freq.ctx = req->ctx;
@@ -3562,7 +3470,7 @@ static nn_Exit nn_fsHandler(nn_ComponentRequest *req) {
if(req->action == NN_COMP_DROP) { if(req->action == NN_COMP_DROP) {
freq.action = NN_FS_DROP; freq.action = NN_FS_DROP;
state->handler(&freq); state->handler(&freq);
nn_free(req->ctx, state, sizeof(*state)); nn_free(ctx, state, sizeof(*state));
return NN_OK; return NN_OK;
} }
nn_Computer *C = req->computer; nn_Computer *C = req->computer;
@@ -3580,6 +3488,10 @@ static nn_Exit nn_fsHandler(nn_ComponentRequest *req) {
req->returnCount = 1; req->returnCount = 1;
return nn_pushinteger(C, freq.spaceUsed); return nn_pushinteger(C, freq.spaceUsed);
} }
if(method == NN_FSNUM_GETMAXREAD) {
req->returnCount = 1;
return nn_pushinteger(C, state->fs.maxReadSize);
}
if(method == NN_FSNUM_GETLABEL) { if(method == NN_FSNUM_GETLABEL) {
char buf[NN_MAX_LABEL]; char buf[NN_MAX_LABEL];
freq.action = NN_FS_GETLABEL; freq.action = NN_FS_GETLABEL;
@@ -3628,23 +3540,32 @@ static nn_Exit nn_fsHandler(nn_ComponentRequest *req) {
} }
if(method == NN_FSNUM_READ) { if(method == NN_FSNUM_READ) {
if(nn_checkinteger(C, 0, "bad argument #1 (fd expected)")) return NN_EBADCALL; if(nn_checkinteger(C, 0, "bad argument #1 (fd expected)")) return NN_EBADCALL;
e = nn_defaultinteger(C, 1, NN_MAX_READ); e = nn_defaultinteger(C, 1, state->fs.maxReadSize);
if(e) return e; if(e) return e;
if(nn_checknumber(C, 1, "bad argument #2 (number expected)")) return NN_EBADCALL; if(nn_checknumber(C, 1, "bad argument #2 (number expected)")) return NN_EBADCALL;
double requested = nn_tonumber(C, 1); double requested = nn_tonumber(C, 1);
if(requested > NN_MAX_READ) requested = NN_MAX_READ; if(requested > state->fs.maxReadSize) requested = state->fs.maxReadSize;
freq.action = NN_FS_READ; freq.action = NN_FS_READ;
freq.fd = nn_tointeger(C, 0); freq.fd = nn_tointeger(C, 0);
char buf[NN_MAX_READ]; char *buf = nn_alloc(ctx, state->fs.maxReadSize);
if(buf == NULL) return NN_ENOMEM;
freq.read.buf = buf; freq.read.buf = buf;
freq.read.len = requested; freq.read.len = requested;
e = state->handler(&freq); e = state->handler(&freq);
if(e) return e; if(e) {
if(freq.read.buf == NULL) return NN_OK; nn_free(ctx, buf, state->fs.maxReadSize);
return e;
}
if(freq.read.buf == NULL) {
nn_free(ctx, buf, state->fs.maxReadSize);
return NN_OK;
}
nn_costComponent(C, req->compAddress, state->fs.readsPerTick); nn_costComponent(C, req->compAddress, state->fs.readsPerTick);
nn_removeEnergy(C, state->fs.dataEnergyCost * freq.read.len); nn_removeEnergy(C, state->fs.dataEnergyCost * freq.read.len);
req->returnCount = 1; req->returnCount = 1;
return nn_pushlstring(C, buf, freq.read.len); e = nn_pushlstring(C, buf, freq.read.len);
nn_free(ctx, buf, state->fs.maxReadSize);
return e;
} }
if(method == NN_FSNUM_WRITE) { if(method == NN_FSNUM_WRITE) {
if(nn_checkinteger(C, 0, "bad argument #1 (fd expected)")) return NN_EBADCALL; if(nn_checkinteger(C, 0, "bad argument #1 (fd expected)")) return NN_EBADCALL;
@@ -3663,7 +3584,7 @@ static nn_Exit nn_fsHandler(nn_ComponentRequest *req) {
if(nn_checkinteger(C, 0, "bad argument #1 (fd expected)")) return NN_EBADCALL; if(nn_checkinteger(C, 0, "bad argument #1 (fd expected)")) return NN_EBADCALL;
e = nn_defaultstring(C, 1, "cur"); e = nn_defaultstring(C, 1, "cur");
if(e) return e; if(e) return e;
if(nn_checkinteger(C, 1, "bad argument #2 (whence expected)")) return NN_EBADCALL; if(nn_checkstring(C, 1, "bad argument #2 (whence expected)")) return NN_EBADCALL;
e = nn_defaultinteger(C, 2, 0); e = nn_defaultinteger(C, 2, 0);
if(e) return e; if(e) return e;
if(nn_checkinteger(C, 2, "bad argument #3 (integer expected)")) return NN_EBADCALL; if(nn_checkinteger(C, 2, "bad argument #3 (integer expected)")) return NN_EBADCALL;
@@ -3678,7 +3599,7 @@ static nn_Exit nn_fsHandler(nn_ComponentRequest *req) {
if(nn_strcmp(whence, "end") == 0) { if(nn_strcmp(whence, "end") == 0) {
seek = NN_SEEK_END; seek = NN_SEEK_END;
} }
freq.action = NN_FS_CLOSE; freq.action = NN_FS_SEEK;
freq.fd = nn_tointeger(C, 0); freq.fd = nn_tointeger(C, 0);
freq.seek.whence = seek; freq.seek.whence = seek;
freq.seek.off = nn_tointeger(C, 2); freq.seek.off = nn_tointeger(C, 2);
@@ -3686,7 +3607,7 @@ static nn_Exit nn_fsHandler(nn_ComponentRequest *req) {
if(e) return e; if(e) return e;
req->returnCount = 1; req->returnCount = 1;
nn_costComponent(C, req->compAddress, state->fs.readsPerTick); nn_costComponent(C, req->compAddress, state->fs.readsPerTick);
return nn_pushbool(C, true); return nn_pushinteger(C, freq.seek.off);
} }
if(method == NN_FSNUM_CLOSE) { if(method == NN_FSNUM_CLOSE) {
if(nn_checkinteger(C, 0, "bad argument #1 (fd expected)")) return NN_EBADCALL; if(nn_checkinteger(C, 0, "bad argument #1 (fd expected)")) return NN_EBADCALL;
@@ -3843,6 +3764,7 @@ nn_Component *nn_createFilesystem(nn_Universe *universe, const char *address, co
const nn_Method methods[NN_FSNUM_COUNT] = { const nn_Method methods[NN_FSNUM_COUNT] = {
[NN_FSNUM_SPACETOTAL] = {"spaceTotal", "function(): integer - Capacity of the drive", NN_DIRECT}, [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_SPACEUSED] = {"spaceUsed", "function(): integer - Amount of space used", NN_DIRECT},
[NN_FSNUM_GETMAXREAD] = {"getMaxRead", "function(): integer - Capacity of read buffer, the maximum amount of data which can be read", NN_DIRECT},
[NN_FSNUM_GETLABEL] = {"getLabel", "function(): string? - Gets the label of the drive, if any", 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_INDIRECT}, [NN_FSNUM_SETLABEL] = {"setLabel", "function(label?: string): string - Sets the label of the drive. Returns the new label, which may be truncated", NN_INDIRECT},
[NN_FSNUM_ISRO] = {"isReadOnly", "function(): boolean - Returns whether the drive is read-only", NN_DIRECT}, [NN_FSNUM_ISRO] = {"isReadOnly", "function(): boolean - Returns whether the drive is read-only", NN_DIRECT},
@@ -3886,8 +3808,6 @@ static void nn_drive_seekPenalty(nn_Computer *C, size_t lastSector, size_t newSe
size_t maxSectors = drive->capacity / drive->sectorSize; size_t maxSectors = drive->capacity / drive->sectorSize;
size_t sectorsPerPlatter = maxSectors / drive->platterCount; size_t sectorsPerPlatter = maxSectors / drive->platterCount;
// RPM over the number of sectors, over 60 seconds.
double latencyPerSector = 1.0 / ((double)drive->rpm / 60 * maxSectors);
// magic // magic
lastSector %= sectorsPerPlatter; lastSector %= sectorsPerPlatter;
@@ -3901,14 +3821,30 @@ static void nn_drive_seekPenalty(nn_Computer *C, size_t lastSector, size_t newSe
} else { } else {
sectorDelta = lastSector - newSector; sectorDelta = lastSector - newSector;
} }
if(sectorDelta < drive->cacheLineSize) {
sectorDelta = 0; // within cache
} else {
// align to cache line
if(sectorDelta % drive->cacheLineSize != 0) {
sectorDelta += drive->cacheLineSize - sectorDelta % drive->cacheLineSize;
}
}
nn_addIdleTime(C, sectorDelta * latencyPerSector); // RPM over the number of sectors, over 60 seconds.
double latency = (double)sectorDelta * 60 / ((double)drive->rpm * maxSectors);
nn_addIdleTime(C, latency);
}
// 1-indexed
static size_t nn_drive_cachelineOf(size_t sector, size_t perCache) {
return (sector - 1) / perCache;
} }
typedef enum nn_DrvNum { typedef enum nn_DrvNum {
NN_DRVNUM_GETCAPACITY, NN_DRVNUM_GETCAPACITY,
NN_DRVNUM_GETSECTORSIZE, NN_DRVNUM_GETSECTORSIZE,
NN_DRVNUM_GETPLATTERCOUNT, NN_DRVNUM_GETPLATTERCOUNT,
NN_DRVNUM_GETCACHESIZE,
NN_DRVNUM_ISRO, NN_DRVNUM_ISRO,
NN_DRVNUM_GETLABEL, NN_DRVNUM_GETLABEL,
NN_DRVNUM_SETLABEL, NN_DRVNUM_SETLABEL,
@@ -3936,6 +3872,11 @@ static nn_Exit nn_drvHandler(nn_ComponentRequest *request) {
dreq.ctx = ctx; dreq.ctx = ctx;
dreq.computer = C; dreq.computer = C;
dreq.state = request->state; dreq.state = request->state;
dreq.drv = &state->drive;
nn_Exit e;
if(request->action == NN_COMP_SIGNAL) return NN_OK;
if(request->action == NN_COMP_CHECKMETHOD) return NN_OK;
if(request->action == NN_COMP_DROP) { if(request->action == NN_COMP_DROP) {
dreq.action = NN_DRIVE_DROP; dreq.action = NN_DRIVE_DROP;
@@ -3943,7 +3884,87 @@ static nn_Exit nn_drvHandler(nn_ComponentRequest *request) {
nn_free(ctx, state, sizeof(*state)); nn_free(ctx, state, sizeof(*state));
return NN_OK; return NN_OK;
} }
if(C) nn_setError(C, "bad call"); size_t ss = state->drive.sectorSize;
size_t cacheline = state->drive.cacheLineSize;
size_t cacheByteSize = cacheline * ss;
size_t sectorCount = state->drive.capacity / ss;
unsigned int method = request->methodIdx;
if(method == NN_DRVNUM_GETCAPACITY) {
request->returnCount = 1;
return nn_pushinteger(C, state->drive.capacity);
}
if(method == NN_DRVNUM_GETSECTORSIZE) {
request->returnCount = 1;
return nn_pushinteger(C, ss);
}
if(method == NN_DRVNUM_GETCACHESIZE) {
request->returnCount = 1;
return nn_pushinteger(C, cacheline);
}
if(method == NN_DRVNUM_ISRO) {
dreq.action = NN_DRIVE_ISRO;
e = state->handler(&dreq);
if(e) return e;
request->returnCount = 1;
return nn_pushbool(C, dreq.readonly);
}
if(method == NN_DRVNUM_GETLABEL) {
dreq.action = NN_DRIVE_GETLABEL;
char buf[NN_MAX_LABEL];
dreq.getlabel.buf = buf;
dreq.getlabel.len = NN_MAX_LABEL;
e = state->handler(&dreq);
if(e) return e;
request->returnCount = 1;
return nn_pushlstring(C, dreq.getlabel.buf, dreq.getlabel.len);
}
if(method == NN_DRVNUM_SETLABEL) {
e = nn_defaultstring(C, 0, "");
if(e) return e;
if(nn_checkstring(C, 0, "bad argument #1 (string expected)")) return NN_EBADCALL;
dreq.action = NN_DRIVE_SETLABEL;
dreq.setlabel.label = nn_tolstring(C, 0, &dreq.setlabel.len);
e = state->handler(&dreq);
if(e) return e;
request->returnCount = 1;
return nn_pushlstring(C, dreq.setlabel.label, dreq.setlabel.len);
}
if(method == NN_DRVNUM_READSECTOR) {
if(nn_checkinteger(C, 0, "bad argument #1 (integer expected)")) return NN_EBADCALL;
int sec = nn_tointeger(C, 0);
if(sec < 1 || sec > sectorCount) {
nn_setError(C, "sector out of bounds");
return NN_EBADCALL;
}
int curPos = 0;
dreq.action = NN_DRIVE_CURPOS;
e = state->handler(&dreq);
if(e) return e;
curPos = dreq.curpos;
if(nn_drive_cachelineOf(curPos, cacheline) != nn_drive_cachelineOf(sec, cacheline)) {
nn_drive_seekPenalty(C, curPos, sec, &state->drive);
nn_costComponent(C, request->compAddress, state->drive.readsPerTick);
}
char *sector = nn_alloc(ctx, ss);
if(sector == NULL) return NN_ENOMEM;
dreq.action = NN_DRIVE_READSECTOR;
dreq.readSector.sector = sec;
dreq.readSector.buf = sector;
e = state->handler(&dreq);
if(e) {
nn_free(ctx, sector, ss);
return e;
}
request->returnCount = 1;
e = nn_pushlstring(C, sector, ss);
nn_free(ctx, sector, ss);
return e;
}
if(C) nn_setError(C, "drive: not implemented yet");
return NN_EBADCALL; return NN_EBADCALL;
} }
@@ -3954,6 +3975,7 @@ nn_Component *nn_createDrive(nn_Universe *universe, const char *address, const n
[NN_DRVNUM_GETCAPACITY] = {"getCapacity", "function(): integer - Get drive capacity", NN_DIRECT}, [NN_DRVNUM_GETCAPACITY] = {"getCapacity", "function(): integer - Get drive capacity", NN_DIRECT},
[NN_DRVNUM_GETSECTORSIZE] = {"getSectorSize", "function(): integer - Get sector size", NN_DIRECT}, [NN_DRVNUM_GETSECTORSIZE] = {"getSectorSize", "function(): integer - Get sector size", NN_DIRECT},
[NN_DRVNUM_GETPLATTERCOUNT] = {"getPlatterCount", "function(): integer - Get number of platters on this drive", NN_DIRECT}, [NN_DRVNUM_GETPLATTERCOUNT] = {"getPlatterCount", "function(): integer - Get number of platters on this drive", NN_DIRECT},
[NN_DRVNUM_GETCACHESIZE] = {"getCacheSize", "function(): integer - Get number of sectors cached in a single read", NN_DIRECT},
[NN_DRVNUM_ISRO] = {"isReadOnly", "function(): boolean - Get whether the drive is read-only", NN_DIRECT}, [NN_DRVNUM_ISRO] = {"isReadOnly", "function(): boolean - Get whether the drive is read-only", NN_DIRECT},
[NN_DRVNUM_GETLABEL] = {"getLabel", "function(): string? - Get drive label", NN_DIRECT}, [NN_DRVNUM_GETLABEL] = {"getLabel", "function(): string? - Get drive label", NN_DIRECT},
[NN_DRVNUM_SETLABEL] = {"setLabel", "function(label: string?): string - Set drive label", NN_INDIRECT}, [NN_DRVNUM_SETLABEL] = {"setLabel", "function(label: string?): string - Set drive label", NN_INDIRECT},

View File

@@ -86,9 +86,6 @@ void *_alloca(size_t);
#define NN_MAX_STACK 256 #define NN_MAX_STACK 256
// the maximum size a path is allowed to have, including the NULL terminator! // 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.
// 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 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
@@ -641,6 +638,7 @@ const char *nn_getComponentDoc(nn_Component *c, const char *method);
nn_MethodFlags nn_getComponentMethodFlags(nn_Component *c, const char *method); nn_MethodFlags nn_getComponentMethodFlags(nn_Component *c, const char *method);
const char *nn_getComponentType(nn_Component *c); const char *nn_getComponentType(nn_Component *c);
const char *nn_getComponentTypeID(nn_Component *c); const char *nn_getComponentTypeID(nn_Component *c);
const char *nn_getComponentAddress(nn_Component *c);
// Adds a component to the computer on a given slot. // Adds a component to the computer on a given slot.
// This will also queue a component_added signal if the computer is in a running state. // This will also queue a component_added signal if the computer is in a running state.
@@ -937,19 +935,7 @@ typedef nn_Exit (nn_EEPROMHandler)(nn_EEPROMRequest *request);
// Tier 4- The best EEPROM // Tier 4- The best EEPROM
extern const nn_EEPROM nn_defaultEEPROMs[4]; extern const nn_EEPROM nn_defaultEEPROMs[4];
typedef struct nn_VEEPROM {
const char *code;
size_t codelen;
const char *data;
size_t datalen;
const char *label;
size_t labellen;
const char *arch;
bool isReadonly;
} nn_VEEPROM;
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);
nn_Component *nn_createVEEPROM(nn_Universe *universe, const char *address, const nn_VEEPROM *veeprom, const nn_EEPROM *eeprom);
// Filesystem class // Filesystem class
@@ -964,6 +950,11 @@ typedef struct nn_Filesystem {
// 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;
// maximum size of a read.
// Do note that this entire buffer is allocated, and thus if you
// set it to a high number, you may get weird high allocations.
// This also determines read performance.
size_t maxReadSize;
} nn_Filesystem; } nn_Filesystem;
typedef enum nn_FSAction { typedef enum nn_FSAction {
@@ -1095,18 +1086,22 @@ typedef struct nn_Drive {
// However, if it has 2 platters, it'd be seen as 1 to 4 being at the same angle as 5 to 8, which // However, if it has 2 platters, it'd be seen as 1 to 4 being at the same angle as 5 to 8, which
// would mean only 3 rotations. // would mean only 3 rotations.
size_t platterCount; size_t platterCount;
// how many sectors are cached together.
// Each platter has "its own cache."
size_t cacheLineSize;
// how many reads can be issued per tick. // how many reads can be issued per tick.
// Reading either a sector or a byte counts as 1 read. // Anything that kicks out the current cacheline counts as a read.
size_t readsPerTick; size_t readsPerTick;
// how many writes can be issued per tick. // how many writes can be issued per tick.
// Writing a sector counts as 1 write. // Writing a sector counts as 1 write.
// Writing a byte counts as 1 read and 1 write, // Writing a byte counts as 1 read (may be eaten by cache) and 1 write,
// you can imagine it as reading the sector, editing the byte, // you can imagine it as reading the sector, editing the byte,
// then writing the sector back. // then writing the sector back.
size_t writesPerTick; size_t writesPerTick;
// Set to 0 for *infinite*, effectively an SSD. // Set to 0 for *infinite*, effectively an SSD.
// This would mean there is 0 penalty for seeking (technically unreliastic even for an SSD). // This would mean there is 0 penalty for seeking (technically unreliastic even for an SSD).
// This is simply used to compute idle time. It is in literal full rotations per minute. // This is simply used to compute idle time. It is in literal full rotations per minute.
// It is aligned to the cache lines.
size_t rpm; size_t rpm;
// If false, it behaves like a normal OC drive, where the drive can spin backwards to seek. // If false, it behaves like a normal OC drive, where the drive can spin backwards to seek.
// However, this is unrealistic, as doing so may crack the sensitive platter and make the // However, this is unrealistic, as doing so may crack the sensitive platter and make the
@@ -1130,8 +1125,8 @@ typedef enum nn_DriveAction {
NN_DRIVE_GETLABEL, NN_DRIVE_GETLABEL,
// set or remove current label // set or remove current label
NN_DRIVE_SETLABEL, NN_DRIVE_SETLABEL,
// get the last read position // get the current head position, as a sector
NN_DRIVE_LASTREADPOS, NN_DRIVE_CURPOS,
// read a sector // read a sector
NN_DRIVE_READSECTOR, NN_DRIVE_READSECTOR,
// write a sector // write a sector
@@ -1140,13 +1135,15 @@ typedef enum nn_DriveAction {
NN_DRIVE_READBYTE, NN_DRIVE_READBYTE,
// write a byte // write a byte
NN_DRIVE_WRITEBYTE, NN_DRIVE_WRITEBYTE,
// is drive read-only
NN_DRIVE_ISRO,
} nn_DriveAction; } nn_DriveAction;
typedef struct nn_DriveRequest { typedef struct nn_DriveRequest {
nn_Context *ctx; nn_Context *ctx;
nn_Computer *computer; nn_Computer *computer;
void *state; void *state;
const nn_Filesystem *fs; const nn_Drive *drv;
nn_DriveAction action; nn_DriveAction action;
union { union {
struct { struct {
@@ -1157,7 +1154,7 @@ typedef struct nn_DriveRequest {
const char *label; const char *label;
size_t len; size_t len;
} setlabel; } setlabel;
size_t lastReadPos; size_t curpos;
struct { struct {
// 1-indexed // 1-indexed
size_t sector; size_t sector;
@@ -1178,6 +1175,7 @@ typedef struct nn_DriveRequest {
size_t byte; size_t byte;
unsigned char value; unsigned char value;
} writeByte; } writeByte;
bool readonly;
}; };
} nn_DriveRequest; } nn_DriveRequest;