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.
This commit is contained in:
Kumi 2024-01-09 16:29:07 +01:00
parent afe216263e
commit 90a3fc18e2
Signed by: kumi
GPG key ID: ECBCC9082395383F
13 changed files with 224 additions and 22 deletions

View file

@ -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 := \

3
src/clib/stdlib.c Normal file
View file

@ -0,0 +1,3 @@
int abs(int x) {
return x < 0 ? -x : x;
}

6
src/clib/stdlib.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef STDLIB_H
#define STDLIB_H 1
int abs(int x);
#endif // STDLIB_H

15
src/clib/string.c Normal file
View file

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

7
src/clib/string.h Normal file
View file

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

View file

@ -1,6 +1,50 @@
#include "fonts.h"
#include <stdlib.h> // 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;
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);
}

View file

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

46
src/console/graphics.c Normal file
View file

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

15
src/console/graphics.h Normal file
View file

@ -0,0 +1,15 @@
#include <sys/types.h>
#ifndef GRAPHICS_H
#define GRAPHICS_H 1
#include <stdint.h>
#include <stdlib.h>
#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

View file

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

View file

@ -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 <stddef.h>
#include <stdint.h>
#include <unistd.h>

View file

@ -1,3 +1,6 @@
#ifndef MEMORY_H
#define MEMORY_H 1
#include <stddef.h>
#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

64
src/tests/memory.c Normal file
View file

@ -0,0 +1,64 @@
#include <stdio.h>
#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;
}