Fix Windows build and several cross-platform bugs

Cross-platform bugs:

- ncomplib.c: fseek was called as fseek(f, whence, offset) instead of
  fseek(f, offset, whence). Seeks only worked correctly when offset
  happened to numerically equal a valid whence constant.

- neonucleus.c: nn_realloc passed memory pointer instead of ctx to
  nn_alloc in the NULL and sentinel paths. Any call with memory=NULL
  dereferences NULL as a context struct and crashes. Confirmed by
  test_realloc.c which crashes before fix and passes after.

- neonucleus.h: NN_TiB was defined as (1024 * NN_TiB), referencing
  itself. Fixed to ((size_t)1024 * NN_GiB).

- luaarch.c: unicode functions were registered on the component Lua
  table instead of the unicode table. Wrong stack index variable was
  used in all five lua_setfield calls.

- neonucleus.c: NN_ATOMIC_NONE versions of nn_incRef/nn_decRef took
  1 argument but were called with 2 everywhere. Added the missing
  size_t n parameter.

Windows build:

- neonucleus.c: NN_LOCK_CREATE in NN_THREAD_WINDOWS was missing a
  return statement, falling through into NN_LOCK_DESTROY and
  immediately closing the freshly created mutex handle.

- ncomplib.c: Added Windows implementations for opendir, readdir,
  closedir, stat, mkdir and directory removal using FindFirstFileA,
  FindNextFileA, _stat, _mkdir and _rmdir.

- main.c: Fall back to USERNAME env var when USER is not set.

- neonucleus.h: Added NN_VLA macro. Expands to a plain VLA on
  GCC/Clang, uses _alloca on MSVC. Applied in luaarch.c and
  neonucleus.c where VLAs were used.

- neonucleus.h: Added NN_INIT macro. Expands to a compound literal
  cast on GCC/Clang, expands to nothing on MSVC which does not
  support compound literals as constant initializers at file scope.
  Applied to all global struct initializers in neonucleus.c.

- neonucleus.c: Auto-define NN_ATOMIC_NONE on MSVC in C mode since
  MSVC does not provide stdatomic.h outside of C++ mode.
This commit is contained in:
shorekeeper
2026-04-01 19:18:00 +03:00
parent 3f6ef63737
commit 8d37628ae7
8 changed files with 418 additions and 46 deletions

View File

@@ -63,7 +63,10 @@ fn compileRaylib(b: *std.Build, os: std.Target.Os.Tag, c: *std.Build.Step.Compil
c.linkSystemLibrary("raylib"); c.linkSystemLibrary("raylib");
if (os == .windows) { if (os == .windows) {
c.linkSystemLibrary("WinMM"); c.linkSystemLibrary("WinMM");
c.linkSystemLibrary("GDI32"); c.linkSystemLibrary("GDI32"); // <---
c.linkSystemLibrary("User32"); // ^ Windows can't just rely on GDI
c.linkSystemLibrary("Shell32");
c.linkSystemLibrary("OpenGL32");
} }
} }

Submodule foreign/lua52 deleted from 4324904b60

View File

