diff --git a/TODO.md b/TODO.md index 1109bcb..200feb1 100644 --- a/TODO.md +++ b/TODO.md @@ -43,7 +43,7 @@ # Internal changes -- rework some interfaces to account for possibility of errors +- make sure OOMs are recoverable +- rework some interfaces to use pre-allocated or stack-allocated memory more - use dynamic arrays for signals (and maybe components), but still keep the maximums to prevent memory hogging - setup an extensive testing system to find bugs easier -- check if ports are open in `nn_pushNetworkMessage` diff --git a/src/component.c b/src/component.c index 23825ce..e27c08a 100644 --- a/src/component.c +++ b/src/component.c @@ -123,13 +123,13 @@ nn_bool_t nn_invokeComponentMethod(nn_component *component, const char *name) { for(nn_size_t i = 0; i < table->methodCount; i++) { nn_method_t method = table->methods[i]; if(nn_strcmp(method.name, name) == 0) { - nn_callCost(component->computer, NN_CALL_COST); - if(!method.direct) { - nn_triggerIndirect(component->computer); - } - if(!nni_checkMethodEnabled(method, component->statePtr)) { - return false; // pretend it's gone - } + nn_callCost(component->computer, NN_CALL_COST); + if(!method.direct) { + nn_triggerIndirect(component->computer); + } + if(!nni_checkMethodEnabled(method, component->statePtr)) { + return false; // pretend it's gone + } method.method(component->statePtr, method.userdata, component, component->computer); return true; } diff --git a/src/components/gpu.c b/src/components/gpu.c index f50c942..f39956b 100644 --- a/src/components/gpu.c +++ b/src/components/gpu.c @@ -55,6 +55,17 @@ nn_bool_t nni_inBounds(nni_gpu *gpu, int x, int y) { true; } +nn_size_t nni_vramNeededForSize(int w, int h) { + return w * h; +} + +nn_size_t nni_vramNeededForScreen(nn_screen *screen) { + if(screen == NULL) return 0; + int w, h; + nn_maxResolution(screen, &w, &h); + return nni_vramNeededForSize(w, h); +} + // VRAM nni_buffer *nni_vram_newBuffer(nn_Alloc *alloc, int width, int height) { @@ -66,6 +77,15 @@ nni_buffer *nni_vram_newBuffer(nn_Alloc *alloc, int width, int height) { buf->width = width; buf->height = height; buf->data = nn_alloc(alloc, sizeof(nn_scrchr_t) * area); + for(int i = 0; i < area; i++) { + buf->data[i] = (nn_scrchr_t) { + .codepoint = ' ', + .fg = 0xFFFFFF, + .bg = 0x000000, + .isFgPalette = false, + .isBgPalette = false, + }; + } if(buf->data == NULL) { nn_dealloc(alloc, buf, sizeof(nni_buffer)); } @@ -123,6 +143,55 @@ void nni_vram_set(nni_gpu *gpu, int x, int y, const char *s, nn_bool_t vertical) } } +void nni_vram_fill(nni_gpu *gpu, int x, int y, int w, int h, const char *s) { + nni_buffer *buffer = gpu->buffers[gpu->activeBuffer - 1]; + // DoS mitigation + if(x < 0) x = 0; + if(y < 0) y = 0; + if(w > buffer->width) w = buffer->width - x; + if(h > buffer->height) h = buffer->height - y; + + nn_scrchr_t p = nni_gpu_makePixel(gpu, s); + + for(int py = 0; py < h; py++) { + for(int px = 0; px < w; px++) { + nni_vram_setPixel(buffer, px, py, p); + } + } +} + +void nni_vram_copy(nni_gpu *gpu, int x, int y, int w, int h, int tx, int ty, nn_errorbuf_t err) { + nni_buffer *buffer = gpu->buffers[gpu->activeBuffer - 1]; + // DoS mitigation + if(x < 0) x = 0; + if(y < 0) y = 0; + if(w > buffer->width) w = buffer->width - x; + if(h > buffer->height) h = buffer->height - y; + + nn_size_t tmpBufSize = sizeof(nn_scrchr_t) * w * h; + nn_scrchr_t *tmpBuf = nn_alloc(&gpu->alloc, tmpBufSize); + if(tmpBuf == NULL) { + nn_error_write(err, "out of memory"); + return; + } + + // copy + for(int iy = 0; iy < h; iy++) { + for(int ix = 0; ix < w; ix++) { + tmpBuf[ix + iy * w] = nni_vram_getPixel(buffer, x, y); + } + } + + for(int iy = 0; iy < h; iy++) { + for(int ix = 0; ix < w; ix++) { + nn_scrchr_t p = tmpBuf[ix + iy * w]; + nni_vram_setPixel(buffer, x + ix + tx, y + iy + ty, p); + } + } + + nn_dealloc(&gpu->alloc, tmpBuf, tmpBufSize); +} + // GPU stuff nni_gpu *nni_newGPU(nn_Alloc *alloc, nn_gpuControl *ctrl) { @@ -200,11 +269,22 @@ void nni_gpu_bind(nni_gpu *gpu, void *_, nn_component *component, nn_computer *c return; } + nn_screen *oldScreen = gpu->currentScreen; + nn_size_t oldScreenVRAM = nni_vramNeededForScreen(oldScreen); nn_screen *screen = nn_getComponentUserdata(c); + nn_size_t screenVRAM = nni_vramNeededForScreen(screen); + + if(gpu->usedVRAM - oldScreenVRAM + screenVRAM > gpu->ctrl.totalVRAM) { + nn_setCError(computer, "out of vram"); + return; + } + + gpu->usedVRAM -= oldScreenVRAM; + gpu->usedVRAM += screenVRAM; + nn_retainScreen(screen); - if(gpu->currentScreen != NULL) nn_destroyScreen(gpu->currentScreen); + if(oldScreen != NULL) nn_destroyScreen(oldScreen); gpu->currentScreen = screen; - if(reset) { for(nn_size_t i = 0; i < screen->width; i++) { @@ -222,13 +302,13 @@ void nni_gpu_bind(nni_gpu *gpu, void *_, nn_component *component, nn_computer *c if(gpu->screenAddress != NULL) { nn_deallocStr(&gpu->alloc, gpu->screenAddress); } + // TODO: fix OOM here gpu->screenAddress = nn_strdup(&gpu->alloc, addr); nn_return(computer, nn_values_boolean(true)); } void nni_gpu_set(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - if(gpu->currentScreen == NULL) return; int x = nn_toInt(nn_getArgument(computer, 0)) - 1; int y = nn_toInt(nn_getArgument(computer, 1)) - 1; const char *s = nn_toCString(nn_getArgument(computer, 2)); @@ -239,6 +319,14 @@ void nni_gpu_set(nni_gpu *gpu, void *_, nn_component *component, nn_computer *co return; } + if(gpu->activeBuffer != 0) { + nni_buffer *buffer = gpu->buffers[gpu->activeBuffer - 1]; + nni_vram_set(gpu, x, y, s, isVertical); + return; + } + + if(gpu->currentScreen == NULL) return; + nn_size_t current = 0; while(s[current] != 0) { unsigned int codepoint = nn_unicode_nextCodepointPermissive(s, ¤t); @@ -343,8 +431,6 @@ void nni_gpu_setBackground(nni_gpu *gpu, void *_, nn_component *component, nn_co gpu->currentBg = color; gpu->isBgPalette = isPalette; - nn_simulateBufferedIndirect(component, 1, gpu->ctrl.screenColorChangesPerTick); - nn_return(computer, nn_values_integer(old)); if(idx != -1) { nn_return(computer, nn_values_integer(idx)); @@ -375,8 +461,6 @@ void nni_gpu_setForeground(nni_gpu *gpu, void *_, nn_component *component, nn_co gpu->currentFg = color; gpu->isFgPalette = isPalette; - nn_simulateBufferedIndirect(component, 1, gpu->ctrl.screenColorChangesPerTick); - nn_return(computer, nn_values_integer(old)); if(idx != -1) { nn_return(computer, nn_values_integer(idx)); @@ -388,7 +472,6 @@ void nni_gpu_getForeground(nni_gpu *gpu, void *_, nn_component *component, nn_co } void nni_gpu_fill(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - if(gpu->currentScreen == NULL) return; int x = nn_toInt(nn_getArgument(computer, 0)) - 1; int y = nn_toInt(nn_getArgument(computer, 1)) - 1; int w = nn_toInt(nn_getArgument(computer, 2)); @@ -398,6 +481,13 @@ void nni_gpu_fill(nni_gpu *gpu, void *_, nn_component *component, nn_computer *c nn_setCError(computer, "bad argument #5 (character expected)"); return; } + + if(gpu->activeBuffer != 0) { + nni_vram_fill(gpu, x, y, w, h, s); + return; + } + + if(gpu->currentScreen == NULL) return; nn_size_t startIdx = 0; int codepoint = nn_unicode_nextCodepointPermissive(s, &startIdx); @@ -436,7 +526,6 @@ void nni_gpu_fill(nni_gpu *gpu, void *_, nn_component *component, nn_computer *c } void nni_gpu_copy(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - if(gpu->currentScreen == NULL) return; int x = nn_toInt(nn_getArgument(computer, 0)) - 1; int y = nn_toInt(nn_getArgument(computer, 1)) - 1; int w = nn_toInt(nn_getArgument(computer, 2)); @@ -444,6 +533,17 @@ void nni_gpu_copy(nni_gpu *gpu, void *_, nn_component *component, nn_computer *c int tx = nn_toInt(nn_getArgument(computer, 4)); int ty = nn_toInt(nn_getArgument(computer, 5)); + if(gpu->activeBuffer != 0) { + nn_errorbuf_t err = ""; + nni_vram_copy(gpu, x, y, w, h, tx, ty, err); + if(!nn_error_isEmpty(err)) { + nn_setError(computer, err); + } + return; + } + + if(gpu->currentScreen == NULL) return; + // prevent DoS if(x < 0) x = 0; if(y < 0) y = 0; @@ -539,8 +639,284 @@ void nni_gpu_maxDepth(nni_gpu *gpu, void *_, nn_component *component, nn_compute nn_return(computer, nn_values_integer(gpu->currentScreen->maxDepth)); } -void nni_gpu_useless(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - nn_return_boolean(computer, true); +void nni_gpu_totalMemory(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { + nn_return_integer(computer, gpu->ctrl.totalVRAM); +} + +void nni_gpu_usedMemory(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { + nn_return_integer(computer, gpu->usedVRAM); +} + +void nni_gpu_freeMemory(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { + nn_return_integer(computer, gpu->ctrl.totalVRAM - gpu->usedVRAM); +} + +void nni_gpu_getActiveBuffer(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { + nn_return_integer(computer, gpu->activeBuffer); +} + +void nni_gpu_setActiveBuffer(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { + int buf = nn_toInt(nn_getArgument(computer, 0)); + if(!nni_gpu_validActiveScreen(gpu, buf)) { + nn_setCError(computer, "invalid buffer"); + return; + } + + gpu->activeBuffer = buf; + nn_return_integer(computer, buf); +} + +void nni_gpu_buffers(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { + int bufCount = 0; + for(int i = 0; i < gpu->ctrl.maximumBufferCount; i++) { + if(gpu->buffers[i] != NULL) { + gpu->vramIDBuf[bufCount] = i + 1; + bufCount++; + } + } + + nn_value arr = nn_return_array(computer, bufCount); + for(int i = 0; i < bufCount; i++) { + nn_values_set(arr, i, nn_values_integer(gpu->vramIDBuf[i])); + } +} + +void nni_gpu_allocateBuffer(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { + int width = gpu->ctrl.defaultBufferWidth; + int height = gpu->ctrl.defaultBufferHeight; + + nn_value widthVal = nn_getArgument(computer, 0); + nn_value heightVal = nn_getArgument(computer, 1); + + if(widthVal.tag != NN_VALUE_NIL) { + width = nn_toInt(widthVal); + } + if(heightVal.tag != NN_VALUE_NIL) { + height = nn_toInt(heightVal); + } + + if(width < 0 || height < 0) { + nn_setCError(computer, "invalid size"); + return; + } + + nn_size_t vramNeeded = nni_vramNeededForSize(width, height); + + if(gpu->usedVRAM + vramNeeded > gpu->ctrl.totalVRAM) { + nn_setCError(computer, "out of vram"); + return; + } + + nn_size_t idx = 0; + for(nn_size_t i = 0; i < gpu->ctrl.maximumBufferCount; i++) { + if(gpu->buffers[i] == NULL) { + idx = i + 1; + break; + } + } + + if(idx == 0) { + nn_setCError(computer, "too many buffers"); + return; + } + + nni_buffer *buf = nni_vram_newBuffer(&gpu->alloc, width, height); + if(buf == NULL) { + nn_setCError(computer, "out of memory"); + return; + } + gpu->buffers[idx - 1] = buf; + + gpu->usedVRAM += vramNeeded; + + nn_return_integer(computer, idx); +} + +void nni_gpu_freeBuffer(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { + int bufidx = gpu->activeBuffer; + nn_value bufVal = nn_getArgument(computer, 0); + if(bufVal.tag != NN_VALUE_NIL) { + bufidx = nn_toInt(bufVal); + } + + if(!nni_gpu_validActiveScreen(gpu, bufidx) || bufidx == 0) { + nn_setCError(computer, "invalid buffer"); + return; + } + + nni_buffer *buf = gpu->buffers[bufidx - 1]; + if(buf == NULL) { + nn_setCError(computer, "invalid buffer"); + return; + } + + int vramUsed = buf->width * buf->height; + nni_vram_deinit(&gpu->alloc, buf); + gpu->buffers[bufidx - 1] = NULL; + + if(bufidx == gpu->activeBuffer) gpu->activeBuffer = 0; + gpu->usedVRAM -= vramUsed; +} + +void nni_gpu_freeAllBuffers(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { + gpu->activeBuffer = 0; + + for(nn_size_t i = 0; i < gpu->ctrl.maximumBufferCount; i++) { + if(gpu->buffers[i] != NULL) { + int vramUsed = gpu->buffers[i]->width * gpu->buffers[i]->height; + nni_vram_deinit(&gpu->alloc, gpu->buffers[i]); + gpu->buffers[i] = NULL; + gpu->usedVRAM -= vramUsed; + } + } +} + +void nni_gpu_getBufferSize(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { + int bufidx = gpu->activeBuffer; + nn_value bufVal = nn_getArgument(computer, 0); + if(bufVal.tag != NN_VALUE_NIL) { + bufidx = nn_toInt(bufVal); + } + + if(!nni_gpu_validActiveScreen(gpu, bufidx)) { + nn_setCError(computer, "invalid buffer"); + return; + } + + if(bufidx == 0) { + if(gpu->currentScreen == NULL) { + nn_setCError(computer, "invalid buffer"); + return; + } + int w, h; + nn_getResolution(gpu->currentScreen, &w, &h); + nn_return_integer(computer, w); + nn_return_integer(computer, h); + return; + } + + nni_buffer *buf = gpu->buffers[bufidx - 1]; + if(buf == NULL) { + nn_setCError(computer, "invalid buffer"); + return; + } + + nn_return_integer(computer, buf->width); + nn_return_integer(computer, buf->height); +} + +void nni_gpu_bitblt(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { + // I will kill OC creators for this + int dst = nn_toIntOr(nn_getArgument(computer, 0), 0); + int x = nn_toIntOr(nn_getArgument(computer, 1), 1); + int y = nn_toIntOr(nn_getArgument(computer, 2), 1); + int width = nn_toIntOr(nn_getArgument(computer, 3), 0); + int height = nn_toIntOr(nn_getArgument(computer, 4), 0); + int src = nn_toIntOr(nn_getArgument(computer, 5), gpu->activeBuffer); + int fromCol = nn_toIntOr(nn_getArgument(computer, 6), 1); + int fromRow = nn_toIntOr(nn_getArgument(computer, 7), 1); + + if(x < 1) x = 1; + if(y < 1) y = 1; + if(fromCol < 1) fromCol = 1; + if(fromRow < 1) fromRow = 1; + + if(!nni_gpu_validActiveScreen(gpu, dst)) { + nn_setCError(computer, "invalid destination buffer"); + return; + } + + if(!nni_gpu_validActiveScreen(gpu, src)) { + nn_setCError(computer, "invalid source buffer"); + return; + } + + // such great parsing + if(dst == 0) { + if(gpu->currentScreen == NULL) return; + int w, h; + nn_getResolution(gpu->currentScreen, &w, &h); + width = width == 0 ? w : width; + height = height == 0 ? h : height; + } else { + nni_buffer *buffer = gpu->buffers[dst - 1]; + if(buffer == NULL) return; + width = width == 0 ? buffer->width : width; + height = height == 0 ? buffer->height : height; + } + + if(dst == src) { + nn_setCError(computer, "invalid operation, use copy() instead"); + return; + } + + // from buffer to screen + if(dst == 0 && src != 0) { + nn_screen *screen = gpu->currentScreen; + nni_buffer *buf = gpu->buffers[src - 1]; + if(buf == NULL) { + nn_setCError(computer, "invalid source buffer"); + return; + } + + int w, h; + nn_getResolution(gpu->currentScreen, &w, &h); + + if(width > w) width = w; + if(height > h) height = h; + + for(int j = 0; j < height; j++) { + for(int i = 0; i < width; i++) { + nn_scrchr_t src = nni_vram_getPixel(buf, i + fromCol - 1, j + fromRow - 1); + nn_setPixel(screen, i + x - 1, j + y - 1, src); + } + } + return; + } + // from screen to buffer + if(dst != 0 && src == 0) { + nn_screen *screen = gpu->currentScreen; + nni_buffer *buf = gpu->buffers[dst - 1]; + if(buf == NULL) { + nn_setCError(computer, "invalid destination buffer"); + return; + } + + if(width > buf->width) width = buf->width; + if(height > buf->height) height = buf->height; + + for(int j = 0; j < height; j++) { + for(int i = 0; i < width; i++) { + nn_scrchr_t src = nn_getPixel(screen, i + fromCol - 1, j + fromRow - 1); + nni_vram_setPixel(buf, i + x - 1, j + y - 1, src); + } + } + return; + } + // from buffer to buffer + if(dst != 0 && src != 0) { + nni_buffer *srcBuf = gpu->buffers[src - 1]; + if(srcBuf == NULL) { + nn_setCError(computer, "invalid destination buffer"); + return; + } + nni_buffer *destBuf = gpu->buffers[dst - 1]; + if(destBuf == NULL) { + nn_setCError(computer, "invalid destination buffer"); + return; + } + + if(width > destBuf->width) width = destBuf->width; + if(height > destBuf->height) height = destBuf->height; + + for(int j = 0; j < height; j++) { + for(int i = 0; i < width; i++) { + nn_scrchr_t src = nni_vram_getPixel(srcBuf, i + fromCol - 1, j + fromRow - 1); + nni_vram_setPixel(destBuf, i + x - 1, j + y - 1, src); + } + } + return; + } } void nn_loadGraphicsCardTable(nn_universe *universe) { @@ -570,7 +946,17 @@ void nn_loadGraphicsCardTable(nn_universe *universe) { nn_defineMethod(gpuTable, "getViewport", (nn_componentMethod *)nni_gpu_getViewport, "getViewport(): integer, integer - Gets the current viewport resolution"); // VRAM buffers - nn_defineMethod(gpuTable, "freeAllBuffers", (nn_componentMethod *)nni_gpu_useless, "dummy for now"); + nn_defineMethod(gpuTable, "totalMemory", (nn_componentMethod *)nni_gpu_totalMemory, "totalMemory(): integer - Returns the VRAM capacity of the card"); + nn_defineMethod(gpuTable, "usedMemory", (nn_componentMethod *)nni_gpu_usedMemory, "usedMemory(): integer - Returns the amount of used VRAM"); + nn_defineMethod(gpuTable, "freeMemory", (nn_componentMethod *)nni_gpu_freeMemory, "freeMemory(): integer - Returns the amount of unused VRAM"); + nn_defineMethod(gpuTable, "buffers", (nn_componentMethod *)nni_gpu_buffers, "buffers(): integer[] - Returns the VRAM buffers allocated (not including the screen)"); + nn_defineMethod(gpuTable, "setActiveBuffer", (nn_componentMethod *)nni_gpu_setActiveBuffer, "setActiveBuffer(buffer: integer): integer - Changes the current buffer"); + nn_defineMethod(gpuTable, "getActiveBuffer", (nn_componentMethod *)nni_gpu_getActiveBuffer, "getActiveBuffer(): integer - Returns the current buffer"); + nn_defineMethod(gpuTable, "allocateBuffer", (nn_componentMethod *)nni_gpu_allocateBuffer, "allocateBuffer([width: integer, height: integer]): integer - Allocates a new VRAM buffer. Default size depends on GPU."); + nn_defineMethod(gpuTable, "freeBuffer", (nn_componentMethod *)nni_gpu_freeBuffer, "freeBuffer([buffer: integer]): boolean - Frees a buffer. By default, the current buffer. If the current buffer is freed, it will switch back to the screen."); + nn_defineMethod(gpuTable, "freeAllBuffers", (nn_componentMethod *)nni_gpu_freeAllBuffers, "freeAllBuffers() - Frees every VRAM buffer (if any). Also switches back to the screen."); + nn_defineMethod(gpuTable, "getBufferSize", (nn_componentMethod *)nni_gpu_getBufferSize, "getBufferSize(buffer: integer): integer, integer - Returns the size of the specified buffer."); + nn_defineMethod(gpuTable, "bitblt", (nn_componentMethod *)nni_gpu_bitblt, "dummy func"); } nn_component *nn_addGPU(nn_computer *computer, nn_address address, int slot, nn_gpuControl *control) { diff --git a/src/emulator.c b/src/emulator.c index 3ab7df2..acc9d44 100644 --- a/src/emulator.c +++ b/src/emulator.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -12,8 +13,6 @@ #ifdef NN_POSIX -#include - static double nni_realTime() { struct timespec time; if(clock_gettime(CLOCK_MONOTONIC, &time) < 0) return 0; // oh no @@ -621,7 +620,7 @@ int main() { // 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_setCallBudget(computer, 1*1024*1024); nn_addSupportedArchitecture(computer, arch); nn_eepromTable genericEEPROMTable = { @@ -645,7 +644,7 @@ int main() { nn_addEEPROM(computer, NULL, 0, genericEEPROM); - nn_address fsFolder = "OpenOS"; + nn_address fsFolder = "Halyde"; nn_filesystemTable genericFSTable = { .userdata = fsFolder, .deinit = NULL, @@ -716,7 +715,7 @@ int main() { nn_drive *genericDrive = nn_volatileDrive(&ctx, vdriveOpts, vdriveCtrl); nn_addDrive(computer, NULL, 4, genericDrive); - int maxWidth = 160, maxHeight = 48; + int maxWidth = 80, maxHeight = 32; nn_screen *s = nn_newScreen(&ctx, maxWidth, maxHeight, 24, 16, 256); nn_setDepth(s, 4); // looks cool @@ -729,11 +728,12 @@ int main() { nn_gpuControl gpuCtrl = { .totalVRAM = 16*1024, .maximumBufferCount = 64, // probably too many + .defaultBufferWidth = maxWidth, + .defaultBufferHeight = maxHeight, .screenCopyPerTick = 8, .screenFillPerTick = 16, .screenSetsPerTick = 32, - .screenColorChangesPerTick = 64, .heatPerPixelChange = 0.00005, .heatPerPixelReset = 0.00001, @@ -782,12 +782,15 @@ int main() { double idleTime = 0; int tps = 20; // mc TPS double interval = 1.0/tps; + double totalTime = 0; while(true) { if(WindowShouldClose()) break; nn_setEnergyInfo(computer, 5000, 5000); double dt = GetFrameTime(); + + totalTime += dt; double heat = nn_getTemperature(computer); double roomHeat = nn_getRoomTemperature(computer); @@ -797,6 +800,7 @@ int main() { // remove some heat per second nn_removeHeat(computer, dt * (rand() % 3) * tx * (heat - roomHeat)); if(nn_isOverheating(computer)) { + TraceLog(LOG_WARNING, "%3.2lf OVERHEATING", totalTime); goto render; } @@ -927,7 +931,7 @@ render: DrawTextCodepoint(unscii, p.codepoint, (Vector2) {x * pixelWidth + offX, y * pixelHeight + offY}, pixelHeight - 5, fgColor); } } - + EndDrawing(); } diff --git a/src/neonucleus.h b/src/neonucleus.h index 28cb79b..ed33636 100644 --- a/src/neonucleus.h +++ b/src/neonucleus.h @@ -592,6 +592,10 @@ nn_bool_t nn_toBoolean(nn_value val); const char *nn_toCString(nn_value val); const char *nn_toString(nn_value val, nn_size_t *len); +nn_intptr_t nn_toIntOr(nn_value val, nn_intptr_t defaultVal); +double nn_toNumberOr(nn_value val, double defaultVal); +nn_bool_t nn_toBooleanOr(nn_value val, nn_bool_t defaultVal); + /* * Computes the "packet size" of the values, using the same algorithm as OC. * This is used by pushSignal to check the size @@ -884,12 +888,13 @@ typedef struct nn_gpuControl { // VRAM Buffers int totalVRAM; int maximumBufferCount; + int defaultBufferWidth; + int defaultBufferHeight; // Calls per tick, only applicable to screens double screenCopyPerTick; double screenFillPerTick; double screenSetsPerTick; - double screenColorChangesPerTick; double bitbltPerTick; // for bitblit // Heat diff --git a/src/value.c b/src/value.c index 246a3bc..a87da72 100644 --- a/src/value.c +++ b/src/value.c @@ -205,6 +205,23 @@ const char *nn_toString(nn_value val, nn_size_t *len) { return c; } +nn_intptr_t nn_toIntOr(nn_value val, nn_intptr_t defaultVal) { + if(val.tag == NN_VALUE_INT) return val.integer; + if(val.tag == NN_VALUE_NUMBER) return val.number; + return defaultVal; +} + +double nn_toNumberOr(nn_value val, double defaultVal) { + if(val.tag == NN_VALUE_INT) return val.integer; + if(val.tag == NN_VALUE_NUMBER) return val.number; + return defaultVal; +} + +nn_bool_t nn_toBooleanOr(nn_value val, nn_bool_t defaultVal) { + if(val.tag == NN_VALUE_BOOL) return val.boolean; + return defaultVal; +} + nn_size_t nn_measurePacketSize(nn_value *vals, nn_size_t len) { nn_size_t size = 0; for(nn_size_t i = 0; i < len; i++) {