// The main file of the test emulator // This is not a serious emulator intended for practical use, // it is simply just to test stuff and showcase the API. // Error handling has been omitted in most places. #include "neonucleus.h" #include #include #include #include #include nn_Architecture getLuaArch(); #if defined(NN_WINDOWS) #define NE_PATHSEP '\\' #include #error "Windows is not supported yet" #elif defined(NN_POSIX) #define NE_PATHSEP '/' #include #include #include typedef DIR ne_dir; ne_dir *ne_opendir(const char *path) { return opendir(path); } void ne_closedir(ne_dir *dir) { closedir(dir); } bool ne_readdir(ne_dir *dir, char path[NN_MAX_PATH]) { struct dirent *ent = readdir(dir); if(ent == NULL) return true; strncpy(path, ent->d_name, NN_MAX_PATH-1); return false; } bool ne_exists(const char *path) { return access(path, F_OK) == 0; } size_t ne_sizeAt(const char *path) { struct stat buf; if(stat(path, &buf) != 0) return 0; if(S_ISDIR(buf.st_mode)) return 0; return buf.st_size; } bool ne_isDirectory(const char *path) { struct stat buf; if(stat(path, &buf) != 0) return false; return S_ISDIR(buf.st_mode); } size_t ne_lastModified(const char *path) { struct stat buf; if(stat(path, &buf) != 0) return 0; return buf.st_mtime; } #endif static const char minBIOS[] = { #embed "minBIOS.lua" ,'\0' }; static nn_Exit sandbox_handler(nn_ComponentRequest *req) { nn_Computer *c = req->computer; switch(req->action) { case NN_COMP_INIT: return NN_OK; case NN_COMP_DEINIT: return NN_OK; case NN_COMP_CALL: if(nn_getstacksize(c) != 1) { nn_setError(c, "bad argument count"); return NN_EBADCALL; } const char *s = nn_tostring(c, 0); puts(s); return NN_OK; case NN_COMP_ENABLED: req->methodEnabled = true; // all methods always enabled return NN_OK; case NN_COMP_FREETYPE: return NN_OK; } return NN_OK; } typedef struct ne_FsState { char path[NN_MAX_PATH]; bool isReadonly; FILE *files[NN_MAX_OPENFILES]; ne_dir *dir; } ne_FsState; void ne_fsState_truepath(ne_FsState *state, char truepath[NN_MAX_PATH], const char *path) { snprintf(truepath, sizeof(char) * NN_MAX_PATH, "%s%c%s", state->path, NE_PATHSEP, path); for(size_t i = 0; truepath[i] != 0; i++) { if(truepath[i] == '/') truepath[i] = NE_PATHSEP; } } nn_Exit ne_fsState_handler(nn_FilesystemRequest *req) { nn_Computer *C = req->computer; ne_FsState *state = req->instance; FILE *f; char truepath[NN_MAX_PATH]; switch(req->action) { case NN_FS_DROP: for(size_t i = 0; i < NN_MAX_OPENFILES; i++) { if(state->files[i] != NULL) fclose(state->files[i]); } if(state->dir != NULL) { ne_closedir(state->dir); } free(state); return NN_OK; case NN_FS_SPACEUSED: req->size = 0; return NN_OK; case NN_FS_GETLABEL: req->strarg1 = NULL; return NN_OK; case NN_FS_SETLABEL: req->strarg1 = NULL; return NN_OK; case NN_FS_OPEN:; req->fd = NN_MAX_OPENFILES; for(size_t i = 0; i < NN_MAX_OPENFILES; i++) { if(state->files[i] == NULL) { req->fd = i; break; } } if(req->fd == NN_MAX_OPENFILES) { nn_setError(C, "too many open handles"); return NN_EBADCALL; } const char *path = req->strarg1; const char *mode = req->strarg2; switch(mode[0]) { case 'r': mode = "rb"; case 'w': mode = "wb"; case 'a': mode = "ab"; default: mode = "rb"; } ne_fsState_truepath(state, truepath, path); f = fopen(truepath, mode); if(f == NULL) { nn_setError(C, strerror(errno)); return NN_EBADCALL; } state->files[req->fd] = f; return NN_OK; case NN_FS_CLOSE: if(req->fd < 0 || req->fd >= NN_MAX_OPENFILES) { nn_setError(C, "bad file descriptor"); return NN_EBADCALL; } f = state->files[req->fd]; if(f == NULL) { nn_setError(C, "bad file descriptor"); return NN_EBADCALL; } fclose(f); state->files[req->fd] = NULL; return NN_OK; case NN_FS_READ: if(req->fd < 0 || req->fd >= NN_MAX_OPENFILES) { nn_setError(C, "bad file descriptor"); return NN_EBADCALL; } f = state->files[req->fd]; if(f == NULL) { nn_setError(C, "bad file descriptor"); return NN_EBADCALL; } if(feof(f)) { req->strarg1 = NULL; } else { req->strarg1len = fread(req->strarg1, sizeof(char), req->strarg1len, f); } return NN_OK; case NN_FS_WRITE: if(req->fd < 0 || req->fd >= NN_MAX_OPENFILES) { nn_setError(C, "bad file descriptor"); return NN_EBADCALL; } f = state->files[req->fd]; if(f == NULL) { nn_setError(C, "bad file descriptor"); return NN_EBADCALL; } fwrite(req->strarg1, sizeof(char), req->strarg1len, f); return NN_OK; case NN_FS_OPENDIR: ne_fsState_truepath(state, truepath, req->strarg1); state->dir = ne_opendir(truepath); if(state->dir == NULL) { nn_setError(C, strerror(errno)); return NN_EBADCALL; } return NN_OK; case NN_FS_READDIR:; char ent[NN_MAX_PATH]; if(ne_readdir(state->dir, ent)) { req->strarg1 = NULL; return NN_OK; } strcpy(req->strarg1, ent); req->strarg1len = strlen(ent); return NN_OK; case NN_FS_CLOSEDIR: ne_closedir(state->dir); state->dir = NULL; return NN_OK; case NN_FS_EXISTS: ne_fsState_truepath(state, truepath, req->strarg1); req->size = ne_exists(truepath) ? 1 : 0; return NN_OK; case NN_FS_SIZE: ne_fsState_truepath(state, truepath, req->strarg1); if(!ne_exists(truepath)) { nn_setError(C, "no such file or directory"); return NN_EBADCALL; } req->size = ne_sizeAt(truepath); return NN_OK; case NN_FS_LASTMODIFIED: ne_fsState_truepath(state, truepath, req->strarg1); if(!ne_exists(truepath)) { nn_setError(C, "no such file or directory"); return NN_EBADCALL; } req->size = ne_lastModified(truepath); return NN_OK; case NN_FS_ISDIRECTORY: ne_fsState_truepath(state, truepath, req->strarg1); if(!ne_exists(truepath)) { nn_setError(C, "no such file or directory"); return NN_EBADCALL; } req->size = ne_isDirectory(truepath) ? 1 : 0; return NN_OK; default: break; } nn_setError(C, "not implemented"); return NN_EBADCALL; } ne_FsState *ne_newFS(const char *path, bool readonly) { ne_FsState *fs = malloc(sizeof(*fs)); for(size_t i = 0; i < NN_MAX_OPENFILES; i++) { fs->files[i] = NULL; } sprintf(fs->path, "data%c%s", NE_PATHSEP, path); fs->isReadonly = readonly; return fs; } // this struct is quite wasteful and could be made like 10x better // for performance. But like, this test emulator is ahh anyways typedef struct ne_Pixel { int fg; int bg; int truefg; int truebg; nn_codepoint codepoint; bool isFgPalette; bool isBgPalette; } ne_Pixel; typedef struct ne_ScreenBuffer { int maxWidth; int maxHeight; int width; int height; char maxDepth; ne_Pixel *pixels; int maxPalette; int editableColors; int *virtualPalette; int *mappedPalette; const char *keyboard; } ne_ScreenBuffer; 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->maxDepth = conf.maxDepth; buf->maxPalette = conf.paletteColors; buf->editableColors = conf.editableColors; 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 = malloc(sizeof(int) * conf.paletteColors); buf->keyboard = keyboard; int *palette = NULL; // TODO: uncomment once the palettes are implemented if(buf->maxPalette == 16) { //palette = nn_mcpalette4; } if(buf->maxPalette == 256) { //palette = nn_ocpalette8; } if(palette) memcpy(buf->virtualPalette, palette, sizeof(int) * buf->maxPalette); memcpy(buf->mappedPalette, buf->virtualPalette, sizeof(int) * buf->maxPalette); return buf; } nn_Exit ne_screen_handler(nn_ScreenRequest *req) { ne_ScreenBuffer *buf = req->instance; switch(req->action) { case NN_SCR_DROP: free(buf->pixels); free(buf->mappedPalette); free(buf->virtualPalette); free(buf); return NN_OK; case NN_SCR_GETASPECTRATIO: req->w = 1; req->h = 1; return NN_OK; case NN_SCR_GETKEYBOARD: if(buf->keyboard == NULL) { req->keyboard = NULL; return NN_OK; } size_t keylen = strlen(buf->keyboard); if(keylen > req->w) keylen = req->w; memcpy(req->keyboard, buf->keyboard, keylen); req->w = keylen; return NN_OK; case NN_SCR_ISON: req->w = 1; return NN_OK; case NN_SCR_TURNON: req->w = 1; req->h = 1; return NN_OK; case NN_SCR_TURNOFF: req->w = 1; req->h = 1; return NN_OK; case NN_SCR_ISPRECISE: req->w = 0; return NN_OK; case NN_SCR_SETPRECISE: req->w = 0; return NN_OK; case NN_SCR_ISTOUCHINVERTED: req->w = 0; return NN_OK; case NN_SCR_SETTOUCHINVERTED: req->w = 0; return NN_OK; } return NN_OK; } int main() { nn_Context ctx; nn_initContext(&ctx); // create the universe nn_Universe *u = nn_createUniverse(&ctx); nn_Architecture arch = getLuaArch(); nn_Method sandboxMethods[] = { {"log", "log(msg: string) - Log to stdout", true}, {NULL}, }; nn_ComponentType *ctype = nn_createComponentType(u, "sandbox", NULL, sandboxMethods, sandbox_handler); nn_VEEPROM veeprom = { .code = minBIOS, .codelen = strlen(minBIOS), .data = NULL, .datalen = 0, .label = NULL, .labellen = 0, .arch = NULL, .isReadonly = false, }; nn_ComponentType *etype = nn_createVEEPROM(u, &nn_defaultEEPROM, &veeprom); nn_ComponentType *fstype[5]; fstype[0] = nn_createFilesystem(u, &nn_defaultFloppy, ne_fsState_handler, NULL); for(size_t i = 1; i < 5; i++) { fstype[i] = nn_createFilesystem(u, &nn_defaultFilesystems[i-1], ne_fsState_handler, NULL); } nn_ComponentType *scrtype = nn_createScreen(u, NULL, ne_screen_handler); nn_Computer *c = nn_createComputer(u, NULL, "computer0", 8 * NN_MiB, 256, 256); nn_setArchitecture(c, &arch); nn_addSupportedArchitecture(c, &arch); nn_addComponent(c, ctype, "sandbox", -1, NULL); nn_addComponent(c, etype, "eeprom", 0, etype); ne_FsState *mainFS = ne_newFS("OpenOS", false); nn_addComponent(c, fstype[1], "mainFS", 2, mainFS); ne_ScreenBuffer *scrbuf = ne_newScreenBuf(nn_defaultScreens[3], NULL); nn_addComponent(c, scrtype, "mainScreen", -1, scrbuf); while(true) { nn_Exit e = nn_tick(c); if(e != NN_OK) { nn_setErrorFromExit(c, e); printf("error: %s\n", nn_getError(c)); goto cleanup; } nn_ComputerState state = nn_getComputerState(c); if(state == NN_POWEROFF) break; if(state == NN_CRASHED) { printf("error: %s\n", nn_getError(c)); goto cleanup; } if(state == NN_CHARCH) { printf("new arch: %s\n", nn_getDesiredArchitecture(c).name); goto cleanup; } if(state == NN_BLACKOUT) { printf("out of energy\n"); goto cleanup; } if(state == NN_RESTART) { printf("restart requested\n"); goto cleanup; } } cleanup:; nn_destroyComputer(c); nn_destroyComponentType(ctype); nn_destroyComponentType(etype); nn_destroyComponentType(scrtype); for(size_t i = 0; i < 5; i++) nn_destroyComponentType(fstype[i]); // rip the universe nn_destroyUniverse(u); return 0; }