diff --git a/core/include/nova64_framebuffer.h b/core/include/nova64_framebuffer.h new file mode 100644 index 0000000..d662218 --- /dev/null +++ b/core/include/nova64_framebuffer.h @@ -0,0 +1,40 @@ +#ifndef NOVA64_FRAMEBUFFER_H +#define NOVA64_FRAMEBUFFER_H + +#include "nova64_core.h" +#include +#include +#include + +// Display dimensions +#define NOVA64_DISPLAY_WIDTH 480 +#define NOVA64_DISPLAY_HEIGHT 800 +#define NOVA64_DISPLAY_PIXELS (NOVA64_DISPLAY_WIDTH * NOVA64_DISPLAY_HEIGHT) + +// Pixel format: RGB565 (uint16_t) +typedef uint16_t pixel_t; + +// Framebuffer size in bytes (768,000 bytes) +#define NOVA64_FRAMEBUFFER_SIZE_BYTES (NOVA64_DISPLAY_PIXELS * sizeof(pixel_t)) + +// Alignment requirement for PPU/DMA2D on ESP32-P4 (cache line alignment) +#define NOVA64_DMA_ALIGNMENT 64 + +typedef struct { + pixel_t *front_buffer; // Buffer currently being read by display/MIPI-DSI + pixel_t *back_buffer; // Buffer currently being written to by WASM/PPU + + volatile bool vsync_pending; // True when a buffer swap has been requested + uint32_t frame_count; // Frame counter +} nova64_framebuffer_t; + +// Global instance access +NOVA64_API nova64_framebuffer_t nova64_framebuffer; + +// Function prototypes +bool nova64_framebuffer_init(void); +void nova64_framebuffer_destroy(void); +void nova64_framebuffer_swap(void); +void nova64_framebuffer_clear(pixel_t color); + +#endif // NOVA64_FRAMEBUFFER_H \ No newline at end of file diff --git a/core/src/nova64_core.c b/core/src/nova64_core.c index 78c4afd..895c6b8 100644 --- a/core/src/nova64_core.c +++ b/core/src/nova64_core.c @@ -1,5 +1,6 @@ #include "nova64_core.h" #include "FreeRTOS.h" +#include "nova64_framebuffer.h" #include "nova64_internal.h" #include "queue.h" #include "task.h" @@ -7,6 +8,7 @@ #include #include + #if defined(ESP_PLATFORM) #include "esp_pm.h" #include "esp_rom_sys.h" @@ -152,6 +154,12 @@ NOVA64_API bool nova64_init(const nova64_io_interface_t *io_interface) { } nova64_log("Initializing...\n"); + + if (!nova64_framebuffer_init()) { + nova64_log("Framebuffer initialization failed.\n"); + return false; + } + if (!initialize_wasm_runtime()) { nova64_log("WASM runtime initialization failed.\n"); return false; @@ -199,6 +207,7 @@ NOVA64_API bool nova64_shutdown(void) { } wasm_runner_terminate(); + nova64_framebuffer_destroy(); nova64_host_sleep_ms(100); // Allow time for tasks to clean up nova64_log_scheduler_state(); diff --git a/core/src/nova64_framebuffer.c b/core/src/nova64_framebuffer.c new file mode 100644 index 0000000..8ce8510 --- /dev/null +++ b/core/src/nova64_framebuffer.c @@ -0,0 +1,94 @@ +#include "nova64_framebuffer.h" +#include "nova64_internal.h" +#include + +// Framebuffer allocation +#if defined(ESP_PLATFORM) +// ESP-IDF heap-cap-APIs for PSRAM allocation +#include "esp_heap_caps.h" +#define ALLOC_FRAMEBUFFER(size) \ + heap_caps_aligned_alloc(NOVA64_DMA_ALIGNMENT, size, \ + MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT) +#define FREE_FRAMEBUFFER(ptr) heap_caps_free(ptr) +#else +#include +#define ALLOC_FRAMEBUFFER(size) malloc(size) +#define FREE_FRAMEBUFFER(ptr) free(ptr) +#endif + +nova64_framebuffer_t nova64_framebuffer = {0}; + +bool nova64_framebuffer_init(void) { + nova64_log("Initializing Framebuffer (Resolution: %dx%d, RGB565)...\n"); + + nova64_framebuffer.front_buffer = + (pixel_t *)ALLOC_FRAMEBUFFER(NOVA64_FRAMEBUFFER_SIZE_BYTES); + + if (!nova64_framebuffer.front_buffer) { + nova64_log("ERROR: Failed to allocate Front-Buffer (%d Bytes) in PSRAM!\n", + NOVA64_FRAMEBUFFER_SIZE_BYTES); + return false; + } + + nova64_framebuffer.back_buffer = + (pixel_t *)ALLOC_FRAMEBUFFER(NOVA64_FRAMEBUFFER_SIZE_BYTES); + + if (!nova64_framebuffer.back_buffer) { + nova64_log("ERROR: Failed to allocate Back-Buffer (%d Bytes) in PSRAM!\n", + NOVA64_FRAMEBUFFER_SIZE_BYTES); + FREE_FRAMEBUFFER(nova64_framebuffer.front_buffer); + return false; + } + + // Initialize buffers + memset(nova64_framebuffer.front_buffer, 0, NOVA64_FRAMEBUFFER_SIZE_BYTES); + memset(nova64_framebuffer.back_buffer, 0, NOVA64_FRAMEBUFFER_SIZE_BYTES); + + nova64_framebuffer.vsync_pending = false; + nova64_framebuffer.frame_count = 0; + + nova64_log("Framebuffer initialized successfully in PSRAM. Address: " + "Front=%p, Back=%p\n", + (void *)nova64_framebuffer.front_buffer, + (void *)nova64_framebuffer.back_buffer); + + return true; +} + +void nova64_framebuffer_destroy(void) { + nova64_log("Destroying Framebuffer and freeing PSRAM...\n"); + + if (nova64_framebuffer.front_buffer) { + FREE_FRAMEBUFFER(nova64_framebuffer.front_buffer); + nova64_framebuffer.front_buffer = NULL; + } + + if (nova64_framebuffer.back_buffer) { + FREE_FRAMEBUFFER(nova64_framebuffer.back_buffer); + nova64_framebuffer.back_buffer = NULL; + } + + nova64_framebuffer.vsync_pending = false; + nova64_framebuffer.frame_count = 0; + + nova64_log("Framebuffer successfully destroyed.\n"); +} + +void nova64_framebuffer_swap(void) { + pixel_t *temp = nova64_framebuffer.front_buffer; + nova64_framebuffer.front_buffer = nova64_framebuffer.back_buffer; + nova64_framebuffer.back_buffer = temp; + + nova64_framebuffer.frame_count++; + nova64_framebuffer.vsync_pending = false; +} + +void nova64_framebuffer_clear(pixel_t color) { + uint32_t *p32 = (uint32_t *)nova64_framebuffer.back_buffer; + uint32_t color32 = ((uint32_t)color << 16) | color; + size_t count32 = NOVA64_DISPLAY_PIXELS / 2; + + for (size_t i = 0; i < count32; i++) { + p32[i] = color32; + } +} \ No newline at end of file