progress on unicode lib

This commit is contained in:
IonutParau 2026-02-13 22:12:19 +01:00
parent 66b73ec006
commit a2ddcfa030
7 changed files with 1325 additions and 239 deletions

View File

@ -40,5 +40,5 @@ shell.setWorkingDirectory(os.getenv("HOME"))
local home_shrc = shell.resolve(".shrc")
if fs.exists(home_shrc) then
loadfile(shell.resolve("source", "lua"))(home_shrc)
assert(loadfile(shell.resolve("source", "lua")))(home_shrc)
end

View File

@ -14,10 +14,12 @@ do
end
while true do
debug.print("try shell")
local result, reason = xpcall(require("shell").getShell(), function(msg)
return tostring(msg).."\n"..debug.traceback()
end)
if not result then
debug.print(reason)
io.stderr:write((reason ~= nil and tostring(reason) or "unknown error") .. "\n")
io.write("Press any key to continue.\n")
os.sleep(0.5)

View File

@ -409,6 +409,81 @@ static int luaArch_component_fields(lua_State *L) {
return 1;
}
static int luaArch_unicode_char(lua_State *L) {
size_t argc = lua_gettop(L);
size_t len = 0;
for(int i = 1; i <= argc; i++) {
nn_codepoint codepoint = lua_tointeger(L, i);
size_t size = nn_unicode_codepointSize(codepoint);
if(size == 0) luaL_error(L, "codepoint #%d out of range", i);
len += size;
}
char *buf = malloc(len);
len = 0;
for(int i = 1; i <= argc; i++) {
nn_codepoint codepoint = lua_tointeger(L, i);
size_t size = nn_unicode_codepointToChar(buf + len, codepoint);
len += size;
}
lua_pushlstring(L, buf, len);
free(buf);
return 1;
}
static int luaArch_unicode_len(lua_State *L) {
size_t len;
const char *s = lua_tolstring(L, 1, &len);
len = nn_unicode_lenPermissive(s, len);
lua_pushinteger(L, len);
return 1;
}
static int luaArch_unicode_sub(lua_State *L) {
size_t slen;
const char *s = lua_tolstring(L, 1, &slen);
if(lua_gettop(L) < 2) lua_pushinteger(L, 1);
if(lua_gettop(L) < 3) lua_pushinteger(L, -1);
size_t len = nn_unicode_lenPermissive(s, slen);
int start = lua_tointeger(L, 2);
int end = lua_tointeger(L, 3);
if(end == 0) {
lua_pushstring(L, "");
return 1;
}
if(start > 0) start--;
if(start < 0) start = len + start;
if(end > 0) end--;
if(end < 0) end = len + end;
if(start < 0) start = 0;
if(start >= len) start = len-1;
if(end < 0) end = 0;
if(end >= len) end = len-1;
nn_codepoint *cp = malloc(sizeof(*cp) * len);
nn_unicode_codepointsPermissive(s, slen, cp);
size_t substrlen = nn_unicode_countBytes(cp + start, end - start + 1);
char *buf = malloc(substrlen);
nn_unicode_writeBytes(buf, cp + start, end - start + 1);
lua_pushlstring(L, buf, substrlen);
free(buf);
free(cp);
return 1;
}
static int luaArch_unicode_wlen(lua_State *L) {
size_t slen;
const char *s = lua_tolstring(L, 1, &slen);
size_t len = nn_unicode_wlenPermissive(s, slen);
lua_pushinteger(L, len);
return 1;
}
static void luaArch_loadEnv(lua_State *L) {
lua_createtable(L, 0, 10);
int computer = lua_gettop(L);
@ -464,6 +539,17 @@ static void luaArch_loadEnv(lua_State *L) {
lua_pushcfunction(L, luaArch_component_fields);
lua_setfield(L, component, "fields");
lua_setglobal(L, "component");
lua_createtable(L, 0, 10);
int unicode = lua_gettop(L);
lua_pushcfunction(L, luaArch_unicode_char);
lua_setfield(L, component, "char");
lua_pushcfunction(L, luaArch_unicode_len);
lua_setfield(L, component, "len");
lua_pushcfunction(L, luaArch_unicode_sub);
lua_setfield(L, component, "sub");
lua_pushcfunction(L, luaArch_unicode_wlen);
lua_setfield(L, component, "wlen");
lua_setglobal(L, "unicode");
}
static nn_Exit luaArch_handler(nn_ArchitectureRequest *req) {

View File

@ -163,9 +163,6 @@ function checkArg(arg, val, ...)
error("bad argument #" .. arg .. " (" .. table.concat(t, ", ") .. ") expected", 2)
end
-- HORRENDOUS approximation
unicode = string
if os.getenv("NN_REPL") == "1" then
while true do
io.write("lua> ")

View File

@ -9,6 +9,7 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <raylib.h>
nn_Architecture getLuaArch();
@ -292,6 +293,7 @@ typedef struct ne_ScreenBuffer {
int maxHeight;
int width;
int height;
char depth;
char maxDepth;
ne_Pixel *pixels;
int maxPalette;
@ -301,13 +303,39 @@ typedef struct ne_ScreenBuffer {
const char *keyboard;
} ne_ScreenBuffer;
bool ne_ocCompatibleColors = true;
void ne_remapScreen(ne_ScreenBuffer *buf) {
int depth = buf->maxDepth;
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[virtfg];
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->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);
@ -315,26 +343,73 @@ ne_ScreenBuffer *ne_newScreenBuf(nn_ScreenConfig conf, const char *keyboard) {
buf->keyboard = keyboard;
int *palette = NULL;
// TODO: uncomment once the palettes are implemented
if(buf->maxPalette == 16) {
//palette = nn_mcpalette4;
if(buf->maxDepth == 4) {
palette = nn_mcpalette4;
}
if(buf->maxPalette == 256) {
//palette = nn_ocpalette8;
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:
free(buf->pixels);
free(buf->mappedPalette);
free(buf->virtualPalette);
free(buf);
// this'd require the GPU is dropped first...
// its best we just dont bother and free later
//ne_dropScreenBuf(buf);
return NN_OK;
case NN_SCR_GETASPECTRATIO:
req->w = 1;
@ -377,9 +452,181 @@ nn_Exit ne_screen_handler(nn_ScreenRequest *req) {
return NN_OK;
}
#define NE_MAX_VRAMBUF 16
typedef struct ne_GPUState {
ne_ScreenBuffer *screenBuf;
int currentFg;
int currentBg;
bool isFgPalette;
bool isBgPalette;
int freeMemory;
int activeBuffer;
int scrAddrLen;
char scrAddr[NN_MAX_ADDRESS];
ne_ScreenBuffer *vramBufs[NE_MAX_VRAMBUF];
} ne_GPUState;
ne_GPUState *ne_newGPU(nn_GPU gpu) {
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->freeMemory = gpu.totalVRAM;
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) {
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 = ne_gpu_currentBuffer(state);
if(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_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_GETRESOLUTION:
if(state->screenBuf == NULL) {
nn_setError(C, "no screen");
return NN_EBADCALL;
}
req->width = state->screenBuf->width;
req->height = state->screenBuf->height;
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;
for(int i = 0; i < req->width; i++) {
if(!ne_inScreenBuf(activeBuf, x, y)) break;
ne_Pixel p = {
.fg = state->currentFg,
.bg = state->currentBg,
.isFgPalette = state->isFgPalette,
.isBgPalette = state->isBgPalette,
.codepoint = req->text[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;
if(x < 1) x = 1;
if(y < 1) y = 1;
if(w >= activeBuf->width) w = activeBuf->width - 1;
if(h >= activeBuf->height) h = activeBuf->height - 1;
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);
}
}
return NN_OK;
}
return NN_OK;
}
Color ne_processColor(unsigned int color) {
color <<= 8;
color |= 0xFF;
return GetColor(color);
}
int main() {
nn_Context ctx;
nn_initContext(&ctx);
nn_initPalettes();
SetConfigFlags(FLAG_WINDOW_RESIZABLE);
InitWindow(800, 600, "NeoNucleus Test Emulator");
// create the universe
nn_Universe *u = nn_createUniverse(&ctx);
@ -409,7 +656,9 @@ int main() {
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_ComponentType *scrtype = nn_createScreen(u, ne_screen_handler, NULL);
nn_ComponentType *keytype = nn_createKeyboard(u);
nn_ComponentType *gputype = nn_createGPU(u, &nn_defaultGPUs[3], ne_gpu_handler, NULL);
nn_Computer *c = nn_createComputer(u, NULL, "computer0", 8 * NN_MiB, 256, 256);
@ -422,35 +671,81 @@ int main() {
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, keytype, "mainKB", 4, NULL);
ne_ScreenBuffer *scrbuf = ne_newScreenBuf(nn_defaultScreens[1], "mainKB");
nn_addComponent(c, scrtype, "mainScreen", -1, scrbuf);
ne_GPUState *gpu = ne_newGPU(nn_defaultGPUs[3]);
nn_addComponent(c, gputype, "mainGPU", 3, gpu);
SetExitKey(KEY_NULL);
Font font = LoadFont("unscii-16-full.ttf");
double tickDelay = 0.05;
double tickClock = 0;
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;
}
if(WindowShouldClose()) break;
nn_ComputerState state = nn_getComputerState(c);
if(state == NN_POWEROFF) break;
if(state == NN_CRASHED) {
printf("error: %s\n", nn_getError(c));
goto cleanup;
}
BeginDrawing();
ClearBackground(BLACK);
if(state == NN_CHARCH) {
printf("new arch: %s\n", nn_getDesiredArchitecture(c).name);
goto cleanup;
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 - 5, fgColor);
}
}
if(state == NN_BLACKOUT) {
printf("out of energy\n");
goto cleanup;
}
if(state == NN_RESTART) {
printf("restart requested\n");
goto cleanup;
EndDrawing();
tickClock -= GetFrameTime();
if(tickClock <= 0) {
tickClock = tickDelay;
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;
}
}
}
@ -459,8 +754,13 @@ cleanup:;
nn_destroyComponentType(ctype);
nn_destroyComponentType(etype);
nn_destroyComponentType(scrtype);
nn_destroyComponentType(keytype);
nn_destroyComponentType(gputype);
for(size_t i = 0; i < 5; i++) nn_destroyComponentType(fstype[i]);
ne_dropScreenBuf(scrbuf);
// rip the universe
nn_destroyUniverse(u);
UnloadFont(font);
CloseWindow();
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -95,35 +95,33 @@ extern "C" {
typedef unsigned int nn_codepoint;
// validates that a NULL-terminated string is valid UTF-8
bool nn_unicode_validate(const char *s);
bool nn_unicode_validate(const char *s, size_t len);
// validates only the *first* codepoint in the NULL-terminated string.
// This returns the length in bytes of the codepoint, with 0 meaning
// invalid.
size_t nn_unicode_validateFirstChar(const char *s);
size_t nn_unicode_validateFirstChar(const char *s, size_t len);
// returns the amount of unicode codepoints in the UTF-8 string.
// Undefined behavior for invalid UTF-8, make sure to validate it if needed.
size_t nn_unicode_len(const char *s);
size_t nn_unicode_len(const char *s, size_t len);
// returns the amount of unicode codepoints in the UTF-8 string.
// If s is invalid UTF-8, all invalid bytes are considered a 1-byte codepoint.
size_t nn_unicode_lenPermissive(const char *s);
size_t nn_unicode_lenPermissive(const char *s, size_t len);
// Writes the codepoints of s into codepoints.
// Undefined behavior for invalid UTF-8, make sure to validate it if needed.
// The codepoints buffer must be big enough to store the string, use nn_unicode_len()
// to get the required buffer length.
void nn_unicode_codepoints(const char *s, nn_codepoint *codepoints);
void nn_unicode_codepoints(const char *s, size_t len, nn_codepoint *codepoints);
// Writes the codepoints of s into codepoints.
// If s is invalid UTF-8, all invalid bytes are considered a 1-byte codepoint.
// The codepoints buffer must be big enough to store the string, use nn_unicode_lenPermissive()
// to get the required buffer length.
void nn_unicode_codepointsPermissive(const char *s, nn_codepoint *codepoints);
void nn_unicode_codepointsPermissive(const char *s, size_t len, nn_codepoint *codepoints);
// Returns the codepoint at a given byte offset in the string.
// If it is out of bounds, the behavior is undefined.
// If s is invalid UTF-8 at that offset, the behavior is undefined.
nn_codepoint nn_unicode_codepointAt(const char *s, size_t byteOffset);
// Returns the first codepoint from a UTF-8 string.
// If s is invalid UTF-8, the behavior is undefined.
nn_codepoint nn_unicode_firstCodepoint(const char *s);
// Returns the size, in bytes, required by UTF-8 for a codepoint.
size_t nn_unicode_codepointSize(nn_codepoint codepoint);
// Writes the UTF-8 bytes for a given codepoint into buffer.
@ -134,7 +132,18 @@ size_t nn_unicode_codepointToChar(char buffer[NN_MAXIMUM_UNICODE_BUFFER], nn_cod
size_t nn_unicode_charWidth(nn_codepoint codepoint);
// The width, on a screen, for an entire string.
// The behavior is undefined for
size_t nn_unicode_wlen(const char *s);
size_t nn_unicode_wlen(const char *s, size_t len);
size_t nn_unicode_wlenPermissive(const char *s, size_t len);
// Returns the amount of bytes needed to store the UTF-8 encoded text.
// The behavior on invalid codepoints is undefined.
size_t nn_unicode_countBytes(nn_codepoint *codepoints, size_t len);
// Writes the UTF-8 encoded text.
// DOES NOT WRITE A NULL TERMINATOR.
// s must be big enough to store the string, use nn_unicode_bytelen()
// to allocate the correct amount of space.
// The behavior on invalid codepoints is undefined.
void nn_unicode_writeBytes(char *s, nn_codepoint *codepoints, size_t len);
// Returns the uppercase version of the codepoint
nn_codepoint nn_unicode_upper(nn_codepoint codepoint);
@ -178,7 +187,7 @@ typedef struct nn_LockRequest {
// This is used for synchronization. OpenComputers achieves synchronization
// between the worker threads by sending them as requests to a central thread (indirect methods).
// In NeoNucleus, the function pointer is invoked on the calling thead. This technically makes all methods direct,
// however we consider methods to be indirect if they require synchronization of mutable state.
// however methods which are meant to be slow may become indirect, as indirect methods consume the entire call budget.
// Do note that locks are only used in "full" component implementations, such as the volatile storage devices.
// The interfaces do not do any automatic synchronization via locks, all synchronization is assumed
// to be handled in the implementer of the interface, because only you know how to best synchronize
@ -306,6 +315,9 @@ void nn_destroyComputer(nn_Computer *computer);
// get the userdata pointer
void *nn_getComputerUserdata(nn_Computer *computer);
const char *nn_getComputerAddress(nn_Computer *computer);
nn_Universe *nn_getComputerUniverse(nn_Computer *computer);
nn_Context *nn_getUniverseContext(nn_Universe *universe);
nn_Context *nn_getComputerContext(nn_Computer *computer);
// address is copied.
// It can be NULL if you wish to have no tmp address.
@ -411,7 +423,9 @@ void nn_removeDeviceInfo(nn_Computer *computer, const char *address);
const nn_DeviceInfo *nn_getDeviceInfo(nn_Computer *computer, size_t *len);
typedef enum nn_MethodFlags {
// calling will consume the entire call budget
NN_INDIRECT = 0,
// calling will only consume 1 call from the call budget
NN_DIRECT = (1<<0),
// this indicates this method wraps a *field*
// getter means calling it with no arguments will return the current value,
@ -552,6 +566,8 @@ nn_Exit nn_pushnull(nn_Computer *computer);
nn_Exit nn_pushbool(nn_Computer *computer, bool truthy);
// pushes a number on the call stack
nn_Exit nn_pushnumber(nn_Computer *computer, double num);
// casts [num] to a double and pushes it on the call stack
nn_Exit nn_pushinteger(nn_Computer *computer, intptr_t num);
// pushes a NULL-terminated string on the call stack. The string is copied, so you can free it afterwards without worry.
nn_Exit nn_pushstring(nn_Computer *computer, const char *str);
// pushes a string on the call stack. The string is copied, so you can free it afterwards without worry. The copy will have a NULL terminator inserted
@ -601,6 +617,9 @@ bool nn_isboolean(nn_Computer *computer, size_t idx);
// Returns whether the value at [idx] is a number.
// [idx] starts at 0.
bool nn_isnumber(nn_Computer *computer, size_t idx);
// Returns whether the value at [idx] is a number AND
// the number can safely be cast to an intptr_t.
bool nn_isinteger(nn_Computer *computer, size_t idx);
// Returns whether the value at [idx] is a string.
// [idx] starts at 0.
bool nn_isstring(nn_Computer *computer, size_t idx);
@ -614,12 +633,60 @@ bool nn_istable(nn_Computer *computer, size_t idx);
// For out of bounds indexes, "none" is returned.
const char *nn_typenameof(nn_Computer *computer, size_t idx);
// Argument helpers
// Returns true if the argument at that index is not null.
bool nn_checknull(nn_Computer *computer, size_t idx, const char *errMsg);
// Returns true if the argument at that index is not a boolean.
bool nn_checkboolean(nn_Computer *computer, size_t idx, const char *errMsg);
// Returns true if the argument at that index is not a number.
bool nn_checknumber(nn_Computer *computer, size_t idx, const char *errMsg);
// Returns true if the argument at that index is not an integer.
bool nn_checkinteger(nn_Computer *computer, size_t idx, const char *errMsg);
// Returns true if the argument at that index is not a string.
bool nn_checkstring(nn_Computer *computer, size_t idx, const char *errMsg);
// Returns true if the argument at that index is not userdata.
bool nn_checkuserdata(nn_Computer *computer, size_t idx, const char *errMsg);
// Returns true if the argument at that index is a table.
bool nn_checktable(nn_Computer *computer, size_t idx, const char *errMsg);
// Checks if idx is equal to the stack size.
// If it is, it will push a null.
nn_Exit nn_defaultnull(nn_Computer *computer, size_t idx);
// Checks if idx is equal to the stack size.
// If it is, it will push a boolean [value].
nn_Exit nn_defaultboolean(nn_Computer *computer, size_t idx, bool value);
// Checks if idx is equal to the stack size.
// If it is, it will push a number [num].
nn_Exit nn_defaultnumber(nn_Computer *computer, size_t idx, double num);
// Checks if idx is equal to the stack size.
// If it is, it will push an integer [num].
nn_Exit nn_defaultinteger(nn_Computer *computer, size_t idx, intptr_t num);
// Checks if idx is equal to the stack size.
// If it is, it will push a string [str].
nn_Exit nn_defaultstring(nn_Computer *computer, size_t idx, const char *str);
// Checks if idx is equal to the stack size.
// If it is, it will push a string [str].
nn_Exit nn_defaultlstring(nn_Computer *computer, size_t idx, const char *str, size_t len);
// Checks if idx is equal to the stack size.
// If it is, it will push the userdata [userdataIdx].
nn_Exit nn_defaultuserdata(nn_Computer *computer, size_t idx, size_t userdataIdx);
// Checks if idx is equal to the stack size.
// If it is, it will push an empty table.
nn_Exit nn_defaulttable(nn_Computer *computer, size_t idx);
// NOTE: behavior of the nn_to*() functions and nn_dumptable() when the values have the wrong types or at out of bounds indexes is undefined.
// Returns the boolean value at [idx].
bool nn_toboolean(nn_Computer *computer, size_t idx);
// Returns the number value at [idx].
double nn_tonumber(nn_Computer *computer, size_t idx);
// Returns the number value at [idx] cast to an intptr_t.
// NOTE: for numbers where nn_isinteger() returns false,
// the cast is undefined.
// This includes values such as infinity and NaN, where
// the behavior is platform, ABI and compiler-specific.
intptr_t nn_tointeger(nn_Computer *computer, size_t idx);
// Returns the string value at [idx].
const char *nn_tostring(nn_Computer *computer, size_t idx);
// Returns the string value and its length at [idx].
@ -953,6 +1020,8 @@ typedef enum nn_ScreenFeatures {
NN_SCRF_PRECISE = 1<<1,
// Whether touch inverted is supported.
NN_SCRF_TOUCHINVERTED = 1<<2,
// it indicates that the palette can be edited.
NN_SCRF_EDITABLECOLORS = 1<<3,
} nn_ScreenFeatures;
// A struct for the reference screen configurations
@ -963,7 +1032,6 @@ typedef struct nn_ScreenConfig {
int maxWidth;
int maxHeight;
nn_ScreenFeatures features;
int editableColors;
int paletteColors;
char maxDepth;
} nn_ScreenConfig;
@ -973,7 +1041,9 @@ extern nn_ScreenConfig nn_defaultScreens[4];
typedef nn_Exit nn_ScreenHandler(nn_ScreenRequest *req);
nn_ComponentType *nn_createScreen(nn_Universe *universe, void *userdata, nn_ScreenHandler *handler);
nn_ComponentType *nn_createScreen(nn_Universe *universe, nn_ScreenHandler *handler, void *userdata);
// a useless component which does nothing
nn_ComponentType *nn_createKeyboard(nn_Universe *universe);
// Remember:
// - Colors are in 0xRRGGBB format.
@ -987,9 +1057,11 @@ typedef enum nn_GPUAction {
// Conventional GPU functions
// requests to bind to a screen connected to the computer.
// The address, as well as its length, are stored in text, with the length in width.
// 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.
@ -1007,6 +1079,7 @@ typedef enum nn_GPUAction {
// 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.
@ -1015,6 +1088,7 @@ typedef enum nn_GPUAction {
// 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.
@ -1054,6 +1128,12 @@ typedef enum nn_GPUAction {
// 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.
@ -1120,6 +1200,7 @@ typedef struct nn_GPURequest {
void *userdata;
void *instance;
nn_Computer *computer;
struct nn_GPU *gpuConf;
nn_GPUAction action;
int x;
int y;
@ -1161,9 +1242,13 @@ typedef struct nn_GPU {
double energyPerClear;
} nn_GPU;
typedef nn_Exit nn_GPUHandler(nn_GPURequest *req);
// 1 GPU tier for every screen.
extern nn_GPU nn_defaultGPUs[4];
nn_ComponentType *nn_createGPU(nn_Universe *universe, const nn_GPU *gpu, nn_GPUHandler *handler, void *userdata);
// Colors and palettes.
// Do note that the
@ -1182,6 +1267,9 @@ extern int nn_mcpalette4[16];
// The OC 8-bit palette.
extern int nn_ocpalette8[256];
// initializes the contents of the palettes.
void nn_initPalettes();
// Expensive.
// Maps a color to the closest match in a palette.
int nn_mapColor(int color, int *palette, size_t len);