diff --git a/build.zig b/build.zig index ac92f50..493f32f 100644 --- a/build.zig +++ b/build.zig @@ -63,7 +63,10 @@ fn compileRaylib(b: *std.Build, os: std.Target.Os.Tag, c: *std.Build.Step.Compil c.linkSystemLibrary("raylib"); if (os == .windows) { 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"); } } diff --git a/foreign/lua52 b/foreign/lua52 deleted file mode 160000 index 4324904..0000000 --- a/foreign/lua52 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4324904b60db5243ede68d0922c1bf3c0dd05986 diff --git a/src/luaarch.c b/src/luaarch.c index 0649a02..5bddfe7 100644 --- a/src/luaarch.c +++ b/src/luaarch.c @@ -285,7 +285,7 @@ static int luaArch_component_list(lua_State *L) { lua_createtable(L, 0, 0); return 1; } - const char *comps[len]; + NN_VLA(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]); @@ -385,7 +385,7 @@ static int luaArch_component_methods(lua_State *L) { lua_createtable(L, 0, 0); return 1; } - const char *methods[methodLen]; + NN_VLA(const char *, methods, methodLen); nn_getComponentMethods(c, methods, &methodLen); lua_createtable(L, 0, methodLen); 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); return 1; } - const char *methods[methodLen]; + // const char *methods[methodLen]; + NN_VLA(const char *, methods, methodLen); nn_getComponentMethods(c, methods, &methodLen); lua_createtable(L, 0, methodLen); for(size_t i = 0; i < methodLen; i++) { diff --git a/src/main.c b/src/main.c index 22e5478..94d7482 100644 --- a/src/main.c +++ b/src/main.c @@ -12,6 +12,11 @@ #include #include +#ifdef NN_WINDOWS +#include +#include +#endif + nn_Architecture getLuaArch(); static const char minBIOS[] = { @@ -323,8 +328,213 @@ double ne_energy_accumulator(void *state, nn_Computer *c, double n) { 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) { const char *player = getenv("USER"); +#ifdef NN_WINDOWS + if(player == NULL) player = getenv("USERNAME"); +#endif if(player == NULL) player = "me"; bool sandboxMem = getenv("NN_MEMSAND") != NULL; @@ -336,7 +546,9 @@ int main(int argc, char **argv) { nn_Context ctx; nn_initContext(&ctx); nn_initPalettes(); - +#ifdef NN_WINDOWS + nn_run_selftests(&ctx); +#endif ne_memSand sand; sand.buf = NULL; diff --git a/src/ncomplib.c b/src/ncomplib.c index 3f71a88..8f0ce2b 100644 --- a/src/ncomplib.c +++ b/src/ncomplib.c @@ -17,8 +17,19 @@ bool ncl_defaultHandler(ncl_VFSRequest *request) { #include #include +// Read me all my rights #elif defined(NN_WINDOWS) -#error "Windows is not supported yet" + +#include +#include +#include + +typedef struct ncl_WinDir { + HANDLE handle; + WIN32_FIND_DATAA findData; + bool isFirst; +} ncl_WinDir; + #endif 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_CUR) whence = SEEK_CUR; 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); return true; } 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; } #ifdef NN_POSIX @@ -102,8 +149,73 @@ bool ncl_defaultHandler(ncl_VFSRequest *request) { if(request->action == NCL_VFS_MKDIR) { 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 - return false; // not supported + return false; } #endif diff --git a/src/neonucleus.c b/src/neonucleus.c index 64633dc..e4ddcc0 100644 --- a/src/neonucleus.c +++ b/src/neonucleus.c @@ -15,15 +15,16 @@ // to use the numerical accuracy better #define NN_COMPONENT_CALLBUDGET 10000 +// NN_ATOMIC_NONE accepts 1 args, others 2 #ifdef NN_ATOMIC_NONE typedef size_t nn_refc_t; -void nn_incRef(nn_refc_t *refc) { - (*refc)++; +void nn_incRef(nn_refc_t *refc, size_t n) { + (*refc) += n; } -bool nn_decRef(nn_refc_t *refc) { - (*refc)--; +bool nn_decRef(nn_refc_t *refc, size_t n) { + (*refc) -= n; return (*refc) == 0; } #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) { - if(memory == NULL) return nn_alloc(memory, newSize); - if(memory == ctx->alloc) return nn_alloc(memory, newSize); + // nn_realloc passed memory (which is NULL here) as first argument + // 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) { nn_free(ctx, memory, oldSize); return ctx->alloc; @@ -570,7 +576,8 @@ static void nn_defaultLock(void *state, nn_LockRequest *req) { #elif defined(NN_THREAD_WINDOWS) switch(req->action) { 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:; CloseHandle(req->lock); return; @@ -2226,8 +2233,8 @@ nn_Exit nn_popSignal(nn_Computer *computer, size_t *valueCount) { // todo: everything const nn_EEPROM nn_defaultEEPROMs[4] = { - (nn_EEPROM) { - .size = 4 * NN_KiB, + NN_INIT(nn_EEPROM) { + .size = 4 * NN_KiB, .dataSize = 256, .readEnergyCost = 1, .writeEnergyCost = 100, @@ -2236,7 +2243,7 @@ const nn_EEPROM nn_defaultEEPROMs[4] = { .writeDelay = 2, .writeDataDelay = 1, }, - (nn_EEPROM) { + NN_INIT(nn_EEPROM) { .size = 8 * NN_KiB, .dataSize = 1 * NN_KiB, .readEnergyCost = 2, @@ -2246,7 +2253,7 @@ const nn_EEPROM nn_defaultEEPROMs[4] = { .writeDelay = 2, .writeDataDelay = 1, }, - (nn_EEPROM) { + NN_INIT(nn_EEPROM) { .size = 16 * NN_KiB, .dataSize = 2 * NN_KiB, .readEnergyCost = 4, @@ -2256,7 +2263,7 @@ const nn_EEPROM nn_defaultEEPROMs[4] = { .writeDelay = 1, .writeDataDelay = 0.5, }, - (nn_EEPROM) { + NN_INIT(nn_EEPROM) { .size = 32 * NN_KiB, .dataSize = 4 * NN_KiB, .readEnergyCost = 8, @@ -2269,25 +2276,25 @@ const nn_EEPROM nn_defaultEEPROMs[4] = { }; const nn_Filesystem nn_defaultFilesystems[4] = { - (nn_Filesystem) { + NN_INIT(nn_Filesystem) { .spaceTotal = 1 * NN_MiB, .readsPerTick = 4, .writesPerTick = 2, .dataEnergyCost = 256.0 / NN_MiB, }, - (nn_Filesystem) { + NN_INIT(nn_Filesystem) { .spaceTotal = 2 * NN_MiB, .readsPerTick = 4, .writesPerTick = 2, .dataEnergyCost = 512.0 / NN_MiB, }, - (nn_Filesystem) { + NN_INIT(nn_Filesystem) { .spaceTotal = 4 * NN_MiB, .readsPerTick = 7, .writesPerTick = 3, .dataEnergyCost = 1024.0 / NN_MiB, }, - (nn_Filesystem) { + NN_INIT(nn_Filesystem) { .spaceTotal = 8 * NN_MiB, .readsPerTick = 13, .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, .readsPerTick = 1, .writesPerTick = 1, .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, .readsPerTick = 1024, .writesPerTick = 512, @@ -2311,7 +2318,7 @@ const nn_Filesystem nn_defaultTmpFS = (nn_Filesystem) { }; const nn_Drive nn_defaultDrives[4] = { - (nn_Drive) { + NN_INIT(nn_Drive) { .capacity = 1 * NN_MiB, .sectorSize = 512, .platterCount = 2, @@ -2321,7 +2328,7 @@ const nn_Drive nn_defaultDrives[4] = { .onlySpinForwards = false, .dataEnergyCost = 256.0 / NN_MiB, }, - (nn_Drive) { + NN_INIT(nn_Drive) { .capacity = 2 * NN_MiB, .sectorSize = 512, .platterCount = 4, @@ -2331,7 +2338,7 @@ const nn_Drive nn_defaultDrives[4] = { .onlySpinForwards = false, .dataEnergyCost = 512.0 / NN_MiB, }, - (nn_Drive) { + NN_INIT(nn_Drive) { .capacity = 4 * NN_MiB, .sectorSize = 512, .platterCount = 8, @@ -2341,7 +2348,7 @@ const nn_Drive nn_defaultDrives[4] = { .onlySpinForwards = false, .dataEnergyCost = 1024.0 / NN_MiB, }, - (nn_Drive) { + NN_INIT(nn_Drive) { .capacity = 8 * NN_MiB, .sectorSize = 512, .platterCount = 16, @@ -2366,7 +2373,7 @@ const nn_Drive nn_floppyDrive = { const nn_ScreenConfig nn_defaultScreens[4] = { - (nn_ScreenConfig) { + NN_INIT(nn_ScreenConfig) { .maxWidth = 50, .maxHeight = 16, .maxDepth = 1, @@ -2375,7 +2382,7 @@ const nn_ScreenConfig nn_defaultScreens[4] = { .editableColors = 0, .features = NN_SCRF_NONE, }, - (nn_ScreenConfig) { + NN_INIT(nn_ScreenConfig) { .maxWidth = 80, .maxHeight = 25, .maxDepth = 4, @@ -2384,7 +2391,7 @@ const nn_ScreenConfig nn_defaultScreens[4] = { .editableColors = 0, .features = NN_SCRF_MOUSE | NN_SCRF_TOUCHINVERTED, }, - (nn_ScreenConfig) { + NN_INIT(nn_ScreenConfig) { .maxWidth = 160, .maxHeight = 50, .maxDepth = 8, @@ -2393,7 +2400,7 @@ const nn_ScreenConfig nn_defaultScreens[4] = { .editableColors = 16, .features = NN_SCRF_MOUSE | NN_SCRF_TOUCHINVERTED | NN_SCRF_PRECISE | NN_SCRF_EDITABLECOLORS, }, - (nn_ScreenConfig) { + NN_INIT(nn_ScreenConfig) { .maxWidth = 240, .maxHeight = 80, .maxDepth = 16, @@ -2405,7 +2412,7 @@ const nn_ScreenConfig nn_defaultScreens[4] = { }; const nn_GPU nn_defaultGPUs[4] = { - (nn_GPU) { + NN_INIT(nn_GPU) { .maxWidth = 50, .maxHeight = 16, .maxDepth = 1, @@ -2418,7 +2425,7 @@ const nn_GPU nn_defaultGPUs[4] = { .energyPerWrite = 0.0002, .energyPerClear = 0.0001, }, - (nn_GPU) { + NN_INIT(nn_GPU) { .maxWidth = 80, .maxHeight = 25, .maxDepth = 4, @@ -2431,7 +2438,7 @@ const nn_GPU nn_defaultGPUs[4] = { .energyPerWrite = 0.001, .energyPerClear = 0.0005, }, - (nn_GPU) { + NN_INIT(nn_GPU) { .maxWidth = 160, .maxHeight = 50, .maxDepth = 8, @@ -2444,7 +2451,7 @@ const nn_GPU nn_defaultGPUs[4] = { .energyPerWrite = 0.002, .energyPerClear = 0.001, }, - (nn_GPU) { + NN_INIT(nn_GPU) { .maxWidth = 240, .maxHeight = 80, .maxDepth = 16, @@ -3049,10 +3056,12 @@ nn_Exit nn_pushDrop(nn_Computer *computer, const char *screenAddress, double x, 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) { 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) { 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) { ereq.action = NN_EEPROM_GET; - char buf[eeprom.size]; + NN_VLA(char, buf, eeprom.size); ereq.buf = buf; ereq.buflen = eeprom.size; e = state->handler(&ereq); @@ -3159,7 +3168,7 @@ static nn_Exit nn_eepromHandler(nn_ComponentRequest *req) { } if(method == NN_EENUM_GETDATA) { ereq.action = NN_EEPROM_GETDATA; - char buf[eeprom.size]; + NN_VLA(char, buf, eeprom.size); ereq.buf = buf; ereq.buflen = eeprom.size; e = state->handler(&ereq); diff --git a/src/neonucleus.h b/src/neonucleus.h index 400b14a..2b6562f 100644 --- a/src/neonucleus.h +++ b/src/neonucleus.h @@ -9,21 +9,43 @@ extern "C" { // Used internally as well. // 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__) - //define something for Windows (32-bit and 64-bit, this part is common) + #ifndef NN_WINDOWS #define NN_WINDOWS + #endif #elif __APPLE__ + #ifndef NN_MACOS #define NN_MACOS + #endif #elif __linux__ + #ifndef NN_LINUX #define NN_LINUX + #endif #endif -#if __unix__ // all unices not caught above - // Unix +#if __unix__ + #ifndef NN_UNIX #define NN_UNIX + #endif + #ifndef NN_POSIX #define NN_POSIX + #endif #elif defined(_POSIX_VERSION) - // POSIX + #ifndef 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 // every C standard header we depend on, conveniently put here @@ -31,6 +53,19 @@ extern "C" { #include // for intptr_t #include // 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 +#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 // The entire NeoNucleus API, in one header file @@ -40,7 +75,8 @@ extern "C" { #define NN_KiB (1024) #define NN_MiB (1024 * NN_KiB) #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 #define NN_ALLOC_ALIGN 16 diff --git a/src/neonucleus.obj b/src/neonucleus.obj new file mode 100644 index 0000000..1eeb55c Binary files /dev/null and b/src/neonucleus.obj differ