drives, rebalancing, and a broken tmpfs impl
that tmpfs impl needs to be reworked to heck and back!
This commit is contained in:
11
TODO.md
11
TODO.md
@@ -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.
|
||||||
|
|||||||
@@ -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 "",
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
85
src/main.c
85
src/main.c
@@ -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;
|
||||||
|
|||||||
1047
src/ncomplib.c
1047
src/ncomplib.c
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||||
|
|||||||
292
src/neonucleus.c
292
src/neonucleus.c
@@ -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},
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user