bunch of progress

This commit is contained in:
2026-04-05 19:48:13 +02:00
parent 0b119e7dda
commit 79ae1e13f8
7 changed files with 684 additions and 45 deletions

View File

@@ -1,7 +1,6 @@
# For MVP functionality
- move SSDs to nandflash
- remove HDD cachelines (they're pointless)
- NCL computer states, as computers that can be turned on/off/beep/etc.
- write a tester OS, basically a menu with tests to run
- tmpfs (rework the whole thing)
- device info

View File

@@ -385,7 +385,8 @@ int main(int argc, char **argv) {
nn_Component *testingfs = ncl_createTmpFS(u, NULL, &nn_defaultFilesystems[3], NCL_FILECOST_DEFAULT, false);
const char * const testDriveData =
"local g, s, d = component.list('gpu')(), component.list('screen')(), component.list('drive')()\n"
"local g, s = component.list('gpu')(), component.list('screen')()\n"
"local d = computer.getBootAddress()\n"
"component.invoke(g, 'bind', s, true)\n"
"component.invoke(g, 'set', 1, 1, 'starting sequential bench...')\n"
"local start = computer.uptime()\n"
@@ -408,16 +409,13 @@ int main(int argc, char **argv) {
"while computer.uptime() < now + 3 do computer.pullSignal(0.05) end\n"
"computer.shutdown(true)\n"
;
nn_Drive driveconf;
nn_Drive driveparts[] = {
nn_defaultSSDs[3],
};
nn_mergeDrives(&driveconf, driveparts, sizeof(driveparts) / sizeof(driveparts[0]));
nn_Component *testDrive = ncl_createDrive(u, NULL, &driveconf, testDriveData, strlen(testDriveData), false);
nn_Component *testDrive = ncl_createDrive(u, NULL, &nn_defaultDrives[3], testDriveData, strlen(testDriveData), false);
nn_Component *testFlash = ncl_createFlash(u, NULL, &nn_defaultSSDs[3], testDriveData, strlen(testDriveData), false);
ncl_setCLabel(managedfs, "Main Filesystem");
ncl_setCLabel(testingfs, "Secondary Filesystem");
ncl_setCLabel(testDrive, "Unmanaged Storage");
ncl_setCLabel(testFlash, "Flash Storage");
size_t ramTotal = 0;
ramTotal += nn_ramSizes[5];
@@ -472,8 +470,7 @@ restart:;
nn_pushnumber(c, 5.3);
nn_pushbool(c, false);
nn_encodeNetworkContents(c, &contents, 4);
nn_dropNetworkContents(&contents);
nn_popn(c, 4);
printf("size: %zu\n", contents.buflen);
for(size_t i = 0; i < contents.buflen; i++) {
@@ -499,6 +496,7 @@ restart:;
nn_mountComponent(c, gpuCard, 2);
//nn_mountComponent(c, testingfs, 3);
nn_mountComponent(c, testDrive, 4);
nn_mountComponent(c, testFlash, 5);
while(true) {
if(WindowShouldClose()) break;
@@ -580,6 +578,10 @@ restart:;
if(keycode == KEY_TAB) unicode = '\t';
}
if(keycode == KEY_F1) {
nn_pushModemMessage(c, "bullshit", "someone", 1, 5, &contents);
}
nn_pushKeyDown(c, "mainKB", unicode, keycode_to_oc(keycode), player);
}
@@ -632,6 +634,7 @@ restart:;
}
if(state == NN_RESTART) {
printf("restart requested\n");
nn_dropNetworkContents(&contents);
nn_destroyComputer(c);
goto restart;
}
@@ -646,9 +649,11 @@ cleanup:;
nn_dropComponent(tmpfs);
nn_dropComponent(testingfs);
nn_dropComponent(testDrive);
nn_dropComponent(testFlash);
nn_dropComponent(screen);
nn_dropComponent(gpuCard);
nn_dropComponent(keyboard);
nn_dropNetworkContents(&contents);
// rip the universe
nn_destroyUniverse(u);
ncl_destroyGlyphCache(gc);

View File

@@ -95,6 +95,13 @@ for addr in component.list("drive", true) do
end
end
for addr in component.list("nandflash", true) do
local f = getBootCode(addr)
if f then
table.insert(bootables, {code = f, addr = addr})
end
end
local function boot(bootable)
local w, h = component.invoke(gpu, "maxResolution")
component.invoke(gpu, "setResolution", w, h)

View File

@@ -631,6 +631,18 @@ typedef struct ncl_DriveState {
size_t labellen;
} ncl_DriveState;
typedef struct ncl_FlashState {
nn_Context *ctx;
nn_Lock *lock;
nn_NandFlash conf;
bool isReadonly;
size_t usage;
size_t writeCount;
char *data;
char label[NN_MAX_LABEL];
size_t labellen;
} ncl_FlashState;
typedef struct ncl_EEState {
nn_Context *ctx;
nn_Lock *lock;
@@ -1264,6 +1276,7 @@ static nn_Exit ncl_tmpfsHandler(nn_FSRequest *req) {
}
}
nn_destroyLock(ctx, tmpfs->lock);
nn_free(ctx, tmpfs, sizeof(*tmpfs));
return NN_OK;
}
if(req->action == NN_FS_SPACEUSED) {
@@ -1651,6 +1664,7 @@ static nn_Exit ncl_drvHandler(nn_DriveRequest *request) {
size_t ss = drv->conf.sectorSize;
if(request->action == NN_DRIVE_DROP) {
nn_destroyLock(ctx, drv->lock);
nn_free(ctx, drv->data, drv->conf.capacity);
nn_free(ctx, drv, sizeof(*drv));
return NN_OK;
@@ -1661,8 +1675,15 @@ static nn_Exit ncl_drvHandler(nn_DriveRequest *request) {
nn_unlock(ctx, drv->lock);
return NN_OK;
}
if(request->action == NN_DRIVE_ISRO) {
nn_lock(ctx, drv->lock);
request->readonly = drv->isReadonly;
nn_unlock(ctx, drv->lock);
return NN_OK;
}
if(request->action == NN_DRIVE_GETLABEL) {
nn_lock(ctx, drv->lock);
drv->usage++;
memcpy(request->getlabel.buf, drv->label, drv->labellen);
request->getlabel.len = drv->labellen;
nn_unlock(ctx, drv->lock);
@@ -1670,6 +1691,7 @@ static nn_Exit ncl_drvHandler(nn_DriveRequest *request) {
}
if(request->action == NN_DRIVE_READSECTOR) {
nn_lock(ctx, drv->lock);
drv->usage++;
size_t off = (request->readSector.sector - 1) * ss;
memcpy(request->readSector.buf, drv->data + off, ss);
drv->lastSector = request->readSector.sector;
@@ -1724,12 +1746,110 @@ fail:
return NULL;
}
static nn_Exit ncl_flashHandler(nn_FlashRequest *request) {
nn_Context *ctx = request->ctx;
nn_Computer *C = request->computer;
ncl_FlashState *drv = request->state;
size_t ss = drv->conf.sectorSize;
if(request->action == NN_FLASH_DROP) {
nn_destroyLock(ctx, drv->lock);
nn_free(ctx, drv->data, drv->conf.capacity);
nn_free(ctx, drv, sizeof(*drv));
return NN_OK;
}
if(request->action == NN_FLASH_GETWRITES) {
nn_lock(ctx, drv->lock);
request->writeCount = drv->writeCount;
nn_unlock(ctx, drv->lock);
return NN_OK;
}
if(request->action == NN_FLASH_ISRO) {
nn_lock(ctx, drv->lock);
request->readonly = drv->isReadonly;
nn_unlock(ctx, drv->lock);
return NN_OK;
}
if(request->action == NN_FLASH_GETLABEL) {
nn_lock(ctx, drv->lock);
drv->usage++;
memcpy(request->getlabel.buf, drv->label, drv->labellen);
request->getlabel.len = drv->labellen;
nn_unlock(ctx, drv->lock);
return NN_OK;
}
if(request->action == NN_FLASH_READSECTOR) {
nn_lock(ctx, drv->lock);
drv->usage++;
size_t off = (request->readsector.sec - 1) * ss;
memcpy(request->readsector.buf, drv->data + off, ss);
nn_unlock(ctx, drv->lock);
return NN_OK;
}
if(request->action == NN_FLASH_WRITESECTOR) {
nn_lock(ctx, drv->lock);
drv->usage++;
size_t off = (request->writesector.sec - 1) * ss;
memcpy(drv->data + off, request->writesector.buf, ss);
drv->writeCount += request->writesector.writesAdded;
nn_unlock(ctx, drv->lock);
return NN_OK;
}
if(C) nn_setError(C, "ncl-flash: not implemented yet");
return NN_EBADCALL;
}
nn_Component *ncl_createFlash(nn_Universe *universe, const char *address, const nn_NandFlash *flash, const char *data, size_t len, bool isReadonly) {
nn_Context *ctx = nn_getUniverseContext(universe);
nn_Component *c = NULL;
nn_Lock *lock = NULL;
char *databuf = NULL;
ncl_FlashState *state = NULL;
state = nn_alloc(ctx, sizeof(*state));
if(state == NULL) goto fail;
lock = nn_createLock(ctx);
if(lock == NULL) goto fail;
databuf = nn_alloc(ctx, flash->capacity);
if(databuf == NULL) goto fail;
if(len > flash->capacity) len = flash->capacity;
memcpy(databuf, data, len);
memset(databuf + len, 0, flash->capacity - len);
state->ctx = ctx;
state->lock = lock;
state->conf = *flash;
state->usage = 0;
state->labellen = 0;
state->writeCount = 0;
state->data = databuf;
state->isReadonly = isReadonly;
c = nn_createFlash(universe, address, flash, state, ncl_flashHandler);
if(c == NULL) goto fail;
if(nn_setComponentTypeID(c, NCL_FLASH)) goto fail;
return c;
fail:
if(c != NULL) {
nn_dropComponent(c);
return NULL;
}
if(lock != NULL) nn_destroyLock(ctx, lock);
nn_free(ctx, databuf, flash->capacity);
nn_free(ctx, state, sizeof(*state));
return NULL;
}
static nn_Exit ncl_eepromHandler(nn_EEPROMRequest *req) {
nn_Context *ctx = req->ctx;
nn_Computer *C = req->computer;
ncl_EEState *state = req->state;
if(req->action == NN_EEPROM_DROP) {
nn_destroyLock(ctx, state->lock);
nn_free(ctx, state->code, req->eeprom->size);
nn_free(ctx, state->data, req->eeprom->dataSize);
nn_free(ctx, state, sizeof(*state));
@@ -1891,6 +2011,16 @@ size_t ncl_readDrive(nn_Component *component, size_t offset, char *buf, size_t l
nn_unlock(drv->ctx, drv->lock);
return len;
}
if(strcmp(typeid, NCL_FLASH) == 0) {
ncl_FlashState *drv = nn_getComponentState(component);
if(offset > drv->conf.capacity) return 0;
size_t remaining = drv->conf.capacity - offset;
if(remaining < len) len = remaining;
nn_lock(drv->ctx, drv->lock);
memcpy(buf, drv->data + offset, len);
nn_unlock(drv->ctx, drv->lock);
return len;
}
return 0;
}
@@ -1906,6 +2036,16 @@ void ncl_writeDrive(nn_Component *component, size_t offset, const char *buf, siz
nn_unlock(drv->ctx, drv->lock);
return;
}
if(strcmp(typeid, NCL_FLASH) == 0) {
ncl_FlashState *drv = nn_getComponentState(component);
if(offset > drv->conf.capacity) return;
size_t remaining = drv->conf.capacity - offset;
if(remaining < len) len = remaining;
nn_lock(drv->ctx, drv->lock);
memcpy(drv->data + offset, buf, len);
nn_unlock(drv->ctx, drv->lock);
return;
}
}
char *ncl_getDriveBuffer(nn_Component *component, size_t *len) {
@@ -1915,6 +2055,11 @@ char *ncl_getDriveBuffer(nn_Component *component, size_t *len) {
*len = drv->conf.capacity;
return drv->data;
}
if(strcmp(typeid, NCL_FLASH) == 0) {
ncl_FlashState *drv = nn_getComponentState(component);
*len = drv->conf.capacity;
return drv->data;
}
if(len != NULL) *len = 0;
return NULL;
}
@@ -3245,6 +3390,20 @@ void ncl_statComponent(nn_Component *component, ncl_ComponentStat *stat) {
nn_unlock(drv->ctx, drv->lock);
return;
}
if(strcmp(ty, NCL_FLASH) == 0) {
ncl_FlashState *drv = state;
nn_lock(drv->ctx, drv->lock);
stat->isReadonly = drv->isReadonly;
stat->usageCounter = drv->usage;
stat->labellen = drv->labellen;
memcpy(stat->label, drv->label, stat->labellen);
stat->flash.currentWriteCount = drv->writeCount;
// TODO: compute wear level
stat->flash.wearlevel = 0;
stat->flash.conf = &drv->conf;
nn_unlock(drv->ctx, drv->lock);
return;
}
if(strcmp(ty, NCL_EEPROM) == 0) {
ncl_EEState *ee = state;
nn_lock(ee->ctx, ee->lock);
@@ -3294,6 +3453,14 @@ bool ncl_makeReadonly(nn_Component *component) {
nn_unlock(drv->ctx, drv->lock);
return true;
}
if(strcmp(ty, NCL_FLASH) == 0) {
ncl_FlashState *drv = state;
nn_lock(drv->ctx, drv->lock);
drv->isReadonly = true;
drv->usage++;
nn_unlock(drv->ctx, drv->lock);
return true;
}
if(strcmp(ty, NCL_EEPROM) == 0) {
ncl_EEState *ee = state;
nn_lock(ee->ctx, ee->lock);
@@ -3345,6 +3512,14 @@ size_t ncl_getLabel(nn_Component *c, char buf[NN_MAX_LABEL]) {
nn_unlock(s->ctx, s->lock);
return len;
}
if(strcmp(typeid, NCL_FLASH) == 0) {
ncl_FlashState *s = nn_getComponentState(c);
nn_lock(s->ctx, s->lock);
size_t len = s->labellen;
memcpy(buf, s->label, len);
nn_unlock(s->ctx, s->lock);
return len;
}
return 0;
}
@@ -3383,6 +3558,14 @@ size_t ncl_setLabel(nn_Component *c, const char *label, size_t len) {
nn_unlock(s->ctx, s->lock);
return len;
}
if(strcmp(typeid, NCL_FLASH) == 0) {
ncl_FlashState *s = nn_getComponentState(c);
nn_lock(s->ctx, s->lock);
memcpy(s->label, label, len);
s->labellen = len;
nn_unlock(s->ctx, s->lock);
return len;
}
return 0;
}

View File

@@ -8,6 +8,7 @@
#define NCL_EEPROM "ncl-eeprom"
#define NCL_FS "ncl-filesystem"
#define NCL_DRIVE "ncl-drive"
#define NCL_FLASH "ncl-nandflash"
#define NCL_GPU "ncl-gpu"
#define NCL_SCREEN "ncl-screen"
@@ -208,6 +209,9 @@ nn_Component *ncl_createTmpFS(nn_Universe *universe, const char *address, const
// Remember to read the entire drive and save it somewhere before dropping it.
nn_Component *ncl_createDrive(nn_Universe *universe, const char *address, const nn_Drive *drive, const char *data, size_t len, bool isReadonly);
// usable like a drive, but is a nandflash component
nn_Component *ncl_createFlash(nn_Universe *universe, const char *address, const nn_NandFlash *flash, const char *data, size_t len, bool isReadonly);
// data is stored interally
nn_Component *ncl_createEEPROM(nn_Universe *universe, const char *address, const nn_EEPROM *eeprom, const char *code, size_t codelen, bool isReadonly);
@@ -269,6 +273,11 @@ typedef struct ncl_ComponentStat {
const nn_Drive *conf;
size_t lastSector;
} drive;
struct {
const nn_NandFlash *conf;
size_t currentWriteCount;
double wearlevel;
} flash;
struct {
const nn_GPU *conf;
size_t vramFree;

View File

@@ -2537,57 +2537,62 @@ const nn_Drive nn_floppyDrive = {
.dataEnergyCost = 1.0 / NN_MiB,
};
const nn_Drive nn_defaultSSDs[4] = {
NN_INIT(nn_Drive) {
const nn_NandFlash nn_defaultSSDs[4] = {
NN_INIT(nn_NandFlash) {
.capacity = 512 * NN_KiB,
.sectorSize = 512,
.platterCount = 2,
.readsPerTick = 10,
.writesPerTick = 5,
.rpm = 0,
.onlySpinForwards = false,
.cellLevel = 1,
.maxWriteCount = 1<<10,
.maxWriteAmplification = 4,
.writeAmplificationExponent = 2,
.dataEnergyCost = 64.0 / NN_MiB,
},
NN_INIT(nn_Drive) {
NN_INIT(nn_NandFlash) {
.capacity = 1 * NN_MiB,
.sectorSize = 512,
.platterCount = 4,
.readsPerTick = 15,
.writesPerTick = 7,
.rpm = 0,
.onlySpinForwards = false,
.cellLevel = 2,
.maxWriteCount = 1<<10,
.maxWriteAmplification = 8,
.writeAmplificationExponent = 2,
.dataEnergyCost = 128.0 / NN_MiB,
},
NN_INIT(nn_Drive) {
NN_INIT(nn_NandFlash) {
.capacity = 2 * NN_MiB,
.sectorSize = 512,
.platterCount = 8,
.readsPerTick = 20,
.writesPerTick = 10,
.rpm = 0,
.onlySpinForwards = false,
.cellLevel = 3,
.maxWriteCount = 1<<10,
.maxWriteAmplification = 12,
.writeAmplificationExponent = 2,
.dataEnergyCost = 256.0 / NN_MiB,
},
NN_INIT(nn_Drive) {
NN_INIT(nn_NandFlash) {
.capacity = 4 * NN_MiB,
.sectorSize = 512,
.platterCount = 16,
.readsPerTick = 30,
.writesPerTick = 15,
.rpm = 0,
.onlySpinForwards = false,
.cellLevel = 4,
.maxWriteCount = 1<<10,
.maxWriteAmplification = 16,
.writeAmplificationExponent = 2,
.dataEnergyCost = 512.0 / NN_MiB,
},
};
const nn_Drive nn_floppySSD = {
const nn_NandFlash nn_floppySSD = {
.capacity = 256 * NN_KiB,
.sectorSize = 512,
.platterCount = 1,
.readsPerTick = 5,
.writesPerTick = 2,
.rpm = 0,
.onlySpinForwards = true,
.cellLevel = 1,
.maxWriteCount = 1<<10,
.maxWriteAmplification = 4,
.writeAmplificationExponent = 2,
.dataEnergyCost = 16.0 / NN_MiB,
};
@@ -3432,15 +3437,116 @@ nn_Exit nn_encodeNetworkContents(nn_Computer *computer, nn_EncodedNetworkContent
return NN_OK;
}
nn_Exit nn_copyNetworkContents(nn_Context *ctx, nn_EncodedNetworkContents *contents, const char *buf, size_t buflen, size_t valueCount);
nn_Exit nn_copyNetworkContents(nn_Context *ctx, nn_EncodedNetworkContents *contents, const char *buf, size_t buflen, size_t valueCount) {
contents->ctx = ctx;
contents->valueCount = valueCount;
contents->buflen = buflen;
contents->buf = nn_alloc(ctx, buflen);
if(contents->buf == NULL) return NN_ENOMEM;
nn_memcpy(contents->buf, buf, buflen);
return NN_OK;
}
void nn_dropNetworkContents(nn_EncodedNetworkContents *contents) {
nn_free(contents->ctx, contents->buf, contents->buflen);
}
nn_Exit nn_pushNetworkContents(nn_Computer *computer, const nn_EncodedNetworkContents *contents);
static nn_Exit nn_decodeNetworkValue(nn_Value *val, nn_Context *ctx, const char *buf, size_t *len) {
size_t decodedLen = 0, off = 0;
nn_Value tmpval;
switch((nn_NetworkValueTag)buf[0]) {
case NN_NETVAL_NULL:
*len = 1;
val->type = NN_VAL_NULL;
return NN_OK;
case NN_NETVAL_TRUE:
case NN_NETVAL_FALSE:
*len = 1;
val->type = NN_VAL_BOOL;
val->boolean = buf[0] == NN_NETVAL_TRUE;
return NN_OK;
case NN_NETVAL_NUM:
*len = 1 + sizeof(double);
val->type = NN_VAL_NUM;
nn_memcpy(&val->number, buf + 1, sizeof(double));
return NN_OK;
case NN_NETVAL_STR:
nn_memcpy(&decodedLen, buf + 1, sizeof(size_t));
val->type = NN_VAL_STR;
val->string = nn_alloc(ctx, sizeof(nn_String) + decodedLen + 1);
if(val->string == NULL) return NN_ENOMEM;
val->string->ctx = *ctx;
val->string->refc = 1;
val->string->len = decodedLen;
nn_memcpy(val->string->data, buf + 1 + sizeof(size_t), decodedLen);
val->string->data[decodedLen] = '\0';
*len = 1 + sizeof(size_t) + decodedLen;
return NN_OK;
case NN_NETVAL_RESOURCE:
val->type = NN_VAL_USERDATA;
nn_memcpy(&val->userdataIdx, buf + 1, sizeof(size_t));
*len = 1 + sizeof(size_t);
return NN_OK;
case NN_NETVAL_TABLE:
val->type = NN_VAL_TABLE;
nn_memcpy(&decodedLen, buf + 1, sizeof(size_t));
val->table = nn_alloc(ctx, sizeof(nn_Table) + sizeof(nn_Value) * decodedLen * 2);
if(val->table == NULL) return NN_ENOMEM;
val->table->ctx = *ctx;
val->table->refc = 1;
val->table->len = decodedLen;
off = 1 + sizeof(size_t);
for(size_t i = 0; i < decodedLen*2; i++) {
size_t tmplen = 0;
nn_Exit e = nn_decodeNetworkValue(&tmpval, ctx, buf + off, &tmplen);
if(e) {
for(size_t j = 0; j < i; j++) nn_dropValue(val->table->vals[j]);
return e;
}
val->table->vals[i] = tmpval;
off += tmplen;
}
*len = off;
return NN_OK;
}
*len = 1;
val->type = NN_VAL_NULL;
return NN_OK;
}
nn_Exit nn_pushModemMessage(nn_Computer *computer, const char *modemAddress, const char *sender, int port, double distance, const nn_EncodedNetworkContents *contents);
nn_Exit nn_pushNetworkContents(nn_Computer *C, const nn_EncodedNetworkContents *contents) {
nn_Value val;
size_t off = 0;
for(size_t i = 0; i < contents->valueCount; i++) {
size_t len = 0;
nn_Exit e = nn_decodeNetworkValue(&val, &C->universe->ctx, contents->buf + off, &len);
if(e) return e;
e = nn_pushvalue(C, val);
if(e) {
nn_dropValue(val);
return e;
}
off += len;
}
return NN_OK;
}
nn_Exit nn_pushModemMessage(nn_Computer *C, const char *modemAddress, const char *sender, int port, double distance, const nn_EncodedNetworkContents *contents) {
size_t signalVals = 5 + contents->valueCount;
nn_Exit e = nn_pushstring(C, "modem_message");
if(e) return e;
e = nn_pushstring(C, modemAddress);
if(e) return e;
e = nn_pushstring(C, sender);
if(e) return e;
e = nn_pushinteger(C, port);
if(e) return e;
e = nn_pushnumber(C, distance);
if(e) return e;
e = nn_pushNetworkContents(C, contents);
if(e) return e;
return nn_pushSignal(C, signalVals);
}
typedef enum nn_EENum {
NN_EENUM_GETSIZE,
@@ -4133,6 +4239,7 @@ static nn_Exit nn_drvHandler(nn_ComponentRequest *request) {
e = state->handler(&dreq);
if(e) return e;
request->returnCount = 1;
if(dreq.getlabel.len == 0) return nn_pushnull(C);
return nn_pushlstring(C, dreq.getlabel.buf, dreq.getlabel.len);
}
if(method == NN_DRVNUM_SETLABEL) {
@@ -4249,6 +4356,248 @@ bool nn_mergeDrives(nn_Drive *merged, const nn_Drive *drives, size_t len) {
return true;
}
static size_t nn_flash_writesAdded(nn_Context *ctx, const nn_NandFlash *flash) {
double x = nn_randfi(ctx);
double m = 1;
// TODO: use O(log N) algorithm instead of O(N)
for(size_t i = 0; i < flash->writeAmplificationExponent; i++) m *= x;
size_t max = flash->maxWriteAmplification * flash->cellLevel;
size_t amount = m * max;
if(amount < 1) amount = 1;
if(amount > max) amount = max;
return amount;
}
typedef enum nn_FlashNum {
NN_FLASHNUM_GETCAPACITY,
NN_FLASHNUM_GETSECTORSIZE,
NN_FLASHNUM_GETLAYERS,
NN_FLASHNUM_GETWEARLEVEL,
NN_FLASHNUM_ISRO,
NN_FLASHNUM_GETLABEL,
NN_FLASHNUM_SETLABEL,
NN_FLASHNUM_READSECTOR,
NN_FLASHNUM_WRITESECTOR,
NN_FLASHNUM_READBYTE,
NN_FLASHNUM_READUBYTE,
NN_FLASHNUM_WRITEBYTE,
NN_FLASHNUM_COUNT,
} nn_FlashNum;
typedef struct nn_FlashState {
nn_Context *ctx;
nn_NandFlash flash;
nn_FlashHandler *handler;
} nn_FlashState;
static nn_Exit nn_flashHandler(nn_ComponentRequest *request) {
nn_Context *ctx = request->ctx;
nn_Computer *C = request->computer;
nn_FlashState *state = request->classState;
nn_FlashRequest freq;
freq.ctx = ctx;
freq.computer = C;
freq.state = request->state;
freq.flash = &state->flash;
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) {
freq.action = NN_FLASH_DROP;
state->handler(&freq);
nn_free(ctx, state, sizeof(*state));
return NN_OK;
}
size_t ss = state->flash.sectorSize;
size_t sectorCount = state->flash.capacity / ss;
size_t maxWrite = state->flash.maxWriteCount;
nn_FlashNum method = request->methodIdx;
if(method == NN_FLASHNUM_GETCAPACITY) {
request->returnCount = 1;
return nn_pushinteger(C, state->flash.capacity);
}
if(method == NN_FLASHNUM_GETSECTORSIZE) {
request->returnCount = 1;
return nn_pushinteger(C, ss);
}
if(method == NN_FLASHNUM_GETLAYERS) {
request->returnCount = 1;
return nn_pushinteger(C, state->flash.cellLevel);
}
if(method == NN_FLASHNUM_GETWEARLEVEL) {
freq.action = NN_FLASH_GETWRITES;
e = state->handler(&freq);
if(e) return e;
request->returnCount = 1;
// would crash the math
if(maxWrite == 0) return nn_pushnumber(C, 100.0);
if(sectorCount == 0) return nn_pushnumber(C, 100.0);
double num = freq.writeCount * 100.0 / sectorCount / maxWrite;
return nn_pushnumber(C, num);
}
if(method == NN_FLASHNUM_ISRO) {
freq.action = NN_FLASH_ISRO;
e = state->handler(&freq);
if(e) return e;
request->returnCount = 1;
return nn_pushbool(C, freq.readonly);
}
if(method == NN_FLASHNUM_GETLABEL) {
freq.action = NN_FLASH_GETLABEL;
char buf[NN_MAX_LABEL];
freq.getlabel.buf = buf;
freq.getlabel.len = NN_MAX_LABEL;
e = state->handler(&freq);
if(e) return e;
request->returnCount = 1;
if(freq.getlabel.len == 0) return nn_pushnull(C);
return nn_pushlstring(C, freq.getlabel.buf, freq.getlabel.len);
}
if(method == NN_FLASHNUM_SETLABEL) {
e = nn_defaultstring(C, 0, "");
if(e) return e;
if(nn_checkstring(C, 0, "bad argument #1 (string expected)")) return NN_EBADCALL;
freq.action = NN_FLASH_SETLABEL;
freq.setlabel.buf = nn_tolstring(C, 0, &freq.setlabel.len);
e = state->handler(&freq);
if(e) return e;
request->returnCount = 1;
return nn_pushlstring(C, freq.setlabel.buf, freq.setlabel.len);
}
if(method == NN_FLASHNUM_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;
}
nn_costComponent(C, request->compAddress, state->flash.readsPerTick);
nn_removeEnergy(C, state->flash.dataEnergyCost * ss);
char *sector = nn_alloc(ctx, ss);
if(sector == NULL) return NN_ENOMEM;
freq.action = NN_FLASH_READSECTOR;
freq.readsector.sec = sec;
freq.readsector.buf = sector;
e = state->handler(&freq);
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(method == NN_FLASHNUM_WRITESECTOR) {
if(nn_checkinteger(C, 0, "bad argument #1 (integer expected)")) return NN_EBADCALL;
if(nn_checkstring(C, 1, "bad argument #2 (string 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;
}
freq.action = NN_FLASH_GETWRITES;
e = state->handler(&freq);
if(e) return e;
if(freq.writeCount >= maxWrite * sectorCount) {
nn_setError(C, "flash is not conductive enough");
return NN_EBADCALL;
}
nn_costComponent(C, request->compAddress, state->flash.writesPerTick);
nn_removeEnergy(C, state->flash.dataEnergyCost * ss);
size_t len;
const char *sector = nn_tolstring(C, 1, &len);
if(len != ss) {
nn_setError(C, "incorrect sector size");
return NN_EBADCALL;
}
freq.action = NN_FLASH_WRITESECTOR;
freq.writesector.sec = sec;
freq.writesector.buf = sector;
freq.writesector.writesAdded = nn_flash_writesAdded(ctx, &state->flash);
e = state->handler(&freq);
if(e) return e;
request->returnCount = 1;
return nn_pushbool(C, true);
}
if(C) nn_setError(C, "nandflash: not implemented yet");
return NN_EBADCALL;
}
nn_Component *nn_createFlash(nn_Universe *universe, const char *address, const nn_NandFlash *drive, void *state, nn_FlashHandler *handler) {
nn_Component *c = nn_createComponent(universe, address, "nandflash");
if(c == NULL) return NULL;
const nn_Method methods[NN_FLASHNUM_COUNT] = {
[NN_FLASHNUM_GETCAPACITY] = {"getCapacity", "function(): integer - Get the capacity of the flash storage", NN_DIRECT},
[NN_FLASHNUM_GETSECTORSIZE] = {"getSectorSize", "function(): integer - Get the logical sector size", NN_DIRECT},
[NN_FLASHNUM_GETLAYERS] = {"getLayers", "function(): integer - Get the amount of bits in a cell", NN_DIRECT},
[NN_FLASHNUM_GETWEARLEVEL] = {"getWearLevel", "function(): number - Gets a number from 0 to 100 indicitive of estimated drive damage", NN_DIRECT},
[NN_FLASHNUM_ISRO] = {"isReadonly", "function(): boolean - Checks whether the NAND is read-only", NN_DIRECT},
[NN_FLASHNUM_GETLABEL] = {"getLabel", "function(): string? - Get the label of the flash storage", NN_DIRECT},
[NN_FLASHNUM_SETLABEL] = {"setLabel", "function(label?: string): string - Set the label, which may be truncated", NN_INDIRECT},
[NN_FLASHNUM_READSECTOR] = {"readSector", "function(sector: integer): string - Read contents of a logical sector", NN_DIRECT},
[NN_FLASHNUM_WRITESECTOR] = {"writeSector", "function(sector: integer): string - Write contents of a logical sector, may lead to multiple real writes", NN_DIRECT},
[NN_FLASHNUM_READBYTE] = {"readByte", "function(byte: integer): integer - Read an individual signed byte", NN_DIRECT},
[NN_FLASHNUM_READUBYTE] = {"readUByte", "function(byte: integer): integer - Read an individual unsigned byte", NN_DIRECT},
[NN_FLASHNUM_WRITEBYTE] = {"writeByte", "function(byte: integer, value: integer): boolean - Write a byte"},
};
nn_Exit e = nn_setComponentMethodsArray(c, methods, NN_FLASHNUM_COUNT);
if(e) {
nn_dropComponent(c);
return NULL;
}
nn_Context *ctx = &universe->ctx;
nn_FlashState *drvstate = nn_alloc(ctx, sizeof(*drvstate));
if(drvstate == NULL) {
nn_dropComponent(c);
return NULL;
}
drvstate->ctx = ctx;
drvstate->flash = *drive;
drvstate->handler = handler;
nn_setComponentState(c, state);
nn_setComponentClassState(c, drvstate);
nn_setComponentHandler(c, nn_flashHandler);
return c;
}
bool nn_mergeFlash(nn_NandFlash *merged, const nn_NandFlash *flash, size_t len) {
if(len == 0) return false;
*merged = flash[0];
for(size_t i = 1; i < len; i++) {
nn_NandFlash f = flash[i];
merged->readsPerTick += f.readsPerTick;
merged->writesPerTick += f.writesPerTick;
merged->dataEnergyCost += f.dataEnergyCost;
merged->capacity += f.capacity;
merged->maxWriteCount += f.maxWriteCount;
merged->maxWriteAmplification += f.maxWriteAmplification;
merged->writeAmplificationExponent += f.writeAmplificationExponent;
merged->cellLevel += f.cellLevel;
}
merged->readsPerTick /= len;
merged->writesPerTick /= len;
merged->dataEnergyCost /= len;
merged->maxWriteCount /= len;
merged->maxWriteAmplification /= len;
merged->writeAmplificationExponent /= len;
merged->cellLevel /= len;
return true;
}
typedef enum nn_ScreenNum {
NN_SCRNUM_ISON,
NN_SCRNUM_TURNON,

View File

@@ -273,8 +273,11 @@ void nn_unlock(nn_Context *ctx, nn_Lock *lock);
double nn_currentTime(nn_Context *ctx);
// generate a random RNG from 0 to the maximum
size_t nn_rand(nn_Context *ctx);
// generate a random float [0, 1)
double nn_randf(nn_Context *ctx);
// generate a random float [0, 1]
double nn_randfi(nn_Context *ctx);
typedef char nn_uuid[37];
@@ -1103,7 +1106,7 @@ typedef struct nn_Drive {
// you can imagine it as reading the sector, editing the byte,
// then writing the sector back.
size_t writesPerTick;
// Set to 0 for *infinite*, effectively an SSD.
// Set to 0 for *infinite*, effectively an SSD with infinite lifespan.
// 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.
// It is aligned to the cache lines.
@@ -1123,8 +1126,6 @@ typedef struct nn_Drive {
extern const nn_Drive nn_defaultDrives[4];
extern const nn_Drive nn_floppyDrive;
extern const nn_Drive nn_defaultSSDs[4];
extern const nn_Drive nn_floppySSD;
typedef enum nn_DriveAction {
// drive gone
@@ -1139,8 +1140,6 @@ typedef enum nn_DriveAction {
NN_DRIVE_READSECTOR,
// write a sector
NN_DRIVE_WRITESECTOR,
// read a byte
NN_DRIVE_READBYTE,
// write a byte
NN_DRIVE_WRITEBYTE,
// is drive read-only
@@ -1173,11 +1172,6 @@ typedef struct nn_DriveRequest {
size_t sector;
const char *buf;
} writeSector;
struct {
// 1-indexed
size_t byte;
unsigned char value;
} readByte;
struct {
// 1-indexed
size_t byte;
@@ -1193,6 +1187,99 @@ nn_Component *nn_createDrive(nn_Universe *universe, const char *address, const n
bool nn_mergeDrives(nn_Drive *merged, const nn_Drive *drives, size_t len);
typedef struct nn_NandFlash {
// capacity of flash
size_t capacity;
// sector size
size_t sectorSize;
// reads per tick
size_t readsPerTick;
// writes per tick
size_t writesPerTick;
// The layering, in bits.
// 1 is SLC, 2 is MLC, 3 is TLC, etc.
// This number may amplify how quickly the total write count increases.
size_t cellLevel;
// the maximum amount of write amplification.
// Set to 0 to disable amplification RNG.
// The game will generate, using Context RNG, a real number from [0, 1]
// then raise it to writeAmplificationExponent,
// then multiply it by this number, and by the cell level.
// then clamp it to be at least 1 and at most this maximum.
unsigned int maxWriteAmplification;
int writeAmplificationExponent;
// the maximum amount of writes *per sector.*
// Set to 0 to make the nandflash eternal.
size_t maxWriteCount;
// how much per byte
double dataEnergyCost;
} nn_NandFlash;
typedef enum nn_FlashAction {
NN_FLASH_DROP,
NN_FLASH_GETLABEL,
NN_FLASH_SETLABEL,
NN_FLASH_ISRO,
// read a sector
NN_FLASH_READSECTOR,
// write a sector
// also adds an amount of writes
NN_FLASH_WRITESECTOR,
// write a sector
// also adds an amount of writes
NN_FLASH_WRITEBYTE,
// get the amount of writes
NN_FLASH_GETWRITES,
} nn_FlashAction;
typedef struct nn_FlashRequest {
nn_Context *ctx;
nn_Computer *computer;
void *state;
const nn_NandFlash *flash;
nn_FlashAction action;
union {
struct {
char *buf;
size_t len;
} getlabel;
struct {
const char *buf;
size_t len;
} setlabel;
struct {
char *buf;
// 1-indexed
size_t sec;
} readsector;
struct {
const char *buf;
// 1-indexed
size_t sec;
// how many writes to add
size_t writesAdded;
} writesector;
struct {
size_t byte;
char val;
// how many writes to add
size_t writesAdded;
} writebyte;
// for GETWRITES
size_t writeCount;
bool readonly;
};
} nn_FlashRequest;
typedef nn_Exit (nn_FlashHandler)(nn_FlashRequest *request);
extern const nn_NandFlash nn_defaultSSDs[4];
extern const nn_NandFlash nn_floppySSD;
nn_Component *nn_createFlash(nn_Universe *universe, const char *address, const nn_NandFlash *drive, void *state, nn_FlashHandler *handler);
bool nn_mergeFlash(nn_NandFlash *merged, const nn_NandFlash *flash, size_t len);
// Screen class
typedef enum nn_ScreenFeatures {