From 90a3fc18e233afda8b990709b93c789fe6631182 Mon Sep 17 00:00:00 2001 From: Kumi Date: Tue, 9 Jan 2024 16:29:07 +0100 Subject: [PATCH] Updated toolchain and implemented basic C library functions Switched the makefile to use `clang` as the default compiler, enhancing portability and potentially optimizing the build process. New implementations of `abs`, `strcpy`, and `strcmp` provide foundational C library support. Enhanced text and line rendering in the graphics subsystem with the inclusion of STB's easy font printing library, facilitating on-screen debug message display. Memory management functions `malloc` and `free` have been introduced, paving the way for dynamic memory allocation. Accompanying test cases for memory operations validate basic functionality and robustness of the new implementations. The adjustment and enablement of compiler flags further tailor the compilation process to the system's requirements. --- GNUmakefile | 10 +++---- src/clib/stdlib.c | 3 ++ src/clib/stdlib.h | 6 ++++ src/clib/string.c | 15 ++++++++++ src/clib/string.h | 7 +++++ src/console/fonts.c | 52 +++++++++++++++++++++++++++++++--- src/console/fonts.h | 4 +-- src/console/graphics.c | 46 ++++++++++++++++++++++++++++++ src/console/graphics.h | 15 ++++++++++ src/kernel.c | 12 ++++---- src/memory.c | 5 ---- src/memory.h | 7 +++++ src/tests/memory.c | 64 ++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 224 insertions(+), 22 deletions(-) create mode 100644 src/clib/stdlib.c create mode 100644 src/clib/stdlib.h create mode 100644 src/clib/string.c create mode 100644 src/clib/string.h create mode 100644 src/console/graphics.c create mode 100644 src/console/graphics.h create mode 100644 src/tests/memory.c diff --git a/GNUmakefile b/GNUmakefile index d6917aa..97b6d43 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -18,7 +18,7 @@ endef # It is suggested to use a custom built cross toolchain to build a kernel. # We are using the standard "cc" here, it may work by using # the host system's toolchain, but this is not guaranteed. -override DEFAULT_CC := cc +override DEFAULT_CC := clang $(eval $(call DEFAULT_VAR,CC,$(DEFAULT_CC))) # Same thing for "ld" (the linker). @@ -49,16 +49,16 @@ override CFLAGS += \ -ffreestanding \ -fno-stack-protector \ -fno-stack-check \ + -fno-tree-vectorize \ -fno-lto \ -fPIE \ -m64 \ -march=x86-64 \ -mno-80387 \ -mno-mmx \ - -mno-red-zone - -#-mno-sse \ - -mno-sse2 \ + -mno-red-zone \ + -mno-sse \ + -mno-sse2 # Internal C preprocessor flags that should not be changed by the user. override CPPFLAGS := \ diff --git a/src/clib/stdlib.c b/src/clib/stdlib.c new file mode 100644 index 0000000..fb83593 --- /dev/null +++ b/src/clib/stdlib.c @@ -0,0 +1,3 @@ +int abs(int x) { + return x < 0 ? -x : x; +} \ No newline at end of file diff --git a/src/clib/stdlib.h b/src/clib/stdlib.h new file mode 100644 index 0000000..5bddd05 --- /dev/null +++ b/src/clib/stdlib.h @@ -0,0 +1,6 @@ +#ifndef STDLIB_H +#define STDLIB_H 1 + +int abs(int x); + +#endif // STDLIB_H \ No newline at end of file diff --git a/src/clib/string.c b/src/clib/string.c new file mode 100644 index 0000000..e02e8b1 --- /dev/null +++ b/src/clib/string.c @@ -0,0 +1,15 @@ +char *strcpy(char *dest, const char *src) { + char *saved = dest; + while (*src) { + *dest++ = *src++; + } + *dest = 0; // Null-terminate the destination string + return saved; // Return the start of the destination string +} + +int strcmp(const char *s1, const char *s2) { + while (*s1 && (*s1 == *s2)) { + s1++, s2++; + } + return *(const unsigned char *)s1 - *(const unsigned char *)s2; +} diff --git a/src/clib/string.h b/src/clib/string.h new file mode 100644 index 0000000..160e195 --- /dev/null +++ b/src/clib/string.h @@ -0,0 +1,7 @@ +#ifndef STRING_H +#define STRING_H 1 + +char *strcpy(char *dest, const char *src); +int strcmp(const char *s1, const char *s2); + +#endif // STRING_H \ No newline at end of file diff --git a/src/console/fonts.c b/src/console/fonts.c index ab5eacc..a4f3ed1 100644 --- a/src/console/fonts.c +++ b/src/console/fonts.c @@ -1,6 +1,50 @@ #include "fonts.h" -#include // For malloc/free +#include "graphics.h" +#include "../../vendor/stb/stb_easy_font.h" -void draw_text(struct limine_framebuffer *framebuffer, const char *text, float x, float y, uint32_t fg_color, uint32_t bg_color) { - return; -} \ No newline at end of file +void draw_text(struct limine_framebuffer *framebuffer, const char *text, + float x, float y, uint32_t fg_color, uint32_t bg_color) { + // First call to get the total number of quads needed to draw the text + int quads = stb_easy_font_print(0, 0, (char *)text, NULL, NULL, 0); + float *vertex_data = (float *)malloc(quads * 4 * 6 * sizeof(float)); + if (!vertex_data) { + // Handle memory allocation failure + draw_line(framebuffer, 0, 0, framebuffer->width, framebuffer->height, + 0xFF0000); + draw_line(framebuffer, framebuffer->width, 0, 0, framebuffer->height, + 0xFF0000); + } + + // Second call to actually generate the vertex data + stb_easy_font_print(x, y, (char *)text, NULL, vertex_data, 0); + + // Loop over each quad and each vertex + for (int i = 0; i < quads * 4 * 6; i += 6) { + // Extract the position of the quad from the vertex data + float quad_x = vertex_data[i + 0]; + float quad_y = vertex_data[i + 1]; + + // Draw each pixel of the quad onto the framebuffer + int pixel_x = (int)quad_x; + int pixel_y = (int)quad_y; + if (pixel_x >= 0 && pixel_y >= 0 && pixel_x < framebuffer->width && + pixel_y < framebuffer->height) { + uint32_t *pixel = + (uint32_t *)((uintptr_t)framebuffer->address + + (pixel_y * framebuffer->pitch) + (pixel_x * 4)); + + // Check the fourth value that stb_easy_font_print puts in vertex_data, + // if it's below a threshold, it's considered the background. + if (vertex_data[i + 4] < 0.1f) { + if (bg_color != fg_color) { + *pixel = bg_color; + } + } else { + *pixel = fg_color; + } + } + } + + // Free the vertex data after drawing + free(vertex_data); +} diff --git a/src/console/fonts.h b/src/console/fonts.h index 2a7157f..6dd14c5 100644 --- a/src/console/fonts.h +++ b/src/console/fonts.h @@ -1,7 +1,7 @@ #ifndef FONTS_H -#define FONTS_H +#define FONTS_H 1 -#include "../limine.h" // Make sure Limine types are available for use +#include "../limine.h" void draw_text(struct limine_framebuffer *framebuffer, const char *text, float x, float y, uint32_t fg_color, uint32_t bg_color); diff --git a/src/console/graphics.c b/src/console/graphics.c new file mode 100644 index 0000000..781e65a --- /dev/null +++ b/src/console/graphics.c @@ -0,0 +1,46 @@ +#include "graphics.h" + +void draw_pixel(struct limine_framebuffer *framebuffer, int x, int y, + uint32_t color) { + if (x >= 0 && y >= 0 && x < framebuffer->width && y < framebuffer->height) { + uint32_t *fb_ptr = framebuffer->address; + fb_ptr[y * (framebuffer->pitch / 4) + x] = color; + } +} + +void draw_line(struct limine_framebuffer *framebuffer, int x1, int y1, int x2, + int y2, uint32_t color) { + + int dx = abs(x2 - x1); + int sx = x1 < x2 ? 1 : -1; + int dy = -abs(y2 - y1); + int sy = y1 < y2 ? 1 : -1; + + int error = dx + dy; + + while (1) { + draw_pixel(framebuffer, x1, y1, color); + if (x1 == x2 && y1 == y2) + break; + int e2 = 2 * error; + if (e2 >= dy) { + if (x1 == x2) + break; + error = error + dy; + x1 = x1 + sx; + } + if (e2 <= dx) { + if (y1 == y2) + break; + error = error + dx; + y1 = y1 + sy; + } + } +} + +void draw_demo_line(struct limine_framebuffer *framebuffer, uint32_t color) { + // Note: we assume the framebuffer model is RGB with 32-bit pixels. + for (size_t i = 0; i < 100; i++) { + draw_pixel(framebuffer, i, i, color); + } +} diff --git a/src/console/graphics.h b/src/console/graphics.h new file mode 100644 index 0000000..c524d98 --- /dev/null +++ b/src/console/graphics.h @@ -0,0 +1,15 @@ +#include +#ifndef GRAPHICS_H +#define GRAPHICS_H 1 + +#include +#include + +#include "../limine.h" + +void draw_line(struct limine_framebuffer *framebuffer, int x1, int y1, int x2, + int y2, uint32_t color); + +void draw_demo_line(struct limine_framebuffer *framebuffer, uint32_t color); + +#endif // GRAPHICS_H \ No newline at end of file diff --git a/src/kernel.c b/src/kernel.c index 06da0d0..94a737a 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -6,7 +6,10 @@ #include "limine.h" #include "memory.h" +#include "clib/stdlib.h" + #include "console/fonts.h" +#include "console/graphics.h" // Set the base revision to 1, this is recommended as this is the latest // base revision described by the Limine boot protocol specification. @@ -48,18 +51,15 @@ void _start(void) { struct limine_framebuffer *framebuffer = framebuffer_request.response->framebuffers[0]; - // Note: we assume the framebuffer model is RGB with 32-bit pixels. - for (size_t i = 0; i < 100; i++) { - uint32_t *fb_ptr = framebuffer->address; - fb_ptr[i * (framebuffer->pitch / 4) + i] = 0xffffff; - } - // Write to framebuffer float x = 10.0f; float y = 10.0f; uint32_t fg_color = 0xFFFFFFFF; // White text uint32_t bg_color = 0x00000000; // Black background + draw_demo_line(framebuffer, 0x880000); + draw_line(framebuffer, 100, 0, 1000, 1000, 0x00ffff); + // Draw some text draw_text(framebuffer, "Hello, World!", x, y, fg_color, bg_color); diff --git a/src/memory.c b/src/memory.c index 6fb0004..94e9915 100644 --- a/src/memory.c +++ b/src/memory.c @@ -1,8 +1,3 @@ -// GCC and Clang reserve the right to generate calls to the following -// 4 functions even if they are not directly called. -// Implement them as the C specification mandates. -// DO NOT remove or rename these functions, or stuff will eventually break! - #include #include #include diff --git a/src/memory.h b/src/memory.h index 4316df0..4854d85 100644 --- a/src/memory.h +++ b/src/memory.h @@ -1,3 +1,6 @@ +#ifndef MEMORY_H +#define MEMORY_H 1 + #include #define MEMORY_POOL_SIZE 1024 * 1024 * 64 // 64 MB @@ -6,3 +9,7 @@ typedef char ALIGN[16]; typedef union header header_t; +void *malloc(size_t size); +void free(void *ptr); + +#endif // MEMORY_H diff --git a/src/tests/memory.c b/src/tests/memory.c new file mode 100644 index 0000000..dc25bea --- /dev/null +++ b/src/tests/memory.c @@ -0,0 +1,64 @@ +#include + +#include "../clib/string.h" +#include "../memory.h" + +void test_basic_allocation() { + size_t big_size = 0x0F; // An unreasonably large size + void *ptr = malloc(big_size); + if (ptr != NULL) { + //printf("Basic allocation test passed.\n"); + free(ptr); + } else { + //printf("Basic allocation test failed!\n"); + } +} + +void test_allocation_failure() { + size_t big_size = 0xFFFFFFFF; // An unreasonably large size + void *ptr = malloc(big_size); + if (ptr == NULL) { + //printf("Allocation failure test passed.\n"); + } else { + //printf("Allocation failure test failed!\n"); + free(ptr); + } +} + +void test_memory_writing() { + char *ptr = (char *)malloc(10); + if (ptr != NULL) { + strcpy(ptr, "test"); + if (strcmp(ptr, "test") == 0) { + //printf("Memory writing test passed.\n"); + } else { + //printf("Memory writing test failed!\n"); + } + free(ptr); + } else { + //printf("Memory writing test skipped, allocation failed!\n"); + } +} + +void test_free_and_reuse() { + void *ptr1 = malloc(10); + free(ptr1); + void *ptr2 = malloc(10); + if (ptr1 == ptr2) { + //printf("Free and reuse test passed.\n"); + free(ptr2); + } else { + //printf("Free and reuse test failed!\n"); + } +} + +int main() { + test_basic_allocation(); + test_allocation_failure(); + test_memory_writing(); + test_free_and_reuse(); + + // Add other tests as necessary + + return 0; +}