reworking a large part of the architecture
This commit is contained in:
@@ -280,9 +280,15 @@ fail:
|
||||
static int luaArch_component_list(lua_State *L) {
|
||||
luaArch *arch = luaArch_from(L);
|
||||
lua_createtable(L, 64, 0);
|
||||
for(const char *addr = nn_getNextComponent(arch->computer, NULL); addr != NULL; addr = nn_getNextComponent(arch->computer, addr)) {
|
||||
lua_pushstring(L, nn_getComponentType(arch->computer, addr));
|
||||
lua_setfield(L, -2, addr);
|
||||
size_t len = nn_countComponents(arch->computer);
|
||||
const char *comps[len];
|
||||
nn_getComponents(arch->computer, comps);
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
nn_Component *c = nn_getComponent(arch->computer, comps[i]);
|
||||
if(c != NULL) {
|
||||
lua_pushstring(L, nn_getComponentType(nn_getComponent(arch->computer, comps[i])));
|
||||
lua_setfield(L, -2, comps[i]);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -293,22 +299,11 @@ static int luaArch_component_invoke(lua_State *L) {
|
||||
const char *method = luaL_checkstring(L, 2);
|
||||
size_t argc = lua_gettop(L);
|
||||
|
||||
if(!nn_hasComponent(arch->computer, address)) {
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, "no such component");
|
||||
return 2;
|
||||
}
|
||||
if(!nn_hasMethod(arch->computer, address, method)) {
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, "no such method");
|
||||
return 2;
|
||||
}
|
||||
|
||||
nn_clearstack(arch->computer);
|
||||
for(size_t i = 3; i <= argc; i++) {
|
||||
luaArch_luaToNN(arch, L, i);
|
||||
}
|
||||
nn_Exit err = nn_call(arch->computer, address, method);
|
||||
nn_Exit err = nn_invokeComponent(arch->computer, address, method);
|
||||
if(err != NN_OK) {
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, nn_getError(arch->computer));
|
||||
@@ -326,12 +321,13 @@ static int luaArch_component_type(lua_State *L) {
|
||||
luaArch *arch = luaArch_from(L);
|
||||
const char *address = luaL_checkstring(L, 1);
|
||||
|
||||
if(!nn_hasComponent(arch->computer, address)) {
|
||||
nn_Component *c = nn_getComponent(arch->computer, address);
|
||||
if(c == NULL) {
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, "no such component");
|
||||
return 2;
|
||||
}
|
||||
lua_pushstring(L, nn_getComponentType(arch->computer, address));
|
||||
lua_pushstring(L, nn_getComponentType(c));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -340,17 +336,19 @@ static int luaArch_component_doc(lua_State *L) {
|
||||
const char *address = luaL_checkstring(L, 1);
|
||||
const char *method = luaL_checkstring(L, 2);
|
||||
|
||||
if(!nn_hasComponent(arch->computer, address)) {
|
||||
nn_Component *c = nn_getComponent(arch->computer, address);
|
||||
if(c == NULL) {
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, "no such component");
|
||||
return 2;
|
||||
}
|
||||
if(!nn_hasMethod(arch->computer, address, method)) {
|
||||
const char *doc = nn_getComponentDoc(c, method);
|
||||
if(doc == NULL) {
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, "no such method");
|
||||
return 2;
|
||||
}
|
||||
lua_pushstring(L, nn_getComponentDoc(arch->computer, address, method));
|
||||
lua_pushstring(L, doc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -358,7 +356,7 @@ static int luaArch_component_slot(lua_State *L) {
|
||||
luaArch *arch = luaArch_from(L);
|
||||
const char *address = luaL_checkstring(L, 1);
|
||||
|
||||
if(!nn_hasComponent(arch->computer, address)) {
|
||||
if(nn_getComponent(arch->computer, address) == NULL) {
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, "no such component");
|
||||
return 2;
|
||||
@@ -371,18 +369,23 @@ static int luaArch_component_methods(lua_State *L) {
|
||||
luaArch *arch = luaArch_from(L);
|
||||
const char *address = luaL_checkstring(L, 1);
|
||||
|
||||
if(!nn_hasComponent(arch->computer, address)) {
|
||||
nn_Component *c = nn_getComponent(arch->computer, address);
|
||||
|
||||
if(c == NULL) {
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, "no such component");
|
||||
return 2;
|
||||
}
|
||||
const nn_Method *method = nn_nextComponentMethod(arch->computer, address, NULL);
|
||||
lua_createtable(L, 0, 0);
|
||||
for(; method != NULL; method = nn_nextComponentMethod(arch->computer, address, method)) {
|
||||
if(method->flags & NN_FIELD_MASK) continue; // skip
|
||||
size_t methodLen = nn_countComponentMethods(c);
|
||||
const char *methods[methodLen];
|
||||
nn_getComponentMethods(c, methods, &methodLen);
|
||||
lua_createtable(L, 0, methodLen);
|
||||
for(size_t i = 0; i < methodLen; i++) {
|
||||
nn_MethodFlags flags = nn_getComponentMethodFlags(c, methods[i]);
|
||||
if(flags & NN_FIELD_MASK) continue; // skip
|
||||
|
||||
lua_pushboolean(L, (method->flags & NN_DIRECT) != 0);
|
||||
lua_setfield(L, -2, method->name);
|
||||
lua_pushboolean(L, (flags & NN_DIRECT) != 0);
|
||||
lua_setfield(L, -2, methods[i]);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -391,24 +394,29 @@ static int luaArch_component_fields(lua_State *L) {
|
||||
luaArch *arch = luaArch_from(L);
|
||||
const char *address = luaL_checkstring(L, 1);
|
||||
|
||||
if(!nn_hasComponent(arch->computer, address)) {
|
||||
nn_Component *c = nn_getComponent(arch->computer, address);
|
||||
|
||||
if(c == NULL) {
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, "no such component");
|
||||
return 2;
|
||||
}
|
||||
const nn_Method *method = nn_nextComponentMethod(arch->computer, address, NULL);
|
||||
lua_createtable(L, 0, 0);
|
||||
for(; method != NULL; method = nn_nextComponentMethod(arch->computer, address, method)) {
|
||||
if((method->flags & NN_FIELD_MASK) == 0) continue; // skip
|
||||
size_t methodLen = nn_countComponentMethods(c);
|
||||
const char *methods[methodLen];
|
||||
nn_getComponentMethods(c, methods, &methodLen);
|
||||
lua_createtable(L, 0, methodLen);
|
||||
for(size_t i = 0; i < methodLen; i++) {
|
||||
nn_MethodFlags flags = nn_getComponentMethodFlags(c, methods[i]);
|
||||
if((flags & NN_FIELD_MASK) == 0) continue; // skip
|
||||
|
||||
lua_createtable(L, 0, 3);
|
||||
lua_pushboolean(L, (method->flags & NN_DIRECT) != 0);
|
||||
lua_pushboolean(L, (flags & NN_DIRECT) != 0);
|
||||
lua_setfield(L, -2, "direct");
|
||||
lua_pushboolean(L, (method->flags & NN_GETTER) != 0);
|
||||
lua_pushboolean(L, (flags & NN_GETTER) != 0);
|
||||
lua_setfield(L, -2, "getter");
|
||||
lua_pushboolean(L, (method->flags & NN_SETTER) != 0);
|
||||
lua_pushboolean(L, (flags & NN_SETTER) != 0);
|
||||
lua_setfield(L, -2, "setter");
|
||||
lua_setfield(L, -2, method->name);
|
||||
lua_setfield(L, -2, methods[i]);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -593,7 +601,11 @@ static nn_Exit luaArch_handler(nn_ArchitectureRequest *req) {
|
||||
arch = nn_alloc(ctx, sizeof(*arch));
|
||||
arch->freeMem = nn_getTotalMemory(computer) * nn_getMemoryScale(computer);
|
||||
arch->computer = computer;
|
||||
#if LUA_VERSION_NUM >= 505L
|
||||
lua_State *L = lua_newstate(luaArch_alloc, arch, rand());
|
||||
#else
|
||||
lua_State *L = lua_newstate(luaArch_alloc, arch);
|
||||
#endif
|
||||
arch->L = L;
|
||||
req->localState = arch;
|
||||
luaL_openlibs(L);
|
||||
@@ -613,7 +625,12 @@ static nn_Exit luaArch_handler(nn_ArchitectureRequest *req) {
|
||||
case NN_ARCH_TICK:;
|
||||
lua_settop(arch->L, 1);
|
||||
int ret = 0;
|
||||
#if LUA_VERSION_NUM >= 504L
|
||||
int res = lua_resume(arch->L, NULL, 0, &ret);
|
||||
#else
|
||||
int res = lua_resume(arch->L, NULL, 0);
|
||||
ret = lua_gettop(arch->L);
|
||||
#endif
|
||||
//printf("res: %d\n", res);
|
||||
if(res == LUA_OK) {
|
||||
// halted, fuck
|
||||
|
||||
779
src/main.c
779
src/main.c
@@ -4,7 +4,6 @@
|
||||
// Error handling has been omitted in most places.
|
||||
|
||||
#include "neonucleus.h"
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -13,57 +12,6 @@
|
||||
|
||||
nn_Architecture getLuaArch();
|
||||
|
||||
#if defined(NN_WINDOWS)
|
||||
#define NE_PATHSEP '\\'
|
||||
#include <windows.h>
|
||||
#error "Windows is not supported yet"
|
||||
#elif defined(NN_POSIX)
|
||||
#define NE_PATHSEP '/'
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
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'
|
||||
@@ -72,11 +20,7 @@ static const char minBIOS[] = {
|
||||
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:
|
||||
case NN_COMP_INVOKE:
|
||||
if(nn_getstacksize(c) != 1) {
|
||||
nn_setError(c, "bad argument count");
|
||||
return NN_EBADCALL;
|
||||
@@ -84,657 +28,12 @@ static nn_Exit sandbox_handler(nn_ComponentRequest *req) {
|
||||
const char *s = nn_tostring(c, 0);
|
||||
puts(s);
|
||||
return NN_OK;
|
||||
case NN_COMP_ENABLED:
|
||||
case NN_COMP_CHECKMETHOD:
|
||||
req->methodEnabled = true; // all methods always enabled
|
||||
return NN_OK;
|
||||
case NN_COMP_FREETYPE:
|
||||
case NN_COMP_DROP:
|
||||
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_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]);
|
||||
}
|
||||
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";
|
||||
break;
|
||||
case 'w':
|
||||
mode = "wb";
|
||||
break;
|
||||
case 'a':
|
||||
mode = "ab";
|
||||
break;
|
||||
default:
|
||||
mode = "rb";
|
||||
break;
|
||||
}
|
||||
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_SEEK:
|
||||
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;
|
||||
}
|
||||
int whence = SEEK_SET;
|
||||
if(req->whence == NN_SEEK_CUR) {
|
||||
whence = SEEK_CUR;
|
||||
} else if(req->whence == NN_SEEK_END) {
|
||||
whence = SEEK_END;
|
||||
}
|
||||
fseek(f, req->off, whence);
|
||||
req->off = ftell(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_ISREADONLY:
|
||||
req->size = state->isReadonly ? 1 : 0;
|
||||
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;
|
||||
fs->dir = NULL;
|
||||
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 depth;
|
||||
char maxDepth;
|
||||
ne_Pixel *pixels;
|
||||
int maxPalette;
|
||||
int editableColors;
|
||||
int *virtualPalette;
|
||||
int *mappedPalette;
|
||||
const char *keyboard;
|
||||
} ne_ScreenBuffer;
|
||||
|
||||
bool ne_ocCompatibleColors = true;
|
||||
|
||||
void ne_remapScreen(ne_ScreenBuffer *buf) {
|
||||
int depth = buf->depth;
|
||||
|
||||
for(int i = 0; i < buf->maxPalette; i++) {
|
||||
buf->mappedPalette[i] = nn_mapDepth(buf->virtualPalette[i], depth, ne_ocCompatibleColors);
|
||||
}
|
||||
|
||||
for(int y = 0; y < buf->height; y++) {
|
||||
for(int x = 0; x < buf->width; x++) {
|
||||
ne_Pixel *pixel = &buf->pixels[y * buf->maxWidth + x];
|
||||
int virtfg = pixel->fg, virtbg = pixel->bg;
|
||||
if(pixel->isFgPalette) virtfg = buf->mappedPalette[virtfg];
|
||||
else virtfg = nn_mapDepth(virtfg, depth, ne_ocCompatibleColors);
|
||||
if(pixel->isBgPalette) virtbg = buf->mappedPalette[virtbg];
|
||||
else virtbg = nn_mapDepth(virtbg, depth, ne_ocCompatibleColors);
|
||||
|
||||
pixel->truefg = virtfg;
|
||||
pixel->truebg = virtbg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
buf->height = buf->maxHeight;
|
||||
buf->maxDepth = conf.maxDepth;
|
||||
buf->depth = buf->maxDepth;
|
||||
buf->maxPalette = 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 = malloc(sizeof(int) * conf.paletteColors);
|
||||
buf->keyboard = keyboard;
|
||||
|
||||
int *palette = NULL;
|
||||
if(buf->maxDepth == 4) {
|
||||
palette = nn_mcpalette4;
|
||||
}
|
||||
if(buf->maxDepth == 8) {
|
||||
palette = nn_ocpalette8;
|
||||
}
|
||||
if(palette) memcpy(buf->virtualPalette, palette, sizeof(int) * buf->maxPalette);
|
||||
memcpy(buf->mappedPalette, buf->virtualPalette, sizeof(int) * buf->maxPalette);
|
||||
|
||||
for(int y = 0; y < buf->height; y++) {
|
||||
for(int x = 0; x < buf->width; x++) {
|
||||
buf->pixels[y * buf->width + x] = (ne_Pixel) {
|
||||
.fg = 0xFFFFFF,
|
||||
.bg = 0x000000,
|
||||
.isFgPalette = false,
|
||||
.isBgPalette = false,
|
||||
.codepoint = ' ',
|
||||
.truefg = 0xFFFFFF,
|
||||
.truebg = 0x000000,
|
||||
};
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
void ne_dropScreenBuf(ne_ScreenBuffer *buf) {
|
||||
free(buf->pixels);
|
||||
free(buf->mappedPalette);
|
||||
free(buf->virtualPalette);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
ne_Pixel defaultPixel = {
|
||||
.codepoint = ' ',
|
||||
.fg = 0xFFFFFF,
|
||||
.bg = 0x000000,
|
||||
.isFgPalette = false,
|
||||
.isBgPalette = false,
|
||||
.truefg = 0xFFFFFF,
|
||||
.truebg = 0x000000,
|
||||
};
|
||||
|
||||
bool ne_inScreenBuf(ne_ScreenBuffer *buf, int x, int y) {
|
||||
return x > 0 && y > 0 && x <= buf->width && y <= buf->height;
|
||||
}
|
||||
|
||||
ne_Pixel ne_getPixel(ne_ScreenBuffer *buf, int x, int y) {
|
||||
if(!ne_inScreenBuf(buf, x, y)) return defaultPixel;
|
||||
x--;
|
||||
y--;
|
||||
return buf->pixels[y * buf->maxWidth + x];
|
||||
}
|
||||
|
||||
void ne_setPixel(ne_ScreenBuffer *buf, int x, int y, ne_Pixel pixel) {
|
||||
if(!ne_inScreenBuf(buf, x, y)) return;
|
||||
x--;
|
||||
y--;
|
||||
buf->pixels[y * buf->maxWidth + x] = pixel;
|
||||
}
|
||||
|
||||
nn_Exit ne_screen_handler(nn_ScreenRequest *req) {
|
||||
ne_ScreenBuffer *buf = req->instance;
|
||||
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;
|
||||
return NN_OK;
|
||||
case NN_SCR_GETKEYBOARD:
|
||||
if(buf->keyboard == NULL) {
|
||||
req->keyboard = NULL;
|
||||
return NN_OK;
|
||||
}
|
||||
if(req->h != 0) {
|
||||
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;
|
||||
}
|
||||
|
||||
#define NE_MAX_VRAMBUF 16
|
||||
|
||||
typedef struct ne_GPUState {
|
||||
ne_ScreenBuffer *screenBuf;
|
||||
int currentFg;
|
||||
int currentBg;
|
||||
bool isFgPalette;
|
||||
bool isBgPalette;
|
||||
int usedMemory;
|
||||
int activeBuffer;
|
||||
int scrAddrLen;
|
||||
char scrAddr[NN_MAX_ADDRESS];
|
||||
ne_ScreenBuffer *vramBufs[NE_MAX_VRAMBUF];
|
||||
} ne_GPUState;
|
||||
|
||||
ne_GPUState *ne_newGPU() {
|
||||
ne_GPUState *state = malloc(sizeof(*state));
|
||||
state->screenBuf = NULL;
|
||||
state->currentFg = 0xFFFFFF;
|
||||
state->currentBg = 0x000000;
|
||||
state->isFgPalette = false;
|
||||
state->isBgPalette = false;
|
||||
state->activeBuffer = 0;
|
||||
state->usedMemory = 0;
|
||||
for(int i = 0; i < NE_MAX_VRAMBUF; i++) {
|
||||
state->vramBufs[i] = NULL;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
ne_ScreenBuffer *ne_gpu_currentBuffer(ne_GPUState *state) {
|
||||
if(state->activeBuffer == 0) return state->screenBuf;
|
||||
return state->vramBufs[state->activeBuffer - 1];
|
||||
}
|
||||
|
||||
nn_Exit ne_gpu_handler(nn_GPURequest *req) {
|
||||
// completely rewrite this buggy and incomplete mess.
|
||||
nn_Computer *C = req->computer;
|
||||
ne_GPUState *state = req->instance;
|
||||
|
||||
int maxWidth = req->gpuConf->maxWidth;
|
||||
int maxHeight = req->gpuConf->maxHeight;
|
||||
int maxDepth = req->gpuConf->maxDepth;
|
||||
|
||||
ne_ScreenBuffer *activeBuf = state == NULL ? NULL : ne_gpu_currentBuffer(state);
|
||||
|
||||
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;
|
||||
if(maxDepth > buf->maxDepth) maxDepth = buf->maxDepth;
|
||||
}
|
||||
|
||||
int x, y, dx, dy, w, h, fg, bg;
|
||||
ne_Pixel p;
|
||||
|
||||
switch(req->action) {
|
||||
case NN_GPU_DROP:
|
||||
for(int i = 0; i < NE_MAX_VRAMBUF; i++) {
|
||||
ne_ScreenBuffer *buf = state->vramBufs[i];
|
||||
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);
|
||||
state->scrAddrLen = req->width;
|
||||
return NN_OK;
|
||||
case NN_GPU_UNBIND:
|
||||
state->screenBuf = NULL;
|
||||
return NN_OK;
|
||||
case NN_GPU_GETSCREEN:
|
||||
if(state->screenBuf == NULL) {
|
||||
req->text = NULL;
|
||||
return NN_OK;
|
||||
}
|
||||
memcpy(req->text, state->scrAddr, state->scrAddrLen);
|
||||
req->width = state->scrAddrLen;
|
||||
return NN_OK;
|
||||
case NN_GPU_GET:
|
||||
if(activeBuf == NULL) {
|
||||
nn_setError(C, "no screen");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
p = ne_getPixel(activeBuf, req->x, req->y);
|
||||
fg = p.fg;
|
||||
bg = p.bg;
|
||||
if(p.isFgPalette) fg = activeBuf->virtualPalette[fg];
|
||||
if(p.isBgPalette) bg = activeBuf->virtualPalette[bg];
|
||||
|
||||
req->codepoint = p.codepoint;
|
||||
req->width = fg;
|
||||
req->height = bg;
|
||||
req->dest = p.isFgPalette ? p.fg : -1;
|
||||
req->src = p.isBgPalette ? p.bg : -1;
|
||||
return NN_OK;
|
||||
case NN_GPU_SET:
|
||||
case NN_GPU_SETVERTICAL:
|
||||
if(activeBuf == NULL) {
|
||||
nn_setError(C, "no screen");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
dx = 1;
|
||||
dy = 0;
|
||||
if(req->action == NN_GPU_SETVERTICAL) dx = 0, dy = 1;
|
||||
|
||||
x = req->x;
|
||||
y = req->y;
|
||||
const char *s = req->text;
|
||||
for(int i = 0; i < req->width;) {
|
||||
if(!ne_inScreenBuf(activeBuf, x, y)) break;
|
||||
size_t w = nn_unicode_validateFirstChar(s + i, req->width - i);
|
||||
ne_Pixel p = {
|
||||
.fg = state->currentFg,
|
||||
.bg = state->currentBg,
|
||||
.isFgPalette = state->isFgPalette,
|
||||
.isBgPalette = state->isBgPalette,
|
||||
.codepoint = (unsigned char)s[i],
|
||||
};
|
||||
if(w > 0) {
|
||||
p.codepoint = nn_unicode_firstCodepoint(s + i);
|
||||
i += w;
|
||||
} else i++;
|
||||
ne_setPixel(activeBuf, x, y, p);
|
||||
x += dx;
|
||||
y += dy;
|
||||
}
|
||||
ne_remapScreen(activeBuf);
|
||||
return NN_OK;
|
||||
case NN_GPU_FILL:
|
||||
if(activeBuf == NULL) {
|
||||
nn_setError(C, "no screen");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
x = req->x;
|
||||
y = req->y;
|
||||
w = req->width;
|
||||
h = req->height;
|
||||
// prevent CPU DoS
|
||||
if(w > activeBuf->width) w = activeBuf->width;
|
||||
if(h > activeBuf->height) h = activeBuf->height;
|
||||
|
||||
p = (ne_Pixel) {
|
||||
.fg = state->currentFg,
|
||||
.bg = state->currentBg,
|
||||
.isFgPalette = state->isFgPalette,
|
||||
.isBgPalette = state->isBgPalette,
|
||||
.codepoint = req->codepoint,
|
||||
};
|
||||
for(int oy = 0; oy < h; oy++) {
|
||||
for(int ox = 0; ox < w; ox++) {
|
||||
ne_setPixel(activeBuf, x + ox, y + oy, p);
|
||||
}
|
||||
}
|
||||
ne_remapScreen(activeBuf);
|
||||
return NN_OK;
|
||||
case NN_GPU_COPY:
|
||||
if(activeBuf == NULL) {
|
||||
nn_setError(C, "no screen");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
x = req->x;
|
||||
y = req->y;
|
||||
w = req->width;
|
||||
h = req->height;
|
||||
// prevent CPU DoS
|
||||
if(w >= activeBuf->width) w = activeBuf->width - 1;
|
||||
if(h >= activeBuf->height) h = activeBuf->height - 1;
|
||||
|
||||
ne_Pixel *buf = malloc(sizeof(*buf) * w * h);
|
||||
if(buf == NULL) return NN_ENOMEM;
|
||||
|
||||
for(int oy = 0; oy < h; oy++) {
|
||||
for(int ox = 0; ox < w; ox++) {
|
||||
buf[oy * w + ox] = ne_getPixel(activeBuf, x + ox, y + oy);
|
||||
}
|
||||
}
|
||||
|
||||
for(int oy = 0; oy < h; oy++) {
|
||||
for(int ox = 0; ox < w; ox++) {
|
||||
p = buf[oy * w + ox];
|
||||
ne_setPixel(activeBuf, x + ox + req->tx, y + oy + req->ty, p);
|
||||
}
|
||||
}
|
||||
free(buf);
|
||||
ne_remapScreen(activeBuf);
|
||||
return NN_OK;
|
||||
case NN_GPU_GETDEPTH:
|
||||
if(activeBuf != NULL) {
|
||||
req->x = activeBuf->depth;
|
||||
} else {
|
||||
req->x = req->gpuConf->maxDepth;
|
||||
}
|
||||
return NN_OK;
|
||||
case NN_GPU_MAXDEPTH:
|
||||
req->x = maxDepth;
|
||||
return NN_OK;
|
||||
case NN_GPU_GETVIEWPORT:
|
||||
case NN_GPU_GETRESOLUTION:
|
||||
if(activeBuf == NULL) {
|
||||
nn_setError(C, "no screen");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
req->width = activeBuf->width;
|
||||
req->height = activeBuf->height;
|
||||
return NN_OK;
|
||||
case NN_GPU_MAXRESOLUTION:
|
||||
req->width = maxWidth;
|
||||
req->height = maxHeight;
|
||||
return NN_OK;
|
||||
case NN_GPU_GETFOREGROUND:
|
||||
req->x = state->currentFg;
|
||||
req->y = state->isFgPalette ? 1 : 0;
|
||||
return NN_OK;
|
||||
case NN_GPU_GETBACKGROUND:
|
||||
req->x = state->currentBg;
|
||||
req->y = state->isBgPalette ? 1 : 0;
|
||||
return NN_OK;
|
||||
case NN_GPU_SETFOREGROUND:
|
||||
x = req->x;
|
||||
y = req->y;
|
||||
if(y != 0) {
|
||||
// validate the palette index
|
||||
if(activeBuf == NULL || x < 0 || x >= activeBuf->maxPalette) {
|
||||
nn_setError(C, "invalid palette index");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
}
|
||||
req->x = state->currentFg;
|
||||
req->y = state->isFgPalette ? 1 : 0;
|
||||
state->currentFg = x;
|
||||
state->isFgPalette = y != 0;
|
||||
ne_remapScreen(activeBuf);
|
||||
return NN_OK;
|
||||
case NN_GPU_SETBACKGROUND:
|
||||
x = req->x;
|
||||
y = req->y;
|
||||
if(y != 0) {
|
||||
// validate the palette index
|
||||
if(activeBuf == NULL || x < 0 || x >= activeBuf->maxPalette) {
|
||||
nn_setError(C, "invalid palette index");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
}
|
||||
req->x = state->currentBg;
|
||||
req->y = state->isBgPalette ? 1 : 0;
|
||||
state->currentBg = x;
|
||||
state->isBgPalette = y != 0;
|
||||
ne_remapScreen(activeBuf);
|
||||
case NN_COMP_SIGNAL:
|
||||
return NN_OK;
|
||||
}
|
||||
return NN_OK;
|
||||
@@ -1059,12 +358,14 @@ int main(int argc, char **argv) {
|
||||
nn_Architecture arch = getLuaArch();
|
||||
|
||||
nn_Method sandboxMethods[] = {
|
||||
{"log", "log(msg: string) - Log to stdout", true},
|
||||
{"log", "log(msg: string) - Log to stdout", NN_DIRECT},
|
||||
{NULL},
|
||||
};
|
||||
nn_ComponentState *sandstate = nn_createComponentState(u, "ocelot", NULL, sandboxMethods, sandbox_handler);
|
||||
nn_Component *ocelotCard = nn_createComponent(u, "ocelot", "ocelot");
|
||||
nn_setComponentMethods(ocelotCard, sandboxMethods);
|
||||
nn_setComponentHandler(ocelotCard, sandbox_handler);
|
||||
|
||||
nn_VEEPROM veeprom = {
|
||||
const nn_VEEPROM veeprom = {
|
||||
.code = minBIOS,
|
||||
.codelen = strlen(minBIOS),
|
||||
.data = NULL,
|
||||
@@ -1074,16 +375,8 @@ int main(int argc, char **argv) {
|
||||
.arch = NULL,
|
||||
.isReadonly = false,
|
||||
};
|
||||
|
||||
nn_ComponentState *etype = nn_createVEEPROM(u, &nn_defaultEEPROMs[3], &veeprom);
|
||||
nn_ComponentState *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_ComponentState *scrtype = nn_createScreen(u, ne_screen_handler, NULL);
|
||||
nn_ComponentState *keytype = nn_createKeyboard(u);
|
||||
nn_ComponentState *gputype = nn_createGPU(u, &nn_defaultGPUs[3], ne_gpu_handler, NULL);
|
||||
|
||||
nn_Component *eepromCard = nn_createVEEPROM(u, "eeprom", &veeprom, &nn_defaultEEPROMs[3]);
|
||||
|
||||
size_t ramTotal = 0;
|
||||
ramTotal += nn_ramSizes[5];
|
||||
@@ -1100,17 +393,8 @@ int main(int argc, char **argv) {
|
||||
nn_setArchitecture(c, &arch);
|
||||
nn_addSupportedArchitecture(c, &arch);
|
||||
|
||||
nn_addComponent(c, sandstate, "sandbox", -1, NULL);
|
||||
nn_addComponent(c, etype, "eeprom", 0, etype);
|
||||
|
||||
nn_addComponent(c, fstype[4], "mainFS", 2, ne_newFS(mainDir, false));
|
||||
|
||||
nn_addComponent(c, keytype, "mainKB", 4, NULL);
|
||||
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);
|
||||
nn_mountComponent(c, ocelotCard, -1);
|
||||
nn_mountComponent(c, eepromCard, 0);
|
||||
|
||||
const char *driveData = "error('unmanaged drive')";
|
||||
nn_VDrive vdrive = {
|
||||
@@ -1120,9 +404,6 @@ int main(int argc, char **argv) {
|
||||
.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");
|
||||
@@ -1144,30 +425,6 @@ int main(int argc, char **argv) {
|
||||
BeginDrawing();
|
||||
ClearBackground(BLACK);
|
||||
|
||||
int scrW = scrbuf->width;
|
||||
int scrH = scrbuf->height;
|
||||
|
||||
int pixelHeight = GetScreenHeight() / scrH;
|
||||
float spacing = (float)pixelHeight/10;
|
||||
int pixelWidth = MeasureTextEx(font, "A", pixelHeight, spacing).x;
|
||||
|
||||
int depth = scrbuf->depth;
|
||||
|
||||
int offX = (GetScreenWidth() - scrW * pixelWidth) / 2;
|
||||
int offY = (GetScreenHeight() - scrH * pixelHeight) / 2;
|
||||
|
||||
for(int y = 0; y < scrH; y++) {
|
||||
for(int x = 0; x < scrW; x++) {
|
||||
ne_Pixel p = ne_getPixel(scrbuf, x+1, y+1);
|
||||
|
||||
Color fgColor = ne_processColor(p.truefg);
|
||||
Color bgColor = ne_processColor(p.truebg);
|
||||
|
||||
DrawRectangle(x * pixelWidth + offX, y * pixelHeight + offY, pixelWidth, pixelHeight, bgColor);
|
||||
DrawTextCodepoint(font, p.codepoint, (Vector2) {x * pixelWidth + offX, y * pixelHeight + offY}, pixelHeight, fgColor);
|
||||
}
|
||||
}
|
||||
|
||||
int statY = 10;
|
||||
if(sand.buf != NULL) {
|
||||
DrawText(TextFormat("mem used: %.2f%%", (double)sand.used / sand.cap * 100), 10, statY, 20, YELLOW);
|
||||
@@ -1261,14 +518,8 @@ int main(int argc, char **argv) {
|
||||
|
||||
cleanup:;
|
||||
nn_destroyComputer(c);
|
||||
nn_destroyComponentState(sandstate);
|
||||
nn_destroyComponentState(etype);
|
||||
nn_destroyComponentState(gputype);
|
||||
nn_destroyComponentState(scrtype);
|
||||
nn_destroyComponentState(keytype);
|
||||
nn_destroyComponentState(vdriveState);
|
||||
for(size_t i = 0; i < 5; i++) nn_destroyComponentState(fstype[i]);
|
||||
ne_dropScreenBuf(scrbuf);
|
||||
nn_dropComponent(ocelotCard);
|
||||
nn_dropComponent(eepromCard);
|
||||
// rip the universe
|
||||
nn_destroyUniverse(u);
|
||||
UnloadFont(font);
|
||||
|
||||
116
src/ncomplib.c
Normal file
116
src/ncomplib.c
Normal file
@@ -0,0 +1,116 @@
|
||||
#include "neonucleus.h"
|
||||
#include "ncomplib.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct ncl_ScreenPixel {
|
||||
nn_codepoint codepoint;
|
||||
int storedFg;
|
||||
int storedBg;
|
||||
// if negative, its in palette
|
||||
int realFg;
|
||||
// if negative, its in palette
|
||||
int realBg;
|
||||
} ncl_ScreenPixel;
|
||||
|
||||
typedef struct ncl_ScreenState {
|
||||
nn_Context *ctx;
|
||||
nn_ScreenConfig conf;
|
||||
int width;
|
||||
int height;
|
||||
int viewportWidth;
|
||||
int viewportHeight;
|
||||
char depth;
|
||||
int *palette;
|
||||
int *resolvedPalette;
|
||||
ncl_ScreenPixel *pixels;
|
||||
} ncl_ScreenState;
|
||||
|
||||
nn_Component *ncl_createFilesystem(nn_Universe *universe, const char *address, const char *path, const nn_Filesystem *fs);
|
||||
nn_Component *ncl_createDrive(nn_Universe *universe, const char *address, const char *path, const nn_Drive *drive);
|
||||
nn_Component *ncl_createEEPROM(nn_Universe *universe, const char *address, const char *codepath, const char *datapath);
|
||||
|
||||
static ncl_ScreenPixel ncl_getRealScreenPixel(const ncl_ScreenState *state, int x, int y) {
|
||||
if(x < 1 || y < 1 || x >= state->width || y >= state->height) {
|
||||
return (ncl_ScreenPixel) {
|
||||
.codepoint = ' ',
|
||||
.storedFg = 0xFFFFFF,
|
||||
.storedBg = 0x000000,
|
||||
.realFg = 0xFFFFFF,
|
||||
.realBg = 0x000000,
|
||||
};
|
||||
}
|
||||
|
||||
// make it 0-indexed
|
||||
x--;
|
||||
y--;
|
||||
|
||||
return state->pixels[x + y * state->conf.maxWidth];
|
||||
}
|
||||
|
||||
static ncl_ScreenPixel *ncl_getRealScreenPixelPointer(const ncl_ScreenState *state, int x, int y) {
|
||||
if(x < 1 || y < 1 || x >= state->width || y >= state->height) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// make it 0-indexed
|
||||
x--;
|
||||
y--;
|
||||
|
||||
return &state->pixels[x + y * state->conf.maxWidth];
|
||||
}
|
||||
|
||||
static void ncl_setRealScreenPixel(const ncl_ScreenState *state, int x, int y, ncl_ScreenPixel pixel) {
|
||||
if(x < 1 || y < 1 || x >= state->width || y >= state->height) return;
|
||||
x--;
|
||||
y--;
|
||||
|
||||
state->pixels[x + y * state->conf.maxWidth] = pixel;
|
||||
}
|
||||
|
||||
static void ncl_recomputeScreen(const ncl_ScreenState *state) {
|
||||
for(int y = 1; y <= state->height; y++) {
|
||||
for(int x = 1; x <= state->width; x++) {
|
||||
ncl_ScreenPixel *pixel = ncl_getRealScreenPixelPointer(state, x, y);
|
||||
if(pixel == NULL) continue;
|
||||
if(pixel->realFg >= 0) {
|
||||
pixel->realFg = nn_mapDepth(pixel->storedFg, state->depth);
|
||||
}
|
||||
if(pixel->realBg >= 0) {
|
||||
pixel->realBg = nn_mapDepth(pixel->storedBg, state->depth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < state->conf.paletteColors; i++) {
|
||||
state->resolvedPalette[i] = nn_mapDepth(state->palette[i], state->depth);
|
||||
}
|
||||
}
|
||||
|
||||
nn_Component *ncl_createScreen(nn_Universe *universe, const char *address, const nn_ScreenConfig *config);
|
||||
nn_Component *ncl_createGPU(nn_Universe *universe, const char *address, const nn_GPU *gpu);
|
||||
|
||||
void ncl_getScreenResolution(const ncl_ScreenState *state, size_t *width, size_t *height) {
|
||||
*width = state->width;
|
||||
*height = state->height;
|
||||
}
|
||||
|
||||
void ncl_getScreenViewport(const ncl_ScreenState *state, size_t *width, size_t *height) {
|
||||
*width = state->viewportWidth;
|
||||
*height = state->viewportHeight;
|
||||
}
|
||||
|
||||
ncl_Pixel ncl_getScreenPixel(const ncl_ScreenState *state, int x, int y) {
|
||||
ncl_ScreenPixel p = ncl_getRealScreenPixel(state, x, y);
|
||||
return (ncl_Pixel) {
|
||||
.codepoint = p.codepoint,
|
||||
.fgColor = p.realFg < 0 ? state->resolvedPalette[p.storedFg] : p.realFg,
|
||||
.bgColor = p.realBg < 0 ? state->resolvedPalette[p.storedBg] : p.realBg,
|
||||
};
|
||||
}
|
||||
|
||||
// all of these are encoding states
|
||||
|
||||
nn_Exit ncl_encodeComponentState(nn_Universe *universe, nn_Component *comp, ncl_EncodedState *state);
|
||||
void ncl_freeEncodedState(nn_Universe *universe, ncl_EncodedState *state);
|
||||
nn_Exit ncl_loadComponentState(nn_Component *comp, const ncl_EncodedState *state);
|
||||
39
src/ncomplib.h
Normal file
39
src/ncomplib.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef NN_COMPLIB
|
||||
#define NN_COMPLIB
|
||||
|
||||
#include "neonucleus.h"
|
||||
|
||||
typedef struct ncl_EncodedState {
|
||||
char *buf;
|
||||
size_t len;
|
||||
} ncl_EncodedState;
|
||||
|
||||
nn_Exit ncl_encodeComponentState(nn_Universe *universe, nn_Component *comp, ncl_EncodedState *state);
|
||||
void ncl_freeEncodedState(nn_Universe *universe, ncl_EncodedState *state);
|
||||
nn_Exit ncl_loadComponentState(nn_Component *comp, const ncl_EncodedState *state);
|
||||
|
||||
nn_Component *ncl_createFilesystem(nn_Universe *universe, const char *address, const char *path, const nn_Filesystem *fs);
|
||||
nn_Component *ncl_createDrive(nn_Universe *universe, const char *address, const char *path, const nn_Drive *drive);
|
||||
nn_Component *ncl_createEEPROM(nn_Universe *universe, const char *address, const char *codepath, const char *datapath);
|
||||
nn_Component *ncl_createScreen(nn_Universe *universe, const char *address, const nn_ScreenConfig *config);
|
||||
nn_Component *ncl_createGPU(nn_Universe *universe, const char *address, const nn_GPU *gpu);
|
||||
|
||||
// TODO, stuff we could implement:
|
||||
// redstone, hologram, oled, ipu, vt, led, tape_drive, cd_drive, serial, colorful_lamp
|
||||
|
||||
typedef struct ncl_Pixel {
|
||||
// 0xRRGGBB format
|
||||
unsigned int fgColor;
|
||||
// 0xRRGGBB format
|
||||
unsigned int bgColor;
|
||||
// the codepoint
|
||||
nn_codepoint codepoint;
|
||||
} ncl_Pixel;
|
||||
|
||||
typedef struct ncl_ScreenState ncl_ScreenState;
|
||||
|
||||
void ncl_getScreenResolution(const ncl_ScreenState *state, size_t *width, size_t *height);
|
||||
void ncl_getScreenViewport(const ncl_ScreenState *state, size_t *width, size_t *height);
|
||||
ncl_Pixel ncl_getScreenPixel(const ncl_ScreenState *state, int x, int y);
|
||||
|
||||
#endif
|
||||
2536
src/neonucleus.c
2536
src/neonucleus.c
File diff suppressed because it is too large
Load Diff
705
src/neonucleus.h
705
src/neonucleus.h
@@ -227,6 +227,9 @@ void *nn_alloc(nn_Context *ctx, size_t size);
|
||||
void nn_free(nn_Context *ctx, void *memory, size_t size);
|
||||
void *nn_realloc(nn_Context *ctx, void *memory, size_t oldSize, size_t newSize);
|
||||
|
||||
typedef char nn_uuid[37];
|
||||
void nn_randomUUID(nn_Context *ctx, nn_uuid uuid);
|
||||
|
||||
// Basic utils
|
||||
|
||||
// Does canonical path handling. Is used for sandboxing paths.
|
||||
@@ -481,24 +484,9 @@ void nn_resetIdleTime(nn_Computer *computer);
|
||||
// It also sets the idle timestamp to the current uptime.
|
||||
nn_Exit nn_tick(nn_Computer *computer);
|
||||
|
||||
typedef struct nn_DeviceInfoEntry {
|
||||
const char *name;
|
||||
const char *value;
|
||||
} nn_DeviceInfoEntry;
|
||||
// raw component and methods
|
||||
|
||||
typedef struct nn_DeviceInfo {
|
||||
const char *address;
|
||||
const nn_DeviceInfoEntry *entries;
|
||||
} nn_DeviceInfo;
|
||||
|
||||
// adds some device information to the computer. This can also be removed.
|
||||
// Entries is terminated by a NULL name, and preferrably also NULL value.
|
||||
// It is perfectly fine to free entries after the call, it is copied.
|
||||
nn_Exit nn_addDeviceInfo(nn_Computer *computer, const char *address, const nn_DeviceInfoEntry entries[]);
|
||||
// Removes info assicated with a device
|
||||
void nn_removeDeviceInfo(nn_Computer *computer, const char *address);
|
||||
// gets the device info array.
|
||||
const nn_DeviceInfo *nn_getDeviceInfo(nn_Computer *computer, size_t *len);
|
||||
typedef struct nn_Component nn_Component;
|
||||
|
||||
typedef enum nn_MethodFlags {
|
||||
// calling will consume the entire call budget
|
||||
@@ -517,91 +505,108 @@ typedef enum nn_MethodFlags {
|
||||
|
||||
typedef struct nn_Method {
|
||||
const char *name;
|
||||
const char *docString;
|
||||
const char *doc;
|
||||
nn_MethodFlags flags;
|
||||
int idx;
|
||||
} nn_Method;
|
||||
|
||||
typedef struct nn_ComponentState nn_ComponentState;
|
||||
// component signals
|
||||
|
||||
// tells the component to reset its state
|
||||
// sent to the components with slot >= 0 and to tmpfs when computer state is dropped
|
||||
#define NN_CSIGRESET "reset"
|
||||
|
||||
typedef enum nn_ComponentAction {
|
||||
// create the local state
|
||||
NN_COMP_INIT,
|
||||
// delete the local state
|
||||
NN_COMP_DEINIT,
|
||||
// perform a method call
|
||||
NN_COMP_CALL,
|
||||
// check if a method is enabled
|
||||
NN_COMP_ENABLED,
|
||||
// delete the type userdata
|
||||
NN_COMP_FREETYPE,
|
||||
// component dropped
|
||||
NN_COMP_DROP,
|
||||
// component method invoked
|
||||
NN_COMP_INVOKE,
|
||||
// checking if component method is enabled
|
||||
// (may be locked by tier)
|
||||
NN_COMP_CHECKMETHOD,
|
||||
// signal sent to the machine
|
||||
NN_COMP_SIGNAL,
|
||||
} nn_ComponentAction;
|
||||
|
||||
typedef struct nn_ComponentRequest {
|
||||
// the userdata of the component type. This may be an associated VM, for example.
|
||||
void *typeUserdata;
|
||||
// the userdata of the component, passed in addComponent. This may be an associated resource, for example.
|
||||
void *compUserdata;
|
||||
// the local state of the component. NN_COMP_INIT should initialize this pointer.
|
||||
void *state;
|
||||
nn_Context *ctx;
|
||||
nn_Computer *computer;
|
||||
// address of the component
|
||||
const char *compAddress;
|
||||
// the action requested
|
||||
void *state;
|
||||
nn_ComponentAction action;
|
||||
// for NN_COMP_CALL, it is the idx of the method called.
|
||||
// for NN_COMP_ENABLED, it is the idx of the method being checked.
|
||||
int methodCalled;
|
||||
// method index
|
||||
unsigned int methodIdx;
|
||||
union {
|
||||
// for NN_COMP_CALL, it is the amount of return values.
|
||||
// return count
|
||||
size_t returnCount;
|
||||
// for NN_COMP_ENABLED, it is whether the method is enabled.
|
||||
// method enabled
|
||||
bool methodEnabled;
|
||||
// signal invocation
|
||||
const char *signal;
|
||||
};
|
||||
} nn_ComponentRequest;
|
||||
|
||||
typedef nn_Exit nn_ComponentHandler(nn_ComponentRequest *req);
|
||||
typedef nn_Exit (nn_ComponentHandler)(nn_ComponentRequest *request);
|
||||
|
||||
// Creates a new component type. It is safe to free name and methods afterwards.
|
||||
nn_ComponentState *nn_createComponentState(nn_Universe *universe, const char *name, void *userdata, const nn_Method methods[], nn_ComponentHandler *handler);
|
||||
// NOTE: do not destroy this before destroying any components using it, or any computers with components using it.
|
||||
// The component type is still used one last time for the destructor of the components.
|
||||
void nn_destroyComponentState(nn_ComponentState *cstate);
|
||||
// creates a blank component.
|
||||
// It has no methods,
|
||||
nn_Component *nn_createComponent(nn_Universe *universe, const char *address, const char *type);
|
||||
void nn_retainComponent(nn_Component *c);
|
||||
void nn_retainComponentN(nn_Component *c, size_t n);
|
||||
void nn_dropComponent(nn_Component *c);
|
||||
void nn_dropComponentN(nn_Component *c, size_t n);
|
||||
|
||||
// adds a component. Outside of the initialization state (aka after the first tick), it also emits the signal for component added.
|
||||
// You MUST NOT destroy the component type while a component using that type still exists.
|
||||
// You can free the address after the call just fine.
|
||||
nn_Exit nn_addComponent(nn_Computer *computer, nn_ComponentState *cstate, const char *address, int slot, void *userdata);
|
||||
// Checks if a component of that address exists.
|
||||
bool nn_hasComponent(nn_Computer *computer, const char *address);
|
||||
// Checks if the component has that method.
|
||||
// This not only checks if the method exists in the component type,
|
||||
// but also checks if the method is enabled for the component instance.
|
||||
bool nn_hasMethod(nn_Computer *computer, const char *address, const char *method);
|
||||
// removes a component. Outside of the initialization state (aka after the first tick), it also emits the signal for component removed.
|
||||
nn_Exit nn_removeComponent(nn_Computer *computer, const char *address);
|
||||
// Gets the name of a type of a component.
|
||||
const char *nn_getComponentType(nn_Computer *computer, const char *address);
|
||||
// Gets the slot of a component.
|
||||
int nn_getComponentSlot(nn_Computer *computer, const char *address);
|
||||
// Iterates over the methods of a component.
|
||||
// Returns NULL at end of iteration.
|
||||
// name should be NULL at the start.
|
||||
// NOTE: the method pointer MUST be returned by the iterator, as it is offset during iteration.
|
||||
const nn_Method *nn_nextComponentMethod(nn_Computer *computer, const char *address, const nn_Method *old);
|
||||
// iterate over components.
|
||||
// for prev = NULL, returns the first one.
|
||||
// returns NULL at the end of iteration.
|
||||
const char *nn_getNextComponent(nn_Computer *computer, const char *prev);
|
||||
// Returns the doc-string associated with a method.
|
||||
const char *nn_getComponentDoc(nn_Computer *computer, const char *address, const char *method);
|
||||
void *nn_getComponentUserdata(nn_Computer *computer, const char *address);
|
||||
// configure the state
|
||||
void nn_setComponentHandler(nn_Component *c, nn_ComponentHandler *handler);
|
||||
void nn_setComponentState(nn_Component *c, void *state);
|
||||
// sets the methods, same implications as setComponentMethodsArray.
|
||||
// methods is NULL-terminated, as in, it is terminated by a method with a NULL name.
|
||||
nn_Exit nn_setComponentMethods(nn_Component *c, const nn_Method *methods);
|
||||
// sets the methods.
|
||||
// The memory of the strings is copied, so they can be freed after this returns.
|
||||
// This operation is NOT atomic, if it fails, it will clear out the previous methods.
|
||||
nn_Exit nn_setComponentMethodsArray(nn_Component *c, const nn_Method *methods, size_t count);
|
||||
// Sets an internal type ID, which is meant to be a more precise typename.
|
||||
// For example, ncomplib would set ncl-screen for the screen component,
|
||||
// so the GPU can confirm it is being bound to a screen it knows how to use.
|
||||
nn_Exit nn_setComponentTypeID(nn_Component *c, const char *internalTypeID);
|
||||
|
||||
// this uses the call stack.
|
||||
// Component calls must not call other components, it just doesn't work.
|
||||
// The lack of an argument count is because the entire call stack is assumed to be the arguments.
|
||||
// In the case of NN_EBUSY, you should call it again with the same arguments later.
|
||||
nn_Exit nn_call(nn_Computer *computer, const char *address, const char *method);
|
||||
// get component state
|
||||
void *nn_getComponentState(nn_Component *c);
|
||||
// counts how many methods are registered. May return too many if some of them are not enabled.
|
||||
size_t nn_countComponentMethods(nn_Component *c);
|
||||
// will fill the methodnames array with the names of the *enabled* methods.
|
||||
// Will set *len to the amount of methods.
|
||||
void nn_getComponentMethods(nn_Component *c, const char **methodnames, size_t *len);
|
||||
// whether a method is defined and enabled
|
||||
bool nn_hasComponentMethod(nn_Component *c, const char *method);
|
||||
const char *nn_getComponentDoc(nn_Component *c, const char *method);
|
||||
nn_MethodFlags nn_getComponentMethodFlags(nn_Component *c, const char *method);
|
||||
const char *nn_getComponentType(nn_Component *c);
|
||||
const char *nn_getComponentTypeID(nn_Component *c);
|
||||
|
||||
// Adds a component to the computer on a given slot.
|
||||
// This will also queue a component_added signal if the computer is in a running state.
|
||||
// If the component already is mounted, an error is returned.
|
||||
nn_Exit nn_mountComponent(nn_Computer *c, nn_Component *comp, int slot);
|
||||
// Removes a component from the computer.
|
||||
// This will also queue a component_removed signal if the computer is in a running state.
|
||||
// If the component is not mounted, no error is returned.
|
||||
nn_Exit nn_unmountComponent(nn_Computer *c, const char *address);
|
||||
// gets a component by address. Will return NULL if there is none.
|
||||
nn_Component *nn_getComponent(nn_Computer *c, const char *address);
|
||||
int nn_getComponentSlot(nn_Computer *c, const char *address);
|
||||
size_t nn_countComponents(nn_Computer *c);
|
||||
void nn_getComponents(nn_Computer *c, const char **components);
|
||||
|
||||
// invoke the component method.
|
||||
// Everything on-stack is taken as an argument.
|
||||
// Will pop off trailing nulls.
|
||||
// Every remaining is what the component returned.
|
||||
nn_Exit nn_invokeComponent(nn_Computer *computer, const char *compAddress, const char *method);
|
||||
|
||||
// send a signal to a component.
|
||||
// Computer actually can be NULL, but the component may crash if the signal
|
||||
// assumes one is specified.
|
||||
nn_Exit nn_signalComponent(nn_Component *component, nn_Computer *computer, const char *signal);
|
||||
|
||||
// Sets the call budget.
|
||||
// The default is 1,000.
|
||||
@@ -806,44 +811,10 @@ nn_Exit nn_pushSignal(nn_Computer *computer, size_t valueCount);
|
||||
// If there is no signal, it returns EBADSTATE
|
||||
nn_Exit nn_popSignal(nn_Computer *computer, size_t *valueCount);
|
||||
|
||||
// The high-level API of the built-in components.
|
||||
// The high-level API of the built-in component classes.
|
||||
// These components still make no assumptions about the OS, and still require handlers to connect them to the outside work.
|
||||
|
||||
// TODO: screen, gpu, filesystem, eeprom and the rest of the universe
|
||||
|
||||
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,
|
||||
NN_EEPROM_SETDATA,
|
||||
NN_EEPROM_GETLABEL,
|
||||
NN_EEPROM_SETLABEL,
|
||||
NN_EEPROM_GETARCH,
|
||||
NN_EEPROM_SETARCH,
|
||||
NN_EEPROM_ISREADONLY,
|
||||
NN_EEPROM_MAKEREADONLY,
|
||||
} nn_EEPROMAction;
|
||||
|
||||
typedef struct nn_EEPROMRequest {
|
||||
// associated userdata
|
||||
void *userdata;
|
||||
// associated component userdata
|
||||
void *instance;
|
||||
// the computer making the request
|
||||
nn_Computer *computer;
|
||||
const struct nn_EEPROM *eepromConf;
|
||||
nn_EEPROMAction action;
|
||||
// all the get* options should set this to the length,
|
||||
// and its initial value is the capacity of [buf].
|
||||
// For ISREADONLY, this should be set to 0 if false and 1 if true.
|
||||
unsigned int buflen;
|
||||
// this may be the buffer length
|
||||
char *buf;
|
||||
} nn_EEPROMRequest;
|
||||
// EEPROM class
|
||||
|
||||
// reads and writes are always 1/1
|
||||
typedef struct nn_EEPROM {
|
||||
@@ -865,6 +836,39 @@ typedef struct nn_EEPROM {
|
||||
double writeDataDelay;
|
||||
} nn_EEPROM;
|
||||
|
||||
typedef enum nn_EEPROMAction {
|
||||
// component is dropped
|
||||
NN_EEPROM_DROP,
|
||||
// check if readonly. If so, buflen should be 1, else it should be 0.
|
||||
NN_EEPROM_ISRO,
|
||||
// make the EEPROM readonly. Checksum already verified.
|
||||
NN_EEPROM_MKRO,
|
||||
// write the contents of the code into buf.
|
||||
// Set buflen to the length.
|
||||
NN_EEPROM_GET,
|
||||
// store the contents in buf into the EEPROM as code.
|
||||
// the length of buf is in buflen.
|
||||
NN_EEPROM_SET,
|
||||
NN_EEPROM_GETDATA,
|
||||
NN_EEPROM_SETDATA,
|
||||
NN_EEPROM_GETARCH,
|
||||
NN_EEPROM_SETARCH,
|
||||
NN_EEPROM_GETLABEL,
|
||||
NN_EEPROM_SETLABEL,
|
||||
} nn_EEPROMAction;
|
||||
|
||||
typedef struct nn_EEPROMRequest {
|
||||
nn_Context *ctx;
|
||||
nn_Computer *computer;
|
||||
void *state;
|
||||
const nn_EEPROM *eeprom;
|
||||
nn_EEPROMAction action;
|
||||
char *buf;
|
||||
size_t buflen;
|
||||
} nn_EEPROMRequest;
|
||||
|
||||
typedef nn_Exit (nn_EEPROMHandler)(nn_EEPROMRequest *request);
|
||||
|
||||
// Tier 1 - The normal EEPROM equivalent
|
||||
// Tier 2 - A better EEPROM
|
||||
// Tier 3 - An even better EEPROM
|
||||
@@ -882,147 +886,10 @@ typedef struct nn_VEEPROM {
|
||||
bool isReadonly;
|
||||
} nn_VEEPROM;
|
||||
|
||||
typedef nn_Exit nn_EEPROMHandler(nn_EEPROMRequest *request);
|
||||
nn_Component *nn_createEEPROM(nn_Universe *universe, const char *address, const nn_EEPROM *eeprom, void *state, nn_EEPROMHandler *handler);
|
||||
nn_Component *nn_createVEEPROM(nn_Universe *universe, const char *address, const nn_VEEPROM *veeprom, const nn_EEPROM *eeprom);
|
||||
|
||||
// the userdata passed to the component is the userdata
|
||||
// in the handler
|
||||
nn_ComponentState *nn_createEEPROM(nn_Universe *universe, const nn_EEPROM *eeprom, nn_EEPROMHandler *handler, void *userdata);
|
||||
nn_ComponentState *nn_createVEEPROM(nn_Universe *universe, const nn_EEPROM *eeprom, const nn_VEEPROM *vmem);
|
||||
|
||||
// Note on paths:
|
||||
// - Paths given always have their length stored, but also have a NULL terminator.
|
||||
// - Paths are validated. They check for illegal characters as per OC's definition.
|
||||
// - Logical paradoxes such as rename("a", "a/b") are automatically checked and handled.
|
||||
// - \ are automatically replaced with /
|
||||
// - .. and leading / is handled automatically. This also improves sandboxing, as ../a.txt would become just a.txt
|
||||
// - For rename, it automatically checks if the destination exists and if so, errors out.
|
||||
typedef enum nn_FilesystemAction {
|
||||
// the filesystem instance has been dropped.
|
||||
// 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.
|
||||
NN_FS_OPEN,
|
||||
// read a file.
|
||||
// The file descriptor is stored in fd,
|
||||
// make sure to ensure it is valid.
|
||||
// strarg1len is the capacity of strarg1.
|
||||
// Write the result of reading into strarg1.
|
||||
// Update strarg1len to reflect the amount of data read.
|
||||
// Set strarg1 to NULL to indicate EOF.
|
||||
NN_FS_READ,
|
||||
// write to a file.
|
||||
// The file descriptor is stored in fd,
|
||||
// make sure to ensure it is valid.
|
||||
// strarg1len is the amount of data to write.
|
||||
// strarg1 is the contents of the buffer to write.
|
||||
NN_FS_WRITE,
|
||||
// seek a file.
|
||||
// The file descriptor is stored in fd,
|
||||
// make sure to ensure it is valid.
|
||||
// The offset is stored in off.
|
||||
// The seek mode is stored in whence.
|
||||
// It should set off to the new position.
|
||||
NN_FS_SEEK,
|
||||
// close a file.
|
||||
// The file descriptor is stored in fd,
|
||||
// make sure to ensure it is valid.
|
||||
NN_FS_CLOSE,
|
||||
// open a directory file descriptor.
|
||||
// The result should be in fd.
|
||||
NN_FS_OPENDIR,
|
||||
// read a directory file descriptor, stored in fd.
|
||||
// The entry should be stored in strarg2, and strarg2len is the capacity of the buffer.
|
||||
// If the buffer is too short, truncate the result.
|
||||
// Set strarg2len to the length of the entry.
|
||||
// If there are no more entries, set strarg2 to NULL.
|
||||
// Do note that directories should have / appended at the end of their entries.
|
||||
// Directory file descriptors are not exposed to the architecture,
|
||||
// thus they can only come from NN_FS_OPENDIR.
|
||||
// This means you may not need to validate these file descriptors.
|
||||
NN_FS_READDIR,
|
||||
// close a directory file descriptor, stored in fd.
|
||||
// Directory file descriptors are not exposed to the architecture,
|
||||
// thus they can only come from NN_FS_OPENDIR.
|
||||
// This means you may not need to validate these file descriptors.
|
||||
NN_FS_CLOSEDIR,
|
||||
// Create a directory at a given path stored in strarg1.
|
||||
// strarg1len is the length of the path.
|
||||
// It is meant to also create parent directories recursively
|
||||
// as needed.
|
||||
NN_FS_MKDIR,
|
||||
// Return the lastmodified timestamp.
|
||||
// This number is stored in seconds.
|
||||
// The timestamp should be stored in size, it may not make
|
||||
// sense but it is a field and it is there.
|
||||
// Do note that the lastModified() method returns it in milliseconds,
|
||||
// however it must be a multiple of 1000 due to OpenOS depending
|
||||
// on that behavior.
|
||||
NN_FS_LASTMODIFIED,
|
||||
// Checks if a path, stored in strarg1, is a directory.
|
||||
// If it is, size should be set to 1.
|
||||
// If it is not, size should be set to 0.
|
||||
NN_FS_ISDIRECTORY,
|
||||
// Checks if the filesystem is read-only.
|
||||
// If it is, size should be set to 1.
|
||||
// If it is not, size should be set to 0.
|
||||
NN_FS_ISREADONLY,
|
||||
// Checks if a path, stored in strarg1, exists on the filesystem.
|
||||
// If it is, size should be set to 1.
|
||||
// If it is not, size should be set to 0.
|
||||
NN_FS_EXISTS,
|
||||
// Returns the label.
|
||||
// The label should be written into strarg1, with strarg1len as the capacity.
|
||||
// Set strarg1len to the label length.
|
||||
NN_FS_GETLABEL,
|
||||
// Sets the label.
|
||||
// The label is stored in strarg1, with strarg1len as the length.
|
||||
NN_FS_SETLABEL,
|
||||
// Gets the space used, it should be stored in size.
|
||||
NN_FS_SPACEUSED,
|
||||
// Gets 2 paths, strarg1 and strarg2, with their lengths.
|
||||
// It should try to rename strarg1 to strarg2, as in,
|
||||
// it should move strarg1 to be at strarg2, potentially
|
||||
// using recursive directory copies.
|
||||
NN_FS_RENAME,
|
||||
// Removes the path stored in strarg1.
|
||||
NN_FS_REMOVE,
|
||||
// Returns the size of the entry at strarg1.
|
||||
// The size of a directory is typically 0.
|
||||
// The size of a file is typically the amount of bytes in its contents.
|
||||
// Using other measures of size will rarely break code,
|
||||
// but may confuse users.
|
||||
NN_FS_SIZE,
|
||||
} nn_FilesystemAction;
|
||||
|
||||
typedef enum nn_FilesystemWhence {
|
||||
// relative to start
|
||||
NN_SEEK_SET,
|
||||
// relative to the current position
|
||||
NN_SEEK_CUR,
|
||||
// relative to the EOF position.
|
||||
NN_SEEK_END,
|
||||
} nn_FilesystemWhence;
|
||||
|
||||
typedef struct nn_FilesystemRequest {
|
||||
void *userdata;
|
||||
void *instance;
|
||||
nn_Computer *computer;
|
||||
struct nn_Filesystem *fsConf;
|
||||
nn_FilesystemAction action;
|
||||
int fd;
|
||||
nn_FilesystemWhence whence;
|
||||
int off;
|
||||
char *strarg1;
|
||||
size_t strarg1len;
|
||||
char *strarg2;
|
||||
size_t strarg2len;
|
||||
size_t size;
|
||||
} nn_FilesystemRequest;
|
||||
// Filesystem class
|
||||
|
||||
typedef struct nn_Filesystem {
|
||||
// the maximum capacity of the filesystem
|
||||
@@ -1049,8 +916,6 @@ extern const nn_Filesystem nn_defaultFloppy;
|
||||
// a generic tmpfs
|
||||
extern const 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.
|
||||
@@ -1085,63 +950,7 @@ typedef struct nn_VFilesystem {
|
||||
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);
|
||||
// Drive class
|
||||
|
||||
typedef struct nn_Drive {
|
||||
// The capacity of the drive.
|
||||
@@ -1191,64 +1000,9 @@ typedef struct nn_VDrive {
|
||||
} nn_VDrive;
|
||||
|
||||
extern const nn_Drive nn_defaultDrives[4];
|
||||
extern const nn_Drive nn_floppyDrive;
|
||||
|
||||
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,
|
||||
// attempt to turn the screen on.
|
||||
// set w to 1 if it was on, or 0 if it was off.
|
||||
// set h to 1 if it is now on, or 0 if it is now off.
|
||||
NN_SCR_TURNON,
|
||||
// attempt to turn the screen off.
|
||||
// set w to 1 if it was on, or 0 if it was off.
|
||||
// set h to 1 if it is now on, or 0 if it is now off.
|
||||
NN_SCR_TURNOFF,
|
||||
// get a keyboard. The index requested is stored in h.
|
||||
// If the index is out of bounds, set keyboard to NULL.
|
||||
// Else, write the keyboard address into the buffer in keyboard.
|
||||
// The capacity of the buffer is stored in w.
|
||||
NN_SCR_GETKEYBOARD,
|
||||
// change the screen to/from precise mode.
|
||||
// Precise mode means mouse events will have real-number coordinates, as opposed to integer-based ones.
|
||||
// NeoNucleus does not automatically round this, you are meant to round it.
|
||||
// The new precision value is stored in w, where it is a 1 to enable it and 0 to disable it.
|
||||
// Set w to 1 if precise mode is now enabled, or 0 if it isn't.
|
||||
NN_SCR_SETPRECISE,
|
||||
// Set w to 1 if precise mode is enabled, or 0 if it isn't.
|
||||
NN_SCR_ISPRECISE,
|
||||
// change the screen to/from inverted touch mode.
|
||||
// Inverted touch mode normally provides an alternative way to interact with the touchscreen.
|
||||
// For example, in OC, it makes the GUI only open with shift+rightclick, and normal rightclick
|
||||
// triggers a touch event instead. It is best to give it an equivalent meaning to OC's to prevent
|
||||
// unexpected program behavior.
|
||||
// The new inverted touch mode state is stored in w, where it is a 1 to enable it and 0 to disable it.
|
||||
// Set w to 1 if inverted touch mode is now enabled, or 0 if it isn't.
|
||||
NN_SCR_SETTOUCHINVERTED,
|
||||
// Set w to 1 if inverted touch mode is enabled, or 0 if it isn't.
|
||||
NN_SCR_ISTOUCHINVERTED,
|
||||
// Gets the aspect ratio (amount of screen blocks joined together).
|
||||
// Outside of MC, this may not make much sense, in which case you can just set it to 1x1.
|
||||
// Store the width in w and the height in h.
|
||||
NN_SCR_GETASPECTRATIO,
|
||||
} nn_ScreenAction;
|
||||
|
||||
typedef struct nn_ScreenRequest {
|
||||
void *userdata;
|
||||
void *instance;
|
||||
nn_Computer *computer;
|
||||
nn_ScreenAction action;
|
||||
int w;
|
||||
int h;
|
||||
char *keyboard;
|
||||
} nn_ScreenRequest;
|
||||
// Screen class
|
||||
|
||||
typedef enum nn_ScreenFeatures {
|
||||
NN_SCRF_NONE = 0,
|
||||
@@ -1270,196 +1024,30 @@ typedef enum nn_ScreenFeatures {
|
||||
// however it exists as a runtime reference of what
|
||||
// the conventional screen tiers are.
|
||||
typedef struct nn_ScreenConfig {
|
||||
// maximum width
|
||||
int maxWidth;
|
||||
// maximum height
|
||||
int maxHeight;
|
||||
// screen features
|
||||
nn_ScreenFeatures features;
|
||||
// default palette, if applicable.
|
||||
// Can be NULL if there is none,
|
||||
// in which case consider memsetting
|
||||
// them to #000000.
|
||||
int *defaultPalette;
|
||||
// the amount of editable palette colors
|
||||
int paletteColors;
|
||||
// how many editable palette colors there are.
|
||||
// It'd always be the first N ones.
|
||||
int editableColors;
|
||||
// the maximum depth of the screen
|
||||
char maxDepth;
|
||||
} nn_ScreenConfig;
|
||||
|
||||
// OC has 3 tiers, NN adds a 4th one as well.
|
||||
extern const nn_ScreenConfig nn_defaultScreens[4];
|
||||
|
||||
typedef nn_Exit nn_ScreenHandler(nn_ScreenRequest *req);
|
||||
|
||||
nn_ComponentState *nn_createScreen(nn_Universe *universe, nn_ScreenHandler *handler, void *userdata);
|
||||
// a useless component which does nothing
|
||||
nn_ComponentState *nn_createKeyboard(nn_Universe *universe);
|
||||
|
||||
// Remember:
|
||||
// - Colors are in 0xRRGGBB format.
|
||||
// - Screen coordinates and palettes are 1-indexed.
|
||||
// - If NN_GPU_SETRESOLUTION returns NN_OK, a screen_resized signal is queued automatically.
|
||||
// - VRAM is always fast
|
||||
typedef enum nn_GPUAction {
|
||||
// instance dropped
|
||||
NN_GPU_DROP,
|
||||
// component state dropped
|
||||
NN_GPU_FREE,
|
||||
|
||||
// Conventional GPU functions
|
||||
|
||||
// requests to bind to a screen connected to the computer.
|
||||
// The address is stored in text, with the length in width.
|
||||
// The interface does check that the computer does have the screen, but do look out
|
||||
// for time-of-check/time-of-use issues which may occur in multi-threaded environments.
|
||||
// If x is set to 1, the reset flag is enabled. This means the GPU should "reset" the state
|
||||
// of the screen.
|
||||
NN_GPU_BIND,
|
||||
// requests to unbind the GPU from its screen.
|
||||
// If there is no screen, it just does nothing.
|
||||
NN_GPU_UNBIND,
|
||||
// Ask for the screen the GPU is currently bound to.
|
||||
// If it is not bound to any, text should be set to NULL.
|
||||
// If it is, you must write to text the address of the screen.
|
||||
// width stores the capacity of text, so if needed, truncate it to that many bytes.
|
||||
// The length of this address must be stored in width.
|
||||
NN_GPU_GETSCREEN,
|
||||
// Gets the current background.
|
||||
// x should store either the color in 0xRRGGBB format or the palette index.
|
||||
// y should be 1 if x is a palette index and 0 if it is a color.
|
||||
NN_GPU_GETBACKGROUND,
|
||||
// Sets the current background.
|
||||
// x should store either the color in 0xRRGGBB format or the palette index.
|
||||
// y should be 1 if x is a palette index and 0 if it is a color.
|
||||
// The values x and y should be updated to reflect the old state.
|
||||
NN_GPU_SETBACKGROUND,
|
||||
// Gets the current foreground.
|
||||
// x should store either the color in 0xRRGGBB format or the palette index.
|
||||
// y should be 1 if x is a palette index and 0 if it is a color.
|
||||
NN_GPU_GETFOREGROUND,
|
||||
// Sets the current foreground.
|
||||
// x should store either the color in 0xRRGGBB format or the palette index.
|
||||
// y should be 1 if x is a palette index and 0 if it is a color.
|
||||
// The values x and y should be updated to reflect the old state.
|
||||
NN_GPU_SETFOREGROUND,
|
||||
// Gets the palette color.
|
||||
// x is the index.
|
||||
// y should be set to the color.
|
||||
NN_GPU_GETPALETTECOLOR,
|
||||
// Gets the palette color.
|
||||
// x is the index.
|
||||
// y is the color.
|
||||
NN_GPU_SETPALETTECOLOR,
|
||||
// Gets the maximum depth supported by the GPU and screen.
|
||||
// Valid depth values in OC are 1, 4 and 8, however NN also recognizes 2, 3, 16 and 24.
|
||||
// The result should be stored in x.
|
||||
NN_GPU_MAXDEPTH,
|
||||
// Gets the current depth the screen is displaying at.
|
||||
// The result should be stored in x.
|
||||
NN_GPU_GETDEPTH,
|
||||
// Sets the current depth the screen is displaying at.
|
||||
// The new depth is in x.
|
||||
// This should not change the stored color values of neither the palette nor the characters,
|
||||
// but simply change what their color is translated to graphically.
|
||||
// The old depth should be stored in x.
|
||||
NN_GPU_SETDEPTH,
|
||||
// Gets the maximum resolution supported by the GPU and screen.
|
||||
// Result should be in width and height.
|
||||
NN_GPU_MAXRESOLUTION,
|
||||
// Gets the resolution of the screen.
|
||||
// Result should be in width and height.
|
||||
NN_GPU_GETRESOLUTION,
|
||||
// Sets the resolution of the screen.
|
||||
// The new resolution should be stored in width and height.
|
||||
// If successful, a screen_resized event is implicitly queued.
|
||||
NN_GPU_SETRESOLUTION,
|
||||
// Gets the current screen viewport.
|
||||
// The result should be in width and height.
|
||||
NN_GPU_GETVIEWPORT,
|
||||
// Sets the screen viewport.
|
||||
// The new viewport dimensions are stored in width and height.
|
||||
NN_GPU_SETVIEWPORT,
|
||||
// Gets a character.
|
||||
// The position requested is given in x and y.
|
||||
// The codepoint of the character should be set in [codepoint].
|
||||
// The foreground and background color should be set in [width] and [height].
|
||||
// The palette indexes of the foreground and background should be set
|
||||
// in [dest] and [src] respectively. If the pixel color was not from
|
||||
// the palette, the imaginary -1 palette index can be used.
|
||||
NN_GPU_GET,
|
||||
// Sets a horizontal line of text at a given x, y.
|
||||
// The position is stored in x, y, and is the position of the first character.
|
||||
// The text goes left-to-right on the horizontal line. Anything off-screen is discared.
|
||||
// There is no wrapping.
|
||||
// The text is stored in text, with the size of the text, in bytes, being stored in width.
|
||||
NN_GPU_SET,
|
||||
// like NN_GPU_SET, but the text is set vertically.
|
||||
// This means instead of going from left-to-right on the screen on a horizontal line,
|
||||
// it is up-to-down on a vertical line.
|
||||
NN_GPU_SETVERTICAL,
|
||||
// Copies a portion of the screen to another location.
|
||||
// The rectangle being copied is width x height, and has the top-left corner at x, y.
|
||||
// The destination rectangle is also width x height, but has the top-left corner at x + tx, y + ty.
|
||||
// The copy happens as if it is using an intermediary buffer, thus even if the source and destination
|
||||
// intersect, the order in which characters are copied must not change the result.
|
||||
NN_GPU_COPY,
|
||||
// Fills a rectangle
|
||||
// The rectangle's top-left corner is at x, y, and its dimensions are width x height.
|
||||
// The character it should be filled with has its unicode codepoint stored in codepoint.
|
||||
NN_GPU_FILL,
|
||||
|
||||
// VRAM buffers (always blazing fast)
|
||||
|
||||
// Should return the current active buffer.
|
||||
// 0 for the screen, or if there is no screen.
|
||||
// The result should be stored in x.
|
||||
NN_GPU_GETACTIVEBUFFER,
|
||||
// Switches the active buffer to a new one, stored in x.
|
||||
NN_GPU_SETACTIVEBUFFER,
|
||||
// Gets a buffer by index in an imaginary list containing all of them.
|
||||
// The index is in x, the buffer is output in y.
|
||||
// If y is 0, the sequence is assumed to end.
|
||||
NN_GPU_BUFFERS,
|
||||
// Allocates a buffer.
|
||||
// The buffer sizes are in width and height, with 0 x 0 meaning max resolution (default).
|
||||
// This consumes exactly width * height VRAM.
|
||||
// The new buffer should be put in x.
|
||||
// If there was not enough VRAM for this, x can be set to 0.
|
||||
NN_GPU_ALLOCBUFFER,
|
||||
// Frees a buffer.
|
||||
// The buffer is stored in x.
|
||||
// This releases the same VRAM that the buffer consumed when allocated.
|
||||
NN_GPU_FREEBUFFER,
|
||||
// Frees all buffers. The free VRAM should be equal to the total VRAM after this.
|
||||
NN_GPU_FREEBUFFERS,
|
||||
// Gets memory info about the GPU.
|
||||
// x should be set to the amount of free VRAM available.
|
||||
NN_GPU_FREEMEM,
|
||||
// Gets the size of a buffer, stored in x.
|
||||
// The size should be stored in width and height.
|
||||
NN_GPU_GETBUFFERSIZE,
|
||||
// Copy a region between buffers or between the screen and buffers.
|
||||
// The destination buffer is stored in dest. If 0, it refers to the screen.
|
||||
// The source buffer is stored in src. If 0, it refers to the screen.
|
||||
// x, y, width and height define the source rectangle, in the same way as in fill, to copy from the source buffer.
|
||||
// tx, ty refer to the top-left corner for the destination rectangle, in the destination buffer. It has the same width
|
||||
// and height as the source rectangle.
|
||||
// Screen-to-screen copies are illegal and checked, no need to worry about handling them.
|
||||
NN_GPU_BITBLT,
|
||||
} nn_GPUAction;
|
||||
|
||||
typedef struct nn_GPURequest {
|
||||
void *userdata;
|
||||
void *instance;
|
||||
nn_Computer *computer;
|
||||
struct nn_GPU *gpuConf;
|
||||
nn_GPUAction action;
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
union {
|
||||
struct {
|
||||
int tx;
|
||||
int ty;
|
||||
};
|
||||
nn_codepoint codepoint;
|
||||
char *text;
|
||||
};
|
||||
int dest;
|
||||
int src;
|
||||
} nn_GPURequest;
|
||||
// GPU class
|
||||
|
||||
typedef struct nn_GPU {
|
||||
// the minimum between these and the screen's
|
||||
@@ -1485,13 +1073,9 @@ typedef struct nn_GPU {
|
||||
double energyPerClear;
|
||||
} nn_GPU;
|
||||
|
||||
typedef nn_Exit nn_GPUHandler(nn_GPURequest *req);
|
||||
|
||||
// 1 GPU tier for every screen.
|
||||
extern const nn_GPU nn_defaultGPUs[4];
|
||||
|
||||
nn_ComponentState *nn_createGPU(nn_Universe *universe, const nn_GPU *gpu, nn_GPUHandler *handler, void *userdata);
|
||||
|
||||
// Colors and palettes.
|
||||
// Do note that the
|
||||
|
||||
@@ -1518,9 +1102,8 @@ void nn_initPalettes();
|
||||
int nn_mapColor(int color, int *palette, size_t len);
|
||||
// Expensive.
|
||||
// Maps a color within a given depth.
|
||||
// ocCompatible only matters for 4-bit, and determines whether to use the OC palette or the MC palette.
|
||||
// Invalid depths behave identically to 24-bit, in which case the color is left unchanged.
|
||||
int nn_mapDepth(int color, int depth, bool ocCompatible);
|
||||
int nn_mapDepth(int color, int depth);
|
||||
|
||||
// the name of a depth, if valid.
|
||||
// If invalid, NULL is returned, thus this can be used to check
|
||||
|
||||
Reference in New Issue
Block a user