mirror of
https://github.com/NeoFlock/neonucleus.git
synced 2026-02-15 04:03:49 +01:00
initial EEPROM dev
This commit is contained in:
parent
3f39ac27a0
commit
e33a011549
@ -23,6 +23,8 @@ static nn_Exit sandbox_handler(nn_ComponentRequest *req) {
|
||||
case NN_COMP_ENABLED:
|
||||
req->methodEnabled = true; // all methods always enabled
|
||||
return NN_OK;
|
||||
case NN_COMP_FREETYPE:
|
||||
return NN_OK;
|
||||
}
|
||||
return NN_OK;
|
||||
}
|
||||
|
||||
@ -3,18 +3,44 @@
|
||||
// this should be very easy to include in any modern build system, as it is a single C file.
|
||||
// When compiled, you can define:
|
||||
// - NN_BAREMETAL, to remove any runtime dependency on libc and use minimal headers
|
||||
// - NN_NO_LOCKS, to not use mutexes AT ALL.
|
||||
// - NN_NO_C11_LOCKS, to not use C11 mutexes, instead using pthread mutexes for POSIX systems or Windows locks.
|
||||
// - NN_ATOMIC_NONE, to not use atomics.
|
||||
// Most of the time, you only depend on libc.
|
||||
// However, if pthread locks are used, you will need to link in -lpthread.
|
||||
|
||||
// we need the header.
|
||||
#include "neonucleus.h"
|
||||
|
||||
#ifdef NN_ATOMIC_NONE
|
||||
typedef size_t nn_refc_t;
|
||||
|
||||
void nn_incRef(nn_refc_t *refc) {
|
||||
(*refc)++;
|
||||
}
|
||||
|
||||
bool nn_decRef(nn_refc_t *refc) {
|
||||
(*refc)--;
|
||||
return (*refc) == 0;
|
||||
}
|
||||
#else
|
||||
// we need atomics for thread-safe reference counting that will be used
|
||||
// for managing the lifetimes of various resources
|
||||
// TODO: provide a way to use non-atomic values, and evaluate if the context should contain a method for atomics.
|
||||
#include <stdatomic.h>
|
||||
|
||||
typedef atomic_size_t nn_refc_t;
|
||||
|
||||
void nn_incRef(nn_refc_t *refc) {
|
||||
atomic_fetch_add(refc, 1);
|
||||
}
|
||||
|
||||
bool nn_decRef(nn_refc_t *refc) {
|
||||
nn_refc_t old = atomic_fetch_sub(refc, 1);
|
||||
return old == 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Based off https://stackoverflow.com/questions/5919996/how-to-detect-reliably-mac-os-x-ios-linux-windows-in-c-preprocessor
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
|
||||
//define something for Windows (32-bit and 64-bit, this part is common)
|
||||
@ -301,7 +327,20 @@ static size_t nn_defaultRng(void *_) {
|
||||
static void nn_defaultLock(void *state, nn_LockRequest *req) {
|
||||
(void)state;
|
||||
#ifndef NN_BAREMETAL
|
||||
#if defined(NN_THREAD_C11)
|
||||
#if defined(NN_NO_LOCKS)
|
||||
switch(req->action) {
|
||||
case NN_LOCK_CREATE:;
|
||||
req->lock = nn_defaultLock;
|
||||
return;
|
||||
case NN_LOCK_DESTROY:;
|
||||
return;
|
||||
case NN_LOCK_LOCK:;
|
||||
return;
|
||||
case NN_LOCK_UNLOCK:;
|
||||
return;
|
||||
}
|
||||
return;
|
||||
#elif defined(NN_THREAD_C11)
|
||||
switch(req->action) {
|
||||
case NN_LOCK_CREATE:;
|
||||
mtx_t *mem = malloc(sizeof(mtx_t));
|
||||
@ -384,16 +423,6 @@ void nn_initContext(nn_Context *ctx) {
|
||||
ctx->lock = nn_defaultLock;
|
||||
}
|
||||
|
||||
typedef enum nn_BuiltinComponent {
|
||||
NN_BUILTIN_GPU = 0,
|
||||
NN_BUILTIN_SCREEN,
|
||||
NN_BUILTIN_KEYBOARD,
|
||||
NN_BUILTIN_FILESYSTEM,
|
||||
|
||||
// to determine array size
|
||||
NN_BUILTIN_COUNT,
|
||||
} nn_BuiltinComponent;
|
||||
|
||||
typedef struct nn_ComponentType {
|
||||
nn_Universe *universe;
|
||||
void *userdata;
|
||||
@ -405,9 +434,10 @@ typedef struct nn_ComponentType {
|
||||
size_t methodCount;
|
||||
} nn_ComponentType;
|
||||
|
||||
// currently just a wrapper around a context
|
||||
// but will be way more in the future
|
||||
typedef struct nn_Universe {
|
||||
nn_Context ctx;
|
||||
nn_ComponentType *types[NN_BUILTIN_COUNT];
|
||||
} nn_Universe;
|
||||
|
||||
typedef struct nn_Component {
|
||||
@ -466,6 +496,8 @@ typedef struct nn_Computer {
|
||||
void *archState;
|
||||
nn_Architecture arch;
|
||||
nn_Architecture desiredArch;
|
||||
size_t callBudget;
|
||||
size_t totalCallBudget;
|
||||
size_t componentCap;
|
||||
size_t componentLen;
|
||||
nn_Component *components;
|
||||
@ -489,13 +521,11 @@ nn_Universe *nn_createUniverse(nn_Context *ctx) {
|
||||
nn_Universe *u = nn_alloc(ctx, sizeof(nn_Universe));
|
||||
if(u == NULL) return NULL;
|
||||
u->ctx = *ctx;
|
||||
for(size_t i = 0; i < NN_BUILTIN_COUNT; i++) u->types[i] = NULL;
|
||||
return u;
|
||||
}
|
||||
|
||||
void nn_destroyUniverse(nn_Universe *universe) {
|
||||
nn_Context ctx = universe->ctx;
|
||||
for(size_t i = 0; i < NN_BUILTIN_COUNT; i++) nn_destroyComponentType(universe->types[i]);
|
||||
nn_free(&ctx, universe, sizeof(nn_Universe));
|
||||
}
|
||||
|
||||
@ -545,6 +575,16 @@ void nn_destroyComponentType(nn_ComponentType *ctype) {
|
||||
if(ctype == NULL) return;
|
||||
nn_Context *ctx = &ctype->universe->ctx;
|
||||
|
||||
nn_ComponentRequest req;
|
||||
req.typeUserdata = ctype->userdata;
|
||||
req.compUserdata = NULL;
|
||||
req.state = NULL;
|
||||
req.computer = NULL;
|
||||
req.compAddress = NULL;
|
||||
req.action = NN_COMP_FREETYPE;
|
||||
req.methodCalled = NULL;
|
||||
ctype->handler(&req);
|
||||
|
||||
nn_ardestroy(&ctype->arena);
|
||||
nn_free(ctx, ctype, sizeof(nn_ComponentType));
|
||||
}
|
||||
@ -569,6 +609,9 @@ nn_Computer *nn_createComputer(nn_Universe *universe, void *userdata, const char
|
||||
c->desiredArch.name = NULL;
|
||||
c->archState = NULL;
|
||||
|
||||
c->totalCallBudget = 1000;
|
||||
c->callBudget = c->totalCallBudget;
|
||||
|
||||
c->componentCap = maxComponents;
|
||||
c->componentLen = 0;
|
||||
c->components = nn_alloc(ctx, sizeof(nn_Component) * maxComponents);
|
||||
@ -698,7 +741,11 @@ double nn_getEnergy(nn_Computer *computer) {
|
||||
bool nn_removeEnergy(nn_Computer *computer, double energy) {
|
||||
computer->energy -= energy;
|
||||
if(computer->energy < 0) computer->energy = 0;
|
||||
return computer->energy <= 0;
|
||||
if(computer->energy <= 0) {
|
||||
computer->state = NN_BLACKOUT;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t nn_getTotalMemory(nn_Computer *computer) {
|
||||
@ -793,6 +840,7 @@ nn_Exit nn_tick(nn_Computer *computer) {
|
||||
nn_setErrorFromExit(computer, NN_EBADSTATE);
|
||||
return NN_EBADSTATE;
|
||||
}
|
||||
nn_resetCallBudget(computer);
|
||||
computer->state = NN_RUNNING;
|
||||
nn_ArchitectureRequest req;
|
||||
req.computer = computer;
|
||||
@ -971,6 +1019,11 @@ const nn_ComponentMethod *nn_getComponentMethods(nn_Computer *computer, const ch
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *nn_getComponentAddress(nn_Computer *computer, size_t idx) {
|
||||
if(idx >= computer->componentLen) return NULL;
|
||||
return computer->components[idx].address;
|
||||
}
|
||||
|
||||
static void nn_retainValue(nn_Value val) {
|
||||
switch(val.type) {
|
||||
case NN_VAL_NULL:
|
||||
@ -1025,6 +1078,9 @@ nn_Exit nn_call(nn_Computer *computer, const char *address, const char *method)
|
||||
for(size_t i = 0; i < computer->componentLen; i++) {
|
||||
nn_Component c = computer->components[i];
|
||||
if(nn_strcmp(c.address, address) != 0) continue;
|
||||
|
||||
// minimum cost of a component call
|
||||
nn_callCost(computer, 1);
|
||||
|
||||
nn_ComponentRequest req;
|
||||
req.typeUserdata = c.ctype->userdata;
|
||||
@ -1056,6 +1112,31 @@ nn_Exit nn_call(nn_Computer *computer, const char *address, const char *method)
|
||||
return NN_EBADSTATE;
|
||||
}
|
||||
|
||||
void nn_setCallBudget(nn_Computer *computer, size_t budget) {
|
||||
computer->totalCallBudget = budget;
|
||||
}
|
||||
|
||||
size_t nn_getCallBudget(nn_Computer *computer) {
|
||||
return computer->totalCallBudget;
|
||||
}
|
||||
|
||||
void nn_callCost(nn_Computer *computer, size_t callIntensity) {
|
||||
if(computer->callBudget < callIntensity) computer->callBudget = 0;
|
||||
else computer->callBudget -= callIntensity;
|
||||
}
|
||||
|
||||
size_t nn_callBudgetRemaining(nn_Computer *computer) {
|
||||
return computer->callBudget;
|
||||
}
|
||||
|
||||
void nn_resetCallBudget(nn_Computer *computer) {
|
||||
computer->callBudget = computer->totalCallBudget;
|
||||
}
|
||||
|
||||
bool nn_componentsOverused(nn_Computer *computer) {
|
||||
return computer->callBudget == 0;
|
||||
}
|
||||
|
||||
bool nn_checkstack(nn_Computer *computer, size_t amount) {
|
||||
return computer->stackSize + amount <= NN_MAX_STACK;
|
||||
}
|
||||
@ -1342,4 +1423,133 @@ nn_Exit nn_popSignal(nn_Computer *computer, size_t *valueCount) {
|
||||
}
|
||||
|
||||
// todo: everything
|
||||
nn_Exit nn_initComponentsLibrary(nn_Universe *universe);
|
||||
|
||||
typedef struct nn_EEPROM_state {
|
||||
nn_Universe *universe;
|
||||
nn_EEPROM eeprom;
|
||||
void *userdata;
|
||||
} nn_EEPROM_state;
|
||||
|
||||
nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
|
||||
nn_EEPROM_state *state = req->typeUserdata;
|
||||
void *instance = req->compUserdata;
|
||||
nn_Computer *computer = req->computer;
|
||||
nn_Context ctx = state->universe->ctx;
|
||||
|
||||
nn_EEPROMRequest ereq;
|
||||
ereq.userdata = state->userdata;
|
||||
ereq.instance = instance;
|
||||
ereq.computer = computer;
|
||||
|
||||
const char *method = req->methodCalled;
|
||||
|
||||
switch(req->action) {
|
||||
case NN_COMP_FREETYPE:
|
||||
nn_free(&ctx, state, sizeof(*state));
|
||||
break;
|
||||
case NN_COMP_INIT:
|
||||
return NN_OK;
|
||||
case NN_COMP_DEINIT:
|
||||
ereq.action = NN_EEPROM_DROP;
|
||||
return state->eeprom.handler(&ereq);
|
||||
case NN_COMP_ENABLED:
|
||||
req->methodEnabled = true;
|
||||
return NN_OK;
|
||||
case NN_COMP_CALL:
|
||||
if(nn_strcmp(method, "getSize") == 0) {
|
||||
return nn_pushnumber(computer, state->eeprom.size);
|
||||
}
|
||||
if(nn_strcmp(method, "getDataSize") == 0) {
|
||||
return nn_pushnumber(computer, state->eeprom.dataSize);
|
||||
}
|
||||
if(nn_strcmp(method, "getLabel") == 0) {
|
||||
char buf[NN_MAX_LABEL];
|
||||
ereq.action = NN_EEPROM_GETLABEL;
|
||||
ereq.buf = buf;
|
||||
ereq.buflen = NN_MAX_LABEL;
|
||||
nn_Exit e = state->eeprom.handler(&ereq);
|
||||
if(e) return e;
|
||||
req->returnCount = 1;
|
||||
return nn_pushlstring(computer, buf, ereq.buflen);
|
||||
}
|
||||
if(nn_strcmp(method, "setLabel") == 0) {
|
||||
if(nn_getstacksize(computer) < 1) {
|
||||
nn_setError(computer, "bad argument #1 (string expected)");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
if(!nn_isstring(computer, 0)) {
|
||||
nn_setError(computer, "bad argument #1 (string expected)");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
size_t len;
|
||||
const char *s = nn_tolstring(computer, 0, &len);
|
||||
if(len > NN_MAX_LABEL) len = NN_MAX_LABEL;
|
||||
char buf[NN_MAX_LABEL];
|
||||
nn_memcpy(buf, s, sizeof(char) * len);
|
||||
ereq.action = NN_EEPROM_SETLABEL;
|
||||
ereq.buf = buf;
|
||||
ereq.buflen = len;
|
||||
nn_Exit e = state->eeprom.handler(&ereq);
|
||||
if(e) return e;
|
||||
req->returnCount = 1;
|
||||
return nn_pushlstring(computer, buf, ereq.buflen);
|
||||
}
|
||||
if(nn_strcmp(method, "get") == 0) {
|
||||
// yup, on-stack.
|
||||
// Perhaps in the future we'll make it heap-allocated.
|
||||
char buf[state->eeprom.size];
|
||||
ereq.action = NN_EEPROM_GET;
|
||||
ereq.buf = buf;
|
||||
ereq.buflen = state->eeprom.size;
|
||||
nn_Exit e = state->eeprom.handler(&ereq);
|
||||
if(e) return e;
|
||||
req->returnCount = 1;
|
||||
return nn_pushlstring(computer, buf, ereq.buflen);
|
||||
}
|
||||
if(nn_strcmp(method, "getData") == 0) {
|
||||
// yup, on-stack.
|
||||
// Perhaps in the future we'll make it heap-allocated.
|
||||
char buf[state->eeprom.dataSize];
|
||||
ereq.action = NN_EEPROM_GETDATA;
|
||||
ereq.buf = buf;
|
||||
ereq.buflen = state->eeprom.dataSize;
|
||||
nn_Exit e = state->eeprom.handler(&ereq);
|
||||
if(e) return e;
|
||||
req->returnCount = 1;
|
||||
return nn_pushlstring(computer, buf, ereq.buflen);
|
||||
}
|
||||
return NN_OK;
|
||||
}
|
||||
return NN_OK;
|
||||
}
|
||||
|
||||
nn_ComponentType *nn_createEEPROM(nn_Universe *universe, nn_EEPROM *eeprom, void *userdata) {
|
||||
nn_Context ctx = universe->ctx;
|
||||
nn_EEPROM_state *state = nn_alloc(&ctx, sizeof(*state));
|
||||
if(state == NULL) return NULL;
|
||||
state->universe = universe;
|
||||
state->eeprom = *eeprom;
|
||||
state->userdata = userdata;
|
||||
const nn_ComponentMethod methods[] = {
|
||||
{"getSize", "getSize(): number - Get the storage capacity of the EEPROM.", true},
|
||||
{"getDataSize", "getDataSize(): number - Get the storage capacity of the EEPROM data.", true},
|
||||
{"getLabel", "getLabel(): string - Get the EEPROM label", false},
|
||||
{"setLabel", "setLabel(label: string): string - Set the EEPROM label and return what was actually set, which may be truncated.", false},
|
||||
{"get", "get(): string - Get the current EEPROM contents.", false},
|
||||
{"getData", "getData(): string - Get the current EEPROM data contents.", false},
|
||||
{"set", "set(data: string) - Set the current EEPROM contents.", false},
|
||||
{"setData", "setData(data: string) - Set the current EEPROM data contents.", false},
|
||||
{"getArchitecture", "getArchitecture(): string - Get the current EEPROM architecture intended.", false},
|
||||
{"setArchitecture", "setArchitecture(data: string) - Set the current EEPROM architecture intended.", false},
|
||||
{"isReadOnly", "isReadOnly(): boolean - Returns whether the EEPROM is read-only.", false},
|
||||
{"makeReadonly", "makeReadonly() - Makes the EEPROM read-only, this cannot be undone.", false},
|
||||
{"getChecksum", "getChecksum(): string - Returns a simple checksum of the EEPROM's contents and data.", false},
|
||||
{NULL, NULL, false},
|
||||
};
|
||||
nn_ComponentType *t = nn_createComponentType(universe, "eeprom", state, methods, nn_eeprom_handler);
|
||||
if(t == NULL) {
|
||||
nn_free(&ctx, state, sizeof(*state));
|
||||
return NULL;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
@ -30,6 +30,8 @@ extern "C" {
|
||||
// the maximum amount of bytes which can be read from a file.
|
||||
// You are given a buffer you are meant to fill at least partially, this is simply the limit of that buffer's size.
|
||||
#define NN_MAX_READ 65536
|
||||
// the maximum size of a label
|
||||
#define NN_MAX_LABEL 256
|
||||
// maximum size of a wakeup message
|
||||
#define NN_MAX_WAKEUPMSG 2048
|
||||
// the maximum amount of file descriptors that can be open simultaneously
|
||||
@ -312,6 +314,8 @@ typedef enum nn_ComponentAction {
|
||||
NN_COMP_CALL,
|
||||
// check if a method is enabled
|
||||
NN_COMP_ENABLED,
|
||||
// delete the type userdata
|
||||
NN_COMP_FREETYPE,
|
||||
} nn_ComponentAction;
|
||||
|
||||
typedef struct nn_ComponentRequest {
|
||||
@ -363,12 +367,37 @@ const char *nn_getComponentType(nn_Computer *computer, const char *address);
|
||||
int nn_getComponentSlot(nn_Computer *computer, const char *address);
|
||||
// Returns the array of component methods. This can be used for doc strings or just listing methods.
|
||||
const nn_ComponentMethod *nn_getComponentMethods(nn_Computer *computer, const char *address, size_t *len);
|
||||
// get the address at a certain index.
|
||||
// It'll return NULL for out of bounds indexes.
|
||||
// This can be used to iterate over all components.
|
||||
const char *nn_getComponentAddress(nn_Computer *computer, size_t idx);
|
||||
|
||||
// this uses the call stack.
|
||||
// Component calls must not call other components, it just doesn't work.
|
||||
// The lack of an argument count is because the entire call stack is assumed to be the arguments.
|
||||
nn_Exit nn_call(nn_Computer *computer, const char *address, const char *method);
|
||||
|
||||
// Sets the call budget.
|
||||
// The default is 1,000.
|
||||
void nn_setCallBudget(nn_Computer *computer, size_t budget);
|
||||
|
||||
// gets the total call budget
|
||||
size_t nn_getCallBudget(nn_Computer *computer);
|
||||
|
||||
// subtracts from the call budget.
|
||||
// This cannot underflow, it's clamped to 0.
|
||||
void nn_callCost(nn_Computer *computer, size_t callIntensity);
|
||||
|
||||
// returns the remaining call budget
|
||||
size_t nn_callBudgetRemaining(nn_Computer *computer);
|
||||
|
||||
// automatically called by nn_tick()
|
||||
void nn_resetCallBudget(nn_Computer *computer);
|
||||
|
||||
// returns whether there is no more call budget left.
|
||||
// At this point, the architecture should exit from a yield.
|
||||
bool nn_componentsOverused(nn_Computer *computer);
|
||||
|
||||
// call stack operations.
|
||||
// The type system and API are inspired by Lua, as Lua remains the most popular architecture for OpenComputers.
|
||||
// This does support other languages, however it may make some APIs clunky due to the usage of tables and 1-based indexing.
|
||||
@ -486,13 +515,67 @@ nn_Exit nn_popSignal(nn_Computer *computer, size_t *valueCount);
|
||||
// The high-level API of the built-in components.
|
||||
// These components still make no assumptions about the OS, and still require handlers to connect them to the outside work.
|
||||
|
||||
// Initializes the component library for a universe. This just defines the component tables and stores them in the universe.
|
||||
// Using the built-in components without calling this will cause insane levels of undefined behavior and may even cause time travel
|
||||
// and singularities forming inside your computer.
|
||||
nn_Exit nn_initComponentsLibrary(nn_Universe *universe);
|
||||
|
||||
// TODO: screen, gpu, filesystem, eeprom and the rest of the universe
|
||||
|
||||
typedef enum nn_EEPROMAction {
|
||||
// informed that it has been dropped
|
||||
NN_EEPROM_DROP,
|
||||
NN_EEPROM_GET,
|
||||
NN_EEPROM_SET,
|
||||
NN_EEPROM_GETDATA,
|
||||
NN_EEPROM_SETDATA,
|
||||
NN_EEPROM_GETLABEL,
|
||||
NN_EEPROM_SETLABEL,
|
||||
NN_EEPROM_GETARCH,
|
||||
NN_EEPROM_SETARCH,
|
||||
NN_EEPROM_ISREADONLY,
|
||||
NN_EEPROM_MAKEREADONLY,
|
||||
} nn_EEPROMAction;
|
||||
|
||||
typedef struct nn_EEPROMRequest {
|
||||
// associated userdata
|
||||
void *userdata;
|
||||
// associated component userdata
|
||||
void *instance;
|
||||
// the computer making the request
|
||||
nn_Computer *computer;
|
||||
nn_EEPROMAction action;
|
||||
// all the get* options should set this to the length,
|
||||
// and its initial value is the capacity of [buf].
|
||||
// For ISREADONLY, this should be set to 0 if false and 1 if true.
|
||||
unsigned int buflen;
|
||||
// this may be the buffer length
|
||||
char *buf;
|
||||
} nn_EEPROMRequest;
|
||||
|
||||
typedef struct nn_EEPROM {
|
||||
// the maximum capacity of the EEPROM
|
||||
size_t size;
|
||||
// the maximum capacity of the EEPROM's associated data
|
||||
size_t dataSize;
|
||||
// the call cost of reading an EEPROM
|
||||
size_t readCallCost;
|
||||
// the energy cost of reading an EEPROM
|
||||
double readEnergyCost;
|
||||
// the call cost of reading an EEPROM's associated data
|
||||
size_t readDataCallCost;
|
||||
// the energy cost of reading an EEPROM's associated data
|
||||
double readDataEnergyCost;
|
||||
// the call cost of writing to an EEPROM
|
||||
size_t writeCallCost;
|
||||
// the energy cost of writing to an EEPROM
|
||||
double writeEnergyCost;
|
||||
// the call cost of writing to an EEPROM's associated data
|
||||
size_t writeDataCallCost;
|
||||
// the energy cost of writing to an EEPROM's associated data
|
||||
double writeDataEnergyCost;
|
||||
nn_Exit (*handler)(nn_EEPROMRequest *request);
|
||||
} nn_EEPROM;
|
||||
|
||||
// the userdata passed to the component is the userdata
|
||||
// in the handler
|
||||
nn_ComponentType *nn_createEEPROM(nn_Universe *universe, nn_EEPROM *eeprom, void *userdata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user