From 55bcdf37342d782c723166de54ff031d09b1281f Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Wed, 15 Oct 2025 19:32:21 -0700 Subject: Clear framebuffer to pink --- src/framebuffer.c | 84 +++++++++++++++++++++++++++++++++---------------------- src/framebuffer.h | 17 +++++++++++ src/kernel.c | 23 ++++++++++++++- src/mailbox.c | 19 +++++++++---- src/mailbox.h | 2 +- src/string.c | 59 ++++++++++++++++++++++++++++++++++++++ src/string.h | 9 ++++++ src/uart.c | 1 - 8 files changed, 171 insertions(+), 43 deletions(-) create mode 100644 src/string.c create mode 100644 src/string.h (limited to 'src') diff --git a/src/framebuffer.c b/src/framebuffer.c index c3845b1..6505ace 100644 --- a/src/framebuffer.c +++ b/src/framebuffer.c @@ -1,58 +1,74 @@ #include #include +#include #include #include #define WIDTH 640 #define HEIGHT 480 -#define DEPTH 32 +#define DEPTH 24 #define ALIGNMENT 16 // Framebuffer byte alignment. -typedef struct Framebuffer { - volatile void* pixels; - size_t size; -} Framebuffer; - static Framebuffer framebuffer = {}; bool framebuffer_init(uint32_t* error) { - MAIL_T uint32_t ConfigureScreen[20] = { - 80, // Size in bytes, aligned to MAIL_ALIGN. - MAILBOX_REQUEST, - TAG_FRAMEBUFFER_SET_PHYSICAL_SCREEN_SIZE, 8, MAILBOX_REQUEST, WIDTH, HEIGHT, - TAG_FRAMEBUFFER_SET_VIRTUAL_SCREEN_SIZE, 8, MAILBOX_REQUEST, WIDTH, HEIGHT, - TAG_FRAMEBUFFER_SET_DEPTH, 4, MAILBOX_REQUEST, DEPTH, - TAG_END, - 0, 0, 0 // Padding. - }; - mbox_write(PROPERTY_CHANNEL, ConfigureScreen); - const Mail* response = mbox_read(PROPERTY_CHANNEL); - if (response->code != MAILBOX_SUCCESS) { - goto end; - } - - MAIL_T uint32_t InitFramebuffer[8] = { - 32, // Size in bytes, aligned to MAIL_ALIGN. - MAILBOX_REQUEST, - TAG_FRAMEBUFFER_ALLOCATE, 8, MAILBOX_REQUEST, ALIGNMENT, 0, - TAG_END + // TODO: We can combine both messages, this one and the one below, into a + // single array and make a single call to mbox_write(). + MAIL_T uint32_t InitFramebuffer[24] = { + /*00*/96, // Size in bytes, aligned to MAIL_ALIGN. + /*01*/MAILBOX_REQUEST, + /*02*/TAG_FRAMEBUFFER_SET_PHYSICAL_SCREEN_SIZE, 8, MAILBOX_REQUEST, WIDTH, HEIGHT, + /*07*/TAG_FRAMEBUFFER_SET_VIRTUAL_SCREEN_SIZE, 8, MAILBOX_REQUEST, WIDTH, HEIGHT, + /*12*/TAG_FRAMEBUFFER_SET_DEPTH, 4, MAILBOX_REQUEST, DEPTH, + /*16*/TAG_FRAMEBUFFER_ALLOCATE, 8, MAILBOX_REQUEST, ALIGNMENT, 0, + /*21*/TAG_END, + /*22*/0, 0 // Padding. }; - mbox_write(PROPERTY_CHANNEL, InitFramebuffer); - response = mbox_read(PROPERTY_CHANNEL); - if (response->code != MAILBOX_SUCCESS) { + const uint32_t result = mbox_write(PROPERTY_CHANNEL, InitFramebuffer); + if (result != MAILBOX_SUCCESS) { goto end; } // The input mail is overwritten with the response. + // + // TAG_FRAMEBUFFER_ALLOCATE response: // u32 fb base address // u32 fb size - framebuffer.pixels = (void*)(uintptr_t)InitFramebuffer[5]; - framebuffer.size = InitFramebuffer[6]; - + // TODO: Do we need & 0x3FFFFFFF? Something about converting GPU address space + // to ARM address space. + // TODO: Should we read back the pitch, or does pitch match width? + framebuffer = (Framebuffer) { + .pixels = (Pixel*)((uintptr_t)InitFramebuffer[19] & 0x3FFFFFFF), + .size = InitFramebuffer[20], + .width = InitFramebuffer[5], + .height = InitFramebuffer[6], + .depth = InitFramebuffer[15] + }; + end: - *error = response->code; - return response->code == MAILBOX_SUCCESS; + *error = result; + return result == MAILBOX_SUCCESS; +} + +const Framebuffer* framebuffer_get() { + return &framebuffer; +} + +void framebuffer_present(const Pixel* pixels) { + memcpy(framebuffer.pixels, pixels, framebuffer.size); +} + +void framebuffer_clear(Pixel colour) { + const uint32_t num_channels = framebuffer.depth / 8; + const uint32_t num_pixels = framebuffer.size / num_channels; + volatile Pixel* fb = framebuffer.pixels; + for (size_t i = 0; i < num_pixels; i++) { + fb->r = colour.r; + fb->g = colour.g; + fb->b = colour.b; + fb++; + } } diff --git a/src/framebuffer.h b/src/framebuffer.h index d2d57cd..b44fa44 100644 --- a/src/framebuffer.h +++ b/src/framebuffer.h @@ -3,5 +3,22 @@ #include #include +typedef uint8_t Channel; + +typedef struct Pixel { + Channel r, g, b; +} Pixel; + +typedef struct Framebuffer { + Pixel* pixels; + uint32_t size; + uint32_t width; + uint32_t height; + uint32_t depth; +} Framebuffer; + bool framebuffer_init(uint32_t* error); +const Framebuffer* framebuffer_get(); +void framebuffer_present(const Pixel* pixels); +void framebuffer_clear(Pixel colour); diff --git a/src/kernel.c b/src/kernel.c index 151028d..2a4005a 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -2,8 +2,11 @@ #include #include #include +#include #include +#include + static void halt() { while (1) { asm volatile("wfi"); // Wait for interrupt. Core enters low-power state. @@ -13,6 +16,7 @@ static void halt() { void main() { bool success = true; uint32_t error = -1; + char buf[32]; const int raspi = raspi_init(); mmio_init(raspi); // Must be initialized before other peripherals. @@ -31,7 +35,24 @@ void main() { goto end; } - uart_print("Hello world!\n"); + const Framebuffer* fb = framebuffer_get(); + uart_print("Framebuffer:"); + uart_print("\n width: "); + uart_print(utoa(fb->width, buf, sizeof(buf))); + uart_print("\n height: "); + uart_print(utoa(fb->height, buf, sizeof(buf))); + uart_print("\n depth: "); + uart_print(utoa(fb->depth, buf, sizeof(buf))); + uart_print("\n addr: "); + uart_print(ptoa(fb->pixels, buf, sizeof(buf))); + uart_print("\n size: "); + uart_print(utoa(fb->size, buf, sizeof(buf))); + uart_print("\n"); + + uart_print("Clearing framebuffer\n"); + framebuffer_clear((Pixel){255, 0, 255}); + + uart_print("All done\n"); end: halt(); diff --git a/src/mailbox.c b/src/mailbox.c index 310b2b1..fcad179 100644 --- a/src/mailbox.c +++ b/src/mailbox.c @@ -7,7 +7,9 @@ enum { - MAILBOX = 0xB880 // Mailbox address relative to MMIO base address. + MAILBOX = 0xB880, // Mailbox address relative to MMIO base address. + STATUS_EMPTY = 0x40000000, // Empty bit. + STATUS_FULL = 0x80000000, // Full bit. }; typedef uint32_t Message; @@ -21,7 +23,7 @@ static inline Message msg_make(uint8_t channel, volatile const void* data) { } typedef struct Mailbox { - Message inbox; // 0x00 + Message inbox; // 0x00 uint32_t unused_1; // 0x04 uint32_t unused_2; // 0x08 uint32_t unused_3; // 0x0C @@ -29,15 +31,15 @@ typedef struct Mailbox { uint32_t unused_5; // 0x14 uint32_t status; // 0x18 uint32_t unused_6; // 0x1C - Message outbox; // 0x20 + Message outbox; // 0x20 } Mailbox; static inline bool inbox_empty(const volatile Mailbox* pMailbox) { - return (pMailbox->status & 0x40000000) != 0; + return (pMailbox->status & STATUS_EMPTY) != 0; } static inline bool outbox_full(const volatile Mailbox* pMailbox) { - return (pMailbox->status & 0x80000000) != 0; + return (pMailbox->status & STATUS_FULL) != 0; } static volatile Mailbox* pMailbox; @@ -59,10 +61,15 @@ const Mail* mbox_read(uint8_t channel) { return (const Mail*)((uintptr_t)msg & ~0xf); } -void mbox_write(uint8_t channel, volatile const void* mail) { +uint32_t mbox_write(uint8_t channel, volatile const void* mail) { // Wait until the outbox is clear. while (outbox_full(pMailbox)); // Send the mail. pMailbox->outbox = msg_make(channel, mail); + // Wait for the response. + // Mailbox messages cannot be streamed anyway, so wait here so that the API + // does not allow the caller to write two messages in a row without waiting. + const Mail* response = mbox_read(channel); + return response->code; } diff --git a/src/mailbox.h b/src/mailbox.h index 2104265..dbd7be0 100644 --- a/src/mailbox.h +++ b/src/mailbox.h @@ -66,5 +66,5 @@ typedef struct __attribute__((aligned(MAIL_ALIGN))) Mail { void mbox_init(); const Mail* mbox_read(uint8_t channel); -void mbox_write(uint8_t channel, volatile const void* mail); +uint32_t mbox_write(uint8_t channel, volatile const void* mail); diff --git a/src/string.c b/src/string.c new file mode 100644 index 0000000..4801154 --- /dev/null +++ b/src/string.c @@ -0,0 +1,59 @@ +#include + +#include + +void* memcpy(void* dst, const void* src, size_t count) { + // TODO: Can probably use better implementations based on hw instruction set. + uint32_t* dst32 = (uint32_t*)dst; + const uint32_t* src32 = (uint32_t*)src; + for(; count >= 4; count -= 4) { + *dst32++ = *src32++; + } + uint8_t* dst8 = (uint8_t*)dst32; + const uint8_t* src8 = (uint8_t*)src32; + for(; count != 0; count--) { + *dst8++ = *src8++; + } + return dst8; +} + +static int count_digits_u(unsigned int x) { + int count = 0; + for(; x != 0; x /= 10, count++); + return count; +} + +char* utoa(unsigned int x, char* buffer, size_t size) { + if (size > 0) { + const int num_digits = count_digits_u(x); + size_t i = 0; + while ((x != 0) && ((i+1) < size)) { + const unsigned int digit = x % 10; + x /= 10; + buffer[num_digits-1 - i++] = '0' + digit; + } + buffer[i] = 0; + } + return buffer; +} + +char* ptoa(const void* ptr, char* buffer, size_t size) { + if (size > 2) { + size_t i = 0; + buffer[i++] = '0'; + buffer[i++] = 'x'; + uintptr_t x = (uintptr_t)ptr; + const int num_digits = count_digits_u(x); + while ((x != 0) && (i < size)) { + const unsigned int digit = x % 16; + x /= 16; + const char hex = (digit < 10) ? ('0' + digit) : ('a' + (digit-10)); + buffer[num_digits-1+2 - i++] = hex; + } + buffer[i] = 0; + } else if (size > 0) { + buffer[0] = 0; + } + return buffer; +} + diff --git a/src/string.h b/src/string.h new file mode 100644 index 0000000..3f0e200 --- /dev/null +++ b/src/string.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +void* memcpy(void* dest, const void* src, size_t count); + +char* utoa(unsigned int x, char* buffer, size_t size); +char* ptoa(const void* ptr, char* buffer, size_t size); + diff --git a/src/uart.c b/src/uart.c index f8be797..0a2684b 100644 --- a/src/uart.c +++ b/src/uart.c @@ -75,7 +75,6 @@ void uart_init(int raspi) { if (raspi >= 3) { // Send message over property channel to configure UART clock. mbox_write(PROPERTY_CHANNEL, UART_SET_CLK); - mbox_read(PROPERTY_CHANNEL); } // Divider = 3000000 / (16 * 115200) = 1.627 = ~1. -- cgit v1.2.3