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;
|
||||
}
|
||||
if(ptr == NULL) {
|
||||
//if(arch->freeMem < nsize) return NULL;
|
||||
if(arch->freeMem < nsize) return NULL;
|
||||
void *mem = malloc(nsize);
|
||||
if(mem == NULL) return NULL;
|
||||
arch->freeMem -= nsize;
|
||||
return mem;
|
||||
}
|
||||
//if(arch->freeMem + osize < nsize) return NULL;
|
||||
if(arch->freeMem + osize < nsize) return NULL;
|
||||
void *mem = realloc(ptr, nsize);
|
||||
if(mem == NULL) return NULL;
|
||||
arch->freeMem += osize;
|
||||
@@ -231,6 +231,12 @@ static int luaArch_computer_isOverused(lua_State *L) {
|
||||
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) {
|
||||
luaArch *arch = luaArch_from(L);
|
||||
nn_Computer *c = arch->computer;
|
||||
@@ -538,6 +544,8 @@ static void luaArch_loadEnv(lua_State *L) {
|
||||
lua_setfield(L, computer, "shutdown");
|
||||
lua_pushcfunction(L, luaArch_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_setfield(L, computer, "pushSignal");
|
||||
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->freeMem = nn_getTotalMemory(computer);
|
||||
arch->computer = computer;
|
||||
lua_State *L = luaL_newstate();
|
||||
lua_State *L = lua_newstate(luaArch_alloc, arch);
|
||||
arch->L = L;
|
||||
req->localState = arch;
|
||||
luaL_openlibs(L);
|
||||
|
||||
@@ -13,11 +13,13 @@ end
|
||||
local resume = coroutine.resume
|
||||
|
||||
function coroutine.resume(co, ...)
|
||||
local t = {resume(co, ...)}
|
||||
if t[1] and rawequal(t[2], sysyieldobj) then
|
||||
coroutine.yield(sysyieldobj)
|
||||
else
|
||||
return table.unpack(t)
|
||||
while true do
|
||||
local t = {resume(co, ...)}
|
||||
if t[1] and rawequal(t[2], sysyieldobj) then
|
||||
coroutine.yield(sysyieldobj)
|
||||
else
|
||||
return table.unpack(t)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -65,6 +67,7 @@ function component.invoke(address, method, ...)
|
||||
local t = {pcall(cinvoke, address, method, ...)}
|
||||
if computer.energy() <= 0 then sysyield() end -- out of power
|
||||
if computer.isOverused() then sysyield() end -- overused
|
||||
if computer.isIdle() then sysyield() end -- machine idle
|
||||
|
||||
if t[1] then
|
||||
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 a then a = 1 end
|
||||
-- a = math.max(a,1)
|
||||
|
||||
if a < 0 then
|
||||
-- negative
|
||||
|
||||
a = utf8.len(str) + a + 1
|
||||
end
|
||||
|
||||
if b < 0 then
|
||||
b = utf8.len(str) + b + 1
|
||||
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 a > utf8.len(str) then return "" end
|
||||
a = utf8.offset(str,a)
|
||||
|
||||
return str:sub(a,b)
|
||||
-- return str:sub(a, b)
|
||||
end
|
||||
@@ -238,11 +234,14 @@ if os.getenv("NN_REPL") == "1" then
|
||||
print("exiting repl")
|
||||
end
|
||||
|
||||
collectgarbage("stop")
|
||||
-- Save on just a tiny smudgeon of RAM
|
||||
io = nil
|
||||
package = nil
|
||||
|
||||
local eeprom = component.list("eeprom", true)()
|
||||
assert(eeprom, "missing firmware")
|
||||
|
||||
-- this would automatically reboot us if it needs to be a different architecture
|
||||
local arch = component.invoke(eeprom, "getArchitecture")
|
||||
if arch then computer.setArchitecture(arch) end
|
||||
|
||||
|
||||
@@ -114,6 +114,8 @@ nn_Exit ne_fsState_handler(nn_FilesystemRequest *req) {
|
||||
char truepath[NN_MAX_PATH];
|
||||
|
||||
switch(req->action) {
|
||||
case NN_FS_FREE:
|
||||
return NN_OK;
|
||||
case NN_FS_DROP:
|
||||
for(size_t i = 0; i < NN_MAX_OPENFILES; 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 *buf = nn_alloc(ctx, sizeof(*buf));
|
||||
ne_ScreenBuffer *ne_newScreenBuf(nn_ScreenConfig conf, const char *keyboard) {
|
||||
ne_ScreenBuffer *buf = malloc(sizeof(*buf));
|
||||
buf->maxWidth = conf.maxWidth;
|
||||
buf->maxHeight = conf.maxHeight;
|
||||
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->depth = buf->maxDepth;
|
||||
buf->maxPalette = conf.paletteColors;
|
||||
buf->pixels = nn_alloc(ctx, sizeof(ne_Pixel) * conf.maxWidth * conf.maxHeight);
|
||||
buf->virtualPalette = nn_alloc(ctx, sizeof(int) * conf.paletteColors);
|
||||
buf->pixels = malloc(sizeof(ne_Pixel) * conf.maxWidth * conf.maxHeight);
|
||||
buf->virtualPalette = malloc(sizeof(int) * conf.paletteColors);
|
||||
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;
|
||||
|
||||
int *palette = NULL;
|
||||
@@ -374,11 +376,11 @@ ne_ScreenBuffer *ne_newScreenBuf(nn_Context *ctx, nn_ScreenConfig conf, const ch
|
||||
return buf;
|
||||
}
|
||||
|
||||
void ne_dropScreenBuf(nn_Context *ctx, ne_ScreenBuffer *buf) {
|
||||
nn_free(ctx, buf->pixels, sizeof(ne_Pixel) * buf->maxWidth * buf->maxHeight);
|
||||
nn_free(ctx, buf->mappedPalette, sizeof(int) * buf->maxPalette);
|
||||
nn_free(ctx, buf->virtualPalette, sizeof(int) * buf->maxPalette);
|
||||
nn_free(ctx, buf, sizeof(*buf));
|
||||
void ne_dropScreenBuf(ne_ScreenBuffer *buf) {
|
||||
free(buf->pixels);
|
||||
free(buf->mappedPalette);
|
||||
free(buf->virtualPalette);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
ne_Pixel defaultPixel = {
|
||||
@@ -414,6 +416,8 @@ nn_Exit ne_screen_handler(nn_ScreenRequest *req) {
|
||||
switch(req->action) {
|
||||
case NN_SCR_DROP:
|
||||
return NN_OK;
|
||||
case NN_SCR_FREE:
|
||||
return NN_OK;
|
||||
case NN_SCR_GETASPECTRATIO:
|
||||
req->w = 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_Computer *C = req->computer;
|
||||
ne_GPUState *state = req->instance;
|
||||
nn_Context *ctx = nn_getComputerContext(C);
|
||||
|
||||
int maxWidth = req->gpuConf->maxWidth;
|
||||
int maxHeight = req->gpuConf->maxHeight;
|
||||
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;
|
||||
if(maxWidth > buf->maxWidth) maxWidth = buf->maxWidth;
|
||||
if(maxHeight > buf->maxHeight) maxHeight = buf->maxHeight;
|
||||
@@ -515,10 +518,12 @@ nn_Exit ne_gpu_handler(nn_GPURequest *req) {
|
||||
case NN_GPU_DROP:
|
||||
for(int i = 0; i < NE_MAX_VRAMBUF; i++) {
|
||||
ne_ScreenBuffer *buf = state->vramBufs[i];
|
||||
if(buf != NULL) ne_dropScreenBuf(ctx, buf);
|
||||
if(buf != NULL) ne_dropScreenBuf(buf);
|
||||
}
|
||||
free(state);
|
||||
return NN_OK;
|
||||
case NN_GPU_FREE:
|
||||
return NN_OK;
|
||||
case NN_GPU_BIND:
|
||||
state->screenBuf = nn_getComponentUserdata(C, req->text);
|
||||
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(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;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
nn_free(ctx, buf, sizeof(*buf) * w * h);
|
||||
free(buf);
|
||||
ne_remapScreen(activeBuf);
|
||||
return NN_OK;
|
||||
case NN_GPU_GETDEPTH:
|
||||
@@ -992,13 +997,16 @@ double ne_energy_accumulator(void *state, nn_Computer *c, double n) {
|
||||
return nn_getTotalEnergy(c);
|
||||
}
|
||||
|
||||
int main() {
|
||||
int main(int argc, char **argv) {
|
||||
const char *player = getenv("USER");
|
||||
if(player == NULL) player = "me";
|
||||
|
||||
bool sandboxMem = getenv("NN_MEMSAND") != NULL;
|
||||
bool showStats = getenv("NN_STAT") != NULL;
|
||||
|
||||
const char *mainDir = "OpenOS";
|
||||
if(argc > 1) mainDir = argv[1];
|
||||
|
||||
nn_Context ctx;
|
||||
nn_initContext(&ctx);
|
||||
nn_initPalettes();
|
||||
@@ -1029,7 +1037,7 @@ int main() {
|
||||
{"log", "log(msg: string) - Log to stdout", true},
|
||||
{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 = {
|
||||
.code = minBIOS,
|
||||
@@ -1052,7 +1060,11 @@ int main() {
|
||||
nn_ComponentState *keytype = nn_createKeyboard(u);
|
||||
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) {
|
||||
// collects stats
|
||||
nn_setEnergyHandler(c, NULL, ne_energy_accumulator);
|
||||
@@ -1064,16 +1076,26 @@ int main() {
|
||||
nn_addComponent(c, sandstate, "sandbox", -1, NULL);
|
||||
nn_addComponent(c, etype, "eeprom", 0, etype);
|
||||
|
||||
ne_FsState *mainFS = ne_newFS("OpenOS", false);
|
||||
nn_addComponent(c, fstype[4], "mainFS", 2, mainFS);
|
||||
nn_addComponent(c, fstype[4], "mainFS", 2, ne_newFS(mainDir, false));
|
||||
|
||||
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);
|
||||
|
||||
ne_GPUState *gpu = ne_newGPU();
|
||||
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);
|
||||
|
||||
Font font = LoadFont("unscii-16-full.ttf");
|
||||
@@ -1120,15 +1142,18 @@ int main() {
|
||||
|
||||
int statY = 10;
|
||||
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;
|
||||
}
|
||||
if(showStats) {
|
||||
double wattage = accumulatedEnergyCost;
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1212,8 +1237,9 @@ cleanup:;
|
||||
nn_destroyComponentState(scrtype);
|
||||
nn_destroyComponentState(keytype);
|
||||
nn_destroyComponentState(gputype);
|
||||
nn_destroyComponentState(vdriveState);
|
||||
for(size_t i = 0; i < 5; i++) nn_destroyComponentState(fstype[i]);
|
||||
ne_dropScreenBuf(&ctx, scrbuf);
|
||||
ne_dropScreenBuf(scrbuf);
|
||||
// rip the universe
|
||||
nn_destroyUniverse(u);
|
||||
UnloadFont(font);
|
||||
|
||||
@@ -15,7 +15,7 @@ end
|
||||
if gpu and screen then
|
||||
component.invoke(gpu, "bind", screen)
|
||||
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, "setBackground", 0x000000)
|
||||
component.invoke(gpu, "fill", 1, 1, w, h, " ")
|
||||
@@ -56,6 +56,7 @@ local function getBootCode(addr)
|
||||
-- Read first 32K, which is a standard convention
|
||||
local sectorsIn32K = math.ceil(32768 / sectorSize)
|
||||
local bootCode = {firstSector}
|
||||
-- since its null terminated, this is an optimization
|
||||
if not firstSector:find("\0") then
|
||||
for i=2,sectorsIn32K do
|
||||
local sec = drive.readSector(i)
|
||||
@@ -76,33 +77,13 @@ local paths = {
|
||||
"boot/pipes/kernel",
|
||||
}
|
||||
|
||||
local prevboot = computer.getBootAddress()
|
||||
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
|
||||
local bootables = {}
|
||||
|
||||
for addr in component.list("filesystem", true) do
|
||||
for _, path in ipairs(paths) do
|
||||
local code = romRead(addr, path)
|
||||
if code then
|
||||
computer.setBootAddress(addr)
|
||||
assert(load(code))(addr)
|
||||
error("halted")
|
||||
table.insert(bootables, {addr = addr, code = load(code)})
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -110,10 +91,62 @@ end
|
||||
for addr in component.list("drive", true) do
|
||||
local f = getBootCode(addr)
|
||||
if f then
|
||||
computer.setBootAddress(addr)
|
||||
f(addr)
|
||||
error("halted")
|
||||
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)
|
||||
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")
|
||||
|
||||
@@ -649,6 +649,7 @@ typedef struct nn_Computer {
|
||||
size_t archCount;
|
||||
size_t signalCount;
|
||||
size_t userCount;
|
||||
double idleTimestamp;
|
||||
nn_Value callstack[NN_MAX_STACK];
|
||||
char errorBuffer[NN_MAX_ERROR_SIZE];
|
||||
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);
|
||||
}
|
||||
|
||||
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_Context *ctx = &universe->ctx;
|
||||
|
||||
@@ -786,6 +798,7 @@ nn_Computer *nn_createComputer(nn_Universe *universe, void *userdata, const char
|
||||
c->archCount = 0;
|
||||
c->signalCount = 0;
|
||||
c->userCount = 0;
|
||||
c->idleTimestamp = 0;
|
||||
// set to empty string
|
||||
c->errorBuffer[0] = '\0';
|
||||
return c;
|
||||
@@ -1000,6 +1013,10 @@ size_t nn_getFreeMemory(nn_Computer *computer) {
|
||||
return req.freeMemory;
|
||||
}
|
||||
|
||||
size_t nn_getUsedMemory(nn_Computer *computer) {
|
||||
return nn_getTotalMemory(computer) - nn_getFreeMemory(computer);
|
||||
}
|
||||
|
||||
double nn_getUptime(nn_Computer *computer) {
|
||||
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_resetCallBudget(computer);
|
||||
nn_resetComponentBudgets(computer);
|
||||
nn_clearstack(computer);
|
||||
nn_Exit err;
|
||||
// idling pootr
|
||||
if(nn_isComputerIdle(computer)) return NN_OK;
|
||||
computer->idleTimestamp = nn_getUptime(computer);
|
||||
if(computer->state == NN_BOOTUP) {
|
||||
// init state
|
||||
nn_ArchitectureRequest req;
|
||||
@@ -1871,6 +1899,8 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
|
||||
|
||||
switch(req->action) {
|
||||
case NN_COMP_FREETYPE:
|
||||
ereq.action = NN_EEPROM_FREE;
|
||||
state->handler(&ereq);
|
||||
nn_free(&ctx, state, sizeof(*state));
|
||||
break;
|
||||
case NN_COMP_INIT:
|
||||
@@ -2012,6 +2042,7 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
|
||||
}
|
||||
if(nn_strcmp(method, "set") == 0) {
|
||||
nn_removeEnergy(computer, state->eeprom.writeEnergyCost);
|
||||
nn_addIdleTime(computer, state->eeprom.writeDelay);
|
||||
if(nn_getstacksize(computer) < 1) {
|
||||
nn_setError(computer, "bad argument #1 (string expected)");
|
||||
return NN_EBADCALL;
|
||||
@@ -2034,6 +2065,7 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
|
||||
}
|
||||
if(nn_strcmp(method, "setData") == 0) {
|
||||
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;
|
||||
size_t len;
|
||||
const char *s = nn_tolstring(computer, 0, &len);
|
||||
@@ -2076,6 +2108,8 @@ nn_EEPROM nn_defaultEEPROM = (nn_EEPROM) {
|
||||
.writeEnergyCost = 100,
|
||||
.readDataEnergyCost = 0.1,
|
||||
.writeDataEnergyCost = 5,
|
||||
.writeDelay = 2,
|
||||
.writeDataDelay = 1,
|
||||
};
|
||||
|
||||
nn_EEPROM nn_defaultEEPROMs[4] = {
|
||||
@@ -2086,6 +2120,8 @@ nn_EEPROM nn_defaultEEPROMs[4] = {
|
||||
.writeEnergyCost = 100,
|
||||
.readDataEnergyCost = 0.1,
|
||||
.writeDataEnergyCost = 5,
|
||||
.writeDelay = 2,
|
||||
.writeDataDelay = 1,
|
||||
},
|
||||
(nn_EEPROM) {
|
||||
.size = 8 * NN_KiB,
|
||||
@@ -2094,6 +2130,8 @@ nn_EEPROM nn_defaultEEPROMs[4] = {
|
||||
.writeEnergyCost = 200,
|
||||
.readDataEnergyCost = 0.2,
|
||||
.writeDataEnergyCost = 10,
|
||||
.writeDelay = 2,
|
||||
.writeDataDelay = 1,
|
||||
},
|
||||
(nn_EEPROM) {
|
||||
.size = 16 * NN_KiB,
|
||||
@@ -2102,6 +2140,8 @@ nn_EEPROM nn_defaultEEPROMs[4] = {
|
||||
.writeEnergyCost = 400,
|
||||
.readDataEnergyCost = 0.4,
|
||||
.writeDataEnergyCost = 20,
|
||||
.writeDelay = 1,
|
||||
.writeDataDelay = 0.5,
|
||||
},
|
||||
(nn_EEPROM) {
|
||||
.size = 32 * NN_KiB,
|
||||
@@ -2110,6 +2150,8 @@ nn_EEPROM nn_defaultEEPROMs[4] = {
|
||||
.writeEnergyCost = 800,
|
||||
.readDataEnergyCost = 0.8,
|
||||
.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;
|
||||
switch(req->action) {
|
||||
case NN_EEPROM_DROP:
|
||||
return NN_OK;
|
||||
case NN_EEPROM_FREE:
|
||||
nn_free(&ctx, state->code, sizeof(char) * conf->size);
|
||||
nn_free(&ctx, state->data, sizeof(char) * conf->dataSize);
|
||||
nn_free(&ctx, state, sizeof(*state));
|
||||
@@ -2282,6 +2326,13 @@ nn_Filesystem nn_defaultFloppy = (nn_Filesystem) {
|
||||
.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_Filesystem_state *state = req->typeUserdata;
|
||||
void *instance = req->compUserdata;
|
||||
@@ -2300,6 +2351,8 @@ nn_Exit nn_filesystem_handler(nn_ComponentRequest *req) {
|
||||
|
||||
switch(req->action) {
|
||||
case NN_COMP_FREETYPE:
|
||||
fsreq.action = NN_FS_FREE;
|
||||
state->handler(&fsreq);
|
||||
nn_free(&ctx, state, sizeof(*state));
|
||||
break;
|
||||
case NN_COMP_INIT:
|
||||
@@ -2619,6 +2672,309 @@ nn_ComponentState *nn_createFilesystem(nn_Universe *universe, const nn_Filesyste
|
||||
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) {
|
||||
.maxWidth = 50,
|
||||
@@ -2671,6 +3027,8 @@ static nn_Exit nn_screen_handler(nn_ComponentRequest *req) {
|
||||
|
||||
switch(req->action) {
|
||||
case NN_COMP_FREETYPE:
|
||||
scrreq.action = NN_SCR_FREE;
|
||||
state->handler(&scrreq);
|
||||
nn_free(&ctx, state, sizeof(*state));
|
||||
return NN_OK;
|
||||
case NN_COMP_DEINIT:
|
||||
@@ -2807,6 +3165,8 @@ nn_Exit nn_gpu_handler(nn_ComponentRequest *req) {
|
||||
|
||||
switch(req->action) {
|
||||
case NN_COMP_FREETYPE:
|
||||
greq.action = NN_GPU_FREE;
|
||||
state->handler(&greq);
|
||||
nn_free(&ctx, state, sizeof(*state));
|
||||
return NN_OK;
|
||||
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},
|
||||
{"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
|
||||
{"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},
|
||||
};
|
||||
nn_ComponentState *t = nn_createComponentState(universe, "gpu", state, methods, nn_gpu_handler);
|
||||
|
||||
@@ -306,6 +306,11 @@ typedef struct nn_Architecture {
|
||||
nn_ArchitectureHandler *handler;
|
||||
} 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.
|
||||
// Powered off computers shall not have a state, and as far as NeoNucleus is aware,
|
||||
// not exist.
|
||||
@@ -396,6 +401,10 @@ void nn_setEnergyHandler(nn_Computer *computer, void *energyState, nn_EnergyHand
|
||||
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.
|
||||
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.
|
||||
double nn_getUptime(nn_Computer *computer);
|
||||
|
||||
@@ -417,8 +426,13 @@ void nn_setComputerState(nn_Computer *computer, nn_ComputerState state);
|
||||
// gets the current computer state
|
||||
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!
|
||||
// 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);
|
||||
|
||||
typedef struct nn_DeviceInfoEntry {
|
||||
@@ -750,6 +764,8 @@ nn_Exit nn_popSignal(nn_Computer *computer, size_t *valueCount);
|
||||
typedef enum nn_EEPROMAction {
|
||||
// the eeprom instance has been dropped
|
||||
NN_EEPROM_DROP,
|
||||
// the eeprom state has been dropped
|
||||
NN_EEPROM_FREE,
|
||||
NN_EEPROM_GET,
|
||||
NN_EEPROM_SET,
|
||||
NN_EEPROM_GETDATA,
|
||||
@@ -793,6 +809,10 @@ typedef struct nn_EEPROM {
|
||||
double writeEnergyCost;
|
||||
// the energy cost of writing to an EEPROM's associated data
|
||||
double writeDataEnergyCost;
|
||||
// idle time added when writing code
|
||||
double writeDelay;
|
||||
// idle time added when writing data
|
||||
double writeDataDelay;
|
||||
} nn_EEPROM;
|
||||
|
||||
// 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.
|
||||
typedef enum nn_FilesystemAction {
|
||||
// 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,
|
||||
// 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.
|
||||
// strarg1len and strarg2len are their respective lengths.
|
||||
// The output should be in fd.
|
||||
@@ -973,14 +996,160 @@ typedef struct nn_Filesystem {
|
||||
extern nn_Filesystem nn_defaultFilesystems[4];
|
||||
// a basic floppy
|
||||
extern nn_Filesystem nn_defaultFloppy;
|
||||
// a generic tmpfs
|
||||
extern nn_Filesystem nn_defaultTmpFS;
|
||||
|
||||
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_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 {
|
||||
// instance dropped
|
||||
NN_SCR_DROP,
|
||||
// free screen state
|
||||
NN_SCR_FREE,
|
||||
|
||||
// set w to 1 if it is on, or 0 if it is off.
|
||||
NN_SCR_ISON,
|
||||
@@ -1075,6 +1244,8 @@ nn_ComponentState *nn_createKeyboard(nn_Universe *universe);
|
||||
typedef enum nn_GPUAction {
|
||||
// instance dropped
|
||||
NN_GPU_DROP,
|
||||
// component state dropped
|
||||
NN_GPU_FREE,
|
||||
|
||||
// Conventional GPU functions
|
||||
|
||||
|
||||
Reference in New Issue
Block a user