diff options
author | 3gg <3gg@shellblade.net> | 2025-10-15 19:32:21 -0700 |
---|---|---|
committer | 3gg <3gg@shellblade.net> | 2025-10-15 19:32:21 -0700 |
commit | 55bcdf37342d782c723166de54ff031d09b1281f (patch) | |
tree | 11a95bfb390ba6f73e122a17fe0a00312f25dc78 | |
parent | c099bcb7402421985e6e8c025e8cde591eaa073a (diff) |
Clear framebuffer to pink
-rw-r--r-- | src/framebuffer.c | 84 | ||||
-rw-r--r-- | src/framebuffer.h | 17 | ||||
-rw-r--r-- | src/kernel.c | 23 | ||||
-rw-r--r-- | src/mailbox.c | 19 | ||||
-rw-r--r-- | src/mailbox.h | 2 | ||||
-rw-r--r-- | src/string.c | 59 | ||||
-rw-r--r-- | src/string.h | 9 | ||||
-rw-r--r-- | src/uart.c | 1 |
8 files changed, 171 insertions, 43 deletions
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 @@ | |||
1 | #include <framebuffer.h> | 1 | #include <framebuffer.h> |
2 | 2 | ||
3 | #include <mailbox.h> | 3 | #include <mailbox.h> |
4 | #include <string.h> | ||
4 | 5 | ||
5 | #include <stddef.h> | 6 | #include <stddef.h> |
6 | #include <stdint.h> | 7 | #include <stdint.h> |
7 | 8 | ||
8 | #define WIDTH 640 | 9 | #define WIDTH 640 |
9 | #define HEIGHT 480 | 10 | #define HEIGHT 480 |
10 | #define DEPTH 32 | 11 | #define DEPTH 24 |
11 | #define ALIGNMENT 16 // Framebuffer byte alignment. | 12 | #define ALIGNMENT 16 // Framebuffer byte alignment. |
12 | 13 | ||
13 | typedef struct Framebuffer { | ||
14 | volatile void* pixels; | ||
15 | size_t size; | ||
16 | } Framebuffer; | ||
17 | |||
18 | static Framebuffer framebuffer = {}; | 14 | static Framebuffer framebuffer = {}; |
19 | 15 | ||
20 | bool framebuffer_init(uint32_t* error) { | 16 | bool framebuffer_init(uint32_t* error) { |
21 | MAIL_T uint32_t ConfigureScreen[20] = { | 17 | // TODO: We can combine both messages, this one and the one below, into a |
22 | 80, // Size in bytes, aligned to MAIL_ALIGN. | 18 | // single array and make a single call to mbox_write(). |
23 | MAILBOX_REQUEST, | 19 | MAIL_T uint32_t InitFramebuffer[24] = { |
24 | TAG_FRAMEBUFFER_SET_PHYSICAL_SCREEN_SIZE, 8, MAILBOX_REQUEST, WIDTH, HEIGHT, | 20 | /*00*/96, // Size in bytes, aligned to MAIL_ALIGN. |
25 | TAG_FRAMEBUFFER_SET_VIRTUAL_SCREEN_SIZE, 8, MAILBOX_REQUEST, WIDTH, HEIGHT, | 21 | /*01*/MAILBOX_REQUEST, |
26 | TAG_FRAMEBUFFER_SET_DEPTH, 4, MAILBOX_REQUEST, DEPTH, | 22 | /*02*/TAG_FRAMEBUFFER_SET_PHYSICAL_SCREEN_SIZE, 8, MAILBOX_REQUEST, WIDTH, HEIGHT, |
27 | TAG_END, | 23 | /*07*/TAG_FRAMEBUFFER_SET_VIRTUAL_SCREEN_SIZE, 8, MAILBOX_REQUEST, WIDTH, HEIGHT, |
28 | 0, 0, 0 // Padding. | 24 | /*12*/TAG_FRAMEBUFFER_SET_DEPTH, 4, MAILBOX_REQUEST, DEPTH, |
29 | }; | 25 | /*16*/TAG_FRAMEBUFFER_ALLOCATE, 8, MAILBOX_REQUEST, ALIGNMENT, 0, |
30 | mbox_write(PROPERTY_CHANNEL, ConfigureScreen); | 26 | /*21*/TAG_END, |
31 | const Mail* response = mbox_read(PROPERTY_CHANNEL); | 27 | /*22*/0, 0 // Padding. |
32 | if (response->code != MAILBOX_SUCCESS) { | ||
33 | goto end; | ||
34 | } | ||
35 | |||
36 | MAIL_T uint32_t InitFramebuffer[8] = { | ||
37 | 32, // Size in bytes, aligned to MAIL_ALIGN. | ||
38 | MAILBOX_REQUEST, | ||
39 | TAG_FRAMEBUFFER_ALLOCATE, 8, MAILBOX_REQUEST, ALIGNMENT, 0, | ||
40 | TAG_END | ||
41 | }; | 28 | }; |
42 | mbox_write(PROPERTY_CHANNEL, InitFramebuffer); | 29 | const uint32_t result = mbox_write(PROPERTY_CHANNEL, InitFramebuffer); |
43 | response = mbox_read(PROPERTY_CHANNEL); | 30 | if (result != MAILBOX_SUCCESS) { |
44 | if (response->code != MAILBOX_SUCCESS) { | ||
45 | goto end; | 31 | goto end; |
46 | } | 32 | } |
47 | 33 | ||
48 | // The input mail is overwritten with the response. | 34 | // The input mail is overwritten with the response. |
35 | // | ||
36 | // TAG_FRAMEBUFFER_ALLOCATE response: | ||
49 | // u32 fb base address | 37 | // u32 fb base address |
50 | // u32 fb size | 38 | // u32 fb size |
51 | framebuffer.pixels = (void*)(uintptr_t)InitFramebuffer[5]; | 39 | // TODO: Do we need & 0x3FFFFFFF? Something about converting GPU address space |
52 | framebuffer.size = InitFramebuffer[6]; | 40 | // to ARM address space. |
53 | 41 | // TODO: Should we read back the pitch, or does pitch match width? | |
42 | framebuffer = (Framebuffer) { | ||
43 | .pixels = (Pixel*)((uintptr_t)InitFramebuffer[19] & 0x3FFFFFFF), | ||
44 | .size = InitFramebuffer[20], | ||
45 | .width = InitFramebuffer[5], | ||
46 | .height = InitFramebuffer[6], | ||
47 | .depth = InitFramebuffer[15] | ||
48 | }; | ||
49 | |||
54 | end: | 50 | end: |
55 | *error = response->code; | 51 | *error = result; |
56 | return response->code == MAILBOX_SUCCESS; | 52 | return result == MAILBOX_SUCCESS; |
53 | } | ||
54 | |||
55 | const Framebuffer* framebuffer_get() { | ||
56 | return &framebuffer; | ||
57 | } | ||
58 | |||
59 | void framebuffer_present(const Pixel* pixels) { | ||
60 | memcpy(framebuffer.pixels, pixels, framebuffer.size); | ||
61 | } | ||
62 | |||
63 | void framebuffer_clear(Pixel colour) { | ||
64 | const uint32_t num_channels = framebuffer.depth / 8; | ||
65 | const uint32_t num_pixels = framebuffer.size / num_channels; | ||
66 | volatile Pixel* fb = framebuffer.pixels; | ||
67 | for (size_t i = 0; i < num_pixels; i++) { | ||
68 | fb->r = colour.r; | ||
69 | fb->g = colour.g; | ||
70 | fb->b = colour.b; | ||
71 | fb++; | ||
72 | } | ||
57 | } | 73 | } |
58 | 74 | ||
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 @@ | |||
3 | #include <stdbool.h> | 3 | #include <stdbool.h> |
4 | #include <stdint.h> | 4 | #include <stdint.h> |
5 | 5 | ||
6 | typedef uint8_t Channel; | ||
7 | |||
8 | typedef struct Pixel { | ||
9 | Channel r, g, b; | ||
10 | } Pixel; | ||
11 | |||
12 | typedef struct Framebuffer { | ||
13 | Pixel* pixels; | ||
14 | uint32_t size; | ||
15 | uint32_t width; | ||
16 | uint32_t height; | ||
17 | uint32_t depth; | ||
18 | } Framebuffer; | ||
19 | |||
6 | bool framebuffer_init(uint32_t* error); | 20 | bool framebuffer_init(uint32_t* error); |
21 | const Framebuffer* framebuffer_get(); | ||
22 | void framebuffer_present(const Pixel* pixels); | ||
23 | void framebuffer_clear(Pixel colour); | ||
7 | 24 | ||
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 @@ | |||
2 | #include <mailbox.h> | 2 | #include <mailbox.h> |
3 | #include <mmio.h> | 3 | #include <mmio.h> |
4 | #include <raspi.h> | 4 | #include <raspi.h> |
5 | #include <string.h> | ||
5 | #include <uart.h> | 6 | #include <uart.h> |
6 | 7 | ||
8 | #include <stdint.h> | ||
9 | |||
7 | static void halt() { | 10 | static void halt() { |
8 | while (1) { | 11 | while (1) { |
9 | asm volatile("wfi"); // Wait for interrupt. Core enters low-power state. | 12 | asm volatile("wfi"); // Wait for interrupt. Core enters low-power state. |
@@ -13,6 +16,7 @@ static void halt() { | |||
13 | void main() { | 16 | void main() { |
14 | bool success = true; | 17 | bool success = true; |
15 | uint32_t error = -1; | 18 | uint32_t error = -1; |
19 | char buf[32]; | ||
16 | 20 | ||
17 | const int raspi = raspi_init(); | 21 | const int raspi = raspi_init(); |
18 | mmio_init(raspi); // Must be initialized before other peripherals. | 22 | mmio_init(raspi); // Must be initialized before other peripherals. |
@@ -31,7 +35,24 @@ void main() { | |||
31 | goto end; | 35 | goto end; |
32 | } | 36 | } |
33 | 37 | ||
34 | uart_print("Hello world!\n"); | 38 | const Framebuffer* fb = framebuffer_get(); |
39 | uart_print("Framebuffer:"); | ||
40 | uart_print("\n width: "); | ||
41 | uart_print(utoa(fb->width, buf, sizeof(buf))); | ||
42 | uart_print("\n height: "); | ||
43 | uart_print(utoa(fb->height, buf, sizeof(buf))); | ||
44 | uart_print("\n depth: "); | ||
45 | uart_print(utoa(fb->depth, buf, sizeof(buf))); | ||
46 | uart_print("\n addr: "); | ||
47 | uart_print(ptoa(fb->pixels, buf, sizeof(buf))); | ||
48 | uart_print("\n size: "); | ||
49 | uart_print(utoa(fb->size, buf, sizeof(buf))); | ||
50 | uart_print("\n"); | ||
51 | |||
52 | uart_print("Clearing framebuffer\n"); | ||
53 | framebuffer_clear((Pixel){255, 0, 255}); | ||
54 | |||
55 | uart_print("All done\n"); | ||
35 | 56 | ||
36 | end: | 57 | end: |
37 | halt(); | 58 | 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 @@ | |||
7 | 7 | ||
8 | enum | 8 | enum |
9 | { | 9 | { |
10 | MAILBOX = 0xB880 // Mailbox address relative to MMIO base address. | 10 | MAILBOX = 0xB880, // Mailbox address relative to MMIO base address. |
11 | STATUS_EMPTY = 0x40000000, // Empty bit. | ||
12 | STATUS_FULL = 0x80000000, // Full bit. | ||
11 | }; | 13 | }; |
12 | 14 | ||
13 | typedef uint32_t Message; | 15 | typedef uint32_t Message; |
@@ -21,7 +23,7 @@ static inline Message msg_make(uint8_t channel, volatile const void* data) { | |||
21 | } | 23 | } |
22 | 24 | ||
23 | typedef struct Mailbox { | 25 | typedef struct Mailbox { |
24 | Message inbox; // 0x00 | 26 | Message inbox; // 0x00 |
25 | uint32_t unused_1; // 0x04 | 27 | uint32_t unused_1; // 0x04 |
26 | uint32_t unused_2; // 0x08 | 28 | uint32_t unused_2; // 0x08 |
27 | uint32_t unused_3; // 0x0C | 29 | uint32_t unused_3; // 0x0C |
@@ -29,15 +31,15 @@ typedef struct Mailbox { | |||
29 | uint32_t unused_5; // 0x14 | 31 | uint32_t unused_5; // 0x14 |
30 | uint32_t status; // 0x18 | 32 | uint32_t status; // 0x18 |
31 | uint32_t unused_6; // 0x1C | 33 | uint32_t unused_6; // 0x1C |
32 | Message outbox; // 0x20 | 34 | Message outbox; // 0x20 |
33 | } Mailbox; | 35 | } Mailbox; |
34 | 36 | ||
35 | static inline bool inbox_empty(const volatile Mailbox* pMailbox) { | 37 | static inline bool inbox_empty(const volatile Mailbox* pMailbox) { |
36 | return (pMailbox->status & 0x40000000) != 0; | 38 | return (pMailbox->status & STATUS_EMPTY) != 0; |
37 | } | 39 | } |
38 | 40 | ||
39 | static inline bool outbox_full(const volatile Mailbox* pMailbox) { | 41 | static inline bool outbox_full(const volatile Mailbox* pMailbox) { |
40 | return (pMailbox->status & 0x80000000) != 0; | 42 | return (pMailbox->status & STATUS_FULL) != 0; |
41 | } | 43 | } |
42 | 44 | ||
43 | static volatile Mailbox* pMailbox; | 45 | static volatile Mailbox* pMailbox; |
@@ -59,10 +61,15 @@ const Mail* mbox_read(uint8_t channel) { | |||
59 | return (const Mail*)((uintptr_t)msg & ~0xf); | 61 | return (const Mail*)((uintptr_t)msg & ~0xf); |
60 | } | 62 | } |
61 | 63 | ||
62 | void mbox_write(uint8_t channel, volatile const void* mail) { | 64 | uint32_t mbox_write(uint8_t channel, volatile const void* mail) { |
63 | // Wait until the outbox is clear. | 65 | // Wait until the outbox is clear. |
64 | while (outbox_full(pMailbox)); | 66 | while (outbox_full(pMailbox)); |
65 | // Send the mail. | 67 | // Send the mail. |
66 | pMailbox->outbox = msg_make(channel, mail); | 68 | pMailbox->outbox = msg_make(channel, mail); |
69 | // Wait for the response. | ||
70 | // Mailbox messages cannot be streamed anyway, so wait here so that the API | ||
71 | // does not allow the caller to write two messages in a row without waiting. | ||
72 | const Mail* response = mbox_read(channel); | ||
73 | return response->code; | ||
67 | } | 74 | } |
68 | 75 | ||
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 { | |||
66 | 66 | ||
67 | void mbox_init(); | 67 | void mbox_init(); |
68 | const Mail* mbox_read(uint8_t channel); | 68 | const Mail* mbox_read(uint8_t channel); |
69 | void mbox_write(uint8_t channel, volatile const void* mail); | 69 | uint32_t mbox_write(uint8_t channel, volatile const void* mail); |
70 | 70 | ||
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 @@ | |||
1 | #include <string.h> | ||
2 | |||
3 | #include <stdint.h> | ||
4 | |||
5 | void* memcpy(void* dst, const void* src, size_t count) { | ||
6 | // TODO: Can probably use better implementations based on hw instruction set. | ||
7 | uint32_t* dst32 = (uint32_t*)dst; | ||
8 | const uint32_t* src32 = (uint32_t*)src; | ||
9 | for(; count >= 4; count -= 4) { | ||
10 | *dst32++ = *src32++; | ||
11 | } | ||
12 | uint8_t* dst8 = (uint8_t*)dst32; | ||
13 | const uint8_t* src8 = (uint8_t*)src32; | ||
14 | for(; count != 0; count--) { | ||
15 | *dst8++ = *src8++; | ||
16 | } | ||
17 | return dst8; | ||
18 | } | ||
19 | |||
20 | static int count_digits_u(unsigned int x) { | ||
21 | int count = 0; | ||
22 | for(; x != 0; x /= 10, count++); | ||
23 | return count; | ||
24 | } | ||
25 | |||
26 | char* utoa(unsigned int x, char* buffer, size_t size) { | ||
27 | if (size > 0) { | ||
28 | const int num_digits = count_digits_u(x); | ||
29 | size_t i = 0; | ||
30 | while ((x != 0) && ((i+1) < size)) { | ||
31 | const unsigned int digit = x % 10; | ||
32 | x /= 10; | ||
33 | buffer[num_digits-1 - i++] = '0' + digit; | ||
34 | } | ||
35 | buffer[i] = 0; | ||
36 | } | ||
37 | return buffer; | ||
38 | } | ||
39 | |||
40 | char* ptoa(const void* ptr, char* buffer, size_t size) { | ||
41 | if (size > 2) { | ||
42 | size_t i = 0; | ||
43 | buffer[i++] = '0'; | ||
44 | buffer[i++] = 'x'; | ||
45 | uintptr_t x = (uintptr_t)ptr; | ||
46 | const int num_digits = count_digits_u(x); | ||
47 | while ((x != 0) && (i < size)) { | ||
48 | const unsigned int digit = x % 16; | ||
49 | x /= 16; | ||
50 | const char hex = (digit < 10) ? ('0' + digit) : ('a' + (digit-10)); | ||
51 | buffer[num_digits-1+2 - i++] = hex; | ||
52 | } | ||
53 | buffer[i] = 0; | ||
54 | } else if (size > 0) { | ||
55 | buffer[0] = 0; | ||
56 | } | ||
57 | return buffer; | ||
58 | } | ||
59 | |||
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 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <stddef.h> | ||
4 | |||
5 | void* memcpy(void* dest, const void* src, size_t count); | ||
6 | |||
7 | char* utoa(unsigned int x, char* buffer, size_t size); | ||
8 | char* ptoa(const void* ptr, char* buffer, size_t size); | ||
9 | |||
@@ -75,7 +75,6 @@ void uart_init(int raspi) { | |||
75 | if (raspi >= 3) { | 75 | if (raspi >= 3) { |
76 | // Send message over property channel to configure UART clock. | 76 | // Send message over property channel to configure UART clock. |
77 | mbox_write(PROPERTY_CHANNEL, UART_SET_CLK); | 77 | mbox_write(PROPERTY_CHANNEL, UART_SET_CLK); |
78 | mbox_read(PROPERTY_CHANNEL); | ||
79 | } | 78 | } |
80 | 79 | ||
81 | // Divider = 3000000 / (16 * 115200) = 1.627 = ~1. | 80 | // Divider = 3000000 / (16 * 115200) = 1.627 = ~1. |