bunch of progress
This commit is contained in:
3
TODO.md
3
TODO.md
@@ -1,7 +1,6 @@
|
|||||||
# For MVP functionality
|
# For MVP functionality
|
||||||
|
|
||||||
- move SSDs to nandflash
|
- NCL computer states, as computers that can be turned on/off/beep/etc.
|
||||||
- remove HDD cachelines (they're pointless)
|
|
||||||
- write a tester OS, basically a menu with tests to run
|
- write a tester OS, basically a menu with tests to run
|
||||||
- tmpfs (rework the whole thing)
|
- tmpfs (rework the whole thing)
|
||||||
- device info
|
- device info
|
||||||
|
|||||||
23
src/main.c
23
src/main.c
@@ -385,7 +385,8 @@ int main(int argc, char **argv) {
|
|||||||
nn_Component *testingfs = ncl_createTmpFS(u, NULL, &nn_defaultFilesystems[3], NCL_FILECOST_DEFAULT, false);
|
nn_Component *testingfs = ncl_createTmpFS(u, NULL, &nn_defaultFilesystems[3], NCL_FILECOST_DEFAULT, false);
|
||||||
|
|
||||||
const char * const testDriveData =
|
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, 'bind', s, true)\n"
|
||||||
"component.invoke(g, 'set', 1, 1, 'starting sequential bench...')\n"
|
"component.invoke(g, 'set', 1, 1, 'starting sequential bench...')\n"
|
||||||
"local start = computer.uptime()\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"
|
"while computer.uptime() < now + 3 do computer.pullSignal(0.05) end\n"
|
||||||
"computer.shutdown(true)\n"
|
"computer.shutdown(true)\n"
|
||||||
;
|
;
|
||||||
nn_Drive driveconf;
|
nn_Component *testDrive = ncl_createDrive(u, NULL, &nn_defaultDrives[3], testDriveData, strlen(testDriveData), false);
|
||||||
nn_Drive driveparts[] = {
|
nn_Component *testFlash = ncl_createFlash(u, NULL, &nn_defaultSSDs[3], testDriveData, strlen(testDriveData), false);
|
||||||
nn_defaultSSDs[3],
|
|
||||||
};
|
|
||||||
nn_mergeDrives(&driveconf, driveparts, sizeof(driveparts) / sizeof(driveparts[0]));
|
|
||||||
nn_Component *testDrive = ncl_createDrive(u, NULL, &driveconf, testDriveData, strlen(testDriveData), false);
|
|
||||||
|
|
||||||
ncl_setCLabel(managedfs, "Main Filesystem");
|
ncl_setCLabel(managedfs, "Main Filesystem");
|
||||||
ncl_setCLabel(testingfs, "Secondary Filesystem");
|
ncl_setCLabel(testingfs, "Secondary Filesystem");
|
||||||
ncl_setCLabel(testDrive, "Unmanaged Storage");
|
ncl_setCLabel(testDrive, "Unmanaged Storage");
|
||||||
|
ncl_setCLabel(testFlash, "Flash Storage");
|
||||||
|
|
||||||
size_t ramTotal = 0;
|
size_t ramTotal = 0;
|
||||||
ramTotal += nn_ramSizes[5];
|
ramTotal += nn_ramSizes[5];
|
||||||
@@ -472,8 +470,7 @@ restart:;
|
|||||||
nn_pushnumber(c, 5.3);
|
nn_pushnumber(c, 5.3);
|
||||||
nn_pushbool(c, false);
|
nn_pushbool(c, false);
|
||||||
nn_encodeNetworkContents(c, &contents, 4);
|
nn_encodeNetworkContents(c, &contents, 4);
|
||||||
|
nn_popn(c, 4);
|
||||||
nn_dropNetworkContents(&contents);
|
|
||||||
|
|
||||||
printf("size: %zu\n", contents.buflen);
|
printf("size: %zu\n", contents.buflen);
|
||||||
for(size_t i = 0; i < contents.buflen; i++) {
|
for(size_t i = 0; i < contents.buflen; i++) {
|
||||||
@@ -499,6 +496,7 @@ restart:;
|
|||||||
nn_mountComponent(c, gpuCard, 2);
|
nn_mountComponent(c, gpuCard, 2);
|
||||||
//nn_mountComponent(c, testingfs, 3);
|
//nn_mountComponent(c, testingfs, 3);
|
||||||
nn_mountComponent(c, testDrive, 4);
|
nn_mountComponent(c, testDrive, 4);
|
||||||
|
nn_mountComponent(c, testFlash, 5);
|
||||||
while(true) {
|
while(true) {
|
||||||
if(WindowShouldClose()) break;
|
if(WindowShouldClose()) break;
|
||||||
|
|
||||||
@@ -580,6 +578,10 @@ restart:;
|
|||||||
if(keycode == KEY_TAB) unicode = '\t';
|
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);
|
nn_pushKeyDown(c, "mainKB", unicode, keycode_to_oc(keycode), player);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -632,6 +634,7 @@ restart:;
|
|||||||
}
|
}
|
||||||
if(state == NN_RESTART) {
|
if(state == NN_RESTART) {
|
||||||
printf("restart requested\n");
|
printf("restart requested\n");
|
||||||
|
nn_dropNetworkContents(&contents);
|
||||||
nn_destroyComputer(c);
|
nn_destroyComputer(c);
|
||||||
goto restart;
|
goto restart;
|
||||||
}
|
}
|
||||||
@@ -646,9 +649,11 @@ cleanup:;
|
|||||||
nn_dropComponent(tmpfs);
|
nn_dropComponent(tmpfs);
|
||||||
nn_dropComponent(testingfs);
|
nn_dropComponent(testingfs);
|
||||||
nn_dropComponent(testDrive);
|
nn_dropComponent(testDrive);
|
||||||
|
nn_dropComponent(testFlash);
|
||||||
nn_dropComponent(screen);
|
nn_dropComponent(screen);
|
||||||
nn_dropComponent(gpuCard);
|
nn_dropComponent(gpuCard);
|
||||||
nn_dropComponent(keyboard);
|
nn_dropComponent(keyboard);
|
||||||
|
nn_dropNetworkContents(&contents);
|
||||||
// rip the universe
|
// rip the universe
|
||||||
nn_destroyUniverse(u);
|
nn_destroyUniverse(u);
|
||||||
ncl_destroyGlyphCache(gc);
|
ncl_destroyGlyphCache(gc);
|
||||||
|
|||||||
@@ -95,6 +95,13 @@ for addr in component.list("drive", true) do
|
|||||||
end
|
end
|
||||||
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 function boot(bootable)
|
||||||
local w, h = component.invoke(gpu, "maxResolution")
|
local w, h = component.invoke(gpu, "maxResolution")
|
||||||
component.invoke(gpu, "setResolution", w, h)
|
component.invoke(gpu, "setResolution", w, h)
|
||||||
|
|||||||
183
src/ncomplib.c
183
src/ncomplib.c
@@ -631,6 +631,18 @@ typedef struct ncl_DriveState {
|
|||||||
size_t labellen;
|
size_t labellen;
|
||||||
} ncl_DriveState;
|
} 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 {
|
typedef struct ncl_EEState {
|
||||||
nn_Context *ctx;
|
nn_Context *ctx;
|
||||||
nn_Lock *lock;
|
nn_Lock *lock;
|
||||||
@@ -1264,6 +1276,7 @@ static nn_Exit ncl_tmpfsHandler(nn_FSRequest *req) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
nn_destroyLock(ctx, tmpfs->lock);
|
nn_destroyLock(ctx, tmpfs->lock);
|
||||||
|
nn_free(ctx, tmpfs, sizeof(*tmpfs));
|
||||||
return NN_OK;
|
return NN_OK;
|
||||||
}
|
}
|
||||||
if(req->action == NN_FS_SPACEUSED) {
|
if(req->action == NN_FS_SPACEUSED) {
|
||||||
@@ -1651,6 +1664,7 @@ static nn_Exit ncl_drvHandler(nn_DriveRequest *request) {
|
|||||||
size_t ss = drv->conf.sectorSize;
|
size_t ss = drv->conf.sectorSize;
|
||||||
|
|
||||||
if(request->action == NN_DRIVE_DROP) {
|
if(request->action == NN_DRIVE_DROP) {
|
||||||
|
nn_destroyLock(ctx, drv->lock);
|
||||||
nn_free(ctx, drv->data, drv->conf.capacity);
|
nn_free(ctx, drv->data, drv->conf.capacity);
|
||||||
nn_free(ctx, drv, sizeof(*drv));
|
nn_free(ctx, drv, sizeof(*drv));
|
||||||
return NN_OK;
|
return NN_OK;
|
||||||
@@ -1661,8 +1675,15 @@ static nn_Exit ncl_drvHandler(nn_DriveRequest *request) {
|
|||||||
nn_unlock(ctx, drv->lock);
|
nn_unlock(ctx, drv->lock);
|
||||||
return NN_OK;
|
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) {
|
if(request->action == NN_DRIVE_GETLABEL) {
|
||||||
nn_lock(ctx, drv->lock);
|
nn_lock(ctx, drv->lock);
|
||||||
|
drv->usage++;
|
||||||
memcpy(request->getlabel.buf, drv->label, drv->labellen);
|
memcpy(request->getlabel.buf, drv->label, drv->labellen);
|
||||||
request->getlabel.len = drv->labellen;
|
request->getlabel.len = drv->labellen;
|
||||||
nn_unlock(ctx, drv->lock);
|
nn_unlock(ctx, drv->lock);
|
||||||
@@ -1670,6 +1691,7 @@ static nn_Exit ncl_drvHandler(nn_DriveRequest *request) {
|
|||||||
}
|
}
|
||||||
if(request->action == NN_DRIVE_READSECTOR) {
|
if(request->action == NN_DRIVE_READSECTOR) {
|
||||||
nn_lock(ctx, drv->lock);
|
nn_lock(ctx, drv->lock);
|
||||||
|
drv->usage++;
|
||||||
size_t off = (request->readSector.sector - 1) * ss;
|
size_t off = (request->readSector.sector - 1) * ss;
|
||||||
memcpy(request->readSector.buf, drv->data + off, ss);
|
memcpy(request->readSector.buf, drv->data + off, ss);
|
||||||
drv->lastSector = request->readSector.sector;
|
drv->lastSector = request->readSector.sector;
|
||||||
@@ -1724,12 +1746,110 @@ fail:
|
|||||||
return NULL;
|
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) {
|
static nn_Exit ncl_eepromHandler(nn_EEPROMRequest *req) {
|
||||||
nn_Context *ctx = req->ctx;
|
nn_Context *ctx = req->ctx;
|
||||||
nn_Computer *C = req->computer;
|
nn_Computer *C = req->computer;
|
||||||
ncl_EEState *state = req->state;
|
ncl_EEState *state = req->state;
|
||||||
|
|
||||||
if(req->action == NN_EEPROM_DROP) {
|
if(req->action == NN_EEPROM_DROP) {
|
||||||
|
nn_destroyLock(ctx, state->lock);
|
||||||
nn_free(ctx, state->code, req->eeprom->size);
|
nn_free(ctx, state->code, req->eeprom->size);
|
||||||
nn_free(ctx, state->data, req->eeprom->dataSize);
|
nn_free(ctx, state->data, req->eeprom->dataSize);
|
||||||
nn_free(ctx, state, sizeof(*state));
|
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);
|
nn_unlock(drv->ctx, drv->lock);
|
||||||
return len;
|
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;
|
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);
|
nn_unlock(drv->ctx, drv->lock);
|
||||||
return;
|
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) {
|
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;
|
*len = drv->conf.capacity;
|
||||||
return drv->data;
|
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;
|
if(len != NULL) *len = 0;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -3245,6 +3390,20 @@ void ncl_statComponent(nn_Component *component, ncl_ComponentStat *stat) {
|
|||||||
nn_unlock(drv->ctx, drv->lock);
|
nn_unlock(drv->ctx, drv->lock);
|
||||||
return;
|
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) {
|
if(strcmp(ty, NCL_EEPROM) == 0) {
|
||||||
ncl_EEState *ee = state;
|
ncl_EEState *ee = state;
|
||||||
nn_lock(ee->ctx, ee->lock);
|
nn_lock(ee->ctx, ee->lock);
|
||||||
@@ -3294,6 +3453,14 @@ bool ncl_makeReadonly(nn_Component *component) {
|
|||||||
nn_unlock(drv->ctx, drv->lock);
|
nn_unlock(drv->ctx, drv->lock);
|
||||||
return true;
|
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) {
|
if(strcmp(ty, NCL_EEPROM) == 0) {
|
||||||
ncl_EEState *ee = state;
|
ncl_EEState *ee = state;
|
||||||
nn_lock(ee->ctx, ee->lock);
|
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);
|
nn_unlock(s->ctx, s->lock);
|
||||||
return len;
|
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;
|
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);
|
nn_unlock(s->ctx, s->lock);
|
||||||
return len;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#define NCL_EEPROM "ncl-eeprom"
|
#define NCL_EEPROM "ncl-eeprom"
|
||||||
#define NCL_FS "ncl-filesystem"
|
#define NCL_FS "ncl-filesystem"
|
||||||
#define NCL_DRIVE "ncl-drive"
|
#define NCL_DRIVE "ncl-drive"
|
||||||
|
#define NCL_FLASH "ncl-nandflash"
|
||||||
#define NCL_GPU "ncl-gpu"
|
#define NCL_GPU "ncl-gpu"
|
||||||
#define NCL_SCREEN "ncl-screen"
|
#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.
|
// 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);
|
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
|
// 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);
|
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;
|
const nn_Drive *conf;
|
||||||
size_t lastSector;
|
size_t lastSector;
|
||||||
} drive;
|
} drive;
|
||||||
|
struct {
|
||||||
|
const nn_NandFlash *conf;
|
||||||
|
size_t currentWriteCount;
|
||||||
|
double wearlevel;
|
||||||
|
} flash;
|
||||||
struct {
|
struct {
|
||||||
const nn_GPU *conf;
|
const nn_GPU *conf;
|
||||||
size_t vramFree;
|
size_t vramFree;
|
||||||
|
|||||||
397
src/neonucleus.c
397
src/neonucleus.c
@@ -2537,57 +2537,62 @@ const nn_Drive nn_floppyDrive = {
|
|||||||
.dataEnergyCost = 1.0 / NN_MiB,
|
.dataEnergyCost = 1.0 / NN_MiB,
|
||||||
};
|
};
|
||||||
|
|
||||||
const nn_Drive nn_defaultSSDs[4] = {
|
const nn_NandFlash nn_defaultSSDs[4] = {
|
||||||
NN_INIT(nn_Drive) {
|
NN_INIT(nn_NandFlash) {
|
||||||
.capacity = 512 * NN_KiB,
|
.capacity = 512 * NN_KiB,
|
||||||
.sectorSize = 512,
|
.sectorSize = 512,
|
||||||
.platterCount = 2,
|
|
||||||
.readsPerTick = 10,
|
.readsPerTick = 10,
|
||||||
.writesPerTick = 5,
|
.writesPerTick = 5,
|
||||||
.rpm = 0,
|
.cellLevel = 1,
|
||||||
.onlySpinForwards = false,
|
.maxWriteCount = 1<<10,
|
||||||
|
.maxWriteAmplification = 4,
|
||||||
|
.writeAmplificationExponent = 2,
|
||||||
.dataEnergyCost = 64.0 / NN_MiB,
|
.dataEnergyCost = 64.0 / NN_MiB,
|
||||||
},
|
},
|
||||||
NN_INIT(nn_Drive) {
|
NN_INIT(nn_NandFlash) {
|
||||||
.capacity = 1 * NN_MiB,
|
.capacity = 1 * NN_MiB,
|
||||||
.sectorSize = 512,
|
.sectorSize = 512,
|
||||||
.platterCount = 4,
|
|
||||||
.readsPerTick = 15,
|
.readsPerTick = 15,
|
||||||
.writesPerTick = 7,
|
.writesPerTick = 7,
|
||||||
.rpm = 0,
|
.cellLevel = 2,
|
||||||
.onlySpinForwards = false,
|
.maxWriteCount = 1<<10,
|
||||||
|
.maxWriteAmplification = 8,
|
||||||
|
.writeAmplificationExponent = 2,
|
||||||
.dataEnergyCost = 128.0 / NN_MiB,
|
.dataEnergyCost = 128.0 / NN_MiB,
|
||||||
},
|
},
|
||||||
NN_INIT(nn_Drive) {
|
NN_INIT(nn_NandFlash) {
|
||||||
.capacity = 2 * NN_MiB,
|
.capacity = 2 * NN_MiB,
|
||||||
.sectorSize = 512,
|
.sectorSize = 512,
|
||||||
.platterCount = 8,
|
|
||||||
.readsPerTick = 20,
|
.readsPerTick = 20,
|
||||||
.writesPerTick = 10,
|
.writesPerTick = 10,
|
||||||
.rpm = 0,
|
.cellLevel = 3,
|
||||||
.onlySpinForwards = false,
|
.maxWriteCount = 1<<10,
|
||||||
|
.maxWriteAmplification = 12,
|
||||||
|
.writeAmplificationExponent = 2,
|
||||||
.dataEnergyCost = 256.0 / NN_MiB,
|
.dataEnergyCost = 256.0 / NN_MiB,
|
||||||
},
|
},
|
||||||
NN_INIT(nn_Drive) {
|
NN_INIT(nn_NandFlash) {
|
||||||
.capacity = 4 * NN_MiB,
|
.capacity = 4 * NN_MiB,
|
||||||
.sectorSize = 512,
|
.sectorSize = 512,
|
||||||
.platterCount = 16,
|
|
||||||
.readsPerTick = 30,
|
.readsPerTick = 30,
|
||||||
.writesPerTick = 15,
|
.writesPerTick = 15,
|
||||||
.rpm = 0,
|
.cellLevel = 4,
|
||||||
.onlySpinForwards = false,
|
.maxWriteCount = 1<<10,
|
||||||
|
.maxWriteAmplification = 16,
|
||||||
|
.writeAmplificationExponent = 2,
|
||||||
.dataEnergyCost = 512.0 / NN_MiB,
|
.dataEnergyCost = 512.0 / NN_MiB,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const nn_Drive nn_floppySSD = {
|
const nn_NandFlash nn_floppySSD = {
|
||||||
.capacity = 256 * NN_KiB,
|
.capacity = 256 * NN_KiB,
|
||||||
.sectorSize = 512,
|
.sectorSize = 512,
|
||||||
.platterCount = 1,
|
|
||||||
.readsPerTick = 5,
|
.readsPerTick = 5,
|
||||||
.writesPerTick = 2,
|
.writesPerTick = 2,
|
||||||
.rpm = 0,
|
.cellLevel = 1,
|
||||||
.onlySpinForwards = true,
|
.maxWriteCount = 1<<10,
|
||||||
|
.maxWriteAmplification = 4,
|
||||||
|
.writeAmplificationExponent = 2,
|
||||||
.dataEnergyCost = 16.0 / NN_MiB,
|
.dataEnergyCost = 16.0 / NN_MiB,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -3432,15 +3437,116 @@ nn_Exit nn_encodeNetworkContents(nn_Computer *computer, nn_EncodedNetworkContent
|
|||||||
return NN_OK;
|
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) {
|
void nn_dropNetworkContents(nn_EncodedNetworkContents *contents) {
|
||||||
nn_free(contents->ctx, contents->buf, contents->buflen);
|
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 {
|
typedef enum nn_EENum {
|
||||||
NN_EENUM_GETSIZE,
|
NN_EENUM_GETSIZE,
|
||||||
@@ -4133,6 +4239,7 @@ static nn_Exit nn_drvHandler(nn_ComponentRequest *request) {
|
|||||||
e = state->handler(&dreq);
|
e = state->handler(&dreq);
|
||||||
if(e) return e;
|
if(e) return e;
|
||||||
request->returnCount = 1;
|
request->returnCount = 1;
|
||||||
|
if(dreq.getlabel.len == 0) return nn_pushnull(C);
|
||||||
return nn_pushlstring(C, dreq.getlabel.buf, dreq.getlabel.len);
|
return nn_pushlstring(C, dreq.getlabel.buf, dreq.getlabel.len);
|
||||||
}
|
}
|
||||||
if(method == NN_DRVNUM_SETLABEL) {
|
if(method == NN_DRVNUM_SETLABEL) {
|
||||||
@@ -4249,6 +4356,248 @@ bool nn_mergeDrives(nn_Drive *merged, const nn_Drive *drives, size_t len) {
|
|||||||
return true;
|
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 {
|
typedef enum nn_ScreenNum {
|
||||||
NN_SCRNUM_ISON,
|
NN_SCRNUM_ISON,
|
||||||
NN_SCRNUM_TURNON,
|
NN_SCRNUM_TURNON,
|
||||||
|
|||||||
107
src/neonucleus.h
107
src/neonucleus.h
@@ -273,8 +273,11 @@ void nn_unlock(nn_Context *ctx, nn_Lock *lock);
|
|||||||
|
|
||||||
double nn_currentTime(nn_Context *ctx);
|
double nn_currentTime(nn_Context *ctx);
|
||||||
|
|
||||||
|
// generate a random RNG from 0 to the maximum
|
||||||
size_t nn_rand(nn_Context *ctx);
|
size_t nn_rand(nn_Context *ctx);
|
||||||
|
// generate a random float [0, 1)
|
||||||
double nn_randf(nn_Context *ctx);
|
double nn_randf(nn_Context *ctx);
|
||||||
|
// generate a random float [0, 1]
|
||||||
double nn_randfi(nn_Context *ctx);
|
double nn_randfi(nn_Context *ctx);
|
||||||
|
|
||||||
typedef char nn_uuid[37];
|
typedef char nn_uuid[37];
|
||||||
@@ -1103,7 +1106,7 @@ typedef struct nn_Drive {
|
|||||||
// 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 with infinite lifespan.
|
||||||
// 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.
|
// 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_defaultDrives[4];
|
||||||
extern const nn_Drive nn_floppyDrive;
|
extern const nn_Drive nn_floppyDrive;
|
||||||
|
|
||||||
extern const nn_Drive nn_defaultSSDs[4];
|
|
||||||
extern const nn_Drive nn_floppySSD;
|
|
||||||
|
|
||||||
typedef enum nn_DriveAction {
|
typedef enum nn_DriveAction {
|
||||||
// drive gone
|
// drive gone
|
||||||
@@ -1139,8 +1140,6 @@ typedef enum nn_DriveAction {
|
|||||||
NN_DRIVE_READSECTOR,
|
NN_DRIVE_READSECTOR,
|
||||||
// write a sector
|
// write a sector
|
||||||
NN_DRIVE_WRITESECTOR,
|
NN_DRIVE_WRITESECTOR,
|
||||||
// read a byte
|
|
||||||
NN_DRIVE_READBYTE,
|
|
||||||
// write a byte
|
// write a byte
|
||||||
NN_DRIVE_WRITEBYTE,
|
NN_DRIVE_WRITEBYTE,
|
||||||
// is drive read-only
|
// is drive read-only
|
||||||
@@ -1173,11 +1172,6 @@ typedef struct nn_DriveRequest {
|
|||||||
size_t sector;
|
size_t sector;
|
||||||
const char *buf;
|
const char *buf;
|
||||||
} writeSector;
|
} writeSector;
|
||||||
struct {
|
|
||||||
// 1-indexed
|
|
||||||
size_t byte;
|
|
||||||
unsigned char value;
|
|
||||||
} readByte;
|
|
||||||
struct {
|
struct {
|
||||||
// 1-indexed
|
// 1-indexed
|
||||||
size_t byte;
|
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);
|
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
|
// Screen class
|
||||||
|
|
||||||
typedef enum nn_ScreenFeatures {
|
typedef enum nn_ScreenFeatures {
|
||||||
|
|||||||
Reference in New Issue
Block a user