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:
@@ -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++) {
|
||||
|
||||
214
src/main.c
214
src/main.c
@@ -12,6 +12,11 @@
|
||||
#include <raylib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef NN_WINDOWS
|
||||
#include <setjmp.h>
|
||||
#include <signal.h>
|
||||
#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;
|
||||
|
||||
|
||||
118
src/ncomplib.c
118
src/ncomplib.c
@@ -17,8 +17,19 @@ bool ncl_defaultHandler(ncl_VFSRequest *request) {
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
// Read me all my rights
|
||||
#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
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 <stdint.h> // for intptr_t
|
||||
#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
|
||||
|
||||
// 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
|
||||
|
||||
BIN
src/neonucleus.obj
Normal file
BIN
src/neonucleus.obj
Normal file
Binary file not shown.
Reference in New Issue
Block a user