work on network encoding and documenting eventual component interfaces
This commit is contained in:
140
TODO.md
140
TODO.md
@@ -2,8 +2,7 @@
|
||||
|
||||
- implement the RAID merger algorithm (merges multiple drives or filesystems together into a bigger config)
|
||||
- write a tester OS, basically a menu with tests to run
|
||||
- tmpdrive
|
||||
- tmpfs (reork the whole thing)
|
||||
- tmpfs (rework the whole thing)
|
||||
- device info
|
||||
- userdata support
|
||||
|
||||
@@ -49,7 +48,7 @@ Not everything OC has (as a few of them are really MC-centered) but most of it.
|
||||
- `microphone` component, allows reading audio from nearby sources
|
||||
- `tape_drive` component, compatible with Computronics
|
||||
- `cd_drive` to work with CDs (can be read-only, write-only or read-write)
|
||||
- `serial` component, for serial communications with other devices (USB?)
|
||||
- `serial` component, for serial communications with other devices (see Serial)
|
||||
- `iron_noteblock` component
|
||||
- `colorful_lamp` component
|
||||
- OpenSolidState flash storage
|
||||
@@ -67,3 +66,138 @@ NOTE: we're mostly bottlenecked by the architecture (typically a Lua VM) and the
|
||||
|
||||
- make signals use a circular buffer instead of a simple array
|
||||
- use more arenas if possible
|
||||
|
||||
# Unique components
|
||||
|
||||
Subject to change, still being discussed with the other NeoFlock members.
|
||||
|
||||
## Serial
|
||||
|
||||
The `serial` component would have the methods:
|
||||
- `read(len: integer): string`, to read data
|
||||
- `write(data: string): boolean`, to send data
|
||||
- `isConnected(): boolean`, to check if the port is currently connected
|
||||
- `setProtocol(protocol: string): string`, sets the protocol string of our port, which may be truncated.
|
||||
- `getProtocol(): string`, gets the currently registered protocol of our port. Default is `raw`.
|
||||
|
||||
It would also push the following signals:
|
||||
- `serial_connected(portAddress: string, protocol: string)`, when connected
|
||||
- `serial_protocol(portAddress: string, protocol: string)`, when other side changed protocol mid-connection.
|
||||
- `serial_disconnected(portAddress: string)`, when the port is disconnected
|
||||
- `serial_data(portAddress: string, amount: integer)`, when new data is available (and how much)
|
||||
|
||||
## Radio
|
||||
|
||||
For very large-distance telecommunications. Frequency is in Hz.
|
||||
|
||||
The `radio_controller` component, which enables listening to connected radio towers
|
||||
- `isOpen(): boolean`, whether listening is enabled
|
||||
- `open(): boolean`, to open for listening
|
||||
- `close(): boolean`, to close the listener
|
||||
- `setRange(minimum: number, maximum: number): boolean`, sets the frequency range we listen for.
|
||||
- `getRange(): number, number`, gets the frequency range we listen for.
|
||||
|
||||
The `radio_tower` component, which actually receives and sends the waves
|
||||
- `isEmitter(): boolean`, whether this tower can, in fact, send radio waves
|
||||
- `minFrequency(): number`, the minimum frequency it can detect and transmit
|
||||
- `maxFrequency(): number`, the maximum frequency it can detect and transmit
|
||||
- `getFrequencyResolution(): integer`, how many frequencies can be listened for in that range. The frequencies sent to the controller are rounded to one of
|
||||
these
|
||||
- `getRangeOf(frequency: number): number`, to get the maximum range for a given frequency
|
||||
- `maxPacketSize(): integer`, to get the maximum size that can be sent at once, typically 32KiB.
|
||||
- `getBaseFrequency(): number`, gets the base frequency of radio communications, typically 100 kHz.
|
||||
- `emit(frequency: number, data: string): boolean`, to emit a radio packet on a given frequency. The frequency must be in range, and will be rounded to one
|
||||
within resolution
|
||||
|
||||
Radio waves travel at a (likely configurable) speed and have extremely large range on lower frequencies.
|
||||
They can also have bit flips.
|
||||
|
||||
The range and speed are based off the frequency range. Given base frequency f0, base energy cost e0, base range r0, we can compute energy cost e, range r of a
|
||||
given frequency f using:
|
||||
- `r = r0 / (f / f0)`
|
||||
- `e = e0 * (f / f0)`
|
||||
|
||||
When one is received, it will push:
|
||||
- `radio_message(localControllerAddress: string, localTowerAddress: string, distance: number, frequency: number, data: string)`, where frequency is rounded to
|
||||
within the resolution of the tower. It does ensure the frequency is within the controller range and the controller is open. Data may be corrupted by bit flips.
|
||||
Distance is in meters. As you can notice, there is no sender address. It is the responsibility of the protocol to identify and verify senders.
|
||||
|
||||
## VT
|
||||
|
||||
A generic virtual terminal. Like a GPU + screen, but with no VRAM.
|
||||
Has a 256-color palette representing ANSI 256 colors.
|
||||
Subsequently, colors 0-15 represent ANSI escape colors.
|
||||
The entire palette is editable.
|
||||
|
||||
The `vt` component has:
|
||||
- `getResolution(): integer, integer`, to get the resolution. Cannot be changed
|
||||
- `getDepth(): integer`, to get the color depth. Cannot be changed
|
||||
- `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
|
||||
- `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
|
||||
- `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
|
||||
- `get(idx: integer, len: integer): string`, to get the contents of part of the VT's unicode buffer, encoded as UTF-8
|
||||
|
||||
It also pushes the signals of keyboards and screens (no precise) for using input.
|
||||
|
||||
The unicode buffer is a 0-indexed buffer of codepoints alongside the color data. When writing to the unicode buffer, it also writes to the color buffer.
|
||||
If `x` and `y` are 0-indexed, then `x + y * width` is the index in the buffer
|
||||
|
||||
## CD drives
|
||||
|
||||
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)
|
||||
- `isDiskErasable(): boolean`, to check whether the disk is erasable, which requires support from both the disk and the drive
|
||||
- `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
|
||||
- `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
|
||||
- `write(data: string): boolean`, to write some data. Does wrap around.
|
||||
|
||||
It also pushes the signals:
|
||||
- `cd_added(driveAddress: string)`, for when a disk is added
|
||||
- `cd_removed(driveAddress: string)`, for when a disk is removed
|
||||
|
||||
### Writing to non-erasable drives
|
||||
|
||||
Instead of erroring out, it instead ORs the bits between what was there and what is written. This means you can still write the initial contents,
|
||||
as the CD starts out 0'd, but subsequent writes are likely to corrupt previous.
|
||||
|
||||
### CDs vs tape
|
||||
|
||||
Tape is slow. CDs are fast.
|
||||
Tape is always fully writable. CDs may not always be erasable.
|
||||
Tape has high capacity, CDs do not.
|
||||
|
||||
### CDs vs unmanaged floppies
|
||||
|
||||
CDs are slower for random reads as they have no cache.
|
||||
|
||||
## LED
|
||||
|
||||
The `led_matrix` component has:
|
||||
- `getResolution(): integer, integer`, gets the resolution of the LED matrix.
|
||||
- `getIntensity(x: integer, y: integer): number`, gets the intensity of an LED.
|
||||
- `setIntensity(x: integer, y: integer, intensity: number): number`, sets the intensity of an LED and returns the old one.
|
||||
|
||||
Intensity is between 0 (off) and 1 (full brightness).
|
||||
An LED matrix has really high call budget, thus it is ideal for frequency updating status updates.
|
||||
|
||||
## Speaker
|
||||
|
||||
TODO: interface
|
||||
|
||||
## Microphone
|
||||
|
||||
TODO: interface
|
||||
|
||||
## OLED/IPU
|
||||
|
||||
TODO: interface
|
||||
|
||||
39
src/main.c
39
src/main.c
@@ -356,7 +356,7 @@ int main(int argc, char **argv) {
|
||||
InitWindow(800, 600, "NeoNucleus Test Emulator");
|
||||
|
||||
// create the universe
|
||||
nn_Universe *u = nn_createUniverse(&ctx);
|
||||
nn_Universe *u = nn_createUniverse(&ctx, NULL);
|
||||
|
||||
nn_Architecture arch = getLuaArch();
|
||||
|
||||
@@ -370,9 +370,17 @@ int main(int argc, char **argv) {
|
||||
|
||||
nn_Component *eepromCard = ncl_createEEPROM(u, NULL, &nn_defaultEEPROMs[3], minBIOS, strlen(minBIOS), 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];
|
||||
snprintf(mainfspath, NN_MAX_PATH, "data/%s", mainDir);
|
||||
nn_Component *managedfs = ncl_createFilesystem(u, NULL, mainfspath, &nn_defaultFilesystems[3], true);
|
||||
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 *testingfs = ncl_createTmpFS(u, NULL, &nn_defaultFilesystems[3], NCL_FILECOST_DEFAULT, false);
|
||||
|
||||
@@ -400,7 +408,12 @@ 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_defaultSSDs[3], testDriveData, strlen(testDriveData), false);
|
||||
nn_Drive driveconf;
|
||||
nn_Drive driveparts[] = {
|
||||
nn_floppySSD,
|
||||
};
|
||||
nn_mergeDrives(&driveconf, driveparts, sizeof(driveparts) / sizeof(driveparts[0]));
|
||||
nn_Component *testDrive = ncl_createDrive(u, NULL, &driveconf, testDriveData, strlen(testDriveData), false);
|
||||
|
||||
ncl_setCLabel(managedfs, "Main Filesystem");
|
||||
ncl_setCLabel(testingfs, "Secondary Filesystem");
|
||||
@@ -452,6 +465,22 @@ restart:;
|
||||
nn_setEnergyHandler(c, NULL, ne_energy_accumulator);
|
||||
}
|
||||
nn_setCallBudget(c, 0);
|
||||
|
||||
nn_EncodedNetworkContents contents;
|
||||
nn_pushstring(c, "stuff");
|
||||
nn_pushnull(c);
|
||||
nn_pushnumber(c, 5.3);
|
||||
nn_pushbool(c, false);
|
||||
nn_encodeNetworkContents(c, &contents, 4);
|
||||
|
||||
nn_dropNetworkContents(&contents);
|
||||
|
||||
printf("size: %zu\n", contents.buflen);
|
||||
for(size_t i = 0; i < contents.buflen; i++) {
|
||||
unsigned char byte = contents.buf[i];
|
||||
printf("%02X ", byte);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// default for 64-bit
|
||||
if(sizeof(void *) > 4) nn_setMemoryScale(c, 1.8);
|
||||
@@ -468,7 +497,7 @@ restart:;
|
||||
nn_mountComponent(c, eepromCard, 0);
|
||||
nn_mountComponent(c, managedfs, 1);
|
||||
nn_mountComponent(c, gpuCard, 2);
|
||||
nn_mountComponent(c, testingfs, 3);
|
||||
//nn_mountComponent(c, testingfs, 3);
|
||||
nn_mountComponent(c, testDrive, 4);
|
||||
while(true) {
|
||||
if(WindowShouldClose()) break;
|
||||
@@ -576,6 +605,8 @@ restart:;
|
||||
nextTick = tickNow + tickDelay;
|
||||
nn_clearstack(c);
|
||||
|
||||
nn_removeEnergy(c, ncl_getScreenEnergyUsage(nn_getComponentState(screen)));
|
||||
|
||||
if(getenv("NN_NOIDLE") != NULL) nn_resetIdleTime(c);
|
||||
nn_Exit e = nn_tick(c);
|
||||
if(e != NN_OK) {
|
||||
|
||||
@@ -3186,6 +3186,20 @@ const char *ncl_getKeyboard(ncl_ScreenState *state,
|
||||
return state->keyboards[idx];
|
||||
}
|
||||
|
||||
double ncl_getScreenEnergyUsage(ncl_ScreenState *state) {
|
||||
double sum = 0;
|
||||
for(int y = 1; y <= state->viewportHeight; y++) {
|
||||
for(int x = 1; x <= state->viewportWidth; x++) {
|
||||
ncl_Pixel p = ncl_getScreenPixel(state, x, y);
|
||||
sum += state->conf.energyPerPixel * nn_colorLuminance(p.bgColor);
|
||||
if(p.codepoint != 0 && p.codepoint != ' ') {
|
||||
sum += state->conf.energyPerPixel * nn_colorLuminance(p.fgColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
// general stuff
|
||||
|
||||
bool ncl_isNCLID(const char *type) {
|
||||
|
||||
@@ -334,5 +334,6 @@ nn_Exit ncl_mountKeyboard(ncl_ScreenState *state, const char *keyboardAddress);
|
||||
void ncl_unmountKeyboard(ncl_ScreenState *state, const char *keyboardAddress);
|
||||
bool ncl_hasKeyboard(ncl_ScreenState *state, const char *keyboardAddress);
|
||||
const char *ncl_getKeyboard(ncl_ScreenState *state, size_t idx);
|
||||
double ncl_getScreenEnergyUsage(ncl_ScreenState *state);
|
||||
|
||||
#endif
|
||||
|
||||
271
src/neonucleus.c
271
src/neonucleus.c
@@ -266,6 +266,23 @@ void nn_memset(void *dest, int x, size_t len) {
|
||||
for(size_t i = 0; i < len; i++) out[i] = (char)x;
|
||||
}
|
||||
|
||||
void nn_memreverse(void *dest, size_t len) {
|
||||
size_t mid = len/2;
|
||||
char *bytes = (char *)dest;
|
||||
for(size_t i = 0; i < mid; i++) {
|
||||
size_t j = len - i - 1;
|
||||
char tmp = bytes[i];
|
||||
bytes[i] = bytes[j];
|
||||
bytes[j] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
bool nn_isLittleEndian() {
|
||||
union {char c; size_t x;} test;
|
||||
test.x = 1;
|
||||
return test.c == 1;
|
||||
}
|
||||
|
||||
// taken from https://wiki.osdev.org/CRC32
|
||||
// OSDev wiki is really useful sometimes
|
||||
// TODO: maybe allow one that uses compiler intrinsics
|
||||
@@ -911,10 +928,13 @@ static const nn_HashContext nn_methodHasher = {
|
||||
.handler = (nn_HashHandler *)nn_methodHash,
|
||||
};
|
||||
|
||||
// currently just a wrapper around a context
|
||||
// but will be way more in the future
|
||||
typedef struct nn_Universe {
|
||||
nn_Context ctx;
|
||||
void *userdata;
|
||||
// 0 for unbounded
|
||||
size_t memoryLimit;
|
||||
// 0 for unbounded
|
||||
size_t storageLimit;
|
||||
} nn_Universe;
|
||||
|
||||
typedef struct nn_ComponentEntry {
|
||||
@@ -1030,10 +1050,13 @@ typedef struct nn_Computer {
|
||||
char *users[NN_MAX_USERS];
|
||||
} nn_Computer;
|
||||
|
||||
nn_Universe *nn_createUniverse(nn_Context *ctx) {
|
||||
nn_Universe *nn_createUniverse(nn_Context *ctx, void *userdata) {
|
||||
nn_Universe *u = nn_alloc(ctx, sizeof(nn_Universe));
|
||||
if(u == NULL) return NULL;
|
||||
u->ctx = *ctx;
|
||||
u->userdata = userdata;
|
||||
u->memoryLimit = 0;
|
||||
u->storageLimit = 0;
|
||||
return u;
|
||||
}
|
||||
|
||||
@@ -1042,6 +1065,38 @@ void nn_destroyUniverse(nn_Universe *universe) {
|
||||
nn_free(&ctx, universe, sizeof(nn_Universe));
|
||||
}
|
||||
|
||||
void *nn_getUniverseData(nn_Universe *universe) {
|
||||
return universe->userdata;
|
||||
}
|
||||
|
||||
size_t nn_getUniverseMemoryLimit(nn_Universe *universe) {
|
||||
return universe->memoryLimit;
|
||||
}
|
||||
|
||||
void nn_setUniverseMemoryLimit(nn_Universe *universe, size_t limit) {
|
||||
universe->memoryLimit = limit;
|
||||
}
|
||||
|
||||
size_t nn_limitMemory(nn_Universe *universe, size_t memory) {
|
||||
if(universe->memoryLimit == 0) return memory;
|
||||
if(memory > universe->memoryLimit) memory = universe->memoryLimit;
|
||||
return memory;
|
||||
}
|
||||
|
||||
size_t nn_getUniverseStorageLimit(nn_Universe *universe) {
|
||||
return universe->storageLimit;
|
||||
}
|
||||
|
||||
void nn_setUniverseStorageLimit(nn_Universe *universe, size_t limit) {
|
||||
universe->storageLimit = limit;
|
||||
}
|
||||
|
||||
size_t nn_limitStorage(nn_Universe *universe, size_t storage) {
|
||||
if(universe->memoryLimit == 0) return storage;
|
||||
if(storage > universe->memoryLimit) storage = universe->storageLimit;
|
||||
return storage;
|
||||
}
|
||||
|
||||
double nn_default_energyHandler(void *state, nn_Computer *computer, double amount) {
|
||||
(void)state;
|
||||
(void)amount;
|
||||
@@ -1062,6 +1117,8 @@ size_t nn_ramSizes[8] = {
|
||||
nn_Computer *nn_createComputer(nn_Universe *universe, void *userdata, const char *address, size_t totalMemory, size_t maxComponents, size_t maxDevices) {
|
||||
nn_Context *ctx = &universe->ctx;
|
||||
|
||||
totalMemory = nn_limitMemory(universe, totalMemory);
|
||||
|
||||
nn_Computer *c = nn_alloc(ctx, sizeof(nn_Computer));
|
||||
if(c == NULL) return NULL;
|
||||
|
||||
@@ -2491,8 +2548,8 @@ const nn_Drive nn_defaultSSDs[4] = {
|
||||
.sectorSize = 512,
|
||||
.platterCount = 2,
|
||||
.cacheLineSize = 2,
|
||||
.readsPerTick = 20,
|
||||
.writesPerTick = 10,
|
||||
.readsPerTick = 10,
|
||||
.writesPerTick = 5,
|
||||
.rpm = 0,
|
||||
.onlySpinForwards = false,
|
||||
.dataEnergyCost = 64.0 / NN_MiB,
|
||||
@@ -2502,8 +2559,8 @@ const nn_Drive nn_defaultSSDs[4] = {
|
||||
.sectorSize = 512,
|
||||
.platterCount = 4,
|
||||
.cacheLineSize = 4,
|
||||
.readsPerTick = 30,
|
||||
.writesPerTick = 15,
|
||||
.readsPerTick = 15,
|
||||
.writesPerTick = 7,
|
||||
.rpm = 0,
|
||||
.onlySpinForwards = false,
|
||||
.dataEnergyCost = 128.0 / NN_MiB,
|
||||
@@ -2513,8 +2570,8 @@ const nn_Drive nn_defaultSSDs[4] = {
|
||||
.sectorSize = 512,
|
||||
.platterCount = 8,
|
||||
.cacheLineSize = 8,
|
||||
.readsPerTick = 40,
|
||||
.writesPerTick = 20,
|
||||
.readsPerTick = 20,
|
||||
.writesPerTick = 10,
|
||||
.rpm = 0,
|
||||
.onlySpinForwards = false,
|
||||
.dataEnergyCost = 256.0 / NN_MiB,
|
||||
@@ -2524,8 +2581,8 @@ const nn_Drive nn_defaultSSDs[4] = {
|
||||
.sectorSize = 512,
|
||||
.platterCount = 16,
|
||||
.cacheLineSize = 16,
|
||||
.readsPerTick = 60,
|
||||
.writesPerTick = 30,
|
||||
.readsPerTick = 30,
|
||||
.writesPerTick = 15,
|
||||
.rpm = 0,
|
||||
.onlySpinForwards = false,
|
||||
.dataEnergyCost = 512.0 / NN_MiB,
|
||||
@@ -2537,8 +2594,8 @@ const nn_Drive nn_floppySSD = {
|
||||
.sectorSize = 512,
|
||||
.platterCount = 1,
|
||||
.cacheLineSize = 2,
|
||||
.readsPerTick = 10,
|
||||
.writesPerTick = 5,
|
||||
.readsPerTick = 5,
|
||||
.writesPerTick = 2,
|
||||
.rpm = 0,
|
||||
.onlySpinForwards = true,
|
||||
.dataEnergyCost = 16.0 / NN_MiB,
|
||||
@@ -2553,6 +2610,7 @@ const nn_ScreenConfig nn_defaultScreens[4] = {
|
||||
.paletteColors = 0,
|
||||
.editableColors = 0,
|
||||
.features = NN_SCRF_NONE,
|
||||
.energyPerPixel = 0.05,
|
||||
},
|
||||
NN_INIT(nn_ScreenConfig) {
|
||||
.maxWidth = 80,
|
||||
@@ -2562,6 +2620,7 @@ const nn_ScreenConfig nn_defaultScreens[4] = {
|
||||
.paletteColors = 16,
|
||||
.editableColors = 0,
|
||||
.features = NN_SCRF_MOUSE | NN_SCRF_TOUCHINVERTED,
|
||||
.energyPerPixel = 0.05,
|
||||
},
|
||||
NN_INIT(nn_ScreenConfig) {
|
||||
.maxWidth = 160,
|
||||
@@ -2571,6 +2630,7 @@ const nn_ScreenConfig nn_defaultScreens[4] = {
|
||||
.paletteColors = 256,
|
||||
.editableColors = 16,
|
||||
.features = NN_SCRF_MOUSE | NN_SCRF_TOUCHINVERTED | NN_SCRF_PRECISE | NN_SCRF_EDITABLECOLORS,
|
||||
.energyPerPixel = 0.05,
|
||||
},
|
||||
NN_INIT(nn_ScreenConfig) {
|
||||
.maxWidth = 240,
|
||||
@@ -2580,6 +2640,7 @@ const nn_ScreenConfig nn_defaultScreens[4] = {
|
||||
.paletteColors = 256,
|
||||
.editableColors = 256,
|
||||
.features = NN_SCRF_NONE | NN_SCRF_EDITABLECOLORS,
|
||||
.energyPerPixel = 0.05,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -3280,6 +3341,117 @@ nn_Exit nn_pushLClipboard(nn_Computer *computer, const char *keyboardAddress, co
|
||||
return nn_pushSignal(computer, 3);
|
||||
}
|
||||
|
||||
nn_Exit nn_pushRedstoneChanged(nn_Computer *computer, const char *redstoneAddress, int side, int oldValue, int newValue, int color);
|
||||
|
||||
nn_Exit nn_pushMotion(nn_Computer *computer, double relX, double relY, double relZ, const char *entityName);
|
||||
|
||||
typedef enum nn_NetworkValueTag {
|
||||
NN_NETVAL_NULL = 0x00,
|
||||
NN_NETVAL_TRUE = 0x01,
|
||||
NN_NETVAL_FALSE = 0x02,
|
||||
NN_NETVAL_NUM = 0x03,
|
||||
NN_NETVAL_STR = 0x04,
|
||||
NN_NETVAL_RESOURCE = 0x05,
|
||||
NN_NETVAL_TABLE = 0x06,
|
||||
} nn_NetworkValueTag;
|
||||
|
||||
static size_t nn_sizeOfNetworkValue(nn_Value val);
|
||||
|
||||
static size_t nn_sizeOfNetworkContents(nn_Value *vals, size_t len) {
|
||||
size_t s = 0;
|
||||
for(size_t i = 0; i < len; i++) s += nn_sizeOfNetworkValue(vals[i]);
|
||||
return s;
|
||||
}
|
||||
|
||||
static size_t nn_sizeOfNetworkValue(nn_Value val) {
|
||||
// 1-byte tag, + value-dependant encoding
|
||||
size_t n = 1;
|
||||
switch(val.type) {
|
||||
case NN_VAL_NULL:
|
||||
case NN_VAL_BOOL:
|
||||
break;
|
||||
case NN_VAL_NUM:
|
||||
n += sizeof(double);
|
||||
break;
|
||||
case NN_VAL_STR:
|
||||
n += sizeof(size_t) + val.string->len;
|
||||
break;
|
||||
case NN_VAL_USERDATA:
|
||||
n += sizeof(size_t);
|
||||
break;
|
||||
case NN_VAL_TABLE:
|
||||
n += sizeof(size_t) + nn_sizeOfNetworkContents(val.table->vals, val.table->len);
|
||||
break;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static size_t nn_encodeNetworkValue(nn_Value val, char *buf) {
|
||||
size_t n = 0;
|
||||
switch(val.type) {
|
||||
case NN_VAL_NULL:
|
||||
*buf = NN_NETVAL_NULL;
|
||||
return 1;
|
||||
case NN_VAL_BOOL:
|
||||
*buf = val.boolean ? NN_NETVAL_TRUE : NN_NETVAL_FALSE;
|
||||
return 1;
|
||||
case NN_VAL_NUM:
|
||||
*buf = NN_NETVAL_NUM;
|
||||
nn_memcpy(buf + 1, &val.number, sizeof(double));
|
||||
return 1 + sizeof(double);
|
||||
case NN_VAL_STR:
|
||||
*buf = NN_NETVAL_STR;
|
||||
nn_memcpy(buf + 1, &val.string->len, sizeof(size_t));
|
||||
nn_memcpy(buf + 1 + sizeof(size_t), val.string->data, val.string->len);
|
||||
return 1 + sizeof(size_t) + val.string->len;
|
||||
case NN_VAL_USERDATA:
|
||||
*buf = NN_NETVAL_RESOURCE;
|
||||
nn_memcpy(buf + 1, &val.userdataIdx, sizeof(size_t));
|
||||
return 1 + sizeof(size_t);
|
||||
case NN_VAL_TABLE:
|
||||
*buf = NN_NETVAL_TABLE;
|
||||
n = 1;
|
||||
nn_memcpy(buf + n, &val.table->len, sizeof(size_t));
|
||||
n += sizeof(size_t);
|
||||
for(size_t i = 0; i < val.table->len; i++) {
|
||||
n += nn_encodeNetworkValue(val.table->vals[i], buf + n);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
*buf = NN_NETVAL_NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
nn_Exit nn_encodeNetworkContents(nn_Computer *computer, nn_EncodedNetworkContents *contents, size_t valueCount) {
|
||||
if(computer->stackSize < valueCount) return NN_EBELOWSTACK;
|
||||
nn_Value *vals = computer->callstack + computer->stackSize - valueCount;
|
||||
size_t len = nn_sizeOfNetworkContents(vals, valueCount);
|
||||
|
||||
contents->ctx = &computer->universe->ctx;
|
||||
contents->valueCount = valueCount;
|
||||
contents->buflen = len;
|
||||
contents->buf = nn_alloc(contents->ctx, len);
|
||||
if(contents->buf == NULL) return NN_ENOMEM;
|
||||
nn_memset(contents->buf, 0, len);
|
||||
|
||||
size_t n = 0;
|
||||
for(size_t i = 0; i < valueCount; i++) {
|
||||
n += nn_encodeNetworkValue(vals[i], contents->buf + n);
|
||||
}
|
||||
|
||||
return NN_OK;
|
||||
}
|
||||
|
||||
nn_Exit nn_copyNetworkContents(nn_Context *ctx, nn_EncodedNetworkContents *contents, const char *buf, size_t buflen, size_t valueCount);
|
||||
|
||||
void nn_dropNetworkContents(nn_EncodedNetworkContents *contents) {
|
||||
nn_free(contents->ctx, contents->buf, contents->buflen);
|
||||
}
|
||||
|
||||
nn_Exit nn_pushNetworkContents(nn_Computer *computer, const nn_EncodedNetworkContents *contents);
|
||||
|
||||
nn_Exit nn_pushModemMessage(nn_Computer *computer, const char *modemAddress, const char *sender, int port, double distance, const nn_EncodedNetworkContents *contents);
|
||||
|
||||
typedef enum nn_EENum {
|
||||
NN_EENUM_GETSIZE,
|
||||
NN_EENUM_GETDATASIZE,
|
||||
@@ -3860,6 +4032,22 @@ nn_Component *nn_createFilesystem(nn_Universe *universe, const char *address, co
|
||||
return c;
|
||||
}
|
||||
|
||||
bool nn_mergeFilesystems(nn_Filesystem *merged, const nn_Filesystem *fs, size_t len) {
|
||||
if(len == 0) return false;
|
||||
*merged = fs[0];
|
||||
for(size_t i = 1; i < len; i++) {
|
||||
merged->readsPerTick += fs[i].readsPerTick;
|
||||
merged->writesPerTick += fs[i].writesPerTick;
|
||||
if(merged->maxReadSize < fs[i].maxReadSize) merged->maxReadSize = fs[i].maxReadSize;
|
||||
merged->dataEnergyCost += fs[i].dataEnergyCost;
|
||||
merged->spaceTotal += fs[i].spaceTotal;
|
||||
}
|
||||
merged->readsPerTick /= len;
|
||||
merged->writesPerTick /= len;
|
||||
merged->dataEnergyCost /= len;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void nn_drive_seekPenalty(nn_Computer *C, size_t lastSector, size_t newSector, const nn_Drive *drive) {
|
||||
// Check if SSD
|
||||
if(drive->rpm == 0) return;
|
||||
@@ -4064,6 +4252,36 @@ nn_Component *nn_createDrive(nn_Universe *universe, const char *address, const n
|
||||
return c;
|
||||
}
|
||||
|
||||
bool nn_mergeDrives(nn_Drive *merged, const nn_Drive *drives, size_t len) {
|
||||
if(len == 0) return false;
|
||||
*merged = drives[0];
|
||||
for(size_t i = 1; i < len; i++) {
|
||||
nn_Drive d = drives[i];
|
||||
// invalid SSD/HDD combo
|
||||
if(d.rpm == 0 && merged->rpm != 0) return false;
|
||||
if(d.rpm != 0 && merged->rpm == 0) return false;
|
||||
// conflicting sector sizes
|
||||
if(d.sectorSize != merged->sectorSize) return false;
|
||||
if(d.rpm != 0) {
|
||||
if(d.onlySpinForwards && !merged->onlySpinForwards) return false;
|
||||
if(!d.onlySpinForwards && merged->onlySpinForwards) return false;
|
||||
}
|
||||
|
||||
merged->readsPerTick += d.readsPerTick;
|
||||
merged->writesPerTick += d.writesPerTick;
|
||||
merged->dataEnergyCost += d.dataEnergyCost;
|
||||
merged->rpm += d.rpm;
|
||||
merged->capacity += d.capacity;
|
||||
merged->cacheLineSize += d.cacheLineSize;
|
||||
merged->platterCount += d.platterCount;
|
||||
}
|
||||
merged->readsPerTick /= len;
|
||||
merged->writesPerTick /= len;
|
||||
merged->dataEnergyCost /= len;
|
||||
merged->rpm /= len;
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef enum nn_ScreenNum {
|
||||
NN_SCRNUM_ISON,
|
||||
NN_SCRNUM_TURNON,
|
||||
@@ -4656,14 +4874,16 @@ static nn_Exit nn_gpuHandler(nn_ComponentRequest *req) {
|
||||
g.copy.h = nn_tointeger(C, 3);
|
||||
g.copy.tx = nn_tointeger(C, 4);
|
||||
g.copy.ty = nn_tointeger(C, 5);
|
||||
// prevent issues
|
||||
if(g.copy.w < 0) g.copy.w = 0;
|
||||
if(g.copy.w > g.gpu->maxWidth) g.copy.w = g.gpu->maxWidth;
|
||||
if(g.copy.h < 0) g.copy.h = 0;
|
||||
if(g.copy.h > g.gpu->maxHeight) g.copy.h = g.gpu->maxHeight;
|
||||
e = cls->handler(&g);
|
||||
if(e) return e;
|
||||
req->returnCount = 1;
|
||||
nn_costComponent(C, req->compAddress, cls->gpu.copyPerTick);
|
||||
// prevent issues
|
||||
if(g.copy.tx < 1) g.copy.tx = 1;
|
||||
if(g.copy.ty < 1) g.copy.ty = 1;
|
||||
nn_removeEnergy(C, cls->gpu.energyPerWrite * g.copy.tx * g.copy.ty);
|
||||
nn_removeEnergy(C, cls->gpu.energyPerWrite * g.copy.w * g.copy.h);
|
||||
return nn_pushbool(C, true);
|
||||
}
|
||||
// fill
|
||||
@@ -4681,15 +4901,17 @@ static nn_Exit nn_gpuHandler(nn_ComponentRequest *req) {
|
||||
g.fill.y = nn_tointeger(C, 1);
|
||||
g.fill.w = nn_tointeger(C, 2);
|
||||
g.fill.h = nn_tointeger(C, 3);
|
||||
// prevent issues
|
||||
if(g.fill.w < 0) g.fill.w = 0;
|
||||
if(g.fill.w > g.gpu->maxWidth) g.fill.w = g.gpu->maxWidth;
|
||||
if(g.fill.h < 0) g.fill.h = 0;
|
||||
if(g.fill.h > g.gpu->maxHeight) g.fill.h = g.gpu->maxHeight;
|
||||
g.fill.codepoint = nn_unicode_firstCodepoint(
|
||||
nn_tostring(C, 4));
|
||||
e = cls->handler(&g);
|
||||
if(e) return e;
|
||||
req->returnCount = 1;
|
||||
nn_costComponent(C, req->compAddress, cls->gpu.fillPerTick);
|
||||
// prevent issues
|
||||
if(g.fill.w < 1) g.fill.w = 1;
|
||||
if(g.fill.h < 1) g.fill.h = 1;
|
||||
nn_removeEnergy(C, (g.fill.codepoint == ' ' ? cls->gpu.energyPerClear : cls->gpu.energyPerWrite) * g.fill.w * g.fill.h);
|
||||
return nn_pushbool(C, true);
|
||||
}
|
||||
@@ -4798,6 +5020,15 @@ static nn_Exit nn_gpuHandler(nn_ComponentRequest *req) {
|
||||
g.bitblt.src = nn_tointeger(C, 5);
|
||||
g.bitblt.fromCol = nn_tointeger(C, 6);
|
||||
g.bitblt.fromRow = nn_tointeger(C, 7);
|
||||
if(g.bitblt.w < 0) g.copy.w = 0;
|
||||
if(g.bitblt.w > g.gpu->maxWidth) g.bitblt.w = g.gpu->maxWidth;
|
||||
if(g.bitblt.h < 0) g.copy.h = 0;
|
||||
if(g.bitblt.h > g.gpu->maxHeight) g.bitblt.h = g.gpu->maxHeight;
|
||||
if(g.bitblt.dst == 0 || g.bitblt.src == 0) {
|
||||
// taxed as a copy
|
||||
nn_costComponent(C, req->compAddress, g.gpu->copyPerTick);
|
||||
nn_removeEnergy(C, g.gpu->energyPerWrite * g.bitblt.w * g.bitblt.h);
|
||||
}
|
||||
e = cls->handler(&g);
|
||||
if(e) return e;
|
||||
req->returnCount = 1;
|
||||
|
||||
@@ -77,7 +77,6 @@ void *_alloca(size_t);
|
||||
#define NN_KiB (1024)
|
||||
#define NN_MiB (1024 * NN_KiB)
|
||||
#define NN_GiB (1024 * NN_MiB)
|
||||
// probably recursive: #define NN_TiB (1024 * NN_TiB)
|
||||
#define NN_TiB ((size_t)1024 * NN_GiB)
|
||||
|
||||
// the alignment an allocation should have
|
||||
@@ -309,8 +308,15 @@ typedef enum nn_Exit {
|
||||
// This stores necessary data between computers
|
||||
typedef struct nn_Universe nn_Universe;
|
||||
|
||||
nn_Universe *nn_createUniverse(nn_Context *ctx);
|
||||
nn_Universe *nn_createUniverse(nn_Context *ctx, void *userdata);
|
||||
void nn_destroyUniverse(nn_Universe *universe);
|
||||
void *nn_getUniverseData(nn_Universe *universe);
|
||||
size_t nn_getUniverseMemoryLimit(nn_Universe *universe);
|
||||
size_t nn_limitMemory(nn_Universe *universe, size_t memory);
|
||||
void nn_setUniverseMemoryLimit(nn_Universe *universe, size_t limit);
|
||||
size_t nn_getUniverseStorageLimit(nn_Universe *universe);
|
||||
void nn_setUniverseStorageLimit(nn_Universe *universe, size_t limit);
|
||||
size_t nn_limitStorage(nn_Universe *universe, size_t storage);
|
||||
|
||||
// The actual computer
|
||||
typedef struct nn_Computer nn_Computer;
|
||||
@@ -1071,6 +1077,8 @@ extern const nn_Filesystem nn_defaultTmpFS;
|
||||
|
||||
nn_Component *nn_createFilesystem(nn_Universe *universe, const char *address, const nn_Filesystem *fs, void *state, nn_FSHandler *handler);
|
||||
|
||||
bool nn_mergeFilesystems(nn_Filesystem *merged, const nn_Filesystem *fs, size_t len);
|
||||
|
||||
// Drive class
|
||||
|
||||
typedef struct nn_Drive {
|
||||
@@ -1186,6 +1194,8 @@ typedef nn_Exit (nn_DriveHandler)(nn_DriveRequest *request);
|
||||
|
||||
nn_Component *nn_createDrive(nn_Universe *universe, const char *address, const nn_Drive *drive, void *state, nn_DriveHandler *handler);
|
||||
|
||||
bool nn_mergeDrives(nn_Drive *merged, const nn_Drive *drives, size_t len);
|
||||
|
||||
// Screen class
|
||||
|
||||
typedef enum nn_ScreenFeatures {
|
||||
@@ -1226,6 +1236,10 @@ typedef struct nn_ScreenConfig {
|
||||
int editableColors;
|
||||
// the maximum depth of the screen
|
||||
char maxDepth;
|
||||
// energy per fully white pixel.
|
||||
// Scaled to mathc luminance of each pixel.
|
||||
// This is meant to be per Minecraft tick, so 20 times per second.
|
||||
double energyPerPixel;
|
||||
} nn_ScreenConfig;
|
||||
|
||||
// OC has 3 tiers, NN adds a 4th one as well.
|
||||
@@ -1694,12 +1708,21 @@ typedef struct nn_EncodedNetworkContents {
|
||||
// an encoding anyways.
|
||||
// This only encodes the contents, not the sender, hops, or other metadata which may be needed in the queue.
|
||||
// This does not pop the values, in case you need them afterwards. If you don't just call nn_popn().
|
||||
// The encoding is universal, so it is perfectly fine to store on-disk.
|
||||
// The encoding is architecture-dependent, so be careful with storing it on-disk.
|
||||
// Do note that the architecture-dependent parts are sizeof(double), sizeof(size_t) and endianness.
|
||||
// The encoding is simple:
|
||||
// - 0x00 for null
|
||||
// - 0x01 for true
|
||||
// - 0x02 for false
|
||||
// - 0x03 + <bytes of double> for a number
|
||||
// - 0x04 + <bytes of size_t length> + <bytes> for a string
|
||||
// - 0x05 + <bytes of size_t id> for resource
|
||||
// - 0x06 + <bytes of size_t length> + <values> for a table
|
||||
nn_Exit nn_encodeNetworkContents(nn_Computer *computer, nn_EncodedNetworkContents *contents, size_t valueCount);
|
||||
// Allocates a copy of [buf] and stores it in contents.
|
||||
// This is useful for copying network contents, either from storage or from another buffer.
|
||||
nn_Exit nn_copyNetworkContents(nn_Computer *computer, nn_EncodedNetworkContents *contents, const char *buf, size_t buflen, size_t valueCount);
|
||||
void nn_dropNetworkContents(nn_Computer *computer, nn_EncodedNetworkContents *contents);
|
||||
nn_Exit nn_copyNetworkContents(nn_Context *ctx, nn_EncodedNetworkContents *contents, const char *buf, size_t buflen, size_t valueCount);
|
||||
void nn_dropNetworkContents(nn_EncodedNetworkContents *contents);
|
||||
// Pushes the encoded contents onto the stack.
|
||||
// This does not drop the network contents.
|
||||
nn_Exit nn_pushNetworkContents(nn_Computer *computer, const nn_EncodedNetworkContents *contents);
|
||||
@@ -1707,7 +1730,7 @@ nn_Exit nn_pushNetworkContents(nn_Computer *computer, const nn_EncodedNetworkCon
|
||||
// push a modem_message, can be queued by both modems and tunnels.
|
||||
// This does not check if the modem has that port open, so make sure to check it yourself.
|
||||
// It does not check if the distance is within the modem's range, if it is wireless, and thus does not send it.
|
||||
// Note that if a relay with a card should change the sender.
|
||||
// Note that relays should change the sender.
|
||||
nn_Exit nn_pushModemMessage(nn_Computer *computer, const char *modemAddress, const char *sender, int port, double distance, const nn_EncodedNetworkContents *contents);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
Reference in New Issue
Block a user