Compare commits

..

60 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
edca903416 progress on modem and data card 2026-05-05 19:08:31 +03:00
d5a7869d12 improved the makefile 2026-05-05 16:38:36 +03:00
94033f3520 removing deviceinfo 2026-05-04 18:03:10 +03:00
eef7568185 device info 2026-05-04 17:57:35 +03:00
4a5f1d4d07 data card and stuff
anything but device info
2026-05-03 23:30:18 +03:00
cb0dcf03ad drive power, to be revisited 2026-05-03 16:01:31 +03:00
de1553725f new thing to do 2026-05-03 15:56:23 +03:00
571ccb3e3d more balanced power usage and computer environments 2026-05-03 15:55:11 +03:00
cd10093673 patched rendering bug 2026-05-02 00:18:12 +03:00
59d3764825 fixed some oversights 2026-05-01 23:05:27 +03:00
08d1751016 Makefile 2026-05-01 22:40:51 +03:00
d1e2f6c770 fixed uninit memory 2026-05-01 18:09:16 +03:00
45ec779d4d eeprom is complete 2026-05-01 17:57:26 +03:00
223bdf27f6 basic TODO 2026-05-01 17:04:38 +03:00
17656b9805 made it easier to check for compat 2026-05-01 16:55:50 +03:00
f255b417e4 match OC behavior a bit more 2026-05-01 16:21:09 +03:00
b209a033cf THE DOCS WERE WRONG 2026-05-01 16:04:34 +03:00
db30cf4fff improved color mapping algorithm 2026-05-01 14:39:37 +02:00
dff8506d50 removed some debug code 2026-05-01 15:38:36 +03:00
20b9f0d4f1 bugfixes and improvements
credit to blendi for fixing the depth mapper
2026-05-01 15:28:12 +03:00
2b09e5a4f0 frostos works 2026-05-01 14:44:15 +03:00
6834318639 scrolling events 2026-05-01 14:27:29 +03:00
16 changed files with 4135 additions and 1130 deletions

11
.gitignore vendored
View File

