unmanaged drives
This commit is contained in:
@@ -31,13 +31,13 @@ void *luaArch_alloc(void *ud, void *ptr, size_t osize, size_t nsize) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if(ptr == NULL) {
|
if(ptr == NULL) {
|
||||||
//if(arch->freeMem < nsize) return NULL;
|
if(arch->freeMem < nsize) return NULL;
|
||||||
void *mem = malloc(nsize);
|
void *mem = malloc(nsize);
|
||||||
if(mem == NULL) return NULL;
|
if(mem == NULL) return NULL;
|
||||||
arch->freeMem -= nsize;
|
arch->freeMem -= nsize;
|
||||||
return mem;
|
return mem;
|
||||||
}
|
}
|
||||||
//if(arch->freeMem + osize < nsize) return NULL;
|
if(arch->freeMem + osize < nsize) return NULL;
|
||||||
void *mem = realloc(ptr, nsize);
|
void *mem = realloc(ptr, nsize);
|
||||||
if(mem == NULL) return NULL;
|
if(mem == NULL) return NULL;
|
||||||
arch->freeMem += osize;
|
arch->freeMem += osize;
|
||||||
@@ -231,6 +231,12 @@ static int luaArch_computer_isOverused(lua_State *L) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int luaArch_computer_isIdle(lua_State *L) {
|
||||||
|
nn_Computer *c = luaArch_from(L)->computer;
|
||||||
|
lua_pushboolean(L, nn_isComputerIdle(c));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int luaArch_computer_pushSignal(lua_State *L) {
|
static int luaArch_computer_pushSignal(lua_State *L) {
|
||||||
luaArch *arch = luaArch_from(L);
|
luaArch *arch = luaArch_from(L);
|
||||||
nn_Computer *c = arch->computer;
|
nn_Computer *c = arch->computer;
|
||||||
@@ -538,6 +544,8 @@ static void luaArch_loadEnv(lua_State *L) {
|
|||||||
lua_setfield(L, computer, "shutdown");
|
lua_setfield(L, computer, "shutdown");
|
||||||
lua_pushcfunction(L, luaArch_computer_isOverused);
|
lua_pushcfunction(L, luaArch_computer_isOverused);
|
||||||
lua_setfield(L, computer, "isOverused");
|
lua_setfield(L, computer, "isOverused");
|
||||||
|
lua_pushcfunction(L, luaArch_computer_isIdle);
|
||||||
|
lua_setfield(L, computer, "isIdle");
|
||||||
lua_pushcfunction(L, luaArch_computer_pushSignal);
|
lua_pushcfunction(L, luaArch_computer_pushSignal);
|
||||||
lua_setfield(L, computer, "pushSignal");
|
lua_setfield(L, computer, "pushSignal");
|
||||||
lua_pushcfunction(L, luaArch_computer_popSignal);
|
lua_pushcfunction(L, luaArch_computer_popSignal);
|
||||||
@@ -589,7 +597,7 @@ static nn_Exit luaArch_handler(nn_ArchitectureRequest *req) {
|
|||||||
arch = nn_alloc(ctx, sizeof(*arch));
|
arch = nn_alloc(ctx, sizeof(*arch));
|
||||||
arch->freeMem = nn_getTotalMemory(computer);
|
arch->freeMem = nn_getTotalMemory(computer);
|
||||||
arch->computer = computer;
|
arch->computer = computer;
|
||||||
lua_State *L = luaL_newstate();
|
lua_State *L = lua_newstate(luaArch_alloc, arch);
|
||||||
arch->L = L;
|
arch->L = L;
|
||||||
req->localState = arch;
|
req->localState = arch;
|
||||||
luaL_openlibs(L);
|
luaL_openlibs(L);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ end
|
|||||||
local resume = coroutine.resume
|
local resume = coroutine.resume
|
||||||
|
|
||||||
function coroutine.resume(co, ...)
|
function coroutine.resume(co, ...)
|
||||||
|
while true do
|
||||||
local t = {resume(co, ...)}
|
local t = {resume(co, ...)}
|
||||||
if t[1] and rawequal(t[2], sysyieldobj) then
|
if t[1] and rawequal(t[2], sysyieldobj) then
|
||||||
coroutine.yield(sysyieldobj)
|
coroutine.yield(sysyieldobj)
|
||||||
@@ -20,6 +21,7 @@ function coroutine.resume(co, ...)
|
|||||||
return table.unpack(t)
|
return table.unpack(t)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function coroutine.wrap(f)
|
function coroutine.wrap(f)
|
||||||
local co = coroutine.create(f)
|
local co = coroutine.create(f)
|
||||||
@@ -65,6 +67,7 @@ function component.invoke(address, method, ...)
|
|||||||
local t = {pcall(cinvoke, address, method, ...)}
|
local t = {pcall(cinvoke, address, method, ...)}
|
||||||
if computer.energy() <= 0 then sysyield() end -- out of power
|
if computer.energy() <= 0 then sysyield() end -- out of power
|
||||||
if computer.isOverused() then sysyield() end -- overused
|
if computer.isOverused() then sysyield() end -- overused
|
||||||
|
if computer.isIdle() then sysyield() end -- machine idle
|
||||||
|
|
||||||
if t[1] then
|
if t[1] then
|
||||||
return table.unpack(t, 2)
|
return table.unpack(t, 2)
|
||||||
@@ -189,24 +192,17 @@ unicode.sub = function(str, a, b)
|
|||||||
if not b then b = utf8.len(str) end
|
if not b then b = utf8.len(str) end
|
||||||
if not a then a = 1 end
|
if not a then a = 1 end
|
||||||
-- a = math.max(a,1)
|
-- a = math.max(a,1)
|
||||||
|
|
||||||
if a < 0 then
|
if a < 0 then
|
||||||
-- negative
|
-- negative
|
||||||
|
|
||||||
a = utf8.len(str) + a + 1
|
a = utf8.len(str) + a + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
if b < 0 then
|
if b < 0 then
|
||||||
b = utf8.len(str) + b + 1
|
b = utf8.len(str) + b + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
if a > b then return "" end
|
if a > b then return "" end
|
||||||
|
|
||||||
if b >= utf8.len(str) then b = #str else b = utf8.offset(str,b+1)-1 end
|
if b >= utf8.len(str) then b = #str else b = utf8.offset(str,b+1)-1 end
|
||||||
|
|
||||||
if a > utf8.len(str) then return "" end
|
if a > utf8.len(str) then return "" end
|
||||||
a = utf8.offset(str,a)
|
a = utf8.offset(str,a)
|
||||||
|
|
||||||
return str:sub(a,b)
|
return str:sub(a,b)
|
||||||
-- return str:sub(a, b)
|
-- return str:sub(a, b)
|
||||||
end
|
end
|
||||||
@@ -238,11 +234,14 @@ if os.getenv("NN_REPL") == "1" then
|
|||||||
print("exiting repl")
|
print("exiting repl")
|
||||||
end
|
end
|
||||||
|
|
||||||
collectgarbage("stop")
|
-- Save on just a tiny smudgeon of RAM
|
||||||
|
io = nil
|
||||||
|
package = nil
|
||||||
|
|
||||||
local eeprom = component.list("eeprom", true)()
|
local eeprom = component.list("eeprom", true)()
|
||||||
assert(eeprom, "missing firmware")
|
assert(eeprom, "missing firmware")
|
||||||
|
|
||||||
|
-- this would automatically reboot us if it needs to be a different architecture
|
||||||
local arch = component.invoke(eeprom, "getArchitecture")
|
local arch = component.invoke(eeprom, "getArchitecture")
|
||||||
if arch then computer.setArchitecture(arch) end
|
if arch then computer.setArchitecture(arch) end
|
||||||
|
|
||||||
|
|||||||
@@ -114,6 +114,8 @@ nn_Exit ne_fsState_handler(nn_FilesystemRequest *req) {
|
|||||||
char truepath[NN_MAX_PATH];
|
char truepath[NN_MAX_PATH];
|
||||||
|
|
||||||
switch(req->action) {
|
switch(req->action) {
|
||||||
|
case NN_FS_FREE:
|
||||||
|
return NN_OK;
|
||||||
case NN_FS_DROP:
|
case NN_FS_DROP:
|
||||||
for(size_t i = 0; i < NN_MAX_OPENFILES; i++) {
|
for(size_t i = 0; i < NN_MAX_OPENFILES; i++) {
|
||||||
if(state->files[i] != NULL) fclose(state->files[i]);
|
if(state->files[i] != NULL) fclose(state->files[i]);
|
||||||
@@ -333,8 +335,8 @@ void ne_remapScreen(ne_ScreenBuffer *buf) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ne_ScreenBuffer *ne_newScreenBuf(nn_Context *ctx, nn_ScreenConfig conf, const char *keyboard) {
|
ne_ScreenBuffer *ne_newScreenBuf(nn_ScreenConfig conf, const char *keyboard) {
|
||||||
ne_ScreenBuffer *buf = nn_alloc(ctx, sizeof(*buf));
|
ne_ScreenBuffer *buf = malloc(sizeof(*buf));
|
||||||
buf->maxWidth = conf.maxWidth;
|
buf->maxWidth = conf.maxWidth;
|
||||||
buf->maxHeight = conf.maxHeight;
|
buf->maxHeight = conf.maxHeight;
|
||||||
buf->width = buf->maxWidth;
|
buf->width = buf->maxWidth;
|
||||||
@@ -342,10 +344,10 @@ ne_ScreenBuffer *ne_newScreenBuf(nn_Context *ctx, nn_ScreenConfig conf, const ch
|
|||||||
buf->maxDepth = conf.maxDepth;
|
buf->maxDepth = conf.maxDepth;
|
||||||
buf->depth = buf->maxDepth;
|
buf->depth = buf->maxDepth;
|
||||||
buf->maxPalette = conf.paletteColors;
|
buf->maxPalette = conf.paletteColors;
|
||||||
buf->pixels = nn_alloc(ctx, sizeof(ne_Pixel) * conf.maxWidth * conf.maxHeight);
|
buf->pixels = malloc(sizeof(ne_Pixel) * conf.maxWidth * conf.maxHeight);
|
||||||
buf->virtualPalette = nn_alloc(ctx, sizeof(int) * conf.paletteColors);
|
buf->virtualPalette = malloc(sizeof(int) * conf.paletteColors);
|
||||||
memset(buf->virtualPalette, 0, sizeof(int) * buf->maxPalette);
|
memset(buf->virtualPalette, 0, sizeof(int) * buf->maxPalette);
|
||||||
buf->mappedPalette = nn_alloc(ctx, sizeof(int) * conf.paletteColors);
|
buf->mappedPalette = malloc(sizeof(int) * conf.paletteColors);
|
||||||
buf->keyboard = keyboard;
|
buf->keyboard = keyboard;
|
||||||
|
|
||||||
int *palette = NULL;
|
int *palette = NULL;
|
||||||
@@ -374,11 +376,11 @@ ne_ScreenBuffer *ne_newScreenBuf(nn_Context *ctx, nn_ScreenConfig conf, const ch
|
|||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ne_dropScreenBuf(nn_Context *ctx, ne_ScreenBuffer *buf) {
|
void ne_dropScreenBuf(ne_ScreenBuffer *buf) {
|
||||||
nn_free(ctx, buf->pixels, sizeof(ne_Pixel) * buf->maxWidth * buf->maxHeight);
|
free(buf->pixels);
|
||||||
nn_free(ctx, buf->mappedPalette, sizeof(int) * buf->maxPalette);
|
free(buf->mappedPalette);
|
||||||
nn_free(ctx, buf->virtualPalette, sizeof(int) * buf->maxPalette);
|
free(buf->virtualPalette);
|
||||||
nn_free(ctx, buf, sizeof(*buf));
|
free(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
ne_Pixel defaultPixel = {
|
ne_Pixel defaultPixel = {
|
||||||
@@ -414,6 +416,8 @@ nn_Exit ne_screen_handler(nn_ScreenRequest *req) {
|
|||||||
switch(req->action) {
|
switch(req->action) {
|
||||||
case NN_SCR_DROP:
|
case NN_SCR_DROP:
|
||||||
return NN_OK;
|
return NN_OK;
|
||||||
|
case NN_SCR_FREE:
|
||||||
|
return NN_OK;
|
||||||
case NN_SCR_GETASPECTRATIO:
|
case NN_SCR_GETASPECTRATIO:
|
||||||
req->w = 1;
|
req->w = 1;
|
||||||
req->h = 1;
|
req->h = 1;
|
||||||
@@ -493,15 +497,14 @@ ne_ScreenBuffer *ne_gpu_currentBuffer(ne_GPUState *state) {
|
|||||||
nn_Exit ne_gpu_handler(nn_GPURequest *req) {
|
nn_Exit ne_gpu_handler(nn_GPURequest *req) {
|
||||||
nn_Computer *C = req->computer;
|
nn_Computer *C = req->computer;
|
||||||
ne_GPUState *state = req->instance;
|
ne_GPUState *state = req->instance;
|
||||||
nn_Context *ctx = nn_getComputerContext(C);
|
|
||||||
|
|
||||||
int maxWidth = req->gpuConf->maxWidth;
|
int maxWidth = req->gpuConf->maxWidth;
|
||||||
int maxHeight = req->gpuConf->maxHeight;
|
int maxHeight = req->gpuConf->maxHeight;
|
||||||
int maxDepth = req->gpuConf->maxDepth;
|
int maxDepth = req->gpuConf->maxDepth;
|
||||||
|
|
||||||
ne_ScreenBuffer *activeBuf = ne_gpu_currentBuffer(state);
|
ne_ScreenBuffer *activeBuf = state == NULL ? NULL : ne_gpu_currentBuffer(state);
|
||||||
|
|
||||||
if(state->screenBuf != NULL) {
|
if(state != NULL && state->screenBuf != NULL) {
|
||||||
ne_ScreenBuffer *buf = state->screenBuf;
|
ne_ScreenBuffer *buf = state->screenBuf;
|
||||||
if(maxWidth > buf->maxWidth) maxWidth = buf->maxWidth;
|
if(maxWidth > buf->maxWidth) maxWidth = buf->maxWidth;
|
||||||
if(maxHeight > buf->maxHeight) maxHeight = buf->maxHeight;
|
if(maxHeight > buf->maxHeight) maxHeight = buf->maxHeight;
|
||||||
@@ -515,10 +518,12 @@ nn_Exit ne_gpu_handler(nn_GPURequest *req) {
|
|||||||
case NN_GPU_DROP:
|
case NN_GPU_DROP:
|
||||||
for(int i = 0; i < NE_MAX_VRAMBUF; i++) {
|
for(int i = 0; i < NE_MAX_VRAMBUF; i++) {
|
||||||
ne_ScreenBuffer *buf = state->vramBufs[i];
|
ne_ScreenBuffer *buf = state->vramBufs[i];
|
||||||
if(buf != NULL) ne_dropScreenBuf(ctx, buf);
|
if(buf != NULL) ne_dropScreenBuf(buf);
|
||||||
}
|
}
|
||||||
free(state);
|
free(state);
|
||||||
return NN_OK;
|
return NN_OK;
|
||||||
|
case NN_GPU_FREE:
|
||||||
|
return NN_OK;
|
||||||
case NN_GPU_BIND:
|
case NN_GPU_BIND:
|
||||||
state->screenBuf = nn_getComponentUserdata(C, req->text);
|
state->screenBuf = nn_getComponentUserdata(C, req->text);
|
||||||
memcpy(state->scrAddr, req->text, req->width);
|
memcpy(state->scrAddr, req->text, req->width);
|
||||||
@@ -625,7 +630,7 @@ nn_Exit ne_gpu_handler(nn_GPURequest *req) {
|
|||||||
if(w >= activeBuf->width) w = activeBuf->width - 1;
|
if(w >= activeBuf->width) w = activeBuf->width - 1;
|
||||||
if(h >= activeBuf->height) h = activeBuf->height - 1;
|
if(h >= activeBuf->height) h = activeBuf->height - 1;
|
||||||
|
|
||||||
ne_Pixel *buf = nn_alloc(ctx, sizeof(*buf) * w * h);
|
ne_Pixel *buf = malloc(sizeof(*buf) * w * h);
|
||||||
if(buf == NULL) return NN_ENOMEM;
|
if(buf == NULL) return NN_ENOMEM;
|
||||||
|
|
||||||
for(int oy = 0; oy < h; oy++) {
|
for(int oy = 0; oy < h; oy++) {
|
||||||
@@ -640,7 +645,7 @@ nn_Exit ne_gpu_handler(nn_GPURequest *req) {
|
|||||||
ne_setPixel(activeBuf, x + ox + req->tx, y + oy + req->ty, p);
|
ne_setPixel(activeBuf, x + ox + req->tx, y + oy + req->ty, p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nn_free(ctx, buf, sizeof(*buf) * w * h);
|
free(buf);
|
||||||
ne_remapScreen(activeBuf);
|
ne_remapScreen(activeBuf);
|
||||||
return NN_OK;
|
return NN_OK;
|
||||||
case NN_GPU_GETDEPTH:
|
case NN_GPU_GETDEPTH:
|
||||||
@@ -992,13 +997,16 @@ double ne_energy_accumulator(void *state, nn_Computer *c, double n) {
|
|||||||
return nn_getTotalEnergy(c);
|
return nn_getTotalEnergy(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main(int argc, char **argv) {
|
||||||
const char *player = getenv("USER");
|
const char *player = getenv("USER");
|
||||||
if(player == NULL) player = "me";
|
if(player == NULL) player = "me";
|
||||||
|
|
||||||
bool sandboxMem = getenv("NN_MEMSAND") != NULL;
|
bool sandboxMem = getenv("NN_MEMSAND") != NULL;
|
||||||
bool showStats = getenv("NN_STAT") != NULL;
|
bool showStats = getenv("NN_STAT") != NULL;
|
||||||
|
|
||||||
|
const char *mainDir = "OpenOS";
|
||||||
|
if(argc > 1) mainDir = argv[1];
|
||||||
|
|
||||||
nn_Context ctx;
|
nn_Context ctx;
|
||||||
nn_initContext(&ctx);
|
nn_initContext(&ctx);
|
||||||
nn_initPalettes();
|
nn_initPalettes();
|
||||||
@@ -1029,7 +1037,7 @@ int main() {
|
|||||||
{"log", "log(msg: string) - Log to stdout", true},
|
{"log", "log(msg: string) - Log to stdout", true},
|
||||||
{NULL},
|
{NULL},
|
||||||
};
|
};
|
||||||
nn_ComponentState *sandstate = nn_createComponentState(u, "sandbox", NULL, sandboxMethods, sandbox_handler);
|
nn_ComponentState *sandstate = nn_createComponentState(u, "ocelot", NULL, sandboxMethods, sandbox_handler);
|
||||||
|
|
||||||
nn_VEEPROM veeprom = {
|
nn_VEEPROM veeprom = {
|
||||||
.code = minBIOS,
|
.code = minBIOS,
|
||||||
@@ -1052,7 +1060,11 @@ int main() {
|
|||||||
nn_ComponentState *keytype = nn_createKeyboard(u);
|
nn_ComponentState *keytype = nn_createKeyboard(u);
|
||||||
nn_ComponentState *gputype = nn_createGPU(u, &nn_defaultGPUs[3], ne_gpu_handler, NULL);
|
nn_ComponentState *gputype = nn_createGPU(u, &nn_defaultGPUs[3], ne_gpu_handler, NULL);
|
||||||
|
|
||||||
nn_Computer *c = nn_createComputer(u, NULL, "computer0", 8 * NN_MiB, 256, 256);
|
size_t ramTotal = 0;
|
||||||
|
ramTotal += nn_ramSizes[1];
|
||||||
|
ramTotal += nn_ramSizes[1];
|
||||||
|
|
||||||
|
nn_Computer *c = nn_createComputer(u, NULL, "computer0", ramTotal, 256, 256);
|
||||||
if(showStats) {
|
if(showStats) {
|
||||||
// collects stats
|
// collects stats
|
||||||
nn_setEnergyHandler(c, NULL, ne_energy_accumulator);
|
nn_setEnergyHandler(c, NULL, ne_energy_accumulator);
|
||||||
@@ -1064,16 +1076,26 @@ int main() {
|
|||||||
nn_addComponent(c, sandstate, "sandbox", -1, NULL);
|
nn_addComponent(c, sandstate, "sandbox", -1, NULL);
|
||||||
nn_addComponent(c, etype, "eeprom", 0, etype);
|
nn_addComponent(c, etype, "eeprom", 0, etype);
|
||||||
|
|
||||||
ne_FsState *mainFS = ne_newFS("OpenOS", false);
|
nn_addComponent(c, fstype[4], "mainFS", 2, ne_newFS(mainDir, false));
|
||||||
nn_addComponent(c, fstype[4], "mainFS", 2, mainFS);
|
|
||||||
|
|
||||||
nn_addComponent(c, keytype, "mainKB", 4, NULL);
|
nn_addComponent(c, keytype, "mainKB", 4, NULL);
|
||||||
ne_ScreenBuffer *scrbuf = ne_newScreenBuf(&ctx, nn_defaultScreens[2], "mainKB");
|
ne_ScreenBuffer *scrbuf = ne_newScreenBuf(nn_defaultScreens[2], "mainKB");
|
||||||
nn_addComponent(c, scrtype, "mainScreen", -1, scrbuf);
|
nn_addComponent(c, scrtype, "mainScreen", -1, scrbuf);
|
||||||
|
|
||||||
ne_GPUState *gpu = ne_newGPU();
|
ne_GPUState *gpu = ne_newGPU();
|
||||||
nn_addComponent(c, gputype, "mainGPU", 3, gpu);
|
nn_addComponent(c, gputype, "mainGPU", 3, gpu);
|
||||||
|
|
||||||
|
const char *driveData = "error('unmanaged drive')";
|
||||||
|
nn_VDrive vdrive = {
|
||||||
|
.data = driveData,
|
||||||
|
.datalen = strlen(driveData),
|
||||||
|
.label = "",
|
||||||
|
.labellen = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
nn_ComponentState *vdriveState = nn_createVDrive(u, &nn_defaultDrives[3], &vdrive);
|
||||||
|
nn_addComponent(c, vdriveState, "mainDrive", 4, NULL);
|
||||||
|
|
||||||
SetExitKey(KEY_NULL);
|
SetExitKey(KEY_NULL);
|
||||||
|
|
||||||
Font font = LoadFont("unscii-16-full.ttf");
|
Font font = LoadFont("unscii-16-full.ttf");
|
||||||
@@ -1120,15 +1142,18 @@ int main() {
|
|||||||
|
|
||||||
int statY = 10;
|
int statY = 10;
|
||||||
if(sand.buf != NULL) {
|
if(sand.buf != NULL) {
|
||||||
DrawText(TextFormat("mem used: %.2f%%", (double)sand.used / sand.cap * 100), 10, statY, 20, WHITE);
|
DrawText(TextFormat("mem used: %.2f%%", (double)sand.used / sand.cap * 100), 10, statY, 20, YELLOW);
|
||||||
statY += 20;
|
statY += 20;
|
||||||
}
|
}
|
||||||
if(showStats) {
|
if(showStats) {
|
||||||
double wattage = accumulatedEnergyCost;
|
double wattage = accumulatedEnergyCost;
|
||||||
if(tickDelay > 0) wattage /= tickDelay;
|
if(tickDelay > 0) wattage /= tickDelay;
|
||||||
DrawText(TextFormat("power usage: %.2f W", wattage), 10, statY, 20, WHITE);
|
double memUsagePercent = (double)nn_getUsedMemory(c) * 100 / nn_getTotalMemory(c);
|
||||||
|
DrawText(TextFormat("power usage: %.2f W", wattage), 10, statY, 20, GREEN);
|
||||||
statY += 20;
|
statY += 20;
|
||||||
DrawText(TextFormat("energy loss: %.2f J", totalEnergyLoss), 10, statY, 20, WHITE);
|
DrawText(TextFormat("energy loss: %.2f J", totalEnergyLoss), 10, statY, 20, GREEN);
|
||||||
|
statY += 20;
|
||||||
|
DrawText(TextFormat("VM mem usage: %.2f%%", memUsagePercent), 10, statY, 20, GREEN);
|
||||||
statY += 20;
|
statY += 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1212,8 +1237,9 @@ cleanup:;
|
|||||||
nn_destroyComponentState(scrtype);
|
nn_destroyComponentState(scrtype);
|
||||||
nn_destroyComponentState(keytype);
|
nn_destroyComponentState(keytype);
|
||||||
nn_destroyComponentState(gputype);
|
nn_destroyComponentState(gputype);
|
||||||
|
nn_destroyComponentState(vdriveState);
|
||||||
for(size_t i = 0; i < 5; i++) nn_destroyComponentState(fstype[i]);
|
for(size_t i = 0; i < 5; i++) nn_destroyComponentState(fstype[i]);
|
||||||
ne_dropScreenBuf(&ctx, scrbuf);
|
ne_dropScreenBuf(scrbuf);
|
||||||
// rip the universe
|
// rip the universe
|
||||||
nn_destroyUniverse(u);
|
nn_destroyUniverse(u);
|
||||||
UnloadFont(font);
|
UnloadFont(font);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ end
|
|||||||
if gpu and screen then
|
if gpu and screen then
|
||||||
component.invoke(gpu, "bind", screen)
|
component.invoke(gpu, "bind", screen)
|
||||||
local w, h = component.invoke(gpu, "maxResolution")
|
local w, h = component.invoke(gpu, "maxResolution")
|
||||||
component.invoke(gpu, "maxResolution")
|
component.invoke(gpu, "setResolution", w, h)
|
||||||
component.invoke(gpu, "setForeground", 0xFFFFFF)
|
component.invoke(gpu, "setForeground", 0xFFFFFF)
|
||||||
component.invoke(gpu, "setBackground", 0x000000)
|
component.invoke(gpu, "setBackground", 0x000000)
|
||||||
component.invoke(gpu, "fill", 1, 1, w, h, " ")
|
component.invoke(gpu, "fill", 1, 1, w, h, " ")
|
||||||
@@ -56,6 +56,7 @@ local function getBootCode(addr)
|
|||||||
-- Read first 32K, which is a standard convention
|
-- Read first 32K, which is a standard convention
|
||||||
local sectorsIn32K = math.ceil(32768 / sectorSize)
|
local sectorsIn32K = math.ceil(32768 / sectorSize)
|
||||||
local bootCode = {firstSector}
|
local bootCode = {firstSector}
|
||||||
|
-- since its null terminated, this is an optimization
|
||||||
if not firstSector:find("\0") then
|
if not firstSector:find("\0") then
|
||||||
for i=2,sectorsIn32K do
|
for i=2,sectorsIn32K do
|
||||||
local sec = drive.readSector(i)
|
local sec = drive.readSector(i)
|
||||||
@@ -76,33 +77,13 @@ local paths = {
|
|||||||
"boot/pipes/kernel",
|
"boot/pipes/kernel",
|
||||||
}
|
}
|
||||||
|
|
||||||
local prevboot = computer.getBootAddress()
|
local bootables = {}
|
||||||
if prevboot then
|
|
||||||
if component.type(prevboot) == "filesystem" then
|
|
||||||
for _, path in ipairs(paths) do
|
|
||||||
local code = romRead(prevboot, path)
|
|
||||||
if code then
|
|
||||||
assert(load(code))(prevboot)
|
|
||||||
error("halted")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if component.type(prevboot) == "drive" then
|
|
||||||
local f = getBootCode(prevboot)
|
|
||||||
if f then
|
|
||||||
f(prevboot)
|
|
||||||
error("halted")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for addr in component.list("filesystem", true) do
|
for addr in component.list("filesystem", true) do
|
||||||
for _, path in ipairs(paths) do
|
for _, path in ipairs(paths) do
|
||||||
local code = romRead(addr, path)
|
local code = romRead(addr, path)
|
||||||
if code then
|
if code then
|
||||||
computer.setBootAddress(addr)
|
table.insert(bootables, {addr = addr, code = load(code)})
|
||||||
assert(load(code))(addr)
|
|
||||||
error("halted")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -110,10 +91,62 @@ end
|
|||||||
for addr in component.list("drive", true) do
|
for addr in component.list("drive", true) do
|
||||||
local f = getBootCode(addr)
|
local f = getBootCode(addr)
|
||||||
if f then
|
if f then
|
||||||
computer.setBootAddress(addr)
|
table.insert(bootables, {code = f, addr = addr})
|
||||||
f(addr)
|
|
||||||
error("halted")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function boot(bootable)
|
||||||
|
local w, h = component.invoke(gpu, "maxResolution")
|
||||||
|
component.invoke(gpu, "setResolution", w, h)
|
||||||
|
component.invoke(gpu, "setForeground", 0xFFFFFF)
|
||||||
|
component.invoke(gpu, "setBackground", 0x000000)
|
||||||
|
component.invoke(gpu, "fill", 1, 1, w, h, " ")
|
||||||
|
computer.setBootAddress(bootable.addr)
|
||||||
|
bootable.code(bootable.addr)
|
||||||
|
error("halted")
|
||||||
|
end
|
||||||
|
|
||||||
|
if #bootables == 1 then
|
||||||
|
boot(bootables[1])
|
||||||
|
elseif #bootables > 1 then
|
||||||
|
local sel = 1
|
||||||
|
local function showBootable(bootable, i)
|
||||||
|
local w = component.invoke(gpu, "getResolution")
|
||||||
|
component.invoke(gpu, "fill", 1, i, w, 1, " ")
|
||||||
|
local text = component.invoke(bootable.addr, "getLabel") or bootable.addr
|
||||||
|
component.invoke(gpu, "set", 1, i, text)
|
||||||
|
end
|
||||||
|
for i=1,#bootables do
|
||||||
|
if i == 1 then
|
||||||
|
component.invoke(gpu, "setForeground", 0x000000)
|
||||||
|
component.invoke(gpu, "setBackground", 0xFFFFFF)
|
||||||
|
else
|
||||||
|
component.invoke(gpu, "setForeground", 0xFFFFFF)
|
||||||
|
component.invoke(gpu, "setBackground", 0x000000)
|
||||||
|
end
|
||||||
|
showBootable(bootables[i], i)
|
||||||
|
end
|
||||||
|
while true do
|
||||||
|
local e = {computer.pullSignal()}
|
||||||
|
if e[1] == "key_down" then
|
||||||
|
local keycode = e[4]
|
||||||
|
component.invoke(gpu, "setForeground", 0xFFFFFF)
|
||||||
|
component.invoke(gpu, "setBackground", 0x000000)
|
||||||
|
showBootable(bootables[sel], sel)
|
||||||
|
if keycode == 0x1C then
|
||||||
|
break
|
||||||
|
elseif keycode == 0xC8 then
|
||||||
|
sel = math.max(sel - 1, 1)
|
||||||
|
elseif keycode == 0xD0 then
|
||||||
|
sel = math.min(sel + 1, #bootables)
|
||||||
|
end
|
||||||
|
component.invoke(gpu, "setForeground", 0x000000)
|
||||||
|
component.invoke(gpu, "setBackground", 0xFFFFFF)
|
||||||
|
showBootable(bootables[sel], sel)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
boot(bootables[sel])
|
||||||
|
end
|
||||||
|
|
||||||
error("no bootable medium found")
|
error("no bootable medium found")
|
||||||
|
|||||||
@@ -649,6 +649,7 @@ typedef struct nn_Computer {
|
|||||||
size_t archCount;
|
size_t archCount;
|
||||||
size_t signalCount;
|
size_t signalCount;
|
||||||
size_t userCount;
|
size_t userCount;
|
||||||
|
double idleTimestamp;
|
||||||
nn_Value callstack[NN_MAX_STACK];
|
nn_Value callstack[NN_MAX_STACK];
|
||||||
char errorBuffer[NN_MAX_ERROR_SIZE];
|
char errorBuffer[NN_MAX_ERROR_SIZE];
|
||||||
nn_Architecture archs[NN_MAX_ARCHITECTURES];
|
nn_Architecture archs[NN_MAX_ARCHITECTURES];
|
||||||
@@ -734,6 +735,17 @@ double nn_default_energyHandler(void *state, nn_Computer *computer, double amoun
|
|||||||
return nn_getTotalEnergy(computer);
|
return nn_getTotalEnergy(computer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t nn_ramSizes[8] = {
|
||||||
|
192 * NN_KiB,
|
||||||
|
256 * NN_KiB,
|
||||||
|
384 * NN_KiB,
|
||||||
|
512 * NN_KiB,
|
||||||
|
768 * NN_KiB,
|
||||||
|
NN_MiB,
|
||||||
|
NN_MiB + 512 * NN_KiB,
|
||||||
|
2 * NN_MiB,
|
||||||
|
};
|
||||||
|
|
||||||
nn_Computer *nn_createComputer(nn_Universe *universe, void *userdata, const char *address, size_t totalMemory, size_t maxComponents, size_t maxDevices) {
|
nn_Computer *nn_createComputer(nn_Universe *universe, void *userdata, const char *address, size_t totalMemory, size_t maxComponents, size_t maxDevices) {
|
||||||
nn_Context *ctx = &universe->ctx;
|
nn_Context *ctx = &universe->ctx;
|
||||||
|
|
||||||
@@ -786,6 +798,7 @@ nn_Computer *nn_createComputer(nn_Universe *universe, void *userdata, const char
|
|||||||
c->archCount = 0;
|
c->archCount = 0;
|
||||||
c->signalCount = 0;
|
c->signalCount = 0;
|
||||||
c->userCount = 0;
|
c->userCount = 0;
|
||||||
|
c->idleTimestamp = 0;
|
||||||
// set to empty string
|
// set to empty string
|
||||||
c->errorBuffer[0] = '\0';
|
c->errorBuffer[0] = '\0';
|
||||||
return c;
|
return c;
|
||||||
@@ -1000,6 +1013,10 @@ size_t nn_getFreeMemory(nn_Computer *computer) {
|
|||||||
return req.freeMemory;
|
return req.freeMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t nn_getUsedMemory(nn_Computer *computer) {
|
||||||
|
return nn_getTotalMemory(computer) - nn_getFreeMemory(computer);
|
||||||
|
}
|
||||||
|
|
||||||
double nn_getUptime(nn_Computer *computer) {
|
double nn_getUptime(nn_Computer *computer) {
|
||||||
return nn_currentTime(&computer->universe->ctx) - computer->creationTimestamp;
|
return nn_currentTime(&computer->universe->ctx) - computer->creationTimestamp;
|
||||||
}
|
}
|
||||||
@@ -1056,11 +1073,22 @@ void nn_setErrorFromExit(nn_Computer *computer, nn_Exit exit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool nn_isComputerIdle(nn_Computer *computer) {
|
||||||
|
return nn_getUptime(computer) < computer->idleTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nn_addIdleTime(nn_Computer *computer, double time) {
|
||||||
|
computer->idleTimestamp += time;
|
||||||
|
}
|
||||||
|
|
||||||
nn_Exit nn_tick(nn_Computer *computer) {
|
nn_Exit nn_tick(nn_Computer *computer) {
|
||||||
nn_resetCallBudget(computer);
|
nn_resetCallBudget(computer);
|
||||||
nn_resetComponentBudgets(computer);
|
nn_resetComponentBudgets(computer);
|
||||||
nn_clearstack(computer);
|
nn_clearstack(computer);
|
||||||
nn_Exit err;
|
nn_Exit err;
|
||||||
|
// idling pootr
|
||||||
|
if(nn_isComputerIdle(computer)) return NN_OK;
|
||||||
|
computer->idleTimestamp = nn_getUptime(computer);
|
||||||
if(computer->state == NN_BOOTUP) {
|
if(computer->state == NN_BOOTUP) {
|
||||||
// init state
|
// init state
|
||||||
nn_ArchitectureRequest req;
|
nn_ArchitectureRequest req;
|
||||||
@@ -1871,6 +1899,8 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
|
|||||||
|
|
||||||
switch(req->action) {
|
switch(req->action) {
|
||||||
case NN_COMP_FREETYPE:
|
case NN_COMP_FREETYPE:
|
||||||
|
ereq.action = NN_EEPROM_FREE;
|
||||||
|
state->handler(&ereq);
|
||||||
nn_free(&ctx, state, sizeof(*state));
|
nn_free(&ctx, state, sizeof(*state));
|
||||||
break;
|
break;
|
||||||
case NN_COMP_INIT:
|
case NN_COMP_INIT:
|
||||||
@@ -2012,6 +2042,7 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
|
|||||||
}
|
}
|
||||||
if(nn_strcmp(method, "set") == 0) {
|
if(nn_strcmp(method, "set") == 0) {
|
||||||
nn_removeEnergy(computer, state->eeprom.writeEnergyCost);
|
nn_removeEnergy(computer, state->eeprom.writeEnergyCost);
|
||||||
|
nn_addIdleTime(computer, state->eeprom.writeDelay);
|
||||||
if(nn_getstacksize(computer) < 1) {
|
if(nn_getstacksize(computer) < 1) {
|
||||||
nn_setError(computer, "bad argument #1 (string expected)");
|
nn_setError(computer, "bad argument #1 (string expected)");
|
||||||
return NN_EBADCALL;
|
return NN_EBADCALL;
|
||||||
@@ -2034,6 +2065,7 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
|
|||||||
}
|
}
|
||||||
if(nn_strcmp(method, "setData") == 0) {
|
if(nn_strcmp(method, "setData") == 0) {
|
||||||
nn_removeEnergy(computer, state->eeprom.writeDataEnergyCost);
|
nn_removeEnergy(computer, state->eeprom.writeDataEnergyCost);
|
||||||
|
nn_addIdleTime(computer, state->eeprom.writeDataDelay);
|
||||||
if(nn_checkstring(computer, 0, "bad argument #1 (string expected)")) return NN_EBADCALL;
|
if(nn_checkstring(computer, 0, "bad argument #1 (string expected)")) return NN_EBADCALL;
|
||||||
size_t len;
|
size_t len;
|
||||||
const char *s = nn_tolstring(computer, 0, &len);
|
const char *s = nn_tolstring(computer, 0, &len);
|
||||||
@@ -2076,6 +2108,8 @@ nn_EEPROM nn_defaultEEPROM = (nn_EEPROM) {
|
|||||||
.writeEnergyCost = 100,
|
.writeEnergyCost = 100,
|
||||||
.readDataEnergyCost = 0.1,
|
.readDataEnergyCost = 0.1,
|
||||||
.writeDataEnergyCost = 5,
|
.writeDataEnergyCost = 5,
|
||||||
|
.writeDelay = 2,
|
||||||
|
.writeDataDelay = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
nn_EEPROM nn_defaultEEPROMs[4] = {
|
nn_EEPROM nn_defaultEEPROMs[4] = {
|
||||||
@@ -2086,6 +2120,8 @@ nn_EEPROM nn_defaultEEPROMs[4] = {
|
|||||||
.writeEnergyCost = 100,
|
.writeEnergyCost = 100,
|
||||||
.readDataEnergyCost = 0.1,
|
.readDataEnergyCost = 0.1,
|
||||||
.writeDataEnergyCost = 5,
|
.writeDataEnergyCost = 5,
|
||||||
|
.writeDelay = 2,
|
||||||
|
.writeDataDelay = 1,
|
||||||
},
|
},
|
||||||
(nn_EEPROM) {
|
(nn_EEPROM) {
|
||||||
.size = 8 * NN_KiB,
|
.size = 8 * NN_KiB,
|
||||||
@@ -2094,6 +2130,8 @@ nn_EEPROM nn_defaultEEPROMs[4] = {
|
|||||||
.writeEnergyCost = 200,
|
.writeEnergyCost = 200,
|
||||||
.readDataEnergyCost = 0.2,
|
.readDataEnergyCost = 0.2,
|
||||||
.writeDataEnergyCost = 10,
|
.writeDataEnergyCost = 10,
|
||||||
|
.writeDelay = 2,
|
||||||
|
.writeDataDelay = 1,
|
||||||
},
|
},
|
||||||
(nn_EEPROM) {
|
(nn_EEPROM) {
|
||||||
.size = 16 * NN_KiB,
|
.size = 16 * NN_KiB,
|
||||||
@@ -2102,6 +2140,8 @@ nn_EEPROM nn_defaultEEPROMs[4] = {
|
|||||||
.writeEnergyCost = 400,
|
.writeEnergyCost = 400,
|
||||||
.readDataEnergyCost = 0.4,
|
.readDataEnergyCost = 0.4,
|
||||||
.writeDataEnergyCost = 20,
|
.writeDataEnergyCost = 20,
|
||||||
|
.writeDelay = 1,
|
||||||
|
.writeDataDelay = 0.5,
|
||||||
},
|
},
|
||||||
(nn_EEPROM) {
|
(nn_EEPROM) {
|
||||||
.size = 32 * NN_KiB,
|
.size = 32 * NN_KiB,
|
||||||
@@ -2110,6 +2150,8 @@ nn_EEPROM nn_defaultEEPROMs[4] = {
|
|||||||
.writeEnergyCost = 800,
|
.writeEnergyCost = 800,
|
||||||
.readDataEnergyCost = 0.8,
|
.readDataEnergyCost = 0.8,
|
||||||
.writeDataEnergyCost = 40,
|
.writeDataEnergyCost = 40,
|
||||||
|
.writeDelay = 1,
|
||||||
|
.writeDataDelay = 0.5,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2151,6 +2193,8 @@ static nn_Exit nn_veeprom_handler(nn_EEPROMRequest *req) {
|
|||||||
nn_Context ctx = state->universe->ctx;
|
nn_Context ctx = state->universe->ctx;
|
||||||
switch(req->action) {
|
switch(req->action) {
|
||||||
case NN_EEPROM_DROP:
|
case NN_EEPROM_DROP:
|
||||||
|
return NN_OK;
|
||||||
|
case NN_EEPROM_FREE:
|
||||||
nn_free(&ctx, state->code, sizeof(char) * conf->size);
|
nn_free(&ctx, state->code, sizeof(char) * conf->size);
|
||||||
nn_free(&ctx, state->data, sizeof(char) * conf->dataSize);
|
nn_free(&ctx, state->data, sizeof(char) * conf->dataSize);
|
||||||
nn_free(&ctx, state, sizeof(*state));
|
nn_free(&ctx, state, sizeof(*state));
|
||||||
@@ -2282,6 +2326,13 @@ nn_Filesystem nn_defaultFloppy = (nn_Filesystem) {
|
|||||||
.dataEnergyCost = 8.0 / NN_MiB,
|
.dataEnergyCost = 8.0 / NN_MiB,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
nn_Filesystem nn_defaultTmpFS = (nn_Filesystem) {
|
||||||
|
.spaceTotal = 64 * NN_KiB,
|
||||||
|
.readsPerTick = 1024,
|
||||||
|
.writesPerTick = 512,
|
||||||
|
.dataEnergyCost = 512.0 / NN_MiB,
|
||||||
|
};
|
||||||
|
|
||||||
nn_Exit nn_filesystem_handler(nn_ComponentRequest *req) {
|
nn_Exit nn_filesystem_handler(nn_ComponentRequest *req) {
|
||||||
nn_Filesystem_state *state = req->typeUserdata;
|
nn_Filesystem_state *state = req->typeUserdata;
|
||||||
void *instance = req->compUserdata;
|
void *instance = req->compUserdata;
|
||||||
@@ -2300,6 +2351,8 @@ nn_Exit nn_filesystem_handler(nn_ComponentRequest *req) {
|
|||||||
|
|
||||||
switch(req->action) {
|
switch(req->action) {
|
||||||
case NN_COMP_FREETYPE:
|
case NN_COMP_FREETYPE:
|
||||||
|
fsreq.action = NN_FS_FREE;
|
||||||
|
state->handler(&fsreq);
|
||||||
nn_free(&ctx, state, sizeof(*state));
|
nn_free(&ctx, state, sizeof(*state));
|
||||||
break;
|
break;
|
||||||
case NN_COMP_INIT:
|
case NN_COMP_INIT:
|
||||||
@@ -2619,6 +2672,309 @@ nn_ComponentState *nn_createFilesystem(nn_Universe *universe, const nn_Filesyste
|
|||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nn_Drive nn_defaultDrives[4] = {
|
||||||
|
(nn_Drive) {
|
||||||
|
.capacity = 1 * NN_MiB,
|
||||||
|
.sectorSize = 512,
|
||||||
|
.platterCount = 2,
|
||||||
|
.readsPerTick = 10,
|
||||||
|
.writesPerTick = 5,
|
||||||
|
.rpm = 1800,
|
||||||
|
.onlySpinForwards = false,
|
||||||
|
.dataEnergyCost = 256.0 / NN_MiB,
|
||||||
|
},
|
||||||
|
(nn_Drive) {
|
||||||
|
.capacity = 2 * NN_MiB,
|
||||||
|
.sectorSize = 512,
|
||||||
|
.platterCount = 4,
|
||||||
|
.readsPerTick = 20,
|
||||||
|
.writesPerTick = 10,
|
||||||
|
.rpm = 1800,
|
||||||
|
.onlySpinForwards = false,
|
||||||
|
.dataEnergyCost = 512.0 / NN_MiB,
|
||||||
|
},
|
||||||
|
(nn_Drive) {
|
||||||
|
.capacity = 4 * NN_MiB,
|
||||||
|
.sectorSize = 512,
|
||||||
|
.platterCount = 8,
|
||||||
|
.readsPerTick = 30,
|
||||||
|
.writesPerTick = 15,
|
||||||
|
.rpm = 1800,
|
||||||
|
.onlySpinForwards = false,
|
||||||
|
.dataEnergyCost = 1024.0 / NN_MiB,
|
||||||
|
},
|
||||||
|
(nn_Drive) {
|
||||||
|
.capacity = 8 * NN_MiB,
|
||||||
|
.sectorSize = 512,
|
||||||
|
.platterCount = 16,
|
||||||
|
.readsPerTick = 40,
|
||||||
|
.writesPerTick = 20,
|
||||||
|
.rpm = 1800,
|
||||||
|
.onlySpinForwards = false,
|
||||||
|
.dataEnergyCost = 2048.0 / NN_MiB,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct nn_DriveState {
|
||||||
|
nn_Universe *universe;
|
||||||
|
void *userdata;
|
||||||
|
nn_DriveHandler *handler;
|
||||||
|
nn_Drive drive;
|
||||||
|
} nn_DriveState;
|
||||||
|
|
||||||
|
void nn_drive_seekPenalty(nn_Computer *C, size_t lastSector, size_t newSector, const nn_Drive *drive) {
|
||||||
|
size_t maxSectors = drive->capacity / drive->sectorSize;
|
||||||
|
size_t sectorsPerPlatter = maxSectors / drive->platterCount;
|
||||||
|
// RPM over the number of sectors, over 60 seconds.
|
||||||
|
double latencyPerSector = (double)drive->rpm / maxSectors / 60;
|
||||||
|
|
||||||
|
// magic
|
||||||
|
lastSector %= sectorsPerPlatter;
|
||||||
|
newSector %= sectorsPerPlatter;
|
||||||
|
|
||||||
|
size_t sectorDelta;
|
||||||
|
if(newSector >= lastSector) {
|
||||||
|
sectorDelta = newSector - lastSector;
|
||||||
|
} else if(drive->onlySpinForwards) {
|
||||||
|
sectorDelta = sectorsPerPlatter - (lastSector - newSector);
|
||||||
|
} else {
|
||||||
|
sectorDelta = lastSector - newSector;
|
||||||
|
}
|
||||||
|
|
||||||
|
nn_addIdleTime(C, sectorDelta * latencyPerSector);
|
||||||
|
}
|
||||||
|
|
||||||
|
nn_Exit nn_drive_handler(nn_ComponentRequest *req) {
|
||||||
|
nn_DriveState *state = req->typeUserdata;
|
||||||
|
void *instance = req->compUserdata;
|
||||||
|
// NULL for FREETYPE
|
||||||
|
nn_Computer *C = req->computer;
|
||||||
|
nn_Context ctx = state->universe->ctx;
|
||||||
|
|
||||||
|
nn_DriveRequest dreq;
|
||||||
|
dreq.userdata = state->userdata;
|
||||||
|
dreq.instance = instance;
|
||||||
|
dreq.computer = C;
|
||||||
|
dreq.driveConf = &state->drive;
|
||||||
|
nn_Drive conf = state->drive;
|
||||||
|
|
||||||
|
const char *method = req->methodCalled;
|
||||||
|
nn_Exit e;
|
||||||
|
|
||||||
|
size_t maxSectors = conf.capacity / conf.sectorSize;
|
||||||
|
|
||||||
|
switch(req->action) {
|
||||||
|
case NN_COMP_FREETYPE:
|
||||||
|
dreq.action = NN_DRIVE_FREE;
|
||||||
|
state->handler(&dreq);
|
||||||
|
nn_free(&ctx, state, sizeof(*state));
|
||||||
|
return NN_OK;
|
||||||
|
case NN_COMP_INIT:
|
||||||
|
return NN_OK;
|
||||||
|
case NN_COMP_DEINIT:
|
||||||
|
dreq.action = NN_DRIVE_DROP;
|
||||||
|
return state->handler(&dreq);
|
||||||
|
case NN_COMP_ENABLED:
|
||||||
|
req->methodEnabled = true;
|
||||||
|
return NN_OK;
|
||||||
|
case NN_COMP_CALL:
|
||||||
|
if(nn_strcmp(method, "getCapacity") == 0) {
|
||||||
|
req->returnCount = 1;
|
||||||
|
return nn_pushinteger(C, conf.capacity);
|
||||||
|
}
|
||||||
|
if(nn_strcmp(method, "getSectorSize") == 0) {
|
||||||
|
req->returnCount = 1;
|
||||||
|
return nn_pushinteger(C, conf.sectorSize);
|
||||||
|
}
|
||||||
|
if(nn_strcmp(method, "getPlatterCount") == 0) {
|
||||||
|
req->returnCount = 1;
|
||||||
|
return nn_pushinteger(C, conf.platterCount);
|
||||||
|
}
|
||||||
|
if(nn_strcmp(method, "readSector") == 0) {
|
||||||
|
nn_costComponent(C, req->compAddress, conf.readsPerTick);
|
||||||
|
nn_removeEnergy(C, conf.dataEnergyCost * conf.sectorSize);
|
||||||
|
if(nn_checkinteger(C, 0, "bad argument #1 (integer expected)")) return NN_EBADCALL;
|
||||||
|
intptr_t sec = nn_tointeger(C, 0);
|
||||||
|
if(sec < 1 || sec > maxSectors) {
|
||||||
|
nn_setError(C, "sector out of bounds");
|
||||||
|
return NN_EBADCALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dreq.action = NN_DRIVE_GETCURSECTOR;
|
||||||
|
e = state->handler(&dreq);
|
||||||
|
if(e) return e;
|
||||||
|
|
||||||
|
size_t lastSec = dreq.index;
|
||||||
|
|
||||||
|
nn_drive_seekPenalty(C, lastSec, sec, &conf);
|
||||||
|
|
||||||
|
// stack allocated! May be a problem for big sectors!
|
||||||
|
char buf[conf.sectorSize];
|
||||||
|
|
||||||
|
dreq.action = NN_DRIVE_READSECTOR;
|
||||||
|
dreq.buf = buf;
|
||||||
|
dreq.index = sec;
|
||||||
|
e = state->handler(&dreq);
|
||||||
|
if(e) return e;
|
||||||
|
|
||||||
|
req->returnCount = 1;
|
||||||
|
return nn_pushlstring(C, buf, conf.sectorSize);
|
||||||
|
}
|
||||||
|
if(nn_strcmp(method, "writeSector") == 0) {
|
||||||
|
nn_costComponent(C, req->compAddress, conf.writesPerTick);
|
||||||
|
nn_removeEnergy(C, conf.dataEnergyCost * conf.sectorSize);
|
||||||
|
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;
|
||||||
|
intptr_t sec = nn_tointeger(C, 0);
|
||||||
|
if(sec < 1 || sec > maxSectors) {
|
||||||
|
nn_setError(C, "sector out of bounds");
|
||||||
|
return NN_EBADCALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dreq.action = NN_DRIVE_GETCURSECTOR;
|
||||||
|
e = state->handler(&dreq);
|
||||||
|
if(e) return e;
|
||||||
|
|
||||||
|
size_t lastSec = dreq.index;
|
||||||
|
|
||||||
|
nn_drive_seekPenalty(C, lastSec, sec, &conf);
|
||||||
|
|
||||||
|
// stack allocated! May be a problem for big sectors!
|
||||||
|
size_t buflen;
|
||||||
|
const char *buf = nn_tolstring(C, 1, &buflen);
|
||||||
|
if(buflen != conf.sectorSize) {
|
||||||
|
nn_setError(C, "bad sector size");
|
||||||
|
return NN_EBADCALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dreq.action = NN_DRIVE_WRITESECTOR;
|
||||||
|
dreq.buf = (char *)buf;
|
||||||
|
dreq.index = sec;
|
||||||
|
e = state->handler(&dreq);
|
||||||
|
if(e) return e;
|
||||||
|
|
||||||
|
req->returnCount = 1;
|
||||||
|
return nn_pushbool(C, true);
|
||||||
|
}
|
||||||
|
nn_setError(C, "unknown method");
|
||||||
|
return NN_EBADCALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nn_ComponentState *nn_createDrive(nn_Universe *universe, const nn_Drive *drive, nn_DriveHandler *handler, void *userdata) {
|
||||||
|
nn_Context ctx = universe->ctx;
|
||||||
|
nn_DriveState *state = nn_alloc(&ctx, sizeof(*state));
|
||||||
|
if(state == NULL) return NULL;
|
||||||
|
state->universe = universe;
|
||||||
|
state->userdata = userdata;
|
||||||
|
state->handler = handler;
|
||||||
|
state->drive = *drive;
|
||||||
|
const nn_Method methods[] = {
|
||||||
|
{"getLabel", "function(): string - Get the drive label.", NN_INDIRECT},
|
||||||
|
{"setLabel", "function(label: string): string - Set the drive label. Returns the new label.", NN_INDIRECT},
|
||||||
|
{"getCapacity", "function(): integer - Get the drive capacity, in bytes.", NN_DIRECT},
|
||||||
|
{"getPlatterCount", "function(): integer - Get the platter count", NN_DIRECT},
|
||||||
|
{"getSectorSize", "function(): integer - Get the sector size, in bytes.", NN_DIRECT},
|
||||||
|
{"readSector", "function(index: integer): string - Returns the contents of the specified sector. Sectors are 1-indexed.", NN_DIRECT},
|
||||||
|
{"writeSector", "function(index: integer, data: string): boolean - Changes the contents of the specified sector. Sectors are 1-indexed.", NN_DIRECT},
|
||||||
|
{"readByte", "function(index: integer): integer - Reads a single signed byte and returns it. Bytes are 1-indexed.", NN_DIRECT},
|
||||||
|
{"readUByte", "function(index: integer): integer - Reads a single unsigned byte and returns it. Bytes are 1-indexed.", NN_DIRECT},
|
||||||
|
{"writeByte", "function(index: integer, value: integer): boolean - Changes a single byte, can be signed or unsigned. Bytes are 1-indexed.", NN_DIRECT},
|
||||||
|
{NULL, NULL, NN_INDIRECT},
|
||||||
|
};
|
||||||
|
nn_ComponentState *t = nn_createComponentState(universe, "drive", state, methods, nn_drive_handler);
|
||||||
|
if(t == NULL) {
|
||||||
|
nn_free(&ctx, state, sizeof(*state));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct nn_VDriveState {
|
||||||
|
nn_Universe *universe;
|
||||||
|
char *data;
|
||||||
|
size_t lastUsedSector;
|
||||||
|
char label[NN_MAX_LABEL];
|
||||||
|
size_t labellen;
|
||||||
|
} nn_VDriveState;
|
||||||
|
|
||||||
|
static nn_Exit nn_vdrive_handler(nn_DriveRequest *req) {
|
||||||
|
nn_Computer *c = req->computer;
|
||||||
|
nn_VDriveState *state = req->userdata;
|
||||||
|
nn_Context *ctx = &state->universe->ctx;
|
||||||
|
nn_Drive conf = *req->driveConf;
|
||||||
|
|
||||||
|
size_t sectorOff = (req->index - 1) * conf.sectorSize;
|
||||||
|
size_t labelLen = req->index;
|
||||||
|
|
||||||
|
switch(req->action) {
|
||||||
|
case NN_DRIVE_DROP:
|
||||||
|
// no per-state info anyways
|
||||||
|
return NN_OK;
|
||||||
|
case NN_DRIVE_FREE:
|
||||||
|
nn_free(ctx, state->data, conf.capacity);
|
||||||
|
nn_free(ctx, state, sizeof(*state));
|
||||||
|
return NN_OK;
|
||||||
|
case NN_DRIVE_GETLABEL:
|
||||||
|
if(labelLen > state->labellen) labelLen = state->labellen;
|
||||||
|
req->index = labelLen;
|
||||||
|
nn_memcpy(req->buf, state->label, labelLen);
|
||||||
|
return NN_OK;
|
||||||
|
case NN_DRIVE_SETLABEL:
|
||||||
|
if(labelLen > NN_MAX_LABEL) labelLen = NN_MAX_LABEL;
|
||||||
|
state->labellen = labelLen;
|
||||||
|
nn_memcpy(state->label, req->buf, labelLen);
|
||||||
|
return NN_OK;
|
||||||
|
case NN_DRIVE_GETCURSECTOR:
|
||||||
|
req->index = state->lastUsedSector;
|
||||||
|
return NN_OK;
|
||||||
|
case NN_DRIVE_READBYTE:
|
||||||
|
req->byte = state->data[req->index - 1];
|
||||||
|
return NN_OK;
|
||||||
|
case NN_DRIVE_WRITEBYTE:
|
||||||
|
state->data[req->index - 1] = req->byte;
|
||||||
|
return NN_OK;
|
||||||
|
case NN_DRIVE_READSECTOR:
|
||||||
|
state->lastUsedSector = req->index;
|
||||||
|
nn_memcpy(req->buf, state->data + sectorOff, conf.sectorSize);
|
||||||
|
return NN_OK;
|
||||||
|
case NN_DRIVE_WRITESECTOR:
|
||||||
|
state->lastUsedSector = req->index;
|
||||||
|
nn_memcpy(state->data + sectorOff, req->buf, conf.sectorSize);
|
||||||
|
return NN_OK;
|
||||||
|
}
|
||||||
|
return NN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nn_ComponentState *nn_createVDrive(nn_Universe *universe, const nn_Drive *drive, const nn_VDrive *vdrive) {
|
||||||
|
nn_Context ctx = universe->ctx;
|
||||||
|
|
||||||
|
char *data = NULL;
|
||||||
|
nn_VDriveState *state = NULL;
|
||||||
|
|
||||||
|
data = nn_alloc(&ctx, drive->capacity);
|
||||||
|
if(data == NULL) goto cleanup;
|
||||||
|
|
||||||
|
state = nn_alloc(&ctx, sizeof(*state));
|
||||||
|
if(state == NULL) goto cleanup;
|
||||||
|
|
||||||
|
state->data = data;
|
||||||
|
state->lastUsedSector = 1;
|
||||||
|
state->universe = universe;
|
||||||
|
state->labellen = vdrive->labellen;
|
||||||
|
nn_memcpy(state->label, vdrive->label, vdrive->labellen);
|
||||||
|
nn_memcpy(state->data, vdrive->data, vdrive->datalen);
|
||||||
|
nn_memset(state->data + vdrive->datalen, 0, drive->capacity - vdrive->datalen);
|
||||||
|
|
||||||
|
return nn_createDrive(universe, drive, nn_vdrive_handler, state);
|
||||||
|
cleanup:
|
||||||
|
nn_free(&ctx, data, drive->capacity);
|
||||||
|
nn_free(&ctx, state, sizeof(*state));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
nn_ScreenConfig nn_defaultScreens[4] = {
|
nn_ScreenConfig nn_defaultScreens[4] = {
|
||||||
(nn_ScreenConfig) {
|
(nn_ScreenConfig) {
|
||||||
.maxWidth = 50,
|
.maxWidth = 50,
|
||||||
@@ -2671,6 +3027,8 @@ static nn_Exit nn_screen_handler(nn_ComponentRequest *req) {
|
|||||||
|
|
||||||
switch(req->action) {
|
switch(req->action) {
|
||||||
case NN_COMP_FREETYPE:
|
case NN_COMP_FREETYPE:
|
||||||
|
scrreq.action = NN_SCR_FREE;
|
||||||
|
state->handler(&scrreq);
|
||||||
nn_free(&ctx, state, sizeof(*state));
|
nn_free(&ctx, state, sizeof(*state));
|
||||||
return NN_OK;
|
return NN_OK;
|
||||||
case NN_COMP_DEINIT:
|
case NN_COMP_DEINIT:
|
||||||
@@ -2807,6 +3165,8 @@ nn_Exit nn_gpu_handler(nn_ComponentRequest *req) {
|
|||||||
|
|
||||||
switch(req->action) {
|
switch(req->action) {
|
||||||
case NN_COMP_FREETYPE:
|
case NN_COMP_FREETYPE:
|
||||||
|
greq.action = NN_GPU_FREE;
|
||||||
|
state->handler(&greq);
|
||||||
nn_free(&ctx, state, sizeof(*state));
|
nn_free(&ctx, state, sizeof(*state));
|
||||||
return NN_OK;
|
return NN_OK;
|
||||||
case NN_COMP_INIT:
|
case NN_COMP_INIT:
|
||||||
@@ -3112,6 +3472,16 @@ nn_ComponentState *nn_createGPU(nn_Universe *universe, const nn_GPU *gpu, nn_GPU
|
|||||||
{"copy", "function(x: integer, y: integer, width: integer, height: integer, dx: integer, dy: integer) - Copies a rectangle on the screen buffer to a new position. The new position is x + dx, y + dy, thus dx and dy determine the translation of the copy.", NN_DIRECT},
|
{"copy", "function(x: integer, y: integer, width: integer, height: integer, dx: integer, dy: integer) - Copies a rectangle on the screen buffer to a new position. The new position is x + dx, y + dy, thus dx and dy determine the translation of the copy.", NN_DIRECT},
|
||||||
{"fill", "function(x: integer, y: integer, width: integer, height: integer, char: string): boolean - Fills a rectangle on the screen buffer. Returns true on success, false otherwise.", NN_DIRECT},
|
{"fill", "function(x: integer, y: integer, width: integer, height: integer, char: string): boolean - Fills a rectangle on the screen buffer. Returns true on success, false otherwise.", NN_DIRECT},
|
||||||
// TODO: vram buffers
|
// TODO: vram buffers
|
||||||
|
{"freeMemory", "function(): integer - Returns the amount of free VRAM remaining.", NN_DIRECT},
|
||||||
|
{"totalMemory", "function(): integer - Returns the total amount of VRAM usable.", NN_DIRECT},
|
||||||
|
{"getActiveBuffer", "function(): integer - Returns the current buffer. 0 means the screen.", NN_DIRECT},
|
||||||
|
{"setActiveBuffer", "function(buf: integer): integer - Switches to another buffer. 0 means the screen.", NN_DIRECT},
|
||||||
|
{"buffers", "function(): integer[] - Returns a list of all allocated buffers, except 0, which is reserved for the screen.", NN_DIRECT},
|
||||||
|
{"getBufferSize", "function(buf?: integer): integer, integer - Returns the size of the requested buffer. By default, it returns the size of the current current one.", NN_DIRECT},
|
||||||
|
{"allocateBuffer", "function(width?: integer, height?: integer): integer - Allocates a new buffer of a specific size, defaulting to the GPU's maximum resolution.", NN_DIRECT},
|
||||||
|
{"freeBuffer", "function(buffer?: integer): boolean - Frees a buffer, defaulting to the current one. If the current one is freed, it will switch to the screen.", NN_DIRECT},
|
||||||
|
{"freeAllBuffers", "function() - Frees every buffer and switches to the screen. This cannot fail.", NN_DIRECT},
|
||||||
|
{"bitblt", "function(dest?: integer, col?: integer, row?: integer, width?: integer, height?: integer, src?: integer, fromCol?: integer, fromRow?: integer): boolean - Returns the size of the requested buffer. By default, it returns the size of the current current one.", NN_DIRECT},
|
||||||
{NULL, NULL, NN_INDIRECT},
|
{NULL, NULL, NN_INDIRECT},
|
||||||
};
|
};
|
||||||
nn_ComponentState *t = nn_createComponentState(universe, "gpu", state, methods, nn_gpu_handler);
|
nn_ComponentState *t = nn_createComponentState(universe, "gpu", state, methods, nn_gpu_handler);
|
||||||
|
|||||||
@@ -306,6 +306,11 @@ typedef struct nn_Architecture {
|
|||||||
nn_ArchitectureHandler *handler;
|
nn_ArchitectureHandler *handler;
|
||||||
} nn_Architecture;
|
} nn_Architecture;
|
||||||
|
|
||||||
|
// Standard RAM sizes.
|
||||||
|
// Standard OC goes from tier 1 to tier 6,
|
||||||
|
// NN adds 2 more tiers.
|
||||||
|
extern size_t nn_ramSizes[8];
|
||||||
|
|
||||||
// The state of a *RUNNING* computer.
|
// The state of a *RUNNING* computer.
|
||||||
// Powered off computers shall not have a state, and as far as NeoNucleus is aware,
|
// Powered off computers shall not have a state, and as far as NeoNucleus is aware,
|
||||||
// not exist.
|
// not exist.
|
||||||
@@ -396,6 +401,10 @@ void nn_setEnergyHandler(nn_Computer *computer, void *energyState, nn_EnergyHand
|
|||||||
size_t nn_getTotalMemory(nn_Computer *computer);
|
size_t nn_getTotalMemory(nn_Computer *computer);
|
||||||
// Gets the total amount of free memory the computer has available. The total memory - this is the amount of memory used.
|
// Gets the total amount of free memory the computer has available. The total memory - this is the amount of memory used.
|
||||||
size_t nn_getFreeMemory(nn_Computer *computer);
|
size_t nn_getFreeMemory(nn_Computer *computer);
|
||||||
|
// Gets the total amount of used memory the computer has allocated.
|
||||||
|
// This is just the total minus the free, and does not take into
|
||||||
|
// account the overhead of storing the computer instance.
|
||||||
|
size_t nn_getUsedMemory(nn_Computer *computer);
|
||||||
// gets the current uptime of a computer. When the computer is not running, this value can be anything and loses all meaning.
|
// gets the current uptime of a computer. When the computer is not running, this value can be anything and loses all meaning.
|
||||||
double nn_getUptime(nn_Computer *computer);
|
double nn_getUptime(nn_Computer *computer);
|
||||||
|
|
||||||
@@ -417,8 +426,13 @@ void nn_setComputerState(nn_Computer *computer, nn_ComputerState state);
|
|||||||
// gets the current computer state
|
// gets the current computer state
|
||||||
nn_ComputerState nn_getComputerState(nn_Computer *computer);
|
nn_ComputerState nn_getComputerState(nn_Computer *computer);
|
||||||
|
|
||||||
|
// Checks if the uptime is below the idle timestamp.
|
||||||
|
bool nn_isComputerIdle(nn_Computer *computer);
|
||||||
|
// Shifts over the idle timestamp.
|
||||||
|
void nn_addIdleTime(nn_Computer *computer, double time);
|
||||||
// runs a tick of the computer. Make sure to check the state as well!
|
// runs a tick of the computer. Make sure to check the state as well!
|
||||||
// This automatically resets the component budgets and call budget.
|
// This automatically resets the component budgets and call budget.
|
||||||
|
// It also sets the idle timestamp to the current uptime.
|
||||||
nn_Exit nn_tick(nn_Computer *computer);
|
nn_Exit nn_tick(nn_Computer *computer);
|
||||||
|
|
||||||
typedef struct nn_DeviceInfoEntry {
|
typedef struct nn_DeviceInfoEntry {
|
||||||
@@ -750,6 +764,8 @@ nn_Exit nn_popSignal(nn_Computer *computer, size_t *valueCount);
|
|||||||
typedef enum nn_EEPROMAction {
|
typedef enum nn_EEPROMAction {
|
||||||
// the eeprom instance has been dropped
|
// the eeprom instance has been dropped
|
||||||
NN_EEPROM_DROP,
|
NN_EEPROM_DROP,
|
||||||
|
// the eeprom state has been dropped
|
||||||
|
NN_EEPROM_FREE,
|
||||||
NN_EEPROM_GET,
|
NN_EEPROM_GET,
|
||||||
NN_EEPROM_SET,
|
NN_EEPROM_SET,
|
||||||
NN_EEPROM_GETDATA,
|
NN_EEPROM_GETDATA,
|
||||||
@@ -793,6 +809,10 @@ typedef struct nn_EEPROM {
|
|||||||
double writeEnergyCost;
|
double writeEnergyCost;
|
||||||
// the energy cost of writing to an EEPROM's associated data
|
// the energy cost of writing to an EEPROM's associated data
|
||||||
double writeDataEnergyCost;
|
double writeDataEnergyCost;
|
||||||
|
// idle time added when writing code
|
||||||
|
double writeDelay;
|
||||||
|
// idle time added when writing data
|
||||||
|
double writeDataDelay;
|
||||||
} nn_EEPROM;
|
} nn_EEPROM;
|
||||||
|
|
||||||
// Tier 1 - The normal EEPROM equivalent
|
// Tier 1 - The normal EEPROM equivalent
|
||||||
@@ -828,8 +848,11 @@ nn_ComponentState *nn_createVEEPROM(nn_Universe *universe, const nn_EEPROM *eepr
|
|||||||
// - For rename, it automatically checks if the destination exists and if so, errors out.
|
// - For rename, it automatically checks if the destination exists and if so, errors out.
|
||||||
typedef enum nn_FilesystemAction {
|
typedef enum nn_FilesystemAction {
|
||||||
// the filesystem instance has been dropped.
|
// the filesystem instance has been dropped.
|
||||||
// Make sure to close all file descriptors which are still open.
|
// This is just for computer-local state, make sure to free it.
|
||||||
NN_FS_DROP,
|
NN_FS_DROP,
|
||||||
|
// the filesystem state has been dropped.
|
||||||
|
// Make sure to close all file descriptors which are still open.
|
||||||
|
NN_FS_FREE,
|
||||||
// open a file. strarg1 stores the path, and strarg2 stores the mode.
|
// open a file. strarg1 stores the path, and strarg2 stores the mode.
|
||||||
// strarg1len and strarg2len are their respective lengths.
|
// strarg1len and strarg2len are their respective lengths.
|
||||||
// The output should be in fd.
|
// The output should be in fd.
|
||||||
@@ -973,14 +996,160 @@ typedef struct nn_Filesystem {
|
|||||||
extern nn_Filesystem nn_defaultFilesystems[4];
|
extern nn_Filesystem nn_defaultFilesystems[4];
|
||||||
// a basic floppy
|
// a basic floppy
|
||||||
extern nn_Filesystem nn_defaultFloppy;
|
extern nn_Filesystem nn_defaultFloppy;
|
||||||
|
// a generic tmpfs
|
||||||
|
extern nn_Filesystem nn_defaultTmpFS;
|
||||||
|
|
||||||
typedef nn_Exit nn_FilesystemHandler(nn_FilesystemRequest *request);
|
typedef nn_Exit nn_FilesystemHandler(nn_FilesystemRequest *request);
|
||||||
|
|
||||||
|
typedef struct nn_VFileNode {
|
||||||
|
// the name of the node.
|
||||||
|
// This is the raw name, do not append / to directories.
|
||||||
|
const char *name;
|
||||||
|
// if NULL, the node is a directory.
|
||||||
|
const char *data;
|
||||||
|
union {
|
||||||
|
// for files, how much of data to read.
|
||||||
|
size_t dataLen;
|
||||||
|
// for directories, the amount of entries encoded afterwards.
|
||||||
|
// Do note that entry encoding is recursive, so for example
|
||||||
|
// a(1) b(2) c("hi") d("there"), means directory a/ has a directory b/ which has 2 files, c and d,
|
||||||
|
// even though a's entry count is 1.
|
||||||
|
size_t entryCount;
|
||||||
|
};
|
||||||
|
} nn_VFileNode;
|
||||||
|
|
||||||
|
typedef struct nn_VFilesystem {
|
||||||
|
const char *label;
|
||||||
|
size_t labellen;
|
||||||
|
bool isReadOnly;
|
||||||
|
// The maximum amount of directory entries. This is used to pre-allocate an array.
|
||||||
|
// It also helps against memory hogging attacks.
|
||||||
|
size_t maxDirEntries;
|
||||||
|
// the maximum amount of nodes the filesystem can have. This is also used to pre-allocate an array.
|
||||||
|
size_t maxNodeCount;
|
||||||
|
// used to compute lastModified. This, together with the context's time procedure, is used to compute the timestamp.
|
||||||
|
// It must be a UNIX timestamp, else you'll get weird results.
|
||||||
|
size_t creationTime;
|
||||||
|
size_t rootNodeCount;
|
||||||
|
// the flat array of the filesystem. See nn_VFileNode for details.
|
||||||
|
nn_VFileNode *image;
|
||||||
|
} nn_VFilesystem;
|
||||||
|
|
||||||
nn_ComponentState *nn_createFilesystem(nn_Universe *universe, const nn_Filesystem *filesystem, nn_FilesystemHandler *handler, void *userdata);
|
nn_ComponentState *nn_createFilesystem(nn_Universe *universe, const nn_Filesystem *filesystem, nn_FilesystemHandler *handler, void *userdata);
|
||||||
|
nn_ComponentState *nn_createVFilesystem(nn_Universe *universe, const nn_Filesystem *filesystem, const nn_VFilesystem *vfs);
|
||||||
|
|
||||||
|
typedef enum nn_DriveAction {
|
||||||
|
// instance dropped
|
||||||
|
NN_DRIVE_DROP,
|
||||||
|
// free screen state
|
||||||
|
NN_DRIVE_FREE,
|
||||||
|
// Gets the current label.
|
||||||
|
// [index] is set to the capacity of [buf].
|
||||||
|
// You must write the label into [buf], then set [index] to the length of the label.
|
||||||
|
// Empty label means no label.
|
||||||
|
NN_DRIVE_GETLABEL,
|
||||||
|
// Sets the current label.
|
||||||
|
// [index] is set to the length of [buf].
|
||||||
|
// Empty label means no label.
|
||||||
|
// Set [index] to the new length of the label, if it has been truncated.
|
||||||
|
NN_DRIVE_SETLABEL,
|
||||||
|
// gets the current read head, or more accurately, the last sector used
|
||||||
|
// in order to compute seeking penalties.
|
||||||
|
// You must output the current read head in [index].
|
||||||
|
NN_DRIVE_GETCURSECTOR,
|
||||||
|
// Reads a sector.
|
||||||
|
// The sector index is in [index], and the contents are in [buf].
|
||||||
|
NN_DRIVE_READSECTOR,
|
||||||
|
// Writes a sector.
|
||||||
|
// The sector index is in [index].
|
||||||
|
// Output the contents of that sector in [buf].
|
||||||
|
NN_DRIVE_WRITESECTOR,
|
||||||
|
// Reads a byte
|
||||||
|
// The byte index is in [index].
|
||||||
|
// You must output the byte in [byte].
|
||||||
|
NN_DRIVE_READBYTE,
|
||||||
|
// Writes a byte.
|
||||||
|
// The byte index is in [index], the byte is in [byte].
|
||||||
|
NN_DRIVE_WRITEBYTE,
|
||||||
|
} nn_DriveAction;
|
||||||
|
|
||||||
|
// Note that sectors and bytes are 1-indexed.
|
||||||
|
// Bounds checking is done automatically by the interface.
|
||||||
|
typedef struct nn_DriveRequest {
|
||||||
|
void *userdata;
|
||||||
|
void *instance;
|
||||||
|
nn_Computer *computer;
|
||||||
|
struct nn_Drive *driveConf;
|
||||||
|
nn_DriveAction action;
|
||||||
|
size_t index;
|
||||||
|
union {
|
||||||
|
char *buf;
|
||||||
|
// OC explicitly uses *signed* chars.
|
||||||
|
// Helper methods for reading unsigned bytes cast it to an unsigned byte first.
|
||||||
|
// Just, do not ask.
|
||||||
|
signed char byte;
|
||||||
|
};
|
||||||
|
} nn_DriveRequest;
|
||||||
|
|
||||||
|
typedef nn_Exit nn_DriveHandler(nn_DriveRequest *req);
|
||||||
|
|
||||||
|
typedef struct nn_Drive {
|
||||||
|
// The capacity of the drive.
|
||||||
|
// It is in bytes, but it MUST be a multiple of the sector size.
|
||||||
|
// The total amount of sectors, as in capacity / sectorSize, must also be divisible by the platter count.
|
||||||
|
// If it is not, it is UB.
|
||||||
|
size_t capacity;
|
||||||
|
// the sector size, typically 512
|
||||||
|
size_t sectorSize;
|
||||||
|
// the amount of platters the drive has. This contributes to how many "rotations" are needed.
|
||||||
|
// A drive with 8 sectors but 1 platter, when seeking from sector 1 to 8, would mean 7 rotations.
|
||||||
|
// However, if it has 2 platters, it'd be seen as 1 to 4 being at the same angle as 5 to 8, which
|
||||||
|
// would mean only 3 rotations.
|
||||||
|
size_t platterCount;
|
||||||
|
// how many reads can be issued per tick.
|
||||||
|
// Reading either a sector or a byte counts as 1 read.
|
||||||
|
size_t readsPerTick;
|
||||||
|
// how many writes can be issued per tick.
|
||||||
|
// Writing a sector counts as 1 write.
|
||||||
|
// Writing a byte counts as 1 read and 1 write,
|
||||||
|
// 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.
|
||||||
|
// 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.
|
||||||
|
size_t rpm;
|
||||||
|
// If false, it behaves like a normal OC drive, where the drive can spin backwards to seek.
|
||||||
|
// However, this is unrealistic, as doing so may crack the sensitive platter and make the
|
||||||
|
// reader lose lift.
|
||||||
|
// For fans of physics, this option only allows the seeks to go forwards.
|
||||||
|
// This is super punishing at a slow RPM, so it is recommended to bump up
|
||||||
|
// the RPM to something like 7200 RPM.
|
||||||
|
bool onlySpinForwards;
|
||||||
|
// The energy cost of an actual read/write.
|
||||||
|
// It is per-byte, so if a read returns 4096 bytes, then this cost is multiplied by 4096.
|
||||||
|
double dataEnergyCost;
|
||||||
|
} nn_Drive;
|
||||||
|
|
||||||
|
typedef struct nn_VDrive {
|
||||||
|
// initial label
|
||||||
|
const char *label;
|
||||||
|
size_t labellen;
|
||||||
|
// initial data
|
||||||
|
const char *data;
|
||||||
|
size_t datalen;
|
||||||
|
} nn_VDrive;
|
||||||
|
|
||||||
|
extern nn_Drive nn_defaultDrives[4];
|
||||||
|
|
||||||
|
nn_ComponentState *nn_createDrive(nn_Universe *universe, const nn_Drive *drive, nn_DriveHandler *handler, void *userdata);
|
||||||
|
nn_ComponentState *nn_createVDrive(nn_Universe *universe, const nn_Drive *drive, const nn_VDrive *vdrive);
|
||||||
|
|
||||||
typedef enum nn_ScreenAction {
|
typedef enum nn_ScreenAction {
|
||||||
// instance dropped
|
// instance dropped
|
||||||
NN_SCR_DROP,
|
NN_SCR_DROP,
|
||||||
|
// free screen state
|
||||||
|
NN_SCR_FREE,
|
||||||
|
|
||||||
// set w to 1 if it is on, or 0 if it is off.
|
// set w to 1 if it is on, or 0 if it is off.
|
||||||
NN_SCR_ISON,
|
NN_SCR_ISON,
|
||||||
@@ -1075,6 +1244,8 @@ nn_ComponentState *nn_createKeyboard(nn_Universe *universe);
|
|||||||
typedef enum nn_GPUAction {
|
typedef enum nn_GPUAction {
|
||||||
// instance dropped
|
// instance dropped
|
||||||
NN_GPU_DROP,
|
NN_GPU_DROP,
|
||||||
|
// component state dropped
|
||||||
|
NN_GPU_FREE,
|
||||||
|
|
||||||
// Conventional GPU functions
|
// Conventional GPU functions
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user