neonucleus/src/emulator.c
IonutParau 3546fe1093 new call cost system
TODO: change all the ctrls from latency to amount per tick.
2025-07-03 12:36:47 +02:00

772 lines
21 KiB
C

#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "neonucleus.h"
#include "testLuaArch.h"
#include <raylib.h>
Color ne_processColor(unsigned int color) {
color <<= 8;
color |= 0xFF;
return GetColor(color);
}
nn_eepromControl ne_eeprom_getControl(nn_component *component, void *_) {
return (nn_eepromControl) {
.randomLatencyMin = 0.001,
.randomLatencyMax = 0.012,
.readLatency = 0.03,
.writeLatency = 0.05,
.readCost = 3,
.writeCost = 5,
.readEnergyCost = 1,
.writeEnergyCost = 5,
.writeHeatCost = 0.2,
};
}
size_t ne_eeprom_getSize(nn_component *component, void *_) {
return 4096;
}
size_t ne_eeprom_getDataSize(nn_component *component, void *_) {
return 1024;
}
void ne_eeprom_getLabel(nn_component *component, void *_, char *buf, size_t *buflen) {
*buflen = 0;
}
size_t ne_eeprom_setLabel(nn_component *component, void *_, const char *buf, size_t buflen) {
return 0;
}
const char *ne_location(nn_address address) {
static char buffer[256];
snprintf(buffer, 256, "data/%s", address);
return buffer;
}
size_t ne_eeprom_get(nn_component *component, void *_, char *buf) {
FILE *f = fopen(ne_location(nn_getComponentAddress(component)), "rb");
if (f == NULL) {
printf("couldn't read eeprom");
exit(1);
}
fseek(f, 0, SEEK_END);
size_t len = ftell(f);
fseek(f, 0, SEEK_SET);
fread(buf, sizeof(char), len, f);
fclose(f);
return len;
}
void ne_eeprom_set(nn_component *component, void *_, const char *buf, size_t len) {
FILE *f = fopen(ne_location(nn_getComponentAddress(component)), "wb");
if (f == NULL) {
printf("couldn't write eeprom");
exit(1);
}
fwrite(buf, sizeof(char), len, f);
fclose(f);
}
int ne_eeprom_getData(nn_component *component, void *_, char *buf) {
return -1;
}
void ne_eeprom_setData(nn_component *component, void *_, const char *buf, size_t len) {}
bool ne_eeprom_isReadonly(nn_component *component, void *userdata) {
return false;
}
void ne_eeprom_makeReadonly(nn_component *component, void *userdata) {}
#define NE_FS_MAX 128
typedef struct ne_fs {
FILE *files[NE_FS_MAX];
size_t fileLen;
} ne_fs;
nn_filesystemControl ne_fs_getControl(nn_component *component, ne_fs *_) {
return (nn_filesystemControl) {
.pretendChunkSize = 512,
.pretendRPM = 12,
.writeHeatPerChunk = 0.01,
.writeCostPerChunk = 3,
.writeEnergyCost = 0.015,
.writeLatencyPerChunk = 0.0003,
.readEnergyCost = 0.007,
.readCostPerChunk = 1,
.readLatencyPerChunk = 0.0001,
.randomLatencyMin = 0.0005,
.randomLatencyMax = 0.0019,
.seekCostPerChunk = 1,
.motorHeat = 0.03,
.motorEnergyCost = 0.5,
};
}
size_t ne_fs_spaceUsed(nn_component *component, void *_) {
return 0; // ultra accurate
}
size_t ne_fs_spaceTotal(nn_component *component, void *_) {
return 1*1024*1024;
}
const char *ne_fs_diskPath(nn_component *component, const char *path) {
static char buf[256];
const char *root = ne_location(nn_getComponentAddress(component));
if(path[0] == '/') {
snprintf(buf, 256, "%s%s", root, path);
} else {
snprintf(buf, 256, "%s/%s", root, path);
}
return buf;
}
size_t ne_fs_open(nn_component *component, ne_fs *fs, const char *path, const char *mode) {
if(fs->fileLen == NE_FS_MAX) {
nn_setCError(nn_getComputerOfComponent(component), "too many files");
return 0;
}
const char *trueMode = "rb";
if(strcmp(mode, "w") == 0) {
trueMode = "wb";
}
if(strcmp(mode, "a") == 0) {
trueMode = "ab";
}
const char *p = ne_fs_diskPath(component, path);
if(p[0] == '/') p++;
FILE *f = fopen(p, trueMode);
if(f == NULL) {
nn_setCError(nn_getComputerOfComponent(component), strerror(errno));
return 0;
}
for(size_t i = 0; i < fs->fileLen; i++) {
if(fs->files[i] == NULL) {
fs->files[i] = f;
return i;
}
}
size_t i = fs->fileLen++;
fs->files[i] = f;
return i;
}
bool ne_fs_close(nn_component *component, ne_fs *fs, int fd) {
// we pray
fclose(fs->files[fd]);
fs->files[fd] = NULL;
return true;
}
bool ne_fs_write(nn_component *component, ne_fs *fs, int fd, const char *buf, size_t len) {
FILE *f = fs->files[fd];
return fwrite(buf, sizeof(char), len, f) > 0;
}
size_t ne_fs_read(nn_component *component, ne_fs *fs, int fd, char *buf, size_t required) {
FILE *f = fs->files[fd];
if(feof(f)) return 0;
return fread(buf, sizeof(char), required, f);
}
char **ne_fs_list(nn_Alloc *alloc, nn_component *component, ne_fs *fs, const char *path, size_t *len) {
const char *p = ne_fs_diskPath(component, path);
if(p[0] == '/') p++;
FilePathList files = LoadDirectoryFiles(p);
*len = files.count;
char **buf = nn_alloc(alloc, sizeof(char *) * files.count);
for(size_t i = 0; i < files.count; i++) {
buf[i] = nn_strdup(alloc, GetFileName(files.paths[i]));
}
UnloadDirectoryFiles(files);
return buf;
}
size_t ne_fs_size(nn_component *component, ne_fs *fs, const char *path) {
const char *p = ne_fs_diskPath(component, path);
if(p[0] == '/') p++;
if(DirectoryExists(p)) return 0;
return GetFileLength(p);
}
size_t ne_fs_lastModified(nn_component *component, ne_fs *fs, const char *path) {
const char *p = ne_fs_diskPath(component, path);
if(p[0] == '/') p++;
return GetFileModTime(p);
}
bool ne_fs_isDirectory(nn_component *component, ne_fs *fs, const char *path) {
const char *p = ne_fs_diskPath(component, path);
if(p[0] == '/') p++;
return DirectoryExists(p);
}
bool ne_fs_makeDirectory(nn_component *component, ne_fs *fs, const char *path) {
const char *p = ne_fs_diskPath(component, path);
if(p[0] == '/') p++;
return MakeDirectory(p) == 0;
}
bool ne_fs_exists(nn_component *component, ne_fs *fs, const char *path) {
const char *p = ne_fs_diskPath(component, path);
if(p[0] == '/') p++;
return FileExists(p) || DirectoryExists(p);
}
typedef struct ne_drive {
FILE *file;
} ne_drive;
void ne_drive_close(nn_component *component, ne_drive *drive) {
fclose(drive->file);
}
nn_driveControl ne_drive_getControl(nn_component *component, ne_drive *_) {
return (nn_driveControl){};
}
size_t ne_drive_getPlatterCount(nn_component *component, ne_drive *_) {
return 1;
}
size_t ne_drive_getSectorSize(nn_component *component, ne_drive *_) {
return 512;
}
size_t ne_drive_getCapacity(nn_component *component, ne_drive *drive) {
fseek(drive->file, 0, SEEK_END);
return ftell(drive->file);
}
void ne_drive_readSector(nn_component *component, ne_drive *drive, int shifted_sector, char *buf) {
int sector = shifted_sector - 1;
size_t sectorSize = ne_drive_getSectorSize(component, drive);
size_t offset = sector * sectorSize;
fseek(drive->file, offset, SEEK_SET);
fread(buf, sizeof(char), sectorSize, drive->file);
}
void ne_drive_writeSector(nn_component *component, ne_drive *drive, int shifted_sector, const char *buf) {
int sector = shifted_sector - 1;
size_t sectorSize = ne_drive_getSectorSize(component, drive);
size_t offset = sector * sectorSize;
fseek(drive->file, offset, SEEK_SET);
fwrite(buf, sizeof(char), sectorSize, drive->file);
// this is probably not needed but i believe someone isn't running the deinit
fflush(drive->file);
}
int keycode_to_oc(int keycode) {
switch (keycode) {
case KEY_NULL:
return 0;
case KEY_APOSTROPHE:
return 0x28;
case KEY_COMMA:
return 0x33;
case KEY_MINUS:
return 0x0C;
case KEY_PERIOD:
return 0x34;
case KEY_SLASH:
return 0x35;
case KEY_ZERO:
return 0x0B;
case KEY_ONE:
return 0x02;
case KEY_TWO:
return 0x03;
case KEY_THREE:
return 0x04;
case KEY_FOUR:
return 0x05;
case KEY_FIVE:
return 0x06;
case KEY_SIX:
return 0x07;
case KEY_SEVEN:
return 0x08;
case KEY_EIGHT:
return 0x09;
case KEY_NINE:
return 0x0A;
case KEY_SEMICOLON:
return 0x27;
case KEY_EQUAL:
return 0x0D;
case KEY_A:
return 0x1E;
case KEY_B:
return 0x30;
case KEY_C:
return 0x2E;
case KEY_D:
return 0x20;
case KEY_E:
return 0x12;
case KEY_F:
return 0x21;
case KEY_G:
return 0x22;
case KEY_H:
return 0x23;
case KEY_I:
return 0x17;
case KEY_J:
return 0x24;
case KEY_K:
return 0x25;
case KEY_L:
return 0x26;
case KEY_M:
return 0x32;
case KEY_N:
return 0x31;
case KEY_O:
return 0x18;
case KEY_P:
return 0x19;
case KEY_Q:
return 0x10;
case KEY_R:
return 0x13;
case KEY_S:
return 0x1F;
case KEY_T:
return 0x14;
case KEY_U:
return 0x16;
case KEY_V:
return 0x2F;
case KEY_W:
return 0x11;
case KEY_X:
return 0x2D;
case KEY_Y:
return 0x15;
case KEY_Z:
return 0x2C;
case KEY_LEFT_BRACKET:
return 0x1A;
case KEY_BACKSLASH:
return 0x2B;
case KEY_RIGHT_BRACKET:
return 0x1B;
case KEY_GRAVE:
return 0x29;
case KEY_SPACE:
return 0x39;
case KEY_ESCAPE:
return 0;
case KEY_ENTER:
return 0x1C;
case KEY_TAB:
return 0x0F;
case KEY_BACKSPACE:
return 0x0E;
case KEY_INSERT:
return 0xD2;
case KEY_DELETE:
return 0xD3;
case KEY_RIGHT:
return 0xCD;
case KEY_LEFT:
return 0xCB;
case KEY_DOWN:
return 0xD0;
case KEY_UP:
return 0xC8;
case KEY_PAGE_UP:
return 0xC9;
case KEY_PAGE_DOWN:
return 0xD1;
case KEY_HOME:
return 0xC7;
case KEY_END:
return 0xCF;
case KEY_CAPS_LOCK:
return 0x3A;
case KEY_SCROLL_LOCK:
return 0x46;
case KEY_NUM_LOCK:
return 0x45;
case KEY_PRINT_SCREEN:
return 0;
case KEY_PAUSE:
return 0xC5;
case KEY_F1:
return 0x3B;
case KEY_F2:
return 0x3C;
case KEY_F3:
return 0x3D;
case KEY_F4:
return 0x3E;
case KEY_F5:
return 0x3F;
case KEY_F6:
return 0x40;
case KEY_F7:
return 0x41;
case KEY_F8:
return 0x42;
case KEY_F9:
return 0x43;
case KEY_F10:
return 0x44;
case KEY_F11:
return 0x57;
case KEY_F12:
return 0x58;
case KEY_LEFT_SHIFT:
return 0x2A;
case KEY_LEFT_CONTROL:
return 0x1D;
case KEY_LEFT_ALT:
return 0x38;
case KEY_LEFT_SUPER:
return 0;
case KEY_RIGHT_SHIFT:
return 0x36;
case KEY_RIGHT_CONTROL:
return 0x9D;
case KEY_RIGHT_ALT:
return 0xB8;
case KEY_RIGHT_SUPER:
return 0;
case KEY_KB_MENU:
return 0;
case KEY_KP_0:
return 0x52;
case KEY_KP_1:
return 0x4F;
case KEY_KP_2:
return 0x50;
case KEY_KP_3:
return 0x51;
case KEY_KP_4:
return 0x4B;
case KEY_KP_5:
return 0x4C;
case KEY_KP_6:
return 0x4D;
case KEY_KP_7:
return 0x47;
case KEY_KP_8:
return 0x48;
case KEY_KP_9:
return 0x49;
case KEY_KP_DECIMAL:
return 0x54;
case KEY_KP_DIVIDE:
return 0xB5;
case KEY_KP_MULTIPLY:
return 0x37;
case KEY_KP_SUBTRACT:
return 0x4A;
case KEY_KP_ADD:
return 0x4E;
case KEY_KP_ENTER:
return 0x9C;
case KEY_KP_EQUAL:
return 0x8D;
case KEY_BACK:
return 0;
case KEY_MENU:
return 0;
case KEY_VOLUME_DOWN:
return 0;
case KEY_VOLUME_UP:
return 0;
}
return 0;
}
int main() {
printf("Setting up universe\n");
nn_Alloc alloc = nn_libcAllocator();
nn_universe *universe = nn_newUniverse(alloc);
if(universe == NULL) {
printf("Failed to create universe\n");
return 1;
}
nn_loadCoreComponentTables(universe);
nn_architecture *arch = testLuaArch_getArchitecture("src/sandbox.lua");
assert(arch != NULL && "Loading architecture failed");
// 1MB of RAM, 16 components max
nn_computer *computer = nn_newComputer(universe, "testMachine", arch, NULL, 1*1024*1024, 16);
nn_setEnergyInfo(computer, 5000, 5000);
nn_setCallBudget(computer, 18000);
nn_addSupportedArchitecture(computer, arch);
nn_eeprom genericEEPROM = {
.userdata = NULL,
.refc = 1,
.deinit = NULL,
.control = ne_eeprom_getControl,
.getSize = ne_eeprom_getSize,
.getDataSize = ne_eeprom_getDataSize,
.getLabel = ne_eeprom_getLabel,
.setLabel = ne_eeprom_setLabel,
.get = ne_eeprom_get,
.set = ne_eeprom_set,
.getData = ne_eeprom_getData,
.setData = ne_eeprom_setData,
.isReadonly = ne_eeprom_isReadonly,
.makeReadonly = ne_eeprom_makeReadonly,
};
nn_addEeprom(computer, "luaBios.lua", 0, &genericEEPROM);
ne_fs fs = {
.files = {NULL},
.fileLen = 0,
};
nn_filesystem genericFS = {
.refc = 0,
.userdata = &fs,
.deinit = NULL,
.control = (void *)ne_fs_getControl,
.getLabel = ne_eeprom_getLabel,
.setLabel = ne_eeprom_setLabel,
.spaceUsed = ne_fs_spaceUsed,
.spaceTotal = ne_fs_spaceTotal,
.isReadOnly = ne_eeprom_isReadonly,
.size = (void *)ne_fs_size,
.remove = NULL,
.lastModified = (void *)ne_fs_lastModified,
.rename = NULL,
.exists = (void *)ne_fs_exists,
.isDirectory = (void *)ne_fs_isDirectory,
.makeDirectory = (void *)ne_fs_makeDirectory,
.list = (void *)ne_fs_list,
.open = (void *)ne_fs_open,
.close = (void *)ne_fs_close,
.write = (void *)ne_fs_write,
.read = (void *)ne_fs_read,
.seek = NULL,
};
nn_addFileSystem(computer, "OpenOS", 1, &genericFS);
ne_drive drive = {
.file = fopen("data/drive.img", "r+")
};
assert(drive.file != NULL);
nn_drive genericDrive = {
.refc = 0,
.userdata = &drive,
.deinit = (void *)ne_drive_close,
.control = (void *)ne_drive_getControl,
.getLabel = ne_eeprom_getLabel,
.setLabel = ne_eeprom_setLabel,
.getPlatterCount = (void *)ne_drive_getPlatterCount,
.getSectorSize = (void *)ne_drive_getSectorSize,
.getCapacity = (void *)ne_drive_getCapacity,
.readSector = (void *)ne_drive_readSector,
.writeSector = (void *)ne_drive_writeSector,
};
nn_addDrive(computer, "drive.img", 4, &genericDrive);
nn_screen *s = nn_newScreen(&alloc, 80, 32, 16, 16, 256);
nn_addKeyboard(s, "shitty keyboard");
nn_mountKeyboard(computer, "shitty keyboard", 2);
nn_addScreen(computer, "Main Screen", 2, s);
nn_gpuControl gpuCtrl = {
.maxWidth = 240,
.maxHeight = 80,
.maxDepth = 16,
.totalVRAM = 32*1024,
.vramByteChangeCost = 0,
.vramByteChangeEnergy = 0,
.vramByteChangeHeat = 0,
.vramByteChangeLatency = 0,
.pixelChangeCost = 0,
.pixelChangeEnergy = 0,
.pixelChangeHeat = 0,
.pixelChangeLatency = 0,
.pixelResetCost = 0,
.pixelResetEnergy = 0,
.pixelResetHeat = 0,
.pixelResetLatency = 0,
.colorChangeLatency = 0,
.colorChangeCost = 0,
.colorChangeEnergy = 0,
.colorChangeHeat = 0,
};
nn_addGPU(computer, "RTX 6090", 3, &gpuCtrl);
SetConfigFlags(FLAG_WINDOW_RESIZABLE);
InitWindow(800, 600, "emulator");
Font unscii = LoadFont("unscii-16-full.ttf");
double lastTime = nn_realTime();
static int release_check_list[256];
memset(release_check_list, 0, sizeof(int)*256);
uint8_t release_check_ptr;
SetExitKey(KEY_NULL);
while(true) {
if(WindowShouldClose()) break;
while (true) { // TODO: find out if we can check if the keycode and unicode are for the same key event or not
int keycode = GetKeyPressed();
int unicode = GetCharPressed();
if (keycode == 0 && unicode == 0) {
break;
}
if (keycode != 0) {
release_check_list[release_check_ptr++] = keycode;
}
nn_value values[5];
values[0] = nn_values_cstring("key_down");
values[1] = nn_values_cstring("shitty keyboard");
values[2] = nn_values_integer(unicode);
values[3] = nn_values_integer(keycode_to_oc(keycode));
values[4] = nn_values_cstring("USER");
const char* error = nn_pushSignal(computer, values, 5);
if (error != NULL) {
// well fuck
printf("error happened when eventing the keyboarding: %s\n", error);;;;;;
}
}
for (int i = 0; i < 256; i++) {
int key = release_check_list[i];
if (key != 0) {
if (IsKeyReleased(key)) {
// omg
nn_value values[5];
values[0] = nn_values_cstring("key_up");
values[1] = nn_values_cstring("shitty keyboard");
values[2] = nn_values_integer(0); // we can't really know, unless we store it, which i am way too lazy to do.
values[3] = nn_values_integer(keycode_to_oc(key));
values[4] = nn_values_cstring("USER");
const char* error = nn_pushSignal(computer, values, 5);
if (error != NULL) {
// well fuck
printf("error happened when eventing the keyboarding: %s\n", error);;;;;;
}
}
}
}
double now = nn_realTime();
double dt = now - lastTime;
if(dt == 0) dt = 1.0/60;
lastTime = now;
double heat = nn_getTemperature(computer);
double roomHeat = nn_getRoomTemperature(computer);
double tx = 0.1;
// remove some heat per second
nn_removeHeat(computer, dt * (rand() % 3) * tx * (heat - roomHeat));
if(nn_isOverheating(computer)) {
goto render;
}
int state = nn_tickComputer(computer);
if(state == NN_STATE_SWITCH) {
nn_architecture *nextArch = nn_getNextArchitecture(computer);
printf("Next architecture: %s\n", nextArch->archName);
break;
} else if(state == NN_STATE_CLOSING || state == NN_STATE_REPEAT) {
break;
} else if(state == NN_STATE_BLACKOUT) {
printf("blackout\n");
break;
}
const char *e = nn_getError(computer);
if(e != NULL) {
printf("Error: %s\n", e);
break;
}
render:
BeginDrawing();
ClearBackground(BLACK);
int scrW = 1, scrH = 1;
nn_getResolution(s, &scrW, &scrH);
int pixelHeight = GetScreenHeight() / scrH;
float spacing = (float)pixelHeight/10;
int pixelWidth = MeasureTextEx(unscii, "A", pixelHeight, spacing).x;
for(size_t x = 0; x < scrW; x++) {
for(size_t y = 0; y < scrH; y++) {
nn_scrchr_t p = nn_getPixel(s, x, y);
// fuck palettes
Color fgColor = ne_processColor(p.fg);
Color bgColor = ne_processColor(p.bg);
DrawRectangle(x * pixelWidth, y * pixelHeight, pixelWidth, pixelHeight, bgColor);
DrawTextCodepoint(unscii, p.codepoint, (Vector2) {x * pixelWidth, y * pixelHeight}, pixelHeight - 5, fgColor);
}
}
Color heatColor = GREEN;
if(heat > 60) heatColor = YELLOW;
if(heat > 80) heatColor = RED;
size_t memUsage = nn_getComputerMemoryUsed(computer);
size_t memTotal = nn_getComputerMemoryTotal(computer);
DrawText(TextFormat("Heat: %.02lf Memory Used: %.2lf%%", heat, (double)memUsage / memTotal * 100), 10, GetScreenHeight() - 30, 20, heatColor);
EndDrawing();
}
// destroy
nn_deleteComputer(computer);
nn_unsafeDeleteUniverse(universe);
CloseWindow();
return 0;
}