This commit is contained in:
2026-04-02 10:48:20 +02:00
9 changed files with 469 additions and 51 deletions

44
CODING.md Normal file
View File

@@ -0,0 +1,44 @@
# Coding style
NN and NCL have a very specific API and code style.
This is meant to be consistent everywhere to keep the API and codebase easy to learn, navigate, use and maintain.
## Separation of files
- `src/neonucleus.h` for the header to NeoNucleus, the engine. This provides the systems everything interfaces with.
- `src/neonucleus.c`, the implementation of stuff defined in its header file.
- `src/ncomplib.h` for the header to the NeoNucleus Component Library, providing complete implementations for various components.
- `src/ncomplib.c` for the implementation of stuff defined in its header file.
- `src/main.c` for the test emulator used purely to try out the engine and see if it works.
The rationale for single C-file libraries is that they are easy to download and compile. This can simplify the process of updating and compiling into projects.
The reason for using this over shared libraries is that NN makes no ABI stability guarantees. It also makes it super easy to vendor the library,
meaning you can trivially pin to a specific version, and even a trivial build system can work. The reason for not going full STB single-file is that
LSPs can struggle to give diagnostics for the implementation side.
## General coding rules
- For actual source code, aim for 80 cols max on 4-space indentation. This isn't a hard rule, but breaking it should be avoided.
- `goto` should be used for trivial cases, such as the classic `goto fail;` pattern, but also to loop (`goto retry;`) or to skip (`goto found;`)
- While commenting is fine, comments should not be needed most of the time. Avoid writing code which needs to be explained through
comments, aim to ensure the code is readable.
- Prefer fixed capacities. This is useful to reduce the effect of memory hogging. If the fixed capacity is small enough to not use much memory,
prefer pre-allocating the maximum capacity to simplify the code and reduce places that can OOM.
## Component classes/implementations
- NN should provide component classes. These are component instances with a `void *` state specified to them, which stores its data in `classState`.
Their job is to define the methods and its docs and validate the methods to convert them into requests specific to the component type, and
send those requests to a handler (a simple function pointer). They should not have their own locks, they should not store their own state.
- NCL should provide component implementations. These do not have custom state or handlers, as they are *complete*. Prefer functions which set
internal state over many parameters in the constructors. NCL should handle the locking, as all components must be thread-safe, in which case
you should avoid wrapping entire handlers in the locks, and instead prefer locking as late as possible and unlocking as early as possible.
This is to give threads exclusive ownership over the potentially shared component for as little time as possible.
- Assume the component user is experimenting, a hacker, or has an RCE in their code. Make sure everything is validated, from internal state to
arguments. Do not trust the user, we do not have the JVM to save us here.
- Assume anything that can error will error at least once and ensure recovery with an exit code and error message, never panic or forcefully crash.
Assume memory allocation will eventually fail. Assume the filesystem will eventually break. Do not abort/exit/panic.
- Use an enum to keep track of method indexes and method count.
- Avoid separate functions for each method handler. Inline them into the single handler that handles dispatch. This not only net-shrinks the codebase,
but also means that navigating by searching for `== NN_<COMPONENT>_<REQUEST>)` (in NCL) or `== NN_<COMPONENT>NUM_<METHOD>)` (in NN) will show the
dispatch logic and implementation.

View File

@@ -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");
}
}

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);
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++) {
@@ -587,15 +588,15 @@ static void luaArch_loadEnv(lua_State *L) {
lua_createtable(L, 0, 10);
int unicode = lua_gettop(L);
lua_pushcfunction(L, luaArch_unicode_char);
lua_setfield(L, component, "char");
lua_setfield(L, unicode, "char");
lua_pushcfunction(L, luaArch_unicode_len);
lua_setfield(L, component, "len");
lua_setfield(L, unicode, "len");
lua_pushcfunction(L, luaArch_unicode_sub);
lua_setfield(L, component, "sub");
lua_setfield(L, unicode, "sub");
lua_pushcfunction(L, luaArch_unicode_len);
lua_setfield(L, component, "wlen");
lua_setfield(L, unicode, "wlen");
lua_pushcfunction(L, luaArch_unicode_wtrunc);
lua_setfield(L, component, "wtrunc");
lua_setfield(L, unicode, "wtrunc");
lua_setglobal(L, "unicode");
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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,7 +2233,7 @@ nn_Exit nn_popSignal(nn_Computer *computer, size_t *valueCount) {
// todo: everything
const nn_EEPROM nn_defaultEEPROMs[4] = {
(nn_EEPROM) {
NN_INIT(nn_EEPROM) {
.size = 4 * NN_KiB,
.dataSize = 256,
.readEnergyCost = 1,
@@ -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);

View File

@@ -9,28 +9,65 @@ 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
#include <stddef.h> // for NULL,
#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
// avoid #include <malloc.h>, for it can pollute the namespace
// with symbols to functions which are not linked with in baremetal.
void *_alloca(size_t);
#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 +77,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

Binary file not shown.