@@ -1,5 +1,16 @@
# Zig stuff
.zig-cache/
zig-out/
# Data dir
!data/.gitkeep
# Makefile build
build/*
!build/.gitkeep
# VScode slop
.vscode/
# Random logs some tools may output
*.log
# Makefile output
/neonucleus
/libneonucleus.so
/libneonucleus.a

3
.gitmodules vendored
View File

@@ -13,3 +13,6 @@
path = foreign/lua52
url = https://github.com/lua/lua
branch = v5-2
[submodule "data/moirai"]
path = data/moirai
url = https://gitea.codersquack.nl/NeoFlock/moirai

81
Makefile Normal file
View File

@@ -0,0 +1,81 @@
BIN=neonucleus
DYNLIB=libneonucleus.so
LIB=libneonucleus.a
CC=cc
LD=$(CC)
AR=ar
RANLIB=ranlib
WARN=-Wall -Werror -Wno-format-truncation
ifeq ($(MODE), release)
OPT=-Oz
DEBUG=
else ifeq ($(MODE), release-lto)
OPT=-Oz -flto
DEBUG=
else
OPT=-O0
SANITIZE=undefined,address
DEBUG=-g
endif
NN_STD=gnu99
EMU_STD=gnu23
NNFLAGS=
SANITIZE_FLAGS=
ifdef SANITIZE
SANITIZE_FLAGS += -fsanitize=$(SANITIZE)
endif
# 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)
LINKRAYLIB=-lraylib
INCLUA=-I /usr/include/lua5.3
LINKLUA=-llua5.3
LINKLIBM=-lm
LINKLIBC=
BUILD_DIR=build
SRC_DIR=src
all: bin lib dynlib
$(BUILD_DIR)/neonucleus.o: $(SRC_DIR)/neonucleus.c $(SRC_DIR)/neonucleus.h
$(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
$(CC) -o $(BUILD_DIR)/ncomplib.o -c $(SRC_DIR)/ncomplib.c $(CFLAGS) -std=$(NN_STD)
nn: $(BUILD_DIR)/neonucleus.o $(BUILD_DIR)/ncomplib.o
$(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) -std=$(EMU_STD)
$(BUILD_DIR)/glyphcache.o: $(SRC_DIR)/glyphcache.c $(SRC_DIR)/glyphcache.h
$(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
$(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
$(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)
lib: nn
$(AR) rc $(LIB) $(BUILD_DIR)/neonucleus.o $(BUILD_DIR)/ncomplib.o
$(RANLIB) $(LIB)
dynlib: nn
$(LD) $(LDFLAGS) -o $(DYNLIB) -shared $(BUILD_DIR)/neonucleus.o $(BUILD_DIR)/ncomplib.o $(LINKLIBM) $(LINKLIBC)
cleancache:
rm -rf $(BUILD_DIR)/*.o
clean:
rm -rf $(BIN) $(DYNLIB) $(LIB)

51
TODO.md
View File

@@ -1,35 +1,11 @@
# For MVP functionality
- fix coloring issue in UlOS 2 beta 5 (and in OpenOS `dmesg`, it appears color-mapper is wrong)
- fix FrostOS crash due to empty syscalls table (unsure what is happening)
- make `computer` component use callbacks
- make a lot of stuff which likely will need to be indirect actually be indirect (modem comms, computer funcs)
- make beeps, power, etc. be callbacks on one shared local state (computer environment)
- write a tester OS, basically a menu with tests to run
- finish tmpfs (rework the whole thing)
- device info
- 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
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)
- `modem` component
- `tunnel` component
- `computer` component
- `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`)
- `computer` component
- `geolyzer` component
- `net_splitter` component
- `redstone` component
- `motion_sensor` component
- `printer3d` component (note: maybe add signal for when printer completes?)
@@ -55,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)
- `iron_noteblock` component
- `colorful_lamp` component
- OpenSolidState flash storage
# To make it good
- 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
- ensure OOMs are recoverable
- do a hude audit for bugs at some point
@@ -67,9 +43,11 @@ Not everything OC has (as a few of them are really MC-centered) but most of it.
# To make it fast
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
- use more arenas if possible
- reduce pointer nesting, not only for simplicity, but so we spend less time reading from main memory
# Component extensions
@@ -150,9 +128,9 @@ The `vt` component has:
- `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
- `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
- `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
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
@@ -167,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:
- `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
- `getCapacity(): integer`, gets the capacity of 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
- `maxReadSize(): integer`, to return the maximum size of a read
- `read(len: integer): string`, to read some data. Does wrap around
@@ -193,7 +172,9 @@ Tape has high capacity, CDs do not.
### 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
@@ -233,3 +214,11 @@ TODO: interface
## OLED/IPU
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.

0
aux/.gitkeep Normal file
View File

View File

@@ -17,10 +17,9 @@ fn addEngineSources(b: *std.Build, opts: LibBuildOpts) *std.Build.Module {
.strip = if (opts.optimize == .Debug) false else true,
.unwind_tables = if (opts.optimize == .Debug) null else .none,
.pic = true,
.sanitize_c = if(strict) .full else null,
.sanitize_c = if (strict) .full else null,
});
dataMod.addCSourceFiles(.{
.files = &[_][]const u8{
"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?
// TODO: make it not break
const raylib = b.addSystemCommand(&.{ "zig", "build"});
const raylib = b.addSystemCommand(&.{ "zig", "build" });
raylib.setCwd(b.path("foreign/raylib/"));
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
const c = b.addLibrary(.{
.name = "lua",
.root_module = b.addModule("luamod", .{
.link_libc = true,
.optimize = .ReleaseFast,
.target = target,
}),
.root_module = b.addModule("luamod", .{ .link_libc = true, .optimize = .ReleaseFast, .target = target, .pic = true }),
.linkage = .static,
});

0
build/.gitkeep Normal file
View File

View File

@@ -61,12 +61,11 @@ do
-- backwards compatibility, may remove later
local eeprom = component.list("eeprom")()
local bootAddr = nil
computer.getBootAddress = function()
return bootAddr
return boot_invoke(eeprom, "getData")
end
computer.setBootAddress = function(address)
bootAddr = address
boot_invoke(eeprom, "setData", address)
end
do

1
data/moirai Submodule

Submodule data/moirai added at b2f75735a1

View File

@@ -73,6 +73,9 @@ static nn_Exit luaArch_luaToNN(luaArch *arch, lua_State *L, int luaIdx) {
if(lua_isboolean(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));
return NN_EBADSTATE;
}
@@ -112,11 +115,36 @@ static void luaArch_nnToLua(luaArch *arch, lua_State *L, size_t nnIdx) {
nn_popn(C, len * 2);
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));
}
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};
if(lua_isnumber(L, 1)) {
beep.frequency = lua_tonumber(L, 1);
@@ -133,7 +161,8 @@ static int luaArch_computer_beep(lua_State *L) {
if(beep.frequency > 20000) beep.frequency = 20000;
if(beep.duration > 5) beep.duration = 5;
if(beep.volume > 1) beep.volume = 1;
nn_setComputerBeep(luaArch_from(L)->computer, beep);
nn_beepComputer(luaArch_from(L)->computer, beep);
nn_addIdleTime(luaArch_from(L)->computer, beep.duration);
return 0;
}
@@ -298,6 +327,27 @@ fail:
return 0;
}
static int luaArch_computer_getDeviceInfo(lua_State *L) {
luaArch *arch = luaArch_from(L);
nn_Computer *C = arch->computer;
lua_createtable(L, 0, 0);
for(size_t i = 0;; i++) {
const char *devAddr = nn_deviceInfoAt(C, i);
if(devAddr == NULL) break;
size_t len;
const nn_DeviceField *fields = nn_getDeviceInfo(C, i, &len);
lua_pushstring(L, devAddr);
lua_createtable(L, 0, len);
for(size_t j = 0; j < len; j++) {
lua_pushstring(L, fields[j].name);
lua_pushstring(L, fields[j].value);
lua_settable(L, -3);
}
lua_settable(L, -3);
}
return 1;
}
static int luaArch_component_list(lua_State *L) {
luaArch *arch = luaArch_from(L);
lua_createtable(L, 64, 0);
@@ -571,6 +621,84 @@ static int luaArch_unicode_wtrunc(lua_State *L) {
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) {
lua_createtable(L, 0, 10);
int computer = lua_gettop(L);
@@ -612,7 +740,10 @@ static void luaArch_loadEnv(lua_State *L) {
lua_setfield(L, computer, "pushSignal");
lua_pushcfunction(L, luaArch_computer_popSignal);
lua_setfield(L, computer, "popSignal");
lua_pushcfunction(L, luaArch_computer_getDeviceInfo);
lua_setfield(L, computer, "getDeviceInfo");
lua_setglobal(L, "computer");
lua_createtable(L, 0, 10);
int component = lua_gettop(L);
lua_pushcfunction(L, luaArch_component_list);
@@ -632,6 +763,7 @@ static void luaArch_loadEnv(lua_State *L) {
lua_pushcfunction(L, luaArch_component_fields);
lua_setfield(L, component, "fields");
lua_setglobal(L, "component");
lua_createtable(L, 0, 10);
int unicode = lua_gettop(L);
lua_pushcfunction(L, luaArch_unicode_char);
@@ -640,26 +772,39 @@ static void luaArch_loadEnv(lua_State *L) {
lua_setfield(L, unicode, "len");
lua_pushcfunction(L, luaArch_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_pushcfunction(L, luaArch_unicode_wtrunc);
lua_setfield(L, unicode, "wtrunc");
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) {
nn_Computer *computer = req->computer;
luaArch *arch = req->localState;
nn_Context *ctx = nn_getComputerContext(computer);
// the memory scale, used to give Lua extra memory in 64-bit systems
// due to it also using more memory.
double memScale = sizeof(void *) > 4 ? 1.8 : 1;
switch(req->action) {
case NN_ARCH_FREEMEM:
req->freeMemory = arch->freeMem / nn_getMemoryScale(computer);
req->freeMemory = arch->freeMem / memScale;
return NN_OK;
case NN_ARCH_INIT:
// wrapped in a block to prevent L from leaking, because L is common in Lua code so it may be used by mistake
{
arch = nn_alloc(ctx, sizeof(*arch));
arch->freeMem = nn_getTotalMemory(computer) * nn_getMemoryScale(computer);
arch->freeMem = nn_getTotalMemory(computer) * memScale;
arch->computer = computer;
#if LUA_VERSION_NUM >= 505L
lua_State *L = lua_newstate(luaArch_alloc, arch, rand());
@@ -707,8 +852,11 @@ static nn_Exit luaArch_handler(nn_ArchitectureRequest *req) {
return NN_OK;
}
return NN_OK;
default:
break;
case NN_ARCH_SERIALIZE:
return nn_pushstring(computer, "lua stuff");
case NN_ARCH_DESERIALIZE:
// do nothing
return NN_OK;
}
return NN_OK;
}

View File

@@ -70,19 +70,79 @@ end
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 t = {pcall(cinvoke, address, method, ...)}
if not _SYNCED then
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 computer.energy() <= 0 then sysyield() end -- out of power
if computer.isOverused() then sysyield() end -- overused
if computer.isIdle() then sysyield() end -- machine idle
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("got", table.unpack(t))
print(string.format("got %d values", #t), table.unpack(t))
end
for i=1,#t do t[i] = sandboxValue(t[i]) end
if t[1] then
return table.unpack(t, 2)
end
@@ -187,11 +247,12 @@ end
function computer.pullSignal(timeout)
timeout = timeout or math.huge
timeout = math.max(timeout, 0.05)
timeout = math.max(timeout, 0.001)
local deadline = computer.uptime() + timeout
while true do
if computer.uptime() >= deadline then return end
local t = {computer.popSignal()}
for i=1,#t do t[i] = unsandboxValue(t[i]) end
if #t == 0 then
sysyield()
else
@@ -286,7 +347,13 @@ sandbox = {
rawlen = rawlen,
rawset = rawset,
select = select,
setmetatable = setmetatable,
setmetatable = function(t, meta)
if type(meta) == "table" then
-- nope
rawset(meta, "__gc", nil)
end
return setmetatable(t, meta)
end,
tonumber = tonumber,
tostring = tostring,
type = type,
@@ -308,8 +375,8 @@ sandbox = {
debug = {
getinfo = debug.getinfo,
traceback = debug.traceback,
getlocal = debug.getlocal,
getupvalue = debug.getupvalue,
getlocal = function(...) return (debug.getlocal(...)) end,
getupvalue = function(...) return (debug.getupvalue(...)) end,
print = debug.print,
},
@@ -420,7 +487,18 @@ sandbox = {
isRobot = computer.isRobot,
maxEnergy = computer.maxEnergy,
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,
setArchitecture = computer.setArchitecture,
shutdown = computer.shutdown,

View File

@@ -6,6 +6,8 @@
#include "neonucleus.h"
#include "ncomplib.h"
#include "glyphcache.h"
#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -40,12 +42,211 @@ static nn_Exit sandbox_handler(nn_ComponentRequest *req) {
return NN_OK;
case NN_COMP_DROP:
return NN_OK;
case NN_COMP_SIGNAL:
case NN_COMP_USERDATA:
return NN_OK;
}
return NN_OK;
}
static nn_Exit ne_dataBullshit(nn_DataCardRequest *req) {
nn_Computer *C = req->computer;
nn_DataCardAction action = req->action;
nn_Exit e;
if(action == NN_DATA_DROP) {
return NN_OK;
}
if(action == NN_DATA_ENCODE64) {
int outSize = 0;
char *out = EncodeDataBase64((const unsigned char *)req->data, req->datalen, &outSize);
if(out == NULL) return NN_ENOMEM;
// -1 because raylib includes the NUL terminator??
e = nn_pushlstring(C, out, outSize-1);
MemFree(out);
return e;
}
if(action == NN_DATA_DECODE64) {
int outSize = 0;
char *out = (char *)DecodeDataBase64(req->data, &outSize);
if(out == NULL) return NN_ENOMEM;
e = nn_pushlstring(C, out, outSize);
MemFree(out);
return e;
}
if(action == NN_DATA_DEFLATE) {
int outSize = 0;
char *out = (char *)CompressData((const unsigned char *)req->data, req->datalen, &outSize);
if(out == NULL) return NN_ENOMEM;
e = nn_pushlstring(C, out, outSize);
MemFree(out);
return e;
}
if(action == NN_DATA_INFLATE) {
int outSize = 0;
char *out = (char *)DecompressData((unsigned char *)req->data, req->datalen, &outSize);
if(out == NULL) return NN_ENOMEM;
e = nn_pushlstring(C, out, outSize);
MemFree(out);
return e;
}
if(action == NN_DATA_CRC32) {
unsigned int check = nn_computeCRC32(req->crc32.data, req->crc32.datalen);
req->crc32.checksum[0] = (check >> 0) & 0xFF;
req->crc32.checksum[1] = (check >> 8) & 0xFF;
req->crc32.checksum[2] = (check >> 16) & 0xFF;
req->crc32.checksum[3] = (check >> 24) & 0xFF;
return NN_OK;
}
if(action == NN_DATA_MD5) {
unsigned int *out = ComputeMD5((unsigned char *)req->md5.data, req->md5.datalen);
if(out == NULL) return NN_ENOMEM;
memcpy(req->md5.checksum, out, 16);
return NN_OK;
}
if(action == NN_DATA_SHA256) {
unsigned int *out = ComputeSHA256((unsigned char *)req->sha256.data, req->sha256.datalen);
if(out == NULL) return NN_ENOMEM;
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;
}
if(action == NN_DATA_RANDOM) {
for(size_t i = 0; i < req->randbuf.buflen; i++) {
req->randbuf.buf[i] = rand();
}
return NN_OK;
}
if(action == NN_DATA_ENCRYPT) {
return nn_pushlstring(C, req->data, req->datalen);
}
if(action == NN_DATA_DECRYPT) {
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");
return NN_EBADCALL;
}
static nn_Exit ne_modemBullshit(nn_ModemRequest *req) {
nn_Computer *C = req->computer;
if(req->action == NN_MODEM_DROP) {
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) {
req->send.strengthSent = req->modem->maxRange;
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");
return NN_EBADCALL;
}
static unsigned char ne_processColorPart(unsigned char channel, double brightness) {
double n = (double)channel / 255;
n *= brightness;
@@ -299,6 +500,7 @@ typedef struct ne_memSand {
char *buf;
size_t used;
size_t cap;
size_t active;
} ne_memSand;
void *ne_sandbox_alloc(void *state, void *memory, size_t oldSize, size_t newSize) {
@@ -308,12 +510,16 @@ void *ne_sandbox_alloc(void *state, void *memory, size_t oldSize, size_t newSize
newSize = ne_alignAlloc(newSize, NN_ALLOC_ALIGN);
// never free
if(newSize == 0) return NULL;
if(newSize == 0) {
sand->active -= oldSize;
return NULL;
}
if(memory == NULL) {
if(sand->cap - sand->used < newSize) return NULL;
// alloc new
void *mem = sand->buf + sand->used;
sand->used += newSize;
sand->active += newSize;
return mem;
}
// realloc
@@ -321,19 +527,54 @@ void *ne_sandbox_alloc(void *state, void *memory, size_t oldSize, size_t newSize
if(sand->cap - sand->used < newSize) return NULL;
void *mem = sand->buf + sand->used;
sand->used += newSize;
sand->active += newSize;
memcpy(mem, memory, oldSize);
return mem;
}
double accumulatedEnergyCost = 0;
double totalEnergyLoss = 0;
// default capacity of a tablet
double allEnergy = 10000;
double ne_energy_accumulator(void *state, nn_Computer *c, double n) {
accumulatedEnergyCost += n;
totalEnergyLoss += n;
return nn_getTotalEnergy(c);
void ne_env(nn_EnvironmentRequest *req) {
if(req->action == NN_ENV_BEEP) {
printf("beep: %f Hz %fs %.02f%%\n", req->beep.frequency, req->beep.duration, req->beep.volume*100);
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) {
accumulatedEnergyCost += req->energy;
totalEnergyLoss += req->energy;
allEnergy -= req->energy;
req->energy = nn_getTotalEnergy(req->computer);
req->energy = allEnergy;
return;
}
}
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) {
const char *player = getenv("USER");
@@ -354,6 +595,7 @@ int main(int argc, char **argv) {
ne_memSand sand;
sand.buf = NULL;
sand.active = 0;
if(sandboxMem) {
// 1 MiB pre-allocated to prevent erasing the free-list
@@ -367,6 +609,10 @@ int main(int argc, char **argv) {
SetConfigFlags(FLAG_WINDOW_RESIZABLE);
InitWindow(800, 600, "NeoNucleus Test Emulator");
const char *tierStr = getenv("NN_TIER");
if(tierStr == NULL) tierStr = "4";
int tier = atoi(tierStr);
// create the universe
nn_Universe *u = nn_createUniverse(&ctx, NULL);
@@ -380,23 +626,40 @@ int main(int argc, char **argv) {
nn_setComponentMethods(ocelotCard, sandboxMethods);
nn_setComponentHandler(ocelotCard, sandbox_handler);
nn_Component *eepromCard = ncl_createEEPROM(u, NULL, &nn_defaultEEPROMs[3], minBIOS, strlen(minBIOS), false);
nn_Component *dataCard = nn_createDataCard(u, NULL, &nn_defaultDataCards[2], NULL, ne_dataBullshit);
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);
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 *eepromCode = (char *)minBIOS;
size_t eepromSize = strlen(minBIOS);
const char *eepromPath = getenv("NN_EEPROM");
if(eepromPath != NULL) {
FILE *eeprom = fopen(eepromPath, "rb");
if(eeprom == NULL) {
fprintf(stderr, "no such eeprom: %s\n", eepromPath);
return 1;
}
fseek(eeprom, 0, SEEK_END);
eepromSize = ftell(eeprom);
fseek(eeprom, 0, SEEK_SET);
eepromCode = malloc(eepromSize);
size_t amount = 0;
while(amount < eepromSize) {
amount += fread(eepromCode + amount, sizeof(char), eepromSize - amount, eeprom);
}
}
nn_Component *eepromCard = ncl_createEEPROM(u, NULL, &nn_defaultEEPROMs[3], eepromCode, eepromSize, false);
char mainfspath[NN_MAX_PATH];
snprintf(mainfspath, NN_MAX_PATH, "data/%s", mainDir);
nn_Component *managedfs = ncl_createFilesystem(u, NULL, mainfspath, &mainfsconf, true);
//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_createTmpFS(u, NULL, &nn_defaultFilesystems[3], NCL_FILECOST_DEFAULT, false);
//nn_Component *testingfs = ncl_createFilesystem(u, NULL, "test", &nn_defaultFilesystems[3], false);
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 *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 =
"local g, s = component.list('gpu')(), component.list('screen')()\n"
@@ -423,16 +686,18 @@ int main(int argc, char **argv) {
"while computer.uptime() < now + 3 do computer.pullSignal(0.05) end\n"
"computer.shutdown(true)\n"
;
nn_Component *testDrive = ncl_createDrive(u, NULL, &nn_defaultDrives[3], testDriveData, strlen(testDriveData), false);
nn_Component *testFlash = ncl_createFlash(u, NULL, &nn_defaultSSDs[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[tier-1], testDriveData, strlen(testDriveData), false);
ncl_setCLabel(eepromCard, "EEPROM");
ncl_setCLabel(managedfs, "Main Filesystem");
ncl_setCLabel(testingfs, "Secondary Filesystem");
ncl_setCLabel(testDrive, "Unmanaged Storage");
ncl_setCLabel(testFlash, "Flash Storage");
ncl_setCLabel(debugfs, "Debug Filesystem");
size_t ramTotal = 0;
ramTotal += 4 * nn_ramSizes[5];
ramTotal += 4 * nn_ramSizes[tier*2-1];
SetExitKey(KEY_NULL);
@@ -445,10 +710,6 @@ int main(int argc, char **argv) {
if(getenv("NN_TICKDELAY") != NULL) {
tickDelay = atof(getenv("NN_TICKDELAY"));
}
if(getenv("NN_FAST") != NULL) {
tickDelay = 0;
noIdle = true;
}
struct {int key; nn_codepoint unicode;} keybuf[512];
memset(keybuf, 0, sizeof(keybuf));
@@ -458,31 +719,48 @@ int main(int argc, char **argv) {
double nextSecond = 0;
double wattage = 0;
nn_Component *screen = ncl_createScreen(u, NULL, &nn_defaultScreens[2]);
nn_Component *gpuCard = ncl_createGPU(u, NULL, &nn_defaultGPUs[2]);
nn_Component *screen = ncl_createScreen(u, NULL, &nn_defaultScreens[tier-1]);
nn_GPU gpuConf = nn_defaultGPUs[tier-1];
nn_Component *gpuCard = ncl_createGPU(u, NULL, &gpuConf);
nn_Component *keyboard = nn_createComponent(
u, "mainKB", "keyboard");
ncl_ScreenState *scrstate = nn_getComponentState(screen);
ncl_mountKeyboard(scrstate, "mainKB");
nn_Computer *c = nn_createComputer(u, NULL, "computer0", ramTotal, 256, 256);
nn_Component *wrappedC = nn_wrapComputer(c);
if(showStats) {
// collects stats
nn_setEnergyHandler(c, NULL, ne_energy_accumulator);
// we assume server basically
nn_Computer * volatile c = nn_createComputer(u, NULL, NULL, ramTotal, nn_defaultComponentLimits[tier-1] * 4, 256);
nn_Environment cEnv = {
.userdata = NULL,
.handler = ne_env,
};
nn_setComputerEnvironment(c, cEnv);
// 5 cuz CPU + each RAM stick all have same tier call budgets
nn_setCallBudget(c, nn_defaultCallBudgets[tier-1] * 5);
nn_setTotalEnergy(c, allEnergy);
if(getenv("NN_FAST") != NULL) {
tickDelay = 0;
noIdle = true;
nn_setCallBudget(c, 0);
}
nn_setCallBudget(c, 0);
// default for 64-bit
if(sizeof(void *) > 4) nn_setMemoryScale(c, 1.8);
nn_setArchitecture(c, &arch);
nn_addSupportedArchitecture(c, &arch);
nn_setTmpAddress(c, nn_getComponentAddress(tmpfs));
nn_mountComponent(c, wrappedC, 255, false);
nn_CommonDeviceInfo cinfo;
nn_clearCommonDeviceInfo(&cinfo);
cinfo.CLASS = NN_DEVICECLASS_SYSTEM;
cinfo.DESC = "The main computer";
cinfo.VENDOR = "NeoNucleus Inc.";
cinfo.PRODUCT = "NeoComputer";
cinfo.VERSION = "0.-1.0";
cinfo.CLOCK = "1 MHz";
cinfo.CAPACITY = "10 kJ";
nn_addCommonDeviceInfo(c, nn_getComputerAddress(c), cinfo);
nn_mountComponent(c, screen, -1, false);
nn_mountComponent(c, ocelotCard, -1, false);
nn_mountComponent(c, tmpfs, -1, false);
@@ -493,7 +771,15 @@ int main(int argc, char **argv) {
nn_mountComponent(c, testingfs, 3, false);
nn_mountComponent(c, testDrive, 4, false);
nn_mountComponent(c, testFlash, 5, false);
nn_mountComponent(c, dataCard, 6, 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;
double scrollBuf = 0;
double tickTime = 0;
SetTargetFPS(60);
while(true) {
if(WindowShouldClose()) break;
@@ -528,8 +814,17 @@ int main(int argc, char **argv) {
offX + (x - 1) * cwidth,
offY + (y - 1) * cheight,
};
ncl_needGlyph(gc, p.codepoint);
DrawRectangle(pos.x, pos.y, cwidth, cheight, ne_processColor(p.bgColor, scrbright));
}
}
for(int y = 1; y <= scrh; y++) {
for(int x = 1; x <= scrw; x++) {
ncl_Pixel p = ncl_getScreenPixel(scrbuf, x, y);
Vector2 pos = {
offX + (x - 1) * cwidth,
offY + (y - 1) * cheight,
};
ncl_needGlyph(gc, p.codepoint);
if(p.codepoint != 0) {
ncl_drawGlyph(gc, p.codepoint, pos, cheight, ne_processColor(p.fgColor, scrbright));
}
@@ -544,20 +839,34 @@ int main(int argc, char **argv) {
int ty = (double)(GetMouseY() - offY) / cheight + 1;
if(tx >= 1 && ty >= 1 && tx <= scrw && ty <= scrh) {
// we only care about left click here
if(IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) {
nn_pushTouch(c, scraddr, tx, ty, 0, player);
}
if(IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) {
nn_pushDrop(c, scraddr, tx, ty, 0, player);
}
if(IsMouseButtonDown(MOUSE_BUTTON_LEFT)) {
if(ltx != tx || lty != ty) {
ltx = tx;
lty = ty;
nn_pushDrag(c, scraddr, tx, ty, 0, player);
struct {int btn; int ocbtn;} btns[] = {
{MOUSE_BUTTON_LEFT, 0},
{MOUSE_BUTTON_RIGHT, 1},
{MOUSE_BUTTON_MIDDLE, 2},
};
size_t btnc = sizeof(btns) / sizeof(btns[0]);
for(size_t i = 0; i < btnc; i++) {
// we only care about left click here
int mbtn = btns[i].btn;
int ocbtn = btns[i].ocbtn;
if(IsMouseButtonPressed(mbtn)) {
nn_pushTouch(c, scraddr, tx, ty, ocbtn, player);
}
if(IsMouseButtonReleased(mbtn)) {
nn_pushDrop(c, scraddr, tx, ty, ocbtn, player);
}
if(IsMouseButtonDown(mbtn)) {
if(ltx != tx || lty != ty) {
nn_pushDrag(c, scraddr, tx, ty, ocbtn, player);
}
}
}
if(fabs(scrollBuf) >= 1) {
nn_pushScroll(c, scraddr, tx, ty, scrollBuf, player);
scrollBuf = 0;
}
ltx = tx;
lty = ty;
}
}
@@ -574,10 +883,14 @@ int main(int argc, char **argv) {
statY += 20;
DrawText(TextFormat("VM mem usage: %.2f%%", memUsagePercent), 10, statY, 20, GREEN);
statY += 20;
DrawText(TextFormat("Tick time: %.5fs", tickTime), 10, statY, 20, (tickTime < tickDelay || tickDelay == 0) ? GREEN : RED);
statY += 20;
}
EndDrawing();
scrollBuf += GetMouseWheelMove();
// keyboard input
// 1: clipboard
@@ -586,11 +899,6 @@ int main(int argc, char **argv) {
if(t != NULL) nn_pushClipboard(c, "mainKB", t, player);
}
if(IsKeyPressed(KEY_TAB)) {
printf("force crashing\n");
nn_forceCrashComputer(c, "get crashed lol");
}
while(1) {
int keycode = GetKeyPressed();
nn_codepoint unicode = GetCharPressed();
@@ -606,9 +914,8 @@ int main(int argc, char **argv) {
if(keycode == KEY_TAB) unicode = '\t';
bool isCtrlPressed = keybuf[KEY_LEFT_CONTROL].key != 0;
if(isCtrlPressed) {
if(keycode == KEY_C) unicode = 3;
if(keycode == KEY_D) unicode = 4;
if(isCtrlPressed && isalpha(keycode)) {
unicode = keycode - 'A' + 1;
}
}
@@ -645,7 +952,18 @@ int main(int argc, char **argv) {
nn_removeEnergy(c, ncl_getScreenEnergyUsage(nn_getComponentState(screen)));
if(noIdle) nn_resetIdleTime(c);
// OC computers consume 0.5W when running, 0.05W when running but idle
double normalPowerUsage = 0.5, idlePowerUsage = 0.05;
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) {
printf("error: %s\n", nn_getError(c));
goto cleanup;
@@ -679,12 +997,6 @@ int main(int argc, char **argv) {
continue;
}
}
nn_Beep beep;
if(nn_getComputerBeep(c, &beep)) {
nn_clearComputerBeep(c);
printf("beep: %f Hz, %fs, %f%% volume\n", beep.frequency, beep.duration, beep.volume * 100);
}
}
cleanup:;
@@ -699,11 +1011,19 @@ cleanup:;
nn_dropComponent(screen);
nn_dropComponent(gpuCard);
nn_dropComponent(keyboard);
nn_dropComponent(wrappedC);
nn_dropComponent(dataCard);
nn_dropComponent(modem);
nn_dropComponent(tunnel);
nn_dropComponent(debugfs);
nn_dropComponent(inet);
// rip the universe
nn_destroyUniverse(u);
ncl_destroyGlyphCache(gc);
if(eepromPath != NULL) free(eepromCode);
CloseWindow();
free(sand.buf);
if(sand.buf != NULL) {
printf("Leaked: %zu bytes\n", sand.active);
}
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -258,35 +258,29 @@ typedef struct ncl_ComponentStat {
// specific properties
union {
struct {
const nn_EEPROM *conf;
size_t codeUsed;
size_t dataUsed;
} eeprom;
struct {
const nn_Filesystem *conf;
size_t spaceUsed;
size_t realDiskUsage;
size_t filesOpen;
const char *path;
} fs;
struct {
const nn_Drive *conf;
size_t lastSector;
} drive;
struct {
const nn_NandFlash *conf;
size_t currentWriteCount;
double wearlevel;
} flash;
struct {
const nn_GPU *conf;
size_t vramFree;
size_t bufferCount;
// can be NULL if there is none
const char *boundScreen;
} gpu;
struct {
const nn_ScreenConfig *conf;
ncl_ScreenState *state;
ncl_ScreenFlags flags;
int viewportWidth;
@@ -333,6 +327,8 @@ void ncl_unlockScreen(ncl_ScreenState *state);
void ncl_resetScreen(ncl_ScreenState *state);
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_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_setScreenViewport(ncl_ScreenState *state, size_t width, size_t height);
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