Compare commits

...

38 Commits

Author SHA1 Message Date
5f06a5659c Merge branch 'main' of https://gitea.codersquack.nl/NeoFlock/neonucleus 2026-06-06 03:51:15 +02:00
de13eeb85a removed a useless addition that caused pointless complications 2026-06-06 03:51:10 +02:00
7c086f6b8e update readme yayayay 2026-06-02 09:11:02 -03:00
c58e57b7c7 internet card progress 2026-05-24 18:40:53 +02:00
3b04fd45c7 internet progress 2026-05-23 20:30:31 +02:00
b88fd280ea I came, I saw, I conquered 2026-05-23 16:27:47 +02:00
b732f67b06 tmpfs works now 2026-05-23 16:23:44 +02:00
5c905e6f77 screen usage counter 2026-05-23 01:41:08 +02:00
2275cbf47d new tmpfs 2026-05-22 18:13:01 +02:00
79ea1c7f6f simplified the memory management 2026-05-19 23:44:54 +02:00
60b12ee507 support for morse code beeps 2026-05-18 23:57:50 +02:00
a6d7278735 beginnings of the internet (card) 2026-05-16 17:46:48 +03:00
810bf23942 we love accuracyslop 2026-05-14 18:11:18 +03:00
bcf0c06458 filesystem speeds were inaccurate 2026-05-14 18:04:43 +03:00
f0f6a21fef for debugging 2026-05-13 23:31:54 +03:00
a41e53f98f you didn't see that 2026-05-11 21:33:10 +03:00
172b7ce783 even more accuracyslop 2026-05-11 21:32:54 +03:00
37faa466f3 made it more accurateslop 2026-05-11 21:07:27 +03:00
fbd91be56d musl 'support'
musl suh ahh
2026-05-11 20:26:26 +03:00
461416362c more accuracy 2026-05-10 22:13:20 +03:00
e4d342a36f minor discrepancy 2026-05-09 17:57:54 +03:00
e7a166f85c C99 2026-05-09 16:31:19 +03:00
f832369ce0 fuck those invariants anyways 2026-05-09 13:29:47 +03:00
f9905d14fa data cards are so back 2026-05-09 02:24:01 +03:00
754a04de3c yipee data cards are done 2026-05-09 02:22:58 +03:00
f13635fb5e oopsie daisy forgot 2 super imponrtant methods 2026-05-09 00:02:41 +03:00
d739ddbd38 small tweaks 2026-05-08 23:24:02 +03:00
13ecbd3b26 modem methods 2026-05-08 22:41:17 +03:00
7580bfddcb and its done 2026-05-08 03:44:01 +03:00
51bc2ad8e6 call budgets match OC logic 2026-05-08 03:40:02 +03:00
ac79e6af35 regression report 2026-05-08 02:23:20 +03:00
862563b12f already done 2026-05-07 23:12:54 +03:00
6834ef932b bugfix 2026-05-07 22:56:45 +03:00
9b16a7b8e2 userdata 2026-05-07 22:48:55 +03:00
2ccd3a84b5 plans 2026-05-07 00:13:52 +03:00
fa0910d730 todo shi 2026-05-06 21:24:39 +03:00
855fb71b4e tunnel config 2026-05-06 01:39:11 +03:00
63b7db4936 made the energy usage make more sense 2026-05-06 00:16:15 +03:00
10 changed files with 2736 additions and 952 deletions

View File

@@ -6,16 +6,23 @@ CC=cc
LD=$(CC) LD=$(CC)
AR=ar AR=ar
RANLIB=ranlib RANLIB=ranlib
WARN=-Wall -Werror -Wno-format-truncation
ifeq ($(MODE), release) ifeq ($(MODE), release)
OPT=-Oz OPT=-Oz
DEBUG= DEBUG=
else ifeq ($(MODE), release-lto)
OPT=-Oz -flto
DEBUG=
else else
OPT=-O0 OPT=-O0
SANITIZE=undefined,address SANITIZE=undefined,address
DEBUG=-ggdb DEBUG=-g
endif endif
NN_STD=gnu99
EMU_STD=gnu23
NNFLAGS= NNFLAGS=
SANITIZE_FLAGS= SANITIZE_FLAGS=
@@ -24,7 +31,8 @@ ifdef SANITIZE
SANITIZE_FLAGS += -fsanitize=$(SANITIZE) SANITIZE_FLAGS += -fsanitize=$(SANITIZE)
endif endif
CFLAGS=-fPIC $(OPT) $(SANITIZE_FLAGS) $(DEBUG) $(NNFLAGS) # no-omit-frame-pointer so if a crash does happen we can trace it
CFLAGS=-fPIC -fno-omit-frame-pointer $(OPT) $(SANITIZE_FLAGS) $(DEBUG) $(NNFLAGS) $(WARN)
LDFLAGS=$(OPT) $(DEBUG) $(SANITIZE_FLAGS) LDFLAGS=$(OPT) $(DEBUG) $(SANITIZE_FLAGS)
@@ -40,21 +48,21 @@ SRC_DIR=src
all: bin lib dynlib all: bin lib dynlib
$(BUILD_DIR)/neonucleus.o: $(SRC_DIR)/neonucleus.c $(SRC_DIR)/neonucleus.h $(BUILD_DIR)/neonucleus.o: $(SRC_DIR)/neonucleus.c $(SRC_DIR)/neonucleus.h
$(CC) -o $(BUILD_DIR)/neonucleus.o -c $(SRC_DIR)/neonucleus.c $(CFLAGS) $(CC) -o $(BUILD_DIR)/neonucleus.o -c $(SRC_DIR)/neonucleus.c $(CFLAGS) -std=$(NN_STD)
$(BUILD_DIR)/ncomplib.o: $(SRC_DIR)/ncomplib.c $(SRC_DIR)/ncomplib.h $(BUILD_DIR)/ncomplib.o: $(SRC_DIR)/ncomplib.c $(SRC_DIR)/ncomplib.h
$(CC) -o $(BUILD_DIR)/ncomplib.o -c $(SRC_DIR)/ncomplib.c $(CFLAGS) $(CC) -o $(BUILD_DIR)/ncomplib.o -c $(SRC_DIR)/ncomplib.c $(CFLAGS) -std=$(NN_STD)
nn: $(BUILD_DIR)/neonucleus.o $(BUILD_DIR)/ncomplib.o nn: $(BUILD_DIR)/neonucleus.o $(BUILD_DIR)/ncomplib.o
$(BUILD_DIR)/luaarch.o: $(SRC_DIR)/luaarch.c $(SRC_DIR)/machine.lua $(BUILD_DIR)/luaarch.o: $(SRC_DIR)/luaarch.c $(SRC_DIR)/machine.lua
$(CC) -o $(BUILD_DIR)/luaarch.o -c $(SRC_DIR)/luaarch.c $(CFLAGS) $(INCLUA) $(CC) -o $(BUILD_DIR)/luaarch.o -c $(SRC_DIR)/luaarch.c $(CFLAGS) $(INCLUA) -std=$(EMU_STD)
$(BUILD_DIR)/glyphcache.o: $(SRC_DIR)/glyphcache.c $(SRC_DIR)/glyphcache.h $(BUILD_DIR)/glyphcache.o: $(SRC_DIR)/glyphcache.c $(SRC_DIR)/glyphcache.h
$(CC) -o $(BUILD_DIR)/glyphcache.o -c $(SRC_DIR)/glyphcache.c $(CFLAGS) $(CC) -o $(BUILD_DIR)/glyphcache.o -c $(SRC_DIR)/glyphcache.c $(CFLAGS) -std=$(EMU_STD)
$(BUILD_DIR)/main.o: $(SRC_DIR)/main.c $(SRC_DIR)/minBIOS.lua $(BUILD_DIR)/main.o: $(SRC_DIR)/main.c $(SRC_DIR)/minBIOS.lua
$(CC) -o $(BUILD_DIR)/main.o -c $(SRC_DIR)/main.c $(CFLAGS) $(INCLUA) $(CC) -o $(BUILD_DIR)/main.o -c $(SRC_DIR)/main.c $(CFLAGS) $(INCLUA) -std=$(EMU_STD)
bin: nn $(BUILD_DIR)/main.o $(BUILD_DIR)/luaarch.o $(BUILD_DIR)/glyphcache.o bin: nn $(BUILD_DIR)/main.o $(BUILD_DIR)/luaarch.o $(BUILD_DIR)/glyphcache.o
$(LD) $(LDFLAGS) -o $(BIN) $(BUILD_DIR)/neonucleus.o $(BUILD_DIR)/ncomplib.o $(BUILD_DIR)/main.o $(BUILD_DIR)/glyphcache.o $(BUILD_DIR)/luaarch.o $(LINKLIBC) $(LINKLIBM) $(LINKRAYLIB) $(LINKLUA) $(LD) $(LDFLAGS) -o $(BIN) $(BUILD_DIR)/neonucleus.o $(BUILD_DIR)/ncomplib.o $(BUILD_DIR)/main.o $(BUILD_DIR)/glyphcache.o $(BUILD_DIR)/luaarch.o $(LINKLIBC) $(LINKLIBM) $(LINKRAYLIB) $(LINKLUA)

