updated version

This commit is contained in:
2026-02-11 14:04:00 -07:00
parent b934f79878
commit d2d0a508ec
8 changed files with 295 additions and 185 deletions

14
.clangd Normal file
View File

@@ -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

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
.DS_Store
/build
*.o

View File

@@ -1,4 +1,4 @@
#define CUSTOM_STRING_MAX_LEN 64
#define CUSTOM_STRING_MAX_LEN 256
typedef struct {
char buffer[CUSTOM_STRING_MAX_LEN];

View File

@@ -3,21 +3,25 @@
#define FONT_PACK_H
#include <stdint.h>
#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

View File

@@ -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,

View File

@@ -4,6 +4,8 @@
#include <stddef.h>
#include <stdbool.h>
#include "font_pack.h"
#include <math.h> // 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 =====

View File

@@ -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);
}
}

View File

@@ -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;
}