bunch of progress
This commit is contained in:
3
TODO.md
3
TODO.md
@@ -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
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
183
src/ncomplib.c
183
src/ncomplib.c
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
397
src/neonucleus.c
397
src/neonucleus.c
@@ -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,
|
||||
|
||||
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);
|
||||
|
||||
// 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 {
|
||||
|
||||
Reference in New Issue
Block a user