45
TODO.md
View File

@@ -1,29 +1,11 @@
# For MVP functionality
- make `computer` component use callbacks
- finish tmpfs (rework the whole thing)
- userdata support
# To re-evaluate
- Exposing the internal non-resizing hashmap implementation.
- More stack manipulation functions to allow libraries to have better APIs. (rotate)
- Having a copy of the context stored directly in requests instead of having to use getComputerContext, as it simplifies the APIs and allows them
to be made more portable.
- Exposing more internal functions that may be useful to the user to prevent pointless rewriting and duplicate machine code.
# Vanilla components needed # Vanilla components needed
Not everything OC has (as a few of them are really MC-centered) but most of it. Not everything OC has (as a few of them are really MC-centered) but most of it.
- `data` component (note: deflate, sha256 and aes impl are callbacks, to keep NN small and simple) - `computer` component
- `modem` component
- `tunnel` component
- `internet` component (note: NN does not handle internet requests, those are up to the emulator) - `internet` component (note: NN does not handle internet requests, those are up to the emulator)
- `relay` component (note: OCDoc still refers to it as `access_point`) - `relay` component (note: OCDoc still refers to it as `access_point`)
- `computer` component
- `geolyzer` component - `geolyzer` component
- `net_splitter` component
- `redstone` component - `redstone` component
- `motion_sensor` component - `motion_sensor` component
- `printer3d` component (note: maybe add signal for when printer completes?) - `printer3d` component (note: maybe add signal for when printer completes?)
@@ -49,11 +31,11 @@ Not everything OC has (as a few of them are really MC-centered) but most of it.
- `serial` component, for serial communications with other devices (see Serial) - `serial` component, for serial communications with other devices (see Serial)
- `iron_noteblock` component - `iron_noteblock` component
- `colorful_lamp` component - `colorful_lamp` component
- OpenSolidState flash storage
# To make it good # To make it good
- make more stuff const if it can be. Gotta help out the optimizer - make more stuff const if it can be. Gotta help out the optimizer
- put a bunch of internal-only functions as fastcall
- write a bunch of unit tests to ensure the public API works correctly - write a bunch of unit tests to ensure the public API works correctly
- ensure OOMs are recoverable - ensure OOMs are recoverable
- do a hude audit for bugs at some point - do a hude audit for bugs at some point
@@ -61,9 +43,11 @@ Not everything OC has (as a few of them are really MC-centered) but most of it.
# To make it fast # To make it fast
NOTE: we're mostly bottlenecked by the architecture (typically a Lua VM) and the intentional bottlenecking from call costs. NOTE: we're mostly bottlenecked by the architecture (typically a Lua VM) and the intentional bottlenecking from call costs.
breaking changes are allowed but like try to avoid them.
- make signals use a circular buffer instead of a simple array - make signals use a circular buffer instead of a simple array
- use more arenas if possible - use more arenas if possible
- reduce pointer nesting, not only for simplicity, but so we spend less time reading from main memory
# Component extensions # Component extensions
@@ -144,9 +128,9 @@ The `vt` component has:
- `getColor(index: integer): integer`, to get a color - `getColor(index: integer): integer`, to get a color
- `setColor(index: integer, color: integer): integer`, sets a color and returns the old one. Characters who's colors were table indexes would be updated - `setColor(index: integer, color: integer): integer`, sets a color and returns the old one. Characters who's colors were table indexes would be updated
- `setForeground(color: integer, fromTable?: boolean)`, sets a foreground color, optionally as a table index - `setForeground(color: integer, fromTable?: boolean)`, sets a foreground color, optionally as a table index
- `getForeground(): integer, integer?`, returns what the foreground color was, and the palette index if applicable - `getForeground(): integer, integer?`, returns what the foreground color was, and the table index if applicable
- `setBackground(color: integer, fromTable?: boolean)`, sets a background color, optionally as a table index - `setBackground(color: integer, fromTable?: boolean)`, sets a background color, optionally as a table index
- `getBackground(): integer, integer?`, returns what the background color was, and the palette index if applicable - `getBackground(): integer, integer?`, returns what the background color was, and the table index if applicable
- `set(idx: integer, data: string): boolean`, to write to the screen's unicode buffer. While `data` is UTF-8, the buffer stores codepoints. Pretend - `set(idx: integer, data: string): boolean`, to write to the screen's unicode buffer. While `data` is UTF-8, the buffer stores codepoints. Pretend
the terminal does an internal conversion from UTF-8 to UTF-32 the terminal does an internal conversion from UTF-8 to UTF-32
- `getColorOf(idx: integer): integer, integer, integer?, integer?`, to get the foreground color, background color, and palette indexes of a tile - `getColorOf(idx: integer): integer, integer, integer?, integer?`, to get the foreground color, background color, and palette indexes of a tile
@@ -161,10 +145,11 @@ If `x` and `y` are 0-indexed, then `x + y * width` is the index in the buffer
The `cd_drive` component has: The `cd_drive` component has:
- `hasDisk(): boolean`, to check whether a disk is in the drive - `hasDisk(): boolean`, to check whether a disk is in the drive
- `isReadonly(): boolean`, to check where the drive is read-only (often is) - `isReadonly(): boolean`, to check whether the drive is read-only (often is)
- `isDiskErasable(): boolean`, to check whether the disk is erasable, which requires support from both the disk and the drive - `isDiskErasable(): boolean`, to check whether the disk is erasable, which requires support from both the disk and the drive
- `getCapacity(): integer`, gets the capacity of the disk
- `tell(): integer`, current 0-indexed byte offset into the disk - `tell(): integer`, current 0-indexed byte offset into the disk
- `seekTo(position: integer): boolean`, seek to a current 0-indexed byte offset in the disk - `seekTo(position: integer): boolean`, seek to a 0-indexed byte offset in the disk
- `moveBy(delta: integer): boolean`, move the current position by some amount of bytes (+/-), can wrap around - `moveBy(delta: integer): boolean`, move the current position by some amount of bytes (+/-), can wrap around
- `maxReadSize(): integer`, to return the maximum size of a read - `maxReadSize(): integer`, to return the maximum size of a read
- `read(len: integer): string`, to read some data. Does wrap around - `read(len: integer): string`, to read some data. Does wrap around
@@ -187,7 +172,9 @@ Tape has high capacity, CDs do not.
### CDs vs unmanaged floppies ### CDs vs unmanaged floppies
CDs are slower for random reads as they have no cache. CDs require special software support, while floppies appear as just smaller HDDs.
Unmanaged floppies are always erasable, while CDs may not be.
CDs are typically lower capacity.
## LED ## LED
@@ -227,3 +214,11 @@ TODO: interface
## OLED/IPU ## OLED/IPU
TODO: interface TODO: interface
# To re-evaluate
- Make the hashmap growing/shrinking to save on memory.
- Exposing the internal hashmap implementation.
- Having a copy of the context stored directly in requests instead of having to use getComputerContext, as it simplifies the APIs and allows them
to be made more portable.
- Exposing more internal functions that may be useful to the user to prevent pointless rewriting and duplicate machine code.

