From d2d0a508ec3c0818dac23057a5e7fef5da571b74 Mon Sep 17 00:00:00 2001 From: thenatespack Date: Wed, 11 Feb 2026 14:04:00 -0700 Subject: [PATCH] updated version --- .clangd | 14 ++ .gitignore | 1 + include/String.h | 2 +- include/font_pack.h | 18 ++- include/io.h | 8 ++ include/renderer.h | 7 + src/main.c | 108 +++++++-------- src/renderer.c | 322 ++++++++++++++++++++++++++++---------------- 8 files changed, 295 insertions(+), 185 deletions(-) create mode 100644 .clangd diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..0f53c03 --- /dev/null +++ b/.clangd @@ -0,0 +1,14 @@ +# .clangd +CompileFlags: + Add: + - -I./include # Add your project's include directory + - -std=gnu11 # Use C11 standard with GNU extensions + - -Wall # Enable all warnings + - -Wextra # Extra warnings + - -Wno-unused-parameter # Optional: suppress some noisy warnings + +Index: + Threads: 0 # Use all available cores for indexing + +Diagnostics: + Suppress: [] # You can list warnings/errors to ignore if needed diff --git a/.gitignore b/.gitignore index 68fc691..f0e6fd7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .DS_Store /build +*.o diff --git a/include/String.h b/include/String.h index ce821cf..552cbd3 100644 --- a/include/String.h +++ b/include/String.h @@ -1,4 +1,4 @@ -#define CUSTOM_STRING_MAX_LEN 64 +#define CUSTOM_STRING_MAX_LEN 256 typedef struct { char buffer[CUSTOM_STRING_MAX_LEN]; diff --git a/include/font_pack.h b/include/font_pack.h index d7679d3..73cecee 100644 --- a/include/font_pack.h +++ b/include/font_pack.h @@ -3,21 +3,25 @@ #define FONT_PACK_H #include -#define FONT_WIDTH 16 +#define FONT_WIDTH 16 #define FONT_HEIGHT 16 -#define FONT_BITMAP_SIZE ((FONT_WIDTH * FONT_HEIGHT) / 8) // 32 bytes per character +#define FONT_BITMAP_SIZE \ + ((FONT_WIDTH * FONT_HEIGHT) / 8) // 32 bytes per character static const uint8_t empty_char_16x16[32] = {0}; typedef struct { - const uint8_t *bitmaps[26]; // Pointers to each 16x16 bitmap + const uint8_t *bitmaps[26]; // Pointers to each 16x16 bitmap } FontPack; extern const FontPack basic_font_pack; -static inline const uint8_t *font_get_char_bitmap(const FontPack *pack, char c) { - if (c >= 'A' && c <= 'Z') - return pack->bitmaps[c - 'A']; - return empty_char_16x16; // Or point to empty_char_16x16 +static inline const uint8_t *font_get_char_bitmap(const FontPack *pack, + char c) { + if (c >= 'a' && c <= 'z') // convert to uppercase + c = c - 'a' + 'A'; + if (c >= 'A' && c <= 'Z') + return pack->bitmaps[c - 'A']; + return empty_char_16x16; // fallback for other characters } #endif // FONT_PACK_H diff --git a/include/io.h b/include/io.h index 72bdcb6..d71670b 100644 --- a/include/io.h +++ b/include/io.h @@ -53,6 +53,14 @@ static void uart_print_int(unsigned int value) uart_putc(buffer[i]); } +static char uart_getc_nonblocking(void) +{ + if (UART0_FR & (1 << 4)) // RXFE = receive FIFO empty + return 0; // indicate no data + return (char)(UART0_DR & 0xFF); +} + + // UART_IO instance with function pointers assigned static const UART_IO uart_io = { .putc = uart_putc, diff --git a/include/renderer.h b/include/renderer.h index 91d1971..10689f7 100644 --- a/include/renderer.h +++ b/include/renderer.h @@ -4,6 +4,8 @@ #include #include #include "font_pack.h" +#include // For sqrt, round + typedef struct RenderObject { unsigned int x, y; @@ -27,6 +29,11 @@ typedef struct Renderer { void (*draw_rect)(struct Renderer *renderer, RenderObject *obj); void (*draw_text)(struct Renderer *renderer, RenderText *text); void (*draw_objects)(struct Renderer *renderer, RenderObject *objs[], unsigned int count); + void (*draw_line)(struct Renderer *renderer, int x0, int y0, int x1, int y1, unsigned int color); + void (*draw_circle)(struct Renderer *renderer, int cx, int cy, int radius, unsigned int color); + void (*fill_circle)(struct Renderer *renderer, int cx, int cy, int radius, unsigned int color); + void (*draw_triangle)(struct Renderer *renderer, int x0, int y0, int x1, int y1, int x2, int y2, unsigned int color); + void (*fill_triangle)(struct Renderer *renderer, int x0, int y0, int x1, int y1, int x2, int y2, unsigned int color); } Renderer; // ===== Window definition ===== diff --git a/src/main.c b/src/main.c index a600f87..c38951c 100644 --- a/src/main.c +++ b/src/main.c @@ -1,81 +1,65 @@ -#include "renderer.h" +#include "String.h" #include "color.h" #include "font_pack.h" #include "io.h" -#include "String.h" +#include "renderer.h" -static void delay_1s(void) -{ - volatile unsigned long count = 80000000; // Example for ~1 sec at ~80 MHz CPU - while (count--) - { - __asm__ volatile("nop"); - } +static void delay_1s(void) { + volatile unsigned long count = 80000000; // Example for ~1 sec at ~80 MHz CPU + while (count--) { + __asm__ volatile("nop"); + } } -void delay_seconds(unsigned int seconds) -{ - for (unsigned int i = 0; i < seconds; i++) - { - delay_1s(); - } +void delay_seconds(unsigned int seconds) { + for (unsigned int i = 0; i < seconds; i++) { + delay_1s(); + } } -int kernel_main(void) -{ - String str; - cs_init(&str); - cs_set(&str, "HELLO MARIO"); - uart_io.puts("Initializing renderer...\n"); +int kernel_main(void) { + String str; + cs_init(&str); + cs_set(&str, "HELLO MARIO"); + uart_io.puts("Initializing renderer...\n"); - Renderer renderer; - if (!renderer_init(&renderer)) - { - uart_io.puts("Failed to allocate framebuffer.\n"); - while (1) - ; + Renderer renderer; + if (!renderer_init(&renderer)) { + uart_io.puts("Failed to allocate framebuffer.\n"); + while (1) + ; + } + + uart_io.puts("Framebuffer allocated.\n"); + + RenderObject background = {0, 0, renderer.width, renderer.height, COLOR_RED}; + + while (1) { + // Non-blocking UART input + char c = uart_getc_nonblocking(); + + if (c >= 32 && c <= 126) { // printable + cs_append_char(&str, c); + } else if (c == '\b' || c == 127) { // backspace + if (str.length > 0) { + str.length--; + str.buffer[str.length] = '\0'; + } } - uart_io.puts("Framebuffer allocated.\n"); - uart_io.puts("Width: "); - uart_io.print_int(renderer.width); - uart_io.puts("\nHeight: "); - uart_io.print_int(renderer.height); - uart_io.putc('\n'); - - RenderObject background = {0, 0, renderer.width, renderer.height, COLOR_RED}; + // Create a fresh RenderText each loop RenderText text = { - .base = { - .x = 20, - .y = 30, - .color = 0xFFFFFFFF}, + .base = {.x = 20, .y = 30, .color = 0xFFFFFFFF}, .text = str.buffer, .font = &basic_font_pack, }; - while (1) - { - renderer.clear_screen(&renderer, background.color); - renderer.draw_text(&renderer, &text); + renderer.clear_screen(&renderer, background.color); + renderer.draw_text(&renderer, &text); - char c = uart_getc(); + // uart_puts(str.buffer); - if (c >= 32 && c <= 126) // Printable ASCII - { - cs_append_char(&str, c); - } - else if (c == '\b' || c == 127) // Backspace support - { - if (str.length > 0) - { - str.length--; - str.buffer[str.length] = '\0'; - } - } - - uart_puts(str.buffer); - renderer.draw_text(&renderer, &text); - - delay_seconds(4); - } + // Short delay for smoother typingi + delay_seconds(1); + } } diff --git a/src/renderer.c b/src/renderer.c index 954e3bb..ea4dda4 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -6,6 +6,10 @@ volatile unsigned int __attribute__((aligned(16))) mbox[36]; +// -------------------- +// Utility functions +// -------------------- + // Small delay loop static inline void delay(int n) { @@ -13,139 +17,47 @@ static inline void delay(int n) __asm__ volatile("nop"); } -// Mailbox call function for communication with GPU +// Integer absolute value +static inline int int_abs(int x) { return x < 0 ? -x : x; } + +// Integer min/max +static inline int int_min(int a, int b) { return a < b ? a : b; } +static inline int int_max(int a, int b) { return a > b ? a : b; } + +// -------------------- +// Mailbox communication +// -------------------- unsigned int mailbox_call(unsigned int channel) { unsigned int r = ((unsigned int)((unsigned long)&mbox) & ~0xF) | (channel & 0xF); - // Wait until mailbox not full while (*(volatile unsigned int *)(MBOX_BASE + 0x18) & 0x80000000) delay(100); - // Write the channel combined with mailbox address *(volatile unsigned int *)(MBOX_BASE + 0x20) = r; while (1) { - // Wait for response while (*(volatile unsigned int *)(MBOX_BASE + 0x18) & 0x40000000) delay(100); - // Check if response is for our message if (*(volatile unsigned int *)(MBOX_BASE + 0x00) == r) - { - // Return true if response is successful return mbox[1] == 0x80000000; - } } } -void draw_text_impl(Renderer *renderer, RenderText *text_obj) { - if (!renderer || !text_obj || !text_obj->text || !text_obj->font) { - return; // Invalid inputs - } - unsigned int x0 = text_obj->base.x; - unsigned int y0 = text_obj->base.y; - unsigned int color = text_obj->base.color; - const char *str = text_obj->text; +// -------------------- +// Drawing primitives +// -------------------- - // For each character in string - for (const char *p = str; *p != '\0'; p++) { - unsigned char c = (unsigned char)*p; // Ensure unsigned char for safety - - // Get bitmap (guaranteed non-NULL due to empty_char_16x16 fallback) - const uint8_t *bitmap = font_get_char_bitmap(text_obj->font, c); - - // Draw this single character (16x16) - for (unsigned int y = 0; y < FONT_HEIGHT; y++) { - // Read two bytes separately to avoid alignment issues - uint16_t row = ((uint16_t)bitmap[y * 2] << 8) | bitmap[y * 2 + 1]; - for (unsigned int bit = 0; bit < FONT_WIDTH; bit++) { - if (row & (1 << (15 - bit))) { // Check 16 bits, MSB first - unsigned int px = x0 + bit; - unsigned int py = y0 + y; - if (px < renderer->width && py < renderer->height) { - // Assume 32-bit pixels; pitch in bytes - unsigned int *pixel = &renderer->fb[py * (renderer->pitch / 4) + px]; - *pixel = color; - } - } - } - } - // Advance x for next char - x0 += FONT_WIDTH + 1; - } -} -// Initialize the framebuffer via mailbox property channel -bool renderer_init(Renderer *renderer) +// Draw a single pixel +static inline void draw_pixel(Renderer *renderer, int x, int y, unsigned int color) { - mbox[0] = 35 * 4; // Buffer size in bytes - mbox[1] = 0; // Request - - // Set physical width and height - mbox[2] = 0x48003; - mbox[3] = 8; - mbox[4] = 8; - mbox[5] = 640; - mbox[6] = 480; - - // Set virtual width and height - mbox[7] = 0x48004; - mbox[8] = 8; - mbox[9] = 8; - mbox[10] = 640; - mbox[11] = 480; - - // Set depth (bits per pixel) - mbox[12] = 0x48005; - mbox[13] = 4; - mbox[14] = 4; - mbox[15] = 32; - - // Allocate framebuffer - mbox[16] = 0x40001; - mbox[17] = 8; - mbox[18] = 8; - mbox[19] = 16; // alignment - mbox[20] = 0; - - // Get pitch - mbox[21] = 0x40008; - mbox[22] = 4; - mbox[23] = 4; - mbox[24] = 0; - - // End tag - mbox[25] = 0; - - if (!mailbox_call(MBOX_CH_PROP)) - return false; - - renderer->width = mbox[5]; - renderer->height = mbox[6]; - renderer->pitch = mbox[24]; - renderer->fb = (volatile unsigned int *)(mbox[19] & 0x3FFFFFFF); - - renderer->clear_screen = clear_screen_impl; - renderer->draw_rect = draw_rect_impl; - renderer->draw_text = draw_text_impl; - renderer->draw_objects = draw_objects_impl; - - return true; -} -// Render function implementations - -void clear_screen_impl(Renderer *renderer, unsigned int color) -{ - for (unsigned int y = 0; y < renderer->height; y++) - { - for (unsigned int x = 0; x < renderer->width; x++) - { - renderer->fb[y * (renderer->pitch / 4) + x] = color; - } - } + if (x >= 0 && y >= 0 && x < (int)renderer->width && y < (int)renderer->height) + renderer->fb[y * (renderer->pitch / 4) + x] = color; } +// Draw a rectangle void draw_rect_impl(Renderer *renderer, RenderObject *obj) { unsigned int max_y = obj->y + obj->height; @@ -157,18 +69,198 @@ void draw_rect_impl(Renderer *renderer, RenderObject *obj) max_x = renderer->width; for (unsigned int y = obj->y; y < max_y; y++) - { for (unsigned int x = obj->x; x < max_x; x++) - { renderer->fb[y * (renderer->pitch / 4) + x] = obj->color; - } +} + +// Clear the screen +void clear_screen_impl(Renderer *renderer, unsigned int color) +{ + for (unsigned int y = 0; y < renderer->height; y++) + for (unsigned int x = 0; x < renderer->width; x++) + renderer->fb[y * (renderer->pitch / 4) + x] = color; +} + +// Draw a line using Bresenham's algorithm +void draw_line_impl(Renderer *renderer, int x0, int y0, int x1, int y1, unsigned int color) +{ + int dx = int_abs(x1 - x0); + int sx = x0 < x1 ? 1 : -1; + int dy = -int_abs(y1 - y0); + int sy = y0 < y1 ? 1 : -1; + int err = dx + dy; + + while (1) + { + draw_pixel(renderer, x0, y0, color); + if (x0 == x1 && y0 == y1) break; + int e2 = 2 * err; + if (e2 >= dy) { err += dy; x0 += sx; } + if (e2 <= dx) { err += dx; y0 += sy; } } } +// Draw a circle outline using midpoint algorithm +void draw_circle_impl(Renderer *renderer, int cx, int cy, int radius, unsigned int color) +{ + int x = radius; + int y = 0; + int err = 0; + + while (x >= y) + { + draw_pixel(renderer, cx + x, cy + y, color); + draw_pixel(renderer, cx + y, cy + x, color); + draw_pixel(renderer, cx - y, cy + x, color); + draw_pixel(renderer, cx - x, cy + y, color); + draw_pixel(renderer, cx - x, cy - y, color); + draw_pixel(renderer, cx - y, cy - x, color); + draw_pixel(renderer, cx + y, cy - x, color); + draw_pixel(renderer, cx + x, cy - y, color); + + y += 1; + if (err <= 0) err += 2*y + 1; + if (err > 0) { x -= 1; err -= 2*x + 1; } + } +} + +// Fill a circle +void fill_circle_impl(Renderer *renderer, int cx, int cy, int radius, unsigned int color) +{ + for (int y = -radius; y <= radius; y++) + for (int x = -radius; x <= radius; x++) + if (x*x + y*y <= radius*radius) + draw_pixel(renderer, cx + x, cy + y, color); +} + +// Draw triangle outline +void draw_triangle_impl(Renderer *renderer, + int x0, int y0, + int x1, int y1, + int x2, int y2, + unsigned int color) +{ + draw_line_impl(renderer, x0, y0, x1, y1, color); + draw_line_impl(renderer, x1, y1, x2, y2, color); + draw_line_impl(renderer, x2, y2, x0, y0, color); +} + +// Fill triangle using barycentric coordinates +void fill_triangle_impl(Renderer *renderer, + int x0, int y0, + int x1, int y1, + int x2, int y2, + unsigned int color) +{ + int minX = int_min(int_min(x0, x1), x2); + int maxX = int_max(int_max(x0, x1), x2); + int minY = int_min(int_min(y0, y1), y2); + int maxY = int_max(int_max(y0, y1), y2); + + for (int y = minY; y <= maxY; y++) + for (int x = minX; x <= maxX; x++) + { + int w0 = (x1 - x0)*(y - y0) - (x - x0)*(y1 - y0); + int w1 = (x2 - x1)*(y - y1) - (x - x1)*(y2 - y1); + int w2 = (x0 - x2)*(y - y2) - (x - x2)*(y0 - y2); + if ((w0 >= 0 && w1 >= 0 && w2 >= 0) || (w0 <= 0 && w1 <= 0 && w2 <= 0)) + draw_pixel(renderer, x, y, color); + } +} + +// -------------------- +// Text rendering +// -------------------- +void draw_text_impl(Renderer *renderer, RenderText *text_obj) +{ + if (!renderer || !text_obj || !text_obj->text || !text_obj->font) return; + + unsigned int x0 = text_obj->base.x; + unsigned int y0 = text_obj->base.y; + unsigned int color = text_obj->base.color; + const char *str = text_obj->text; + unsigned int line_spacing = 2; + + for (const char *p = str; *p != '\0'; p++) + { + unsigned char c = (unsigned char)*p; + if (c == '\n') + { + x0 = text_obj->base.x; + y0 += FONT_HEIGHT + line_spacing; + if (y0 + FONT_HEIGHT > renderer->height) break; + continue; + } + + if (c >= 'a' && c <= 'z') c = c - 'a' + 'A'; + if (!(c >= 'A' && c <= 'Z')) c = ' '; + + if (x0 + FONT_WIDTH > renderer->width) + { + x0 = text_obj->base.x; + y0 += FONT_HEIGHT + line_spacing; + if (y0 + FONT_HEIGHT > renderer->height) break; + } + + const uint8_t *bitmap = font_get_char_bitmap(text_obj->font, c); + for (unsigned int y = 0; y < FONT_HEIGHT; y++) + { + uint16_t row = ((uint16_t)bitmap[y*2] << 8) | bitmap[y*2 + 1]; + for (unsigned int bit = 0; bit < FONT_WIDTH; bit++) + if (row & (1 << (15 - bit))) + draw_pixel(renderer, x0 + bit, y0 + y, color); + } + + x0 += FONT_WIDTH + 1; + } +} + +// -------------------- +// Draw multiple objects +// -------------------- void draw_objects_impl(Renderer *renderer, RenderObject *objs[], unsigned int count) { for (unsigned int i = 0; i < count; i++) - { draw_rect_impl(renderer, objs[i]); - } +} + +// -------------------- +// Renderer initialization +// -------------------- +bool renderer_init(Renderer *renderer) +{ + mbox[0] = 35 * 4; + mbox[1] = 0; + + // Physical width/height + mbox[2] = 0x48003; mbox[3] = 8; mbox[4] = 8; mbox[5] = 640; mbox[6] = 480; + // Virtual width/height + mbox[7] = 0x48004; mbox[8] = 8; mbox[9] = 8; mbox[10] = 640; mbox[11] = 480; + // Depth + mbox[12] = 0x48005; mbox[13] = 4; mbox[14] = 4; mbox[15] = 32; + // Allocate framebuffer + mbox[16] = 0x40001; mbox[17] = 8; mbox[18] = 8; mbox[19] = 16; mbox[20] = 0; + // Get pitch + mbox[21] = 0x40008; mbox[22] = 4; mbox[23] = 4; mbox[24] = 0; + // End tag + mbox[25] = 0; + + if (!mailbox_call(MBOX_CH_PROP)) return false; + + renderer->width = mbox[5]; + renderer->height = mbox[6]; + renderer->pitch = mbox[24]; + renderer->fb = (volatile unsigned int *)(mbox[19] & 0x3FFFFFFF); + + renderer->clear_screen = clear_screen_impl; + renderer->draw_rect = draw_rect_impl; + renderer->draw_text = draw_text_impl; + renderer->draw_objects = draw_objects_impl; + renderer->draw_line = draw_line_impl; + renderer->draw_circle = draw_circle_impl; + renderer->fill_circle = fill_circle_impl; + renderer->draw_triangle = draw_triangle_impl; + renderer->fill_triangle = fill_triangle_impl; + + return true; }