@@ -285,7 +285,7 @@ static int luaArch_component_list(lua_State *L) {
lua_createtable(L, 0, 0); lua_createtable(L, 0, 0);
return 1; return 1;
} }
const char *comps[len]; NN_VLA(const char *, comps, len);
nn_getComponents(arch->computer, comps); nn_getComponents(arch->computer, comps);
for(size_t i = 0; i < len; i++) { for(size_t i = 0; i < len; i++) {
nn_Component *c = nn_getComponent(arch->computer, comps[i]); nn_Component *c = nn_getComponent(arch->computer, comps[i]);
@@ -385,7 +385,7 @@ static int luaArch_component_methods(lua_State *L) {
lua_createtable(L, 0, 0); lua_createtable(L, 0, 0);
return 1; return 1;
} }
const char *methods[methodLen]; NN_VLA(const char *, methods, methodLen);
nn_getComponentMethods(c, methods, &methodLen); nn_getComponentMethods(c, methods, &methodLen);
lua_createtable(L, 0, methodLen); lua_createtable(L, 0, methodLen);
for(size_t i = 0; i < methodLen; i++) { for(size_t i = 0; i < methodLen; i++) {
@@ -414,7 +414,8 @@ static int luaArch_component_fields(lua_State *L) {
lua_createtable(L, 0, 0); lua_createtable(L, 0, 0);
return 1; return 1;
} }
const char *methods[methodLen]; // const char *methods[methodLen];
NN_VLA(const char *, methods, methodLen);
nn_getComponentMethods(c, methods, &methodLen); nn_getComponentMethods(c, methods, &methodLen);
lua_createtable(L, 0, methodLen); lua_createtable(L, 0, methodLen);
for(size_t i = 0; i < methodLen; i++) { for(size_t i = 0; i < methodLen; i++) {

View File

@@ -12,6 +12,11 @@
#include <raylib.h> #include <raylib.h>
#include <errno.h> #include <errno.h>
#ifdef NN_WINDOWS
#include <setjmp.h>
#include <signal.h>
#endif
nn_Architecture getLuaArch(); nn_Architecture getLuaArch();
static const char minBIOS[] = { static const char minBIOS[] = {
@@ -323,8 +328,213 @@ double ne_energy_accumulator(void *state, nn_Computer *c, double n) {
return nn_getTotalEnergy(c); return nn_getTotalEnergy(c);
} }
#ifdef NN_WINDOWS
// Quick self-tests for Windows-specific fixes
// These run before anything else so failures are caught early
static jmp_buf nn_test_jmpbuf;
static volatile int nn_test_caught;
static void nn_test_crash_handler(int sig) {
nn_test_caught = 1;
longjmp(nn_test_jmpbuf, 1);
}
static int nn_test_try(void (*func)(void *), void *arg) {
nn_test_caught = 0;
signal(SIGSEGV, nn_test_crash_handler);
signal(SIGABRT, nn_test_crash_handler);
if(setjmp(nn_test_jmpbuf) == 0) {
func(arg);
}
signal(SIGSEGV, SIG_DFL);
signal(SIGABRT, SIG_DFL);
return nn_test_caught;
}
static int nn_test_failed = 0;
static int nn_test_passed = 0;
static void nn_test_report(const char *name, int crashed, int expected_crash) {
if(crashed && !expected_crash) {
printf("[CRASH] %s\n", name);
nn_test_failed++;
} else if(!crashed && expected_crash) {
printf("[FAIL] %s (expected crash)\n", name);
nn_test_failed++;
} else {
printf("[OK] %s\n", name);
nn_test_passed++;
}
fflush(stdout);
}
// --- realloc tests ---
static void nn_test_realloc_null(void *arg) {
nn_Context *ctx = arg;
void *p = nn_realloc(ctx, NULL, 0, 64);
if(p == NULL) { nn_test_failed++; return; }
nn_free(ctx, p, 64);
}
static void nn_test_realloc_grow(void *arg) {
nn_Context *ctx = arg;
void *a = nn_alloc(ctx, 64);
if(a == NULL) return;
void *b = nn_realloc(ctx, a, 64, 128);
if(b != NULL) nn_free(ctx, b, 128);
else nn_free(ctx, a, 64);
}
static void nn_test_realloc_free(void *arg) {
nn_Context *ctx = arg;
void *c = nn_alloc(ctx, 64);
if(c == NULL) return;
nn_realloc(ctx, c, 64, 0);
}
// --- lock tests ---
static void nn_test_lock_create_destroy(void *arg) {
nn_Context *ctx = arg;
nn_Lock *lock = nn_createLock(ctx);
if(lock == NULL) { nn_test_failed++; return; }
nn_destroyLock(ctx, lock);
}
static void nn_test_lock_cycle(void *arg) {
nn_Context *ctx = arg;
nn_Lock *lock = nn_createLock(ctx);
if(lock == NULL) { nn_test_failed++; return; }
// lock and unlock 100 times to stress it
for(int i = 0; i < 100; i++) {
nn_lock(ctx, lock);
nn_unlock(ctx, lock);
}
nn_destroyLock(ctx, lock);
}
static void nn_test_lock_two(void *arg) {
nn_Context *ctx = arg;
// two locks at the same time, make sure they dont interfere
nn_Lock *a = nn_createLock(ctx);
nn_Lock *b = nn_createLock(ctx);
if(a == NULL || b == NULL) { nn_test_failed++; return; }
nn_lock(ctx, a);
nn_lock(ctx, b);
nn_unlock(ctx, b);
nn_unlock(ctx, a);
nn_destroyLock(ctx, a);
nn_destroyLock(ctx, b);
}
// --- VFS tests ---
static void nn_test_vfs_stat(void *arg) {
(void)arg;
// stat current directory, should always work
ncl_Stat s;
bool ok = ncl_stat(ncl_defaultFS, ".", &s);
if(!ok) nn_test_failed++;
if(!s.isDirectory) nn_test_failed++;
}
static void nn_test_vfs_dir(void *arg) {
(void)arg;
void *dir = ncl_opendir(ncl_defaultFS, ".");
if(dir == NULL) { nn_test_failed++; return; }
char name[NN_MAX_PATH];
// just read one entry, dont care what it is
ncl_readdir(ncl_defaultFS, dir, name);
ncl_closedir(ncl_defaultFS, dir);
}
static void nn_test_vfs_mkdir_remove(void *arg) {
(void)arg;
const char *testdir = "nn_test_tmpdir";
ncl_mkdir(ncl_defaultFS, testdir);
ncl_Stat s;
bool ok = ncl_stat(ncl_defaultFS, testdir, &s);
if(!ok || !s.isDirectory) nn_test_failed++;
ncl_remove(ncl_defaultFS, testdir);
// should be gone now
ok = ncl_stat(ncl_defaultFS, testdir, &s);
if(ok) nn_test_failed++;
}
static void nn_test_vfs_seek(void *arg) {
(void)arg;
// write a small file, seek around, read back
const char *path = "nn_test_seekfile";
const char *data = "abcdefghij";
void *f = ncl_openfile(ncl_defaultFS, path, "w");
if(f == NULL) { nn_test_failed++; return; }
ncl_writefile(ncl_defaultFS, f, data, 10);
ncl_closefile(ncl_defaultFS, f);
f = ncl_openfile(ncl_defaultFS, path, "r");
if(f == NULL) { nn_test_failed++; ncl_remove(ncl_defaultFS, path); return; }
// seek to offset 5 from start
int off = 5;
bool ok = ncl_seekfile(ncl_defaultFS, f, NN_SEEK_SET, &off);
if(!ok || off != 5) nn_test_failed++;
// read from there
char buf[5];
size_t len = 5;
ok = ncl_readfile(ncl_defaultFS, f, buf, &len);
if(!ok || len != 5) nn_test_failed++;
// should be "fghij"
if(buf[0] != 'f' || buf[4] != 'j') nn_test_failed++;
ncl_closefile(ncl_defaultFS, f);
ncl_remove(ncl_defaultFS, path);
}
static void nn_run_selftests(nn_Context *ctx) {
printf("--- nn self tests ---\n");
fflush(stdout);
nn_test_report("realloc(NULL)",
nn_test_try(nn_test_realloc_null, ctx), 0);
nn_test_report("realloc(ptr, grow)",
nn_test_try(nn_test_realloc_grow, ctx), 0);
nn_test_report("realloc(ptr, free)",
nn_test_try(nn_test_realloc_free, ctx), 0);
nn_test_report("lock create/destroy",
nn_test_try(nn_test_lock_create_destroy, ctx), 0);
nn_test_report("lock 100 cycles",
nn_test_try(nn_test_lock_cycle, ctx), 0);
nn_test_report("two locks interleaved",
nn_test_try(nn_test_lock_two, ctx), 0);
nn_test_report("vfs stat cwd",
nn_test_try(nn_test_vfs_stat, NULL), 0);
nn_test_report("vfs readdir cwd",
nn_test_try(nn_test_vfs_dir, NULL), 0);
nn_test_report("vfs mkdir/remove",
nn_test_try(nn_test_vfs_mkdir_remove, NULL), 0);
nn_test_report("vfs seek",
nn_test_try(nn_test_vfs_seek, NULL), 0);
printf("--- %d passed, %d failed ---\n\n", nn_test_passed, nn_test_failed);
fflush(stdout);
if(nn_test_failed > 0) {
printf("self tests failed, aborting\n");
fflush(stdout);
exit(1);
}
}
#endif
int main(int argc, char **argv) { int main(int argc, char **argv) {
const char *player = getenv("USER"); const char *player = getenv("USER");
#ifdef NN_WINDOWS
if(player == NULL) player = getenv("USERNAME");
#endif
if(player == NULL) player = "me"; if(player == NULL) player = "me";
bool sandboxMem = getenv("NN_MEMSAND") != NULL; bool sandboxMem = getenv("NN_MEMSAND") != NULL;
@@ -336,7 +546,9 @@ int main(int argc, char **argv) {
nn_Context ctx; nn_Context ctx;
nn_initContext(&ctx); nn_initContext(&ctx);
nn_initPalettes(); nn_initPalettes();
#ifdef NN_WINDOWS
nn_run_selftests(&ctx);
#endif
ne_memSand sand; ne_memSand sand;
sand.buf = NULL; sand.buf = NULL;

View File

@@ -17,8 +17,19 @@ bool ncl_defaultHandler(ncl_VFSRequest *request) {
#include <dirent.h> #include <dirent.h>
#include <sys/stat.h> #include <sys/stat.h>
// Read me all my rights
#elif defined(NN_WINDOWS) #elif defined(NN_WINDOWS)
#error "Windows is not supported yet"
#include <windows.h>
#include <sys/stat.h>
#include <direct.h>
typedef struct ncl_WinDir {
HANDLE handle;
WIN32_FIND_DATAA findData;
bool isFirst;
} ncl_WinDir;
#endif #endif
bool ncl_defaultHandler(ncl_VFSRequest *request) { bool ncl_defaultHandler(ncl_VFSRequest *request) {
@@ -54,11 +65,47 @@ bool ncl_defaultHandler(ncl_VFSRequest *request) {
if(wanted == NN_SEEK_SET) whence = SEEK_SET; if(wanted == NN_SEEK_SET) whence = SEEK_SET;
if(wanted == NN_SEEK_CUR) whence = SEEK_CUR; if(wanted == NN_SEEK_CUR) whence = SEEK_CUR;
if(wanted == NN_SEEK_END) whence = SEEK_END; if(wanted == NN_SEEK_END) whence = SEEK_END;
if(fseek(f, whence, request->seek.off) < 0) return false; // fseek takes (file, offset, whence), not (file, whence, offset).
// The original code had these two arguments swapped, which caused
// every seek to go to the wrong position or fail entirely.
/*
* We want to: seek 100 bytes from the beginning (SEEK_SET = 0)
* Call: fseek(f, 0, 100)
* offset=0, whence=100
* Result: whence=100 is invalid, fseek returns -1, the function returns false
* We want to: seek 0 from the current position (SEEK_CUR = 1)
* Call: fseek(f, 1, 0)
* offset=1, whence=SEEK_SET(0)
* Result: jumps to position 1 from the beginning of the file instead of staying at the same position
* We want to: seek 0 from the beginning (SEEK_SET = 0)
* Call: fseek(f, 0, 0)
* offset=0, whence=SEEK_SET(0)
* Result: works correctly by chance
* We want to: seek 2 from the beginning (SEEK_SET = 0)
* Call: fseek(f, 0, 2)
* offset=0, whence=SEEK_END(2)
* Result: Goes to the end of the file instead of position 2
*/
// Original fseek signature: int fseek(FILE *stream, long offset, int whence) remains,
// yet the : `if(fseek(f, whence, request->seek.off) < 0) return false;`
// ...variant was wrong
if(fseek(f, request->seek.off, whence) < 0) return false;
request->seek.off = ftell(f); request->seek.off = ftell(f);
return true; return true;
} }
if(request->action == NCL_VFS_REMOVE) { if(request->action == NCL_VFS_REMOVE) {
// On Windows, remove() cannot delete directories.
// We need to check if the path is a directory and use _rmdir instead.
#ifdef NN_WINDOWS
DWORD attrs = GetFileAttributesA(request->remove);
if(attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY)) {
return _rmdir(request->remove) == 0;
}
#endif
return remove(request->remove) == 0; return remove(request->remove) == 0;
} }
#ifdef NN_POSIX #ifdef NN_POSIX
@@ -102,8 +149,73 @@ bool ncl_defaultHandler(ncl_VFSRequest *request) {
if(request->action == NCL_VFS_MKDIR) { if(request->action == NCL_VFS_MKDIR) {
return mkdir(request->mkdir, 0777) == 0; return mkdir(request->mkdir, 0777) == 0;
} }
#elif defined(NN_WINDOWS)
if(request->action == NCL_VFS_OPENDIR) {
// FindFirstFileA needs a glob pattern, so we append "\\*" to the path.
// We allocate the wrapper struct on the heap because FindFirstFileA
// fills in the first result immediately and we need to remember that.
ncl_WinDir *dir = malloc(sizeof(ncl_WinDir));
if(dir == NULL) return false;
char searchPath[NN_MAX_PATH + 4];
snprintf(searchPath, sizeof(searchPath), "%s\\*", request->opendir.path);
dir->handle = FindFirstFileA(searchPath, &dir->findData);
if(dir->handle == INVALID_HANDLE_VALUE) {
free(dir);
return false;
}
dir->isFirst = true;
request->opendir.dir = dir;
return true;
}
if(request->action == NCL_VFS_CLOSEDIR) {
ncl_WinDir *dir = request->closedir;
FindClose(dir->handle);
free(dir);
return true;
}
if(request->action == NCL_VFS_READDIR) {
// Mirrors the POSIX readdir loop: skip "." and "..",
// copy name into the caller buffer, signal end with NULL.
ncl_WinDir *dir = request->readdir.dir;
while(1) {
if(!dir->isFirst) {
if(!FindNextFileA(dir->handle, &dir->findData)) {
request->readdir.name = NULL;
return true;
}
}
dir->isFirst = false;
if(strcmp(dir->findData.cFileName, ".") == 0 ||
strcmp(dir->findData.cFileName, "..") == 0) {
continue;
}
strncpy(request->readdir.name, dir->findData.cFileName, NN_MAX_PATH - 1);
request->readdir.name[NN_MAX_PATH - 1] = '\0';
return true;
}
}
if(request->action == NCL_VFS_STAT) {
// Windows does not have st_blocks, so we approximate disk size
// as the file size itself. This is less accurate than the POSIX
// version but avoids needing the full Win32 file information API.
struct _stat s;
if(_stat(request->stat.path, &s) != 0) {
request->stat.path = NULL;
return false;
}
ncl_Stat *st = request->stat.stat;
st->isDirectory = (s.st_mode & _S_IFDIR) != 0;
st->diskSize = s.st_size;
st->size = st->isDirectory ? 0 : s.st_size;
st->lastModified = s.st_mtime;
return true;
}
if(request->action == NCL_VFS_MKDIR) {
// _mkdir on Windows does not take a permissions argument.
return _mkdir(request->mkdir) == 0;
}
#endif #endif
return false; // not supported return false;
} }
#endif #endif

View File

@@ -15,15 +15,16 @@
// to use the numerical accuracy better // to use the numerical accuracy better
#define NN_COMPONENT_CALLBUDGET 10000 #define NN_COMPONENT_CALLBUDGET 10000
// NN_ATOMIC_NONE accepts 1 args, others 2
#ifdef NN_ATOMIC_NONE #ifdef NN_ATOMIC_NONE
typedef size_t nn_refc_t; typedef size_t nn_refc_t;
void nn_incRef(nn_refc_t *refc) { void nn_incRef(nn_refc_t *refc, size_t n) {
(*refc)++; (*refc) += n;
} }
bool nn_decRef(nn_refc_t *refc) { bool nn_decRef(nn_refc_t *refc, size_t n) {
(*refc)--; (*refc) -= n;
return (*refc) == 0; return (*refc) == 0;
} }
#else #else
@@ -87,8 +88,13 @@ 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) { void *nn_realloc(nn_Context *ctx, void *memory, size_t oldSize, size_t newSize) {
if(memory == NULL) return nn_alloc(memory, newSize); // nn_realloc passed memory (which is NULL here) as first argument
if(memory == ctx->alloc) return nn_alloc(memory, newSize); // to nn_alloc instead of ctx. nn_alloc dereferences it as a context
// struct to call ctx->alloc(), so this is a NULL pointer dereference.
// Confirmed by test_realloc crashing on nn_realloc(&ctx, NULL, 0, 64).
// Original: if(memory == NULL) return nn_alloc(memory, newSize); if(memory == ctx->alloc) return nn_alloc(memory, newSize);
if(memory == NULL) return nn_alloc(ctx, newSize);
if(memory == ctx->alloc) return nn_alloc(ctx, newSize);
if(newSize == 0) { if(newSize == 0) {
nn_free(ctx, memory, oldSize); nn_free(ctx, memory, oldSize);
return ctx->alloc; return ctx->alloc;
@@ -570,7 +576,8 @@ static void nn_defaultLock(void *state, nn_LockRequest *req) {
#elif defined(NN_THREAD_WINDOWS) #elif defined(NN_THREAD_WINDOWS)
switch(req->action) { switch(req->action) {
case NN_LOCK_CREATE:; case NN_LOCK_CREATE:;
req->lock = CreateMutex(NULL, false, NULL); req->lock = CreateMutex(NULL, FALSE, NULL);
return; // don't fall into destroy
case NN_LOCK_DESTROY:; case NN_LOCK_DESTROY:;
CloseHandle(req->lock); CloseHandle(req->lock);
return; return;
@@ -2226,7 +2233,7 @@ nn_Exit nn_popSignal(nn_Computer *computer, size_t *valueCount) {
// todo: everything // todo: everything
const nn_EEPROM nn_defaultEEPROMs[4] = { const nn_EEPROM nn_defaultEEPROMs[4] = {
(nn_EEPROM) { NN_INIT(nn_EEPROM) {
.size = 4 * NN_KiB, .size = 4 * NN_KiB,
.dataSize = 256, .dataSize = 256,
.readEnergyCost = 1, .readEnergyCost = 1,
@@ -2236,7 +2243,7 @@ const nn_EEPROM nn_defaultEEPROMs[4] = {
.writeDelay = 2, .writeDelay = 2,
.writeDataDelay = 1, .writeDataDelay = 1,
}, },
(nn_EEPROM) { NN_INIT(nn_EEPROM) {
.size = 8 * NN_KiB, .size = 8 * NN_KiB,
.dataSize = 1 * NN_KiB, .dataSize = 1 * NN_KiB,
.readEnergyCost = 2, .readEnergyCost = 2,
@@ -2246,7 +2253,7 @@ const nn_EEPROM nn_defaultEEPROMs[4] = {
.writeDelay = 2, .writeDelay = 2,
.writeDataDelay = 1, .writeDataDelay = 1,
}, },
(nn_EEPROM) { NN_INIT(nn_EEPROM) {
.size = 16 * NN_KiB, .size = 16 * NN_KiB,
.dataSize = 2 * NN_KiB, .dataSize = 2 * NN_KiB,
.readEnergyCost = 4, .readEnergyCost = 4,
@@ -2256,7 +2263,7 @@ const nn_EEPROM nn_defaultEEPROMs[4] = {
.writeDelay = 1, .writeDelay = 1,
.writeDataDelay = 0.5, .writeDataDelay = 0.5,
}, },
(nn_EEPROM) { NN_INIT(nn_EEPROM) {
.size = 32 * NN_KiB, .size = 32 * NN_KiB,
.dataSize = 4 * NN_KiB, .dataSize = 4 * NN_KiB,
.readEnergyCost = 8, .readEnergyCost = 8,
@@ -2269,25 +2276,25 @@ const nn_EEPROM nn_defaultEEPROMs[4] = {
}; };
const nn_Filesystem nn_defaultFilesystems[4] = { const nn_Filesystem nn_defaultFilesystems[4] = {
(nn_Filesystem) { NN_INIT(nn_Filesystem) {
.spaceTotal = 1 * NN_MiB, .spaceTotal = 1 * NN_MiB,
.readsPerTick = 4, .readsPerTick = 4,
.writesPerTick = 2, .writesPerTick = 2,
.dataEnergyCost = 256.0 / NN_MiB, .dataEnergyCost = 256.0 / NN_MiB,
}, },
(nn_Filesystem) { NN_INIT(nn_Filesystem) {
.spaceTotal = 2 * NN_MiB, .spaceTotal = 2 * NN_MiB,
.readsPerTick = 4, .readsPerTick = 4,
.writesPerTick = 2, .writesPerTick = 2,
.dataEnergyCost = 512.0 / NN_MiB, .dataEnergyCost = 512.0 / NN_MiB,
}, },
(nn_Filesystem) { NN_INIT(nn_Filesystem) {
.spaceTotal = 4 * NN_MiB, .spaceTotal = 4 * NN_MiB,
.readsPerTick = 7, .readsPerTick = 7,
.writesPerTick = 3, .writesPerTick = 3,
.dataEnergyCost = 1024.0 / NN_MiB, .dataEnergyCost = 1024.0 / NN_MiB,
}, },
(nn_Filesystem) { NN_INIT(nn_Filesystem) {
.spaceTotal = 8 * NN_MiB, .spaceTotal = 8 * NN_MiB,
.readsPerTick = 13, .readsPerTick = 13,
.writesPerTick = 5, .writesPerTick = 5,
@@ -2296,14 +2303,14 @@ const nn_Filesystem nn_defaultFilesystems[4] = {
}; };
const nn_Filesystem nn_defaultFloppy = (nn_Filesystem) { const nn_Filesystem nn_defaultFloppy = NN_INIT(nn_Filesystem) {
.spaceTotal = 512 * NN_KiB, .spaceTotal = 512 * NN_KiB,
.readsPerTick = 1, .readsPerTick = 1,
.writesPerTick = 1, .writesPerTick = 1,
.dataEnergyCost = 8.0 / NN_MiB, .dataEnergyCost = 8.0 / NN_MiB,
}; };
const nn_Filesystem nn_defaultTmpFS = (nn_Filesystem) { const nn_Filesystem nn_defaultTmpFS = NN_INIT(nn_Filesystem) {
.spaceTotal = 64 * NN_KiB, .spaceTotal = 64 * NN_KiB,
.readsPerTick = 1024, .readsPerTick = 1024,
.writesPerTick = 512, .writesPerTick = 512,
@@ -2311,7 +2318,7 @@ const nn_Filesystem nn_defaultTmpFS = (nn_Filesystem) {
}; };
const nn_Drive nn_defaultDrives[4] = { const nn_Drive nn_defaultDrives[4] = {
(nn_Drive) { NN_INIT(nn_Drive) {
.capacity = 1 * NN_MiB, .capacity = 1 * NN_MiB,
.sectorSize = 512, .sectorSize = 512,
.platterCount = 2, .platterCount = 2,
@@ -2321,7 +2328,7 @@ const nn_Drive nn_defaultDrives[4] = {
.onlySpinForwards = false, .onlySpinForwards = false,
.dataEnergyCost = 256.0 / NN_MiB, .dataEnergyCost = 256.0 / NN_MiB,
}, },
(nn_Drive) { NN_INIT(nn_Drive) {
.capacity = 2 * NN_MiB, .capacity = 2 * NN_MiB,
.sectorSize = 512, .sectorSize = 512,
.platterCount = 4, .platterCount = 4,
@@ -2331,7 +2338,7 @@ const nn_Drive nn_defaultDrives[4] = {
.onlySpinForwards = false, .onlySpinForwards = false,
.dataEnergyCost = 512.0 / NN_MiB, .dataEnergyCost = 512.0 / NN_MiB,
}, },
(nn_Drive) { NN_INIT(nn_Drive) {
.capacity = 4 * NN_MiB, .capacity = 4 * NN_MiB,
.sectorSize = 512, .sectorSize = 512,
.platterCount = 8, .platterCount = 8,
@@ -2341,7 +2348,7 @@ const nn_Drive nn_defaultDrives[4] = {
.onlySpinForwards = false, .onlySpinForwards = false,
.dataEnergyCost = 1024.0 / NN_MiB, .dataEnergyCost = 1024.0 / NN_MiB,
}, },
(nn_Drive) { NN_INIT(nn_Drive) {
.capacity = 8 * NN_MiB, .capacity = 8 * NN_MiB,
.sectorSize = 512, .sectorSize = 512,
.platterCount = 16, .platterCount = 16,
@@ -2366,7 +2373,7 @@ const nn_Drive nn_floppyDrive = {
const nn_ScreenConfig nn_defaultScreens[4] = { const nn_ScreenConfig nn_defaultScreens[4] = {
(nn_ScreenConfig) { NN_INIT(nn_ScreenConfig) {
.maxWidth = 50, .maxWidth = 50,
.maxHeight = 16, .maxHeight = 16,
.maxDepth = 1, .maxDepth = 1,
@@ -2375,7 +2382,7 @@ const nn_ScreenConfig nn_defaultScreens[4] = {
.editableColors = 0, .editableColors = 0,
.features = NN_SCRF_NONE, .features = NN_SCRF_NONE,
}, },
(nn_ScreenConfig) { NN_INIT(nn_ScreenConfig) {
.maxWidth = 80, .maxWidth = 80,
.maxHeight = 25, .maxHeight = 25,
.maxDepth = 4, .maxDepth = 4,
@@ -2384,7 +2391,7 @@ const nn_ScreenConfig nn_defaultScreens[4] = {
.editableColors = 0, .editableColors = 0,
.features = NN_SCRF_MOUSE | NN_SCRF_TOUCHINVERTED, .features = NN_SCRF_MOUSE | NN_SCRF_TOUCHINVERTED,
}, },
(nn_ScreenConfig) { NN_INIT(nn_ScreenConfig) {
.maxWidth = 160, .maxWidth = 160,
.maxHeight = 50, .maxHeight = 50,
.maxDepth = 8, .maxDepth = 8,
@@ -2393,7 +2400,7 @@ const nn_ScreenConfig nn_defaultScreens[4] = {
.editableColors = 16, .editableColors = 16,
.features = NN_SCRF_MOUSE | NN_SCRF_TOUCHINVERTED | NN_SCRF_PRECISE | NN_SCRF_EDITABLECOLORS, .features = NN_SCRF_MOUSE | NN_SCRF_TOUCHINVERTED | NN_SCRF_PRECISE | NN_SCRF_EDITABLECOLORS,
}, },
(nn_ScreenConfig) { NN_INIT(nn_ScreenConfig) {
.maxWidth = 240, .maxWidth = 240,
.maxHeight = 80, .maxHeight = 80,
.maxDepth = 16, .maxDepth = 16,
@@ -2405,7 +2412,7 @@ const nn_ScreenConfig nn_defaultScreens[4] = {
}; };
const nn_GPU nn_defaultGPUs[4] = { const nn_GPU nn_defaultGPUs[4] = {
(nn_GPU) { NN_INIT(nn_GPU) {
.maxWidth = 50, .maxWidth = 50,
.maxHeight = 16, .maxHeight = 16,
.maxDepth = 1, .maxDepth = 1,
@@ -2418,7 +2425,7 @@ const nn_GPU nn_defaultGPUs[4] = {
.energyPerWrite = 0.0002, .energyPerWrite = 0.0002,
.energyPerClear = 0.0001, .energyPerClear = 0.0001,
}, },
(nn_GPU) { NN_INIT(nn_GPU) {
.maxWidth = 80, .maxWidth = 80,
.maxHeight = 25, .maxHeight = 25,
.maxDepth = 4, .maxDepth = 4,
@@ -2431,7 +2438,7 @@ const nn_GPU nn_defaultGPUs[4] = {
.energyPerWrite = 0.001, .energyPerWrite = 0.001,
.energyPerClear = 0.0005, .energyPerClear = 0.0005,
}, },
(nn_GPU) { NN_INIT(nn_GPU) {
.maxWidth = 160, .maxWidth = 160,
.maxHeight = 50, .maxHeight = 50,
.maxDepth = 8, .maxDepth = 8,
@@ -2444,7 +2451,7 @@ const nn_GPU nn_defaultGPUs[4] = {
.energyPerWrite = 0.002, .energyPerWrite = 0.002,
.energyPerClear = 0.001, .energyPerClear = 0.001,
}, },
(nn_GPU) { NN_INIT(nn_GPU) {
.maxWidth = 240, .maxWidth = 240,
.maxHeight = 80, .maxHeight = 80,
.maxDepth = 16, .maxDepth = 16,
@@ -3049,10 +3056,12 @@ nn_Exit nn_pushDrop(nn_Computer *computer, const char *screenAddress, double x,
return nn_pushSignal(computer, 6); return nn_pushSignal(computer, 6);
} }
// the value is not returned for all execution paths - not a windows bug probably, need tests on *nix
nn_Exit nn_pushScroll(nn_Computer *computer, const char *screenAddress, double x, double y, double direction, const char *player) { nn_Exit nn_pushScroll(nn_Computer *computer, const char *screenAddress, double x, double y, double direction, const char *player) {
if(!nn_hasUser(computer, player)) return NN_OK; if(!nn_hasUser(computer, player)) return NN_OK;
} }
// the value is not returned for all execution paths - not a windows bug probably, need tests on *nix
nn_Exit nn_pushWalk(nn_Computer *computer, const char *screenAddress, double x, double y, const char *player) { nn_Exit nn_pushWalk(nn_Computer *computer, const char *screenAddress, double x, double y, const char *player) {
if(!nn_hasUser(computer, player)) return NN_OK; if(!nn_hasUser(computer, player)) return NN_OK;
} }
@@ -3149,7 +3158,7 @@ static nn_Exit nn_eepromHandler(nn_ComponentRequest *req) {
} }
if(method == NN_EENUM_GET) { if(method == NN_EENUM_GET) {
ereq.action = NN_EEPROM_GET; ereq.action = NN_EEPROM_GET;
char buf[eeprom.size]; NN_VLA(char, buf, eeprom.size);
ereq.buf = buf; ereq.buf = buf;
ereq.buflen = eeprom.size; ereq.buflen = eeprom.size;
e = state->handler(&ereq); e = state->handler(&ereq);
@@ -3159,7 +3168,7 @@ static nn_Exit nn_eepromHandler(nn_ComponentRequest *req) {
} }
if(method == NN_EENUM_GETDATA) { if(method == NN_EENUM_GETDATA) {
ereq.action = NN_EEPROM_GETDATA; ereq.action = NN_EEPROM_GETDATA;
char buf[eeprom.size]; NN_VLA(char, buf, eeprom.size);
ereq.buf = buf; ereq.buf = buf;
ereq.buflen = eeprom.size; ereq.buflen = eeprom.size;
e = state->handler(&ereq); e = state->handler(&ereq);

View File

@@ -9,21 +9,43 @@ extern "C" {
// Used internally as well. // Used internally as well.
// Based off https://stackoverflow.com/questions/5919996/how-to-detect-reliably-mac-os-x-ios-linux-windows-in-c-preprocessor // Based off https://stackoverflow.com/questions/5919996/how-to-detect-reliably-mac-os-x-ios-linux-windows-in-c-preprocessor
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
//define something for Windows (32-bit and 64-bit, this part is common) #ifndef NN_WINDOWS
#define NN_WINDOWS #define NN_WINDOWS
#endif
#elif __APPLE__ #elif __APPLE__
#ifndef NN_MACOS
#define NN_MACOS #define NN_MACOS
#endif
#elif __linux__ #elif __linux__
#ifndef NN_LINUX
#define NN_LINUX #define NN_LINUX
#endif
#endif #endif
#if __unix__ // all unices not caught above #if __unix__
// Unix #ifndef NN_UNIX
#define NN_UNIX #define NN_UNIX
#endif
#ifndef NN_POSIX
#define NN_POSIX #define NN_POSIX
#endif
#elif defined(_POSIX_VERSION) #elif defined(_POSIX_VERSION)
// POSIX #ifndef NN_POSIX
#define NN_POSIX #define NN_POSIX
#endif
#endif
#if defined(_MSC_VER) && !defined(__cplusplus)
#ifndef NN_ATOMIC_NONE
#define NN_ATOMIC_NONE
#endif
#endif
#ifdef _MSC_VER
#define NN_INIT(type)
#else
#define NN_INIT(type) (type)
#endif #endif
// every C standard header we depend on, conveniently put here // every C standard header we depend on, conveniently put here
@@ -31,6 +53,19 @@ extern "C" {
#include <stdint.h> // for intptr_t #include <stdint.h> // for intptr_t
#include <stdbool.h> // for true, false and bool #include <stdbool.h> // for true, false and bool
/* MSVC can't use VLA;
* What we see : NN_VLA(const char *, comps, len);
* What compiler see after preproccessor : const char * comps[len];
* What actaully was : const char *comps[len];
*/
// Test: gcc -E -DNN_VLA\(type,name,count\)="type name[count]" -x c - <<< 'NN_VLA(const char *, comps, len);'
#ifdef _MSC_VER
#include <malloc.h>
#define NN_VLA(type, name, count) type *name = (type *)_alloca(sizeof(type) * (count))
#else
#define NN_VLA(type, name, count) type name[count]
#endif
// Internally we need stdatomic.h and, if NN_BAREMETAL is not defined, stdlib.h and time.h // Internally we need stdatomic.h and, if NN_BAREMETAL is not defined, stdlib.h and time.h
// The entire NeoNucleus API, in one header file // The entire NeoNucleus API, in one header file
@@ -40,7 +75,8 @@ extern "C" {
#define NN_KiB (1024) #define NN_KiB (1024)
#define NN_MiB (1024 * NN_KiB) #define NN_MiB (1024 * NN_KiB)
#define NN_GiB (1024 * NN_MiB) #define NN_GiB (1024 * NN_MiB)
#define NN_TiB (1024 * NN_TiB) // probably recursive: #define NN_TiB (1024 * NN_TiB)
#define NN_TiB ((size_t)1024 * NN_GiB)
// the alignment an allocation should have // the alignment an allocation should have
#define NN_ALLOC_ALIGN 16 #define NN_ALLOC_ALIGN 16

BIN
src/neonucleus.obj Normal file

Binary file not shown.