View File

@@ -17,10 +17,9 @@ fn addEngineSources(b: *std.Build, opts: LibBuildOpts) *std.Build.Module {
.strip = if (opts.optimize == .Debug) false else true, .strip = if (opts.optimize == .Debug) false else true,
.unwind_tables = if (opts.optimize == .Debug) null else .none, .unwind_tables = if (opts.optimize == .Debug) null else .none,
.pic = true, .pic = true,
.sanitize_c = if(strict) .full else null, .sanitize_c = if (strict) .full else null,
}); });
dataMod.addCSourceFiles(.{ dataMod.addCSourceFiles(.{
.files = &[_][]const u8{ .files = &[_][]const u8{
"src/neonucleus.c", "src/neonucleus.c",
@@ -62,7 +61,7 @@ fn compileRaylib(b: *std.Build, os: std.Target.Os.Tag, buildOpts: LibBuildOpts,
// passing it breaks it for some reason? // passing it breaks it for some reason?
// TODO: make it not break // TODO: make it not break
const raylib = b.addSystemCommand(&.{ "zig", "build"}); const raylib = b.addSystemCommand(&.{ "zig", "build" });
raylib.setCwd(b.path("foreign/raylib/")); raylib.setCwd(b.path("foreign/raylib/"));
raylib.stdio = .inherit; raylib.stdio = .inherit;
@@ -89,11 +88,7 @@ fn compileTheRightLua(b: *std.Build, target: std.Build.ResolvedTarget, version:
// its a static library because COFF is a pile of shit // its a static library because COFF is a pile of shit
const c = b.addLibrary(.{ const c = b.addLibrary(.{
.name = "lua", .name = "lua",
.root_module = b.addModule("luamod", .{ .root_module = b.addModule("luamod", .{ .link_libc = true, .optimize = .ReleaseFast, .target = target, .pic = true }),
.link_libc = true,
.optimize = .ReleaseFast,
.target = target,
}),
.linkage = .static, .linkage = .static,
}); });

View File

@@ -73,6 +73,9 @@ static nn_Exit luaArch_luaToNN(luaArch *arch, lua_State *L, int luaIdx) {
if(lua_isboolean(L, luaIdx)) { if(lua_isboolean(L, luaIdx)) {
return nn_pushbool(C, lua_toboolean(L, luaIdx)); return nn_pushbool(C, lua_toboolean(L, luaIdx));
} }
if(lua_isuserdata(L, luaIdx)) {
return nn_pushuserdata(C, (size_t)lua_touserdata(L, luaIdx));
}
luaL_error(L, "bad Lua value: %s", luaL_typename(L, luaIdx)); luaL_error(L, "bad Lua value: %s", luaL_typename(L, luaIdx));
return NN_EBADSTATE; return NN_EBADSTATE;
} }
@@ -112,11 +115,36 @@ static void luaArch_nnToLua(luaArch *arch, lua_State *L, size_t nnIdx) {
nn_popn(C, len * 2); nn_popn(C, len * 2);
return; return;
} }
if(nn_isuserdata(C, nnIdx)) {
lua_pushlightuserdata(L, (void *)nn_touserdata(C, nnIdx));
return;
}
luaL_error(L, "bad NN value: %s", nn_typenameof(C, nnIdx)); luaL_error(L, "bad NN value: %s", nn_typenameof(C, nnIdx));
} }
static int luaArch_computer_beep(lua_State *L) { static int luaArch_computer_beep(lua_State *L) {
if(lua_type(L, 1) == LUA_TSTRING) {
nn_MorseBeep beep = {.frequency = 1000, .beepDuration = 200, .volume = 1};
beep.pattern = lua_tostring(L, 1);
if(lua_isnumber(L, 2)) {
beep.frequency = lua_tonumber(L, 2);
}
if(lua_isnumber(L, 3)) {
beep.beepDuration = lua_tonumber(L, 3);
}
if(lua_isnumber(L, 4)) {
beep.volume = lua_tonumber(L, 4);
}
if(beep.frequency < 20) beep.frequency = 20;
if(beep.beepDuration < 0) beep.beepDuration = 0;
if(beep.volume < 0) beep.volume = 0;
if(beep.frequency > 20000) beep.frequency = 20000;
if(beep.beepDuration > 5) beep.beepDuration = 5;
if(beep.volume > 1) beep.volume = 1;
nn_beepComputerMorse(luaArch_from(L)->computer, beep);
return 0;
}
nn_Beep beep = {.frequency = 1000, .duration = 1, .volume = 1}; nn_Beep beep = {.frequency = 1000, .duration = 1, .volume = 1};
if(lua_isnumber(L, 1)) { if(lua_isnumber(L, 1)) {
beep.frequency = lua_tonumber(L, 1); beep.frequency = lua_tonumber(L, 1);
@@ -593,6 +621,84 @@ static int luaArch_unicode_wtrunc(lua_State *L) {
return 1; return 1;
} }
static int luaArch_userdata_free(lua_State *L) {
luaArch *arch = luaArch_from(L);
size_t user = (size_t)lua_touserdata(L, 1);
nn_freeUserdata(arch->computer, user);
return 0;
}
static int luaArch_userdata_methods(lua_State *L) {
luaArch *arch = luaArch_from(L);
size_t user = (size_t)lua_touserdata(L, 1);
if(!nn_isUserdataValid(arch->computer, user)) {
lua_pushnil(L);
lua_pushstring(L, "no such userdata");
return 2;
}
lua_createtable(L, 0, 0);
size_t idx = 0;
while(true) {
nn_Method m;
if(nn_getUserdataMethod(arch->computer, user, idx, &m)) break;
idx++;
if(m.name == NULL) continue;
lua_pushstring(L, m.name);
lua_createtable(L, 0, 4);
// the method data
{
lua_pushstring(L, "doc");
if(m.doc == NULL) lua_pushnil(L); else lua_pushstring(L, m.doc);
lua_settable(L, -3);
lua_pushstring(L, "direct");
lua_pushboolean(L, (m.flags & NN_DIRECT) != 0);
lua_settable(L, -3);
lua_pushstring(L, "getter");
lua_pushboolean(L, (m.flags & NN_GETTER) != 0);
lua_settable(L, -3);
lua_pushstring(L, "setter");
lua_pushboolean(L, (m.flags & NN_SETTER) != 0);
lua_settable(L, -3);
}
lua_settable(L, -3);
}
return 1;
}
static int luaArch_userdata_invoke(lua_State *L) {
luaArch *arch = luaArch_from(L);
size_t user = (size_t)lua_touserdata(L, 1);
if(!nn_isUserdataValid(arch->computer, user)) {
lua_pushnil(L);
lua_pushstring(L, "no such userdata");
return 2;
}
const char *method = luaL_checkstring(L, 2);
size_t argc = lua_gettop(L);
nn_clearstack(arch->computer);
for(size_t i = 3; i <= argc; i++) {
luaArch_luaToNN(arch, L, i);
}
nn_Exit err = nn_invokeUserdata(arch->computer, user, method);
if(err != NN_OK) {
lua_pushnil(L);
lua_pushstring(L, nn_getError(arch->computer));
return 2;
}
size_t retc = nn_getstacksize(arch->computer);
for(size_t i = 0; i < retc; i++) {
luaArch_nnToLua(arch, L, i);
}
nn_clearstack(arch->computer);
return retc;
}
static void luaArch_loadEnv(lua_State *L) { static void luaArch_loadEnv(lua_State *L) {
lua_createtable(L, 0, 10); lua_createtable(L, 0, 10);
int computer = lua_gettop(L); int computer = lua_gettop(L);
@@ -637,6 +743,7 @@ static void luaArch_loadEnv(lua_State *L) {
lua_pushcfunction(L, luaArch_computer_getDeviceInfo); lua_pushcfunction(L, luaArch_computer_getDeviceInfo);
lua_setfield(L, computer, "getDeviceInfo"); lua_setfield(L, computer, "getDeviceInfo");
lua_setglobal(L, "computer"); lua_setglobal(L, "computer");
lua_createtable(L, 0, 10); lua_createtable(L, 0, 10);
int component = lua_gettop(L); int component = lua_gettop(L);
lua_pushcfunction(L, luaArch_component_list); lua_pushcfunction(L, luaArch_component_list);
@@ -656,6 +763,7 @@ static void luaArch_loadEnv(lua_State *L) {
lua_pushcfunction(L, luaArch_component_fields); lua_pushcfunction(L, luaArch_component_fields);
lua_setfield(L, component, "fields"); lua_setfield(L, component, "fields");
lua_setglobal(L, "component"); lua_setglobal(L, "component");
lua_createtable(L, 0, 10); lua_createtable(L, 0, 10);
int unicode = lua_gettop(L); int unicode = lua_gettop(L);
lua_pushcfunction(L, luaArch_unicode_char); lua_pushcfunction(L, luaArch_unicode_char);
@@ -664,11 +772,21 @@ static void luaArch_loadEnv(lua_State *L) {
lua_setfield(L, unicode, "len"); lua_setfield(L, unicode, "len");
lua_pushcfunction(L, luaArch_unicode_sub); lua_pushcfunction(L, luaArch_unicode_sub);
lua_setfield(L, unicode, "sub"); lua_setfield(L, unicode, "sub");
lua_pushcfunction(L, luaArch_unicode_len); lua_pushcfunction(L, luaArch_unicode_wlen);
lua_setfield(L, unicode, "wlen"); lua_setfield(L, unicode, "wlen");
lua_pushcfunction(L, luaArch_unicode_wtrunc); lua_pushcfunction(L, luaArch_unicode_wtrunc);
lua_setfield(L, unicode, "wtrunc"); lua_setfield(L, unicode, "wtrunc");
lua_setglobal(L, "unicode"); lua_setglobal(L, "unicode");
lua_createtable(L, 0, 10);
int userdata = lua_gettop(L);
lua_pushcfunction(L, luaArch_userdata_free);
lua_setfield(L, userdata, "free");
lua_pushcfunction(L, luaArch_userdata_methods);
lua_setfield(L, userdata, "methods");
lua_pushcfunction(L, luaArch_userdata_invoke);
lua_setfield(L, userdata, "invoke");
lua_setglobal(L, "userdata");
} }
static nn_Exit luaArch_handler(nn_ArchitectureRequest *req) { static nn_Exit luaArch_handler(nn_ArchitectureRequest *req) {
@@ -734,8 +852,11 @@ static nn_Exit luaArch_handler(nn_ArchitectureRequest *req) {
return NN_OK; return NN_OK;
} }
return NN_OK; return NN_OK;
default: case NN_ARCH_SERIALIZE:
break; return nn_pushstring(computer, "lua stuff");
case NN_ARCH_DESERIALIZE:
// do nothing
return NN_OK;
} }
return NN_OK; return NN_OK;
} }

View File

@@ -70,19 +70,79 @@ end
local syncedMethodStats local syncedMethodStats
local function unsandboxValue(val)
if type(val) == "table" then
local meta = getmetatable(val)
if meta then
if type(meta.__userdata) == "userdata" then
return meta.__userdata
end
end
local nt = {}
for sk, sv in pairs(val) do
local k, v = unsandboxValue(sk), unsandboxValue(sv)
if k ~= nil then nt[k] = v end
end
return nt
end
return val
end
local function sandboxValue(val)
if type(val) == "table" then
local nt = {}
for k, v in pairs(val) do
local sk, sv = sandboxValue(k), sandboxValue(v)
if sk ~= nil then nt[sk] = sv end
end
return nt
end
if type(val) == "userdata" then
-- This matches how OC wraps it
-- TODO: just make our own shi with fields and stuff
-- like a component proxy, because this sucks
local userMeta = {
__gc = function()
userdata.free(val)
end,
__userdata = val,
}
local wrapped = {type = "userdata", userdata = val}
for name, m in pairs(userdata.methods(val)) do
wrapped[name] = {
name = name,
proxy = wrapped,
}
setmetatable(wrapped[name], {
__tostring = function() return m.doc or "function" end,
__call = function(_, ...)
return userdata.invoke(val, name, ...)
end,
})
end
return setmetatable(wrapped, userMeta)
end
return val
end
local function realInvoke(address, method, ...) local function realInvoke(address, method, ...)
local t = {pcall(cinvoke, address, method, ...)} local args = {...}
for i=1,#args do args[i] = unsandboxValue(args[i]) end
local t = {pcall(cinvoke, address, method, table.unpack(args))}
if not _SYNCED and not os.getenv("NN_FAST") then if not _SYNCED and not os.getenv("NN_FAST") then
if computer.energy() <= 0 then sysyield() end -- out of power if computer.energy() <= 0 then sysyield() end -- out of power
if computer.isOverused() then sysyield() end -- overused if computer.isOverused() then sysyield() end -- overused
if computer.isIdle() then sysyield() end -- machine idle if computer.isIdle() then sysyield() end -- machine idle
end end
if os.getenv("NN_INVDBG") and component.type(address) == os.getenv("NN_INVDBG") then if component.type(address) == os.getenv("NN_INVDBG") or string.find(os.getenv("NN_METDBG") or "", method, nil, true) then
print("invoked", address, method, ...) print("invoked", address, method, ...)
print("got", table.unpack(t)) print(string.format("got %d values", #t), table.unpack(t))
end end
for i=1,#t do t[i] = sandboxValue(t[i]) end
if t[1] then if t[1] then
return table.unpack(t, 2) return table.unpack(t, 2)
end end
@@ -192,6 +252,7 @@ function computer.pullSignal(timeout)
while true do while true do
if computer.uptime() >= deadline then return end if computer.uptime() >= deadline then return end
local t = {computer.popSignal()} local t = {computer.popSignal()}
for i=1,#t do t[i] = unsandboxValue(t[i]) end
if #t == 0 then if #t == 0 then
sysyield() sysyield()
else else
@@ -426,7 +487,18 @@ sandbox = {
isRobot = computer.isRobot, isRobot = computer.isRobot,
maxEnergy = computer.maxEnergy, maxEnergy = computer.maxEnergy,
pullSignal = computer.pullSignal, pullSignal = computer.pullSignal,
pushSignal = computer.pushSignal, pushSignal = function(name, ...)
checkArg(1, name, "string")
local t = {...}
for i=1,#t do
local v = unsandboxValue(t[i])
if type(v) == "userdata" then
return nil, "userdata is forbidden"
end
t[i] = v
end
return computer.pushSignal(name, table.unpack(t))
end,
removeUser = computer.removeUser, removeUser = computer.removeUser,
setArchitecture = computer.setArchitecture, setArchitecture = computer.setArchitecture,
shutdown = computer.shutdown, shutdown = computer.shutdown,

View File

@@ -42,7 +42,7 @@ static nn_Exit sandbox_handler(nn_ComponentRequest *req) {
return NN_OK; return NN_OK;
case NN_COMP_DROP: case NN_COMP_DROP:
return NN_OK; return NN_OK;
case NN_COMP_SIGNAL: case NN_COMP_USERDATA:
return NN_OK; return NN_OK;
} }
return NN_OK; return NN_OK;
@@ -104,10 +104,17 @@ static nn_Exit ne_dataBullshit(nn_DataCardRequest *req) {
return NN_OK; return NN_OK;
} }
if(action == NN_DATA_SHA256) { if(action == NN_DATA_SHA256) {
// does not match OC, dunno why
unsigned int *out = ComputeSHA256((unsigned char *)req->sha256.data, req->sha256.datalen); unsigned int *out = ComputeSHA256((unsigned char *)req->sha256.data, req->sha256.datalen);
if(out == NULL) return NN_ENOMEM; if(out == NULL) return NN_ENOMEM;
memcpy(req->sha256.checksum, out, 32); unsigned char buf[32];
for(size_t i = 0; i < 8; i++) {
// OC does BE
buf[i*4+3] = (out[i] >> 0) & 0xFF;
buf[i*4+2] = (out[i] >> 8) & 0xFF;
buf[i*4+1] = (out[i] >> 16) & 0xFF;
buf[i*4+0] = (out[i] >> 24) & 0xFF;
}
memcpy(req->sha256.checksum, buf, 32);
return NN_OK; return NN_OK;
} }
if(action == NN_DATA_RANDOM) { if(action == NN_DATA_RANDOM) {
@@ -123,6 +130,32 @@ static nn_Exit ne_dataBullshit(nn_DataCardRequest *req) {
return nn_pushlstring(C, req->data, req->datalen); return nn_pushlstring(C, req->data, req->datalen);
} }
if(action == NN_DATA_VALIDATEKEY) {
// its valid, trust
return NN_OK;
}
if(action == NN_DATA_GENKEYS) {
char a = 'A' + rand() % 26;
char b = 'A' + rand() % 26;
nn_pushlstring(C, &a, 1);
return nn_pushlstring(C, &b, 1);
}
if(action == NN_DATA_ECDH) {
char buf[32];
memset(buf, 'e', 32);
return nn_pushlstring(C, buf, 32);
}
if(action == NN_DATA_ECDSA_SIGN) {
return nn_pushstring(C, "epic signature bro");
}
if(action == NN_DATA_ECDSA_VERIFY) {
req->checksig.sigpassed = strcmp(req->checksig.signature, "epic signature bro");
return NN_OK;
}
if(C) nn_setError(C, "ne: data method not implemented"); if(C) nn_setError(C, "ne: data method not implemented");
return NN_EBADCALL; return NN_EBADCALL;
} }
@@ -133,9 +166,81 @@ static nn_Exit ne_modemBullshit(nn_ModemRequest *req) {
if(req->action == NN_MODEM_DROP) { if(req->action == NN_MODEM_DROP) {
return NN_OK; return NN_OK;
} }
if(req->action == NN_MODEM_ISOPEN) {
int port = req->isOpen.port;
req->isOpen.opened = port >= 1 && port <= 3;
return NN_OK;
}
if(req->action == NN_MODEM_OPEN) {
printf("pretend we opened port %zu\n", req->openPort);
return NN_OK;
}
if(req->action == NN_MODEM_CLOSE) {
printf("pretend we closed ");
if(req->closePort == NN_CLOSEPORTS) {
printf("all ports\n");
} else {
printf("port %zu\n", req->closePort);
}
return NN_OK;
}
if(req->action == NN_MODEM_GETPORTS) {
// lies
req->getPorts.len = 3;
req->getPorts.activePorts[0] = 1;
req->getPorts.activePorts[1] = 2;
req->getPorts.activePorts[2] = 3;
return NN_OK;
}
if(req->action == NN_MODEM_SEND) { if(req->action == NN_MODEM_SEND) {
printf("Transmission from %s to %s (port %zu) of %zu bytes (%zu values)\n", req->localAddress, req->send.address == NULL ? "*" : req->send.address, req->send.port, req->send.contents->buflen, req->send.contents->valueCount); req->send.strengthSent = req->modem->maxRange;
return nn_pushModemMessage(C, req->localAddress, nn_getComputerAddress(C), req->send.port, 0, req->send.contents); const char *dest = req->send.address == NULL ? "*" : req->send.address;
printf("Transmission from %s to %s (port %zu) of %zu bytes (%zu values)\n", req->localAddress, dest, req->send.port, req->send.contents->buflen, req->send.contents->valueCount);
return nn_pushModemMessage(C, req->localAddress, dest, req->send.port, 0, req->send.contents);
}
if(req->action == NN_MODEM_GETWAKEMESSAGE) {
req->getWake.len = 0;
req->getWake.isFuzzy = false;
return NN_OK;
}
if(req->action == NN_MODEM_SETWAKEMESSAGE) {
return NN_OK;
}
if(C) nn_setError(C, "ne: modem method not implemented");
return NN_EBADCALL;
}
static nn_Exit ne_tunnelBullshit(nn_TunnelRequest *req) {
nn_Computer *C = req->computer;
if(req->action == NN_TUNNEL_DROP) {
return NN_OK;
}
if(req->action == NN_TUNNEL_GETCHANNEL) {
return nn_pushstring(C, "creative");
}
if(req->action == NN_TUNNEL_SEND) {
printf("Transmission from tunnel %s of %zu bytes (%zu values)\n", req->localAddress, req->toSend->buflen, req->toSend->valueCount);
return nn_pushModemMessage(C, req->localAddress, nn_getComputerAddress(C), NN_TUNNEL_PORT, 0, req->toSend);
}
if(req->action == NN_TUNNEL_GETWAKEMESSAGE) {
req->getWake.len = 0;
req->getWake.isFuzzy = false;
return NN_OK;
}
if(req->action == NN_TUNNEL_SETWAKEMESSAGE) {
return NN_OK;
} }
if(C) nn_setError(C, "ne: modem method not implemented"); if(C) nn_setError(C, "ne: modem method not implemented");
@@ -395,6 +500,7 @@ typedef struct ne_memSand {
char *buf; char *buf;
size_t used; size_t used;
size_t cap; size_t cap;
size_t active;
} ne_memSand; } ne_memSand;
void *ne_sandbox_alloc(void *state, void *memory, size_t oldSize, size_t newSize) { void *ne_sandbox_alloc(void *state, void *memory, size_t oldSize, size_t newSize) {
@@ -404,12 +510,16 @@ void *ne_sandbox_alloc(void *state, void *memory, size_t oldSize, size_t newSize
newSize = ne_alignAlloc(newSize, NN_ALLOC_ALIGN); newSize = ne_alignAlloc(newSize, NN_ALLOC_ALIGN);
// never free // never free
if(newSize == 0) return NULL; if(newSize == 0) {
sand->active -= oldSize;
return NULL;
}
if(memory == NULL) { if(memory == NULL) {
if(sand->cap - sand->used < newSize) return NULL; if(sand->cap - sand->used < newSize) return NULL;
// alloc new // alloc new
void *mem = sand->buf + sand->used; void *mem = sand->buf + sand->used;
sand->used += newSize; sand->used += newSize;
sand->active += newSize;
return mem; return mem;
} }
// realloc // realloc
@@ -417,12 +527,14 @@ void *ne_sandbox_alloc(void *state, void *memory, size_t oldSize, size_t newSize
if(sand->cap - sand->used < newSize) return NULL; if(sand->cap - sand->used < newSize) return NULL;
void *mem = sand->buf + sand->used; void *mem = sand->buf + sand->used;
sand->used += newSize; sand->used += newSize;
sand->active += newSize;
memcpy(mem, memory, oldSize); memcpy(mem, memory, oldSize);
return mem; return mem;
} }
double accumulatedEnergyCost = 0; double accumulatedEnergyCost = 0;
double totalEnergyLoss = 0; double totalEnergyLoss = 0;
// default capacity of a tablet
double allEnergy = 10000; double allEnergy = 10000;
void ne_env(nn_EnvironmentRequest *req) { void ne_env(nn_EnvironmentRequest *req) {
@@ -430,6 +542,10 @@ void ne_env(nn_EnvironmentRequest *req) {
printf("beep: %f Hz %fs %.02f%%\n", req->beep.frequency, req->beep.duration, req->beep.volume*100); printf("beep: %f Hz %fs %.02f%%\n", req->beep.frequency, req->beep.duration, req->beep.volume*100);
return; return;
} }
if(req->action == NN_ENV_BEEPMORSE) {
printf("morse beep: %s, %f Hz %fs %.02f%%\n", req->morseBeep.pattern, req->morseBeep.frequency, req->morseBeep.beepDuration, req->morseBeep.volume*100);
return;
}
if(req->action == NN_ENV_DRAWENERGY) { if(req->action == NN_ENV_DRAWENERGY) {
accumulatedEnergyCost += req->energy; accumulatedEnergyCost += req->energy;
totalEnergyLoss += req->energy; totalEnergyLoss += req->energy;
@@ -440,6 +556,25 @@ void ne_env(nn_EnvironmentRequest *req) {
} }
} }
nn_Exit ne_inetBullshit(nn_InternetRequest *req) {
// welcome to hell
nn_Computer *C = req->computer;
// TODO: make this shi connect to the real internet
// preferrably use libcurl, openssl and a basic blocking socket layer
if(req->action == NN_INTERNET_CONNECT) {
req->connection->state = ne_inetBullshit;
return NN_OK;
}
if(req->action == NN_INTERNET_CLOSE) {
return NN_OK;
}
if(C) nn_setError(C, "inet bullshit: not supported");
return NN_EBADCALL;
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
const char *player = getenv("USER"); const char *player = getenv("USER");
@@ -460,6 +595,7 @@ int main(int argc, char **argv) {
ne_memSand sand; ne_memSand sand;
sand.buf = NULL; sand.buf = NULL;
sand.active = 0;
if(sandboxMem) { if(sandboxMem) {
// 1 MiB pre-allocated to prevent erasing the free-list // 1 MiB pre-allocated to prevent erasing the free-list
@@ -473,6 +609,10 @@ int main(int argc, char **argv) {
SetConfigFlags(FLAG_WINDOW_RESIZABLE); SetConfigFlags(FLAG_WINDOW_RESIZABLE);
InitWindow(800, 600, "NeoNucleus Test Emulator"); InitWindow(800, 600, "NeoNucleus Test Emulator");
const char *tierStr = getenv("NN_TIER");
if(tierStr == NULL) tierStr = "4";
int tier = atoi(tierStr);
// create the universe // create the universe
nn_Universe *u = nn_createUniverse(&ctx, NULL); nn_Universe *u = nn_createUniverse(&ctx, NULL);
@@ -487,7 +627,9 @@ int main(int argc, char **argv) {
nn_setComponentHandler(ocelotCard, sandbox_handler); nn_setComponentHandler(ocelotCard, sandbox_handler);
nn_Component *dataCard = nn_createDataCard(u, NULL, &nn_defaultDataCards[2], NULL, ne_dataBullshit); nn_Component *dataCard = nn_createDataCard(u, NULL, &nn_defaultDataCards[2], NULL, ne_dataBullshit);
nn_Component *modem = nn_createModem(u, NULL, &nn_defaultWiredModem, NULL, ne_modemBullshit); nn_Component *modem = nn_createModem(u, NULL, &nn_defaultWirelessModems[1], NULL, ne_modemBullshit);
nn_Component *tunnel = nn_createTunnel(u, NULL, &nn_defaultTunnel, NULL, ne_tunnelBullshit);
nn_Component *inet = nn_createInternet(u, NULL, &nn_defaultInternetCard, NULL, ne_inetBullshit);
char *eepromCode = (char *)minBIOS; char *eepromCode = (char *)minBIOS;
size_t eepromSize = strlen(minBIOS); size_t eepromSize = strlen(minBIOS);
@@ -512,20 +654,12 @@ int main(int argc, char **argv) {
nn_Component *eepromCard = ncl_createEEPROM(u, NULL, &nn_defaultEEPROMs[3], eepromCode, eepromSize, false); nn_Component *eepromCard = ncl_createEEPROM(u, NULL, &nn_defaultEEPROMs[3], eepromCode, eepromSize, false);
nn_Filesystem mainfsconf;
nn_Filesystem fsparts[] = {
nn_defaultFilesystems[3],
nn_defaultFilesystems[3],
nn_defaultFilesystems[3],
};
nn_mergeFilesystems(&mainfsconf, fsparts, sizeof(fsparts) / sizeof(fsparts[0]));
char mainfspath[NN_MAX_PATH]; char mainfspath[NN_MAX_PATH];
snprintf(mainfspath, NN_MAX_PATH, "data/%s", mainDir); snprintf(mainfspath, NN_MAX_PATH, "data/%s", mainDir);
nn_Component *managedfs = ncl_createFilesystem(u, NULL, mainfspath, &mainfsconf, true); nn_Component *managedfs = ncl_createFilesystem(u, NULL, mainfspath, &nn_defaultFilesystems[tier-1], true);
//nn_Component *tmpfs = ncl_createTmpFS(u, NULL, &nn_defaultTmpFS, NCL_FILECOST_DEFAULT, false); nn_Component *tmpfs = ncl_createTmpFS(u, NULL, &nn_defaultTmpFS, NCL_FILECOST_DEFAULT, false);
nn_Component *tmpfs = ncl_createFilesystem(u, NULL, "/tmp", &mainfsconf, false);
nn_Component *testingfs = ncl_createFilesystem(u, NULL, "aux", &nn_defaultFilesystems[3], false); nn_Component *testingfs = ncl_createFilesystem(u, NULL, "aux", &nn_defaultFilesystems[3], false);
nn_Component *debugfs = ncl_createTmpFS(u, NULL, &nn_defaultFilesystems[3], NCL_FILECOST_DEFAULT, false);
const char * const testDriveData = const char * const testDriveData =
"local g, s = component.list('gpu')(), component.list('screen')()\n" "local g, s = component.list('gpu')(), component.list('screen')()\n"
@@ -552,18 +686,18 @@ int main(int argc, char **argv) {
"while computer.uptime() < now + 3 do computer.pullSignal(0.05) end\n" "while computer.uptime() < now + 3 do computer.pullSignal(0.05) end\n"
"computer.shutdown(true)\n" "computer.shutdown(true)\n"
; ;
nn_Component *testDrive = ncl_createDrive(u, NULL, &nn_defaultDrives[3], testDriveData, strlen(testDriveData), false); nn_Component *testDrive = ncl_createDrive(u, NULL, &nn_defaultDrives[tier-1], testDriveData, strlen(testDriveData), false);
nn_Component *testFlash = ncl_createFlash(u, NULL, &nn_defaultSSDs[3], testDriveData, strlen(testDriveData), false); nn_Component *testFlash = ncl_createFlash(u, NULL, &nn_defaultSSDs[tier-1], testDriveData, strlen(testDriveData), false);
ncl_setCLabel(eepromCard, "EEPROM"); ncl_setCLabel(eepromCard, "EEPROM");
ncl_setCLabel(managedfs, "Main Filesystem"); ncl_setCLabel(managedfs, "Main Filesystem");
ncl_setCLabel(testingfs, "Secondary Filesystem"); ncl_setCLabel(testingfs, "Secondary Filesystem");
ncl_setCLabel(testDrive, "Unmanaged Storage"); ncl_setCLabel(testDrive, "Unmanaged Storage");
ncl_setCLabel(testFlash, "Flash Storage"); ncl_setCLabel(testFlash, "Flash Storage");
ncl_setCLabel(debugfs, "Debug Filesystem");
size_t ramTotal = 0; size_t ramTotal = 0;
ramTotal += 4 * nn_ramSizes[5]; ramTotal += 4 * nn_ramSizes[tier*2-1];
//ramTotal += nn_ramSizes[0];
SetExitKey(KEY_NULL); SetExitKey(KEY_NULL);
@@ -576,10 +710,6 @@ int main(int argc, char **argv) {
if(getenv("NN_TICKDELAY") != NULL) { if(getenv("NN_TICKDELAY") != NULL) {
tickDelay = atof(getenv("NN_TICKDELAY")); tickDelay = atof(getenv("NN_TICKDELAY"));
} }
if(getenv("NN_FAST") != NULL) {
tickDelay = 0;
noIdle = true;
}
struct {int key; nn_codepoint unicode;} keybuf[512]; struct {int key; nn_codepoint unicode;} keybuf[512];
memset(keybuf, 0, sizeof(keybuf)); memset(keybuf, 0, sizeof(keybuf));
@@ -589,22 +719,30 @@ int main(int argc, char **argv) {
double nextSecond = 0; double nextSecond = 0;
double wattage = 0; double wattage = 0;
nn_Component *screen = ncl_createScreen(u, NULL, &nn_defaultScreens[2]); nn_Component *screen = ncl_createScreen(u, NULL, &nn_defaultScreens[tier-1]);
nn_Component *gpuCard = ncl_createGPU(u, NULL, &nn_defaultGPUs[2]); nn_GPU gpuConf = nn_defaultGPUs[tier-1];
nn_Component *gpuCard = ncl_createGPU(u, NULL, &gpuConf);
nn_Component *keyboard = nn_createComponent( nn_Component *keyboard = nn_createComponent(
u, "mainKB", "keyboard"); u, "mainKB", "keyboard");
ncl_ScreenState *scrstate = nn_getComponentState(screen); ncl_ScreenState *scrstate = nn_getComponentState(screen);
ncl_mountKeyboard(scrstate, "mainKB"); ncl_mountKeyboard(scrstate, "mainKB");
nn_Computer *c = nn_createComputer(u, NULL, NULL, ramTotal, 256, 256); // we assume server basically
nn_Computer * volatile c = nn_createComputer(u, NULL, NULL, ramTotal, nn_defaultComponentLimits[tier-1] * 4, 256);
nn_Environment cEnv = { nn_Environment cEnv = {
.userdata = NULL, .userdata = NULL,
.handler = ne_env, .handler = ne_env,
}; };
nn_setComputerEnvironment(c, cEnv); nn_setComputerEnvironment(c, cEnv);
nn_setCallBudget(c, 0); // 5 cuz CPU + each RAM stick all have same tier call budgets
nn_setCallBudget(c, nn_defaultCallBudgets[tier-1] * 5);
nn_setTotalEnergy(c, allEnergy); nn_setTotalEnergy(c, allEnergy);
if(getenv("NN_FAST") != NULL) {
tickDelay = 0;
noIdle = true;
nn_setCallBudget(c, 0);
}
nn_setArchitecture(c, &arch); nn_setArchitecture(c, &arch);
nn_addSupportedArchitecture(c, &arch); nn_addSupportedArchitecture(c, &arch);
@@ -635,8 +773,12 @@ int main(int argc, char **argv) {
nn_mountComponent(c, testFlash, 5, false); nn_mountComponent(c, testFlash, 5, false);
nn_mountComponent(c, dataCard, 6, false); nn_mountComponent(c, dataCard, 6, false);
nn_mountComponent(c, modem, 7, false); nn_mountComponent(c, modem, 7, false);
nn_mountComponent(c, tunnel, 8, false);
nn_mountComponent(c, debugfs, 9, false);
nn_mountComponent(c, inet, 10, false);
int ltx = 0, lty = 0; int ltx = 0, lty = 0;
double scrollBuf = 0; double scrollBuf = 0;
double tickTime = 0;
SetTargetFPS(60); SetTargetFPS(60);
while(true) { while(true) {
if(WindowShouldClose()) break; if(WindowShouldClose()) break;
@@ -741,6 +883,8 @@ int main(int argc, char **argv) {
statY += 20; statY += 20;
DrawText(TextFormat("VM mem usage: %.2f%%", memUsagePercent), 10, statY, 20, GREEN); DrawText(TextFormat("VM mem usage: %.2f%%", memUsagePercent), 10, statY, 20, GREEN);
statY += 20; statY += 20;
DrawText(TextFormat("Tick time: %.5fs", tickTime), 10, statY, 20, (tickTime < tickDelay || tickDelay == 0) ? GREEN : RED);
statY += 20;
} }
EndDrawing(); EndDrawing();
@@ -809,8 +953,17 @@ int main(int argc, char **argv) {
if(noIdle) nn_resetIdleTime(c); if(noIdle) nn_resetIdleTime(c);
// OC computers consume 0.5W when running, 0.05W when running but idle // OC computers consume 0.5W when running, 0.05W when running but idle
nn_removeEnergy(c, nn_isComputerIdle(c) ? 0.05 : 0.5); double normalPowerUsage = 0.5, idlePowerUsage = 0.05;
nn_Exit e = nn_tick(c); nn_Exit e = nn_tick(c);
tickTime = GetTime() - tickNow;
if(tickTime < tickDelay) {
double working = tickTime / tickDelay;
nn_removeEnergy(c, normalPowerUsage * working + idlePowerUsage * (1 - working));
} else if(tickDelay == 0) {
nn_removeEnergy(c, normalPowerUsage);
} else {
nn_removeEnergy(c, normalPowerUsage * tickTime / tickDelay);
}
if(e != NN_OK) { if(e != NN_OK) {
printf("error: %s\n", nn_getError(c)); printf("error: %s\n", nn_getError(c));
goto cleanup; goto cleanup;
@@ -860,11 +1013,17 @@ cleanup:;
nn_dropComponent(keyboard); nn_dropComponent(keyboard);
nn_dropComponent(dataCard); nn_dropComponent(dataCard);
nn_dropComponent(modem); nn_dropComponent(modem);
nn_dropComponent(tunnel);
nn_dropComponent(debugfs);
nn_dropComponent(inet);
// rip the universe // rip the universe
nn_destroyUniverse(u); nn_destroyUniverse(u);
ncl_destroyGlyphCache(gc); ncl_destroyGlyphCache(gc);
if(eepromPath != NULL) free(eepromCode); if(eepromPath != NULL) free(eepromCode);
CloseWindow(); CloseWindow();
free(sand.buf); free(sand.buf);
if(sand.buf != NULL) {
printf("Leaked: %zu bytes\n", sand.active);
}
return 0; return 0;
} }

File diff suppressed because it is too large Load Diff

View File

@@ -258,35 +258,29 @@ typedef struct ncl_ComponentStat {
// specific properties // specific properties
union { union {
struct { struct {
const nn_EEPROM *conf;
size_t codeUsed; size_t codeUsed;
size_t dataUsed; size_t dataUsed;
} eeprom; } eeprom;
struct { struct {
const nn_Filesystem *conf;
size_t spaceUsed; size_t spaceUsed;
size_t realDiskUsage; size_t realDiskUsage;
size_t filesOpen; size_t filesOpen;
const char *path; const char *path;
} fs; } fs;
struct { struct {
const nn_Drive *conf;
size_t lastSector; size_t lastSector;
} drive; } drive;
struct { struct {
const nn_NandFlash *conf;
size_t currentWriteCount; size_t currentWriteCount;
double wearlevel; double wearlevel;
} flash; } flash;
struct { struct {
const nn_GPU *conf;
size_t vramFree; size_t vramFree;
size_t bufferCount; size_t bufferCount;
// can be NULL if there is none // can be NULL if there is none
const char *boundScreen; const char *boundScreen;
} gpu; } gpu;
struct { struct {
const nn_ScreenConfig *conf;
ncl_ScreenState *state; ncl_ScreenState *state;
ncl_ScreenFlags flags; ncl_ScreenFlags flags;
int viewportWidth; int viewportWidth;
@@ -333,6 +327,8 @@ void ncl_unlockScreen(ncl_ScreenState *state);
void ncl_resetScreen(ncl_ScreenState *state); void ncl_resetScreen(ncl_ScreenState *state);
void ncl_getScreenResolution(const ncl_ScreenState *state, size_t *width, size_t *height); void ncl_getScreenResolution(const ncl_ScreenState *state, size_t *width, size_t *height);
void ncl_setScreenResolution(ncl_ScreenState *state, size_t width, size_t height); void ncl_setScreenResolution(ncl_ScreenState *state, size_t width, size_t height);
void ncl_getScreenMaxResolution(const ncl_ScreenState *state, size_t *width, size_t *height);
nn_Exit ncl_setScreenMaxResolution(ncl_ScreenState *state, size_t width, size_t height);
void ncl_getScreenViewport(const ncl_ScreenState *state, size_t *width, size_t *height); void ncl_getScreenViewport(const ncl_ScreenState *state, size_t *width, size_t *height);
void ncl_setScreenViewport(ncl_ScreenState *state, size_t width, size_t height); void ncl_setScreenViewport(ncl_ScreenState *state, size_t width, size_t height);
ncl_Pixel ncl_getScreenPixel(const ncl_ScreenState *state, int x, int y); ncl_Pixel ncl_getScreenPixel(const ncl_ScreenState *state, int x, int y);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff