Add STB submodule and implement text rendering

Introduced the STB (Sean Barrett's libraries) submodule to handle different utilities such as image loading and text rendering. The necessary makefile adjustments have been made to include this third-party library. Separated memory management functions into a new `memory.c` file to clean up `kernel.c`. Defined a basic `draw_text` function as a placeholder in `fonts.c` to set up the text rendering functionality. This change lays the groundwork for enhanced graphical capabilities in the console, utilizing STB for fonts and other graphical tasks.

Included are forward declarations and initial integration into the build system, as well as function stubs and associated header files. This refactor promotes modular code organization and paves the way for future graphical feature implementations, although the `draw_text` function is currently non-operative and serves as a template for future development.

Note: SSE and SSE2 optimizations have been re-commented out within the makefile, suggesting a decision to possibly defer or revisit this optimization approach.
This commit is contained in:
Kumi 2024-01-09 10:57:16 +01:00
parent 877dcc247d
commit afe216263e
Signed by: kumi
GPG key ID: ECBCC9082395383F
8 changed files with 278 additions and 97 deletions

3
.gitmodules vendored
View file

@ -2,3 +2,6 @@
path = vendor/limine
url = https://github.com/limine-bootloader/limine.git
branch = v6.x-branch-binary
[submodule "vendor/stb"]
path = vendor/stb
url = https://github.com/nothings/stb.git

View file

@ -55,10 +55,11 @@ override CFLAGS += \
-march=x86-64 \
-mno-80387 \
-mno-mmx \
-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 := \
-I src \
@ -90,6 +91,13 @@ override NASMFILES := $(shell cd src && find -L * -type f -name '*.asm')
override OBJ := $(addprefix obj/,$(CFILES:.c=.c.o) $(ASFILES:.S=.S.o) $(NASMFILES:.asm=.asm.o))
override HEADER_DEPS := $(addprefix obj/,$(CFILES:.c=.c.d) $(ASFILES:.S=.S.d))
## STB
STB_INCLUDE_PATH = vendor/stb
override CFLAGS += -I$(STB_INCLUDE_PATH)
## End STB
# Default target.
.PHONY: all
all: bin/$(KERNEL) iso hdd

View file

@ -0,0 +1,6 @@
#include "fonts.h"
#include <stdlib.h> // For malloc/free
void draw_text(struct limine_framebuffer *framebuffer, const char *text, float x, float y, uint32_t fg_color, uint32_t bg_color) {
return;
}

9
src/console/fonts.h Normal file
View file

@ -0,0 +1,9 @@
#ifndef FONTS_H
#define FONTS_H
#include "../limine.h" // Make sure Limine types are available for use
void draw_text(struct limine_framebuffer *framebuffer, const char *text,
float x, float y, uint32_t fg_color, uint32_t bg_color);
#endif // FONTS_H

View file

@ -1,9 +1,12 @@
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <limine.h>
#include <stddef.h>
#include <stdint.h>
#include <unistd.h>
#include <console/fonts.c>
#include "limine.h"
#include "memory.h"
#include "console/fonts.h"
// Set the base revision to 1, this is recommended as this is the latest
// base revision described by the Limine boot protocol specification.
@ -16,72 +19,13 @@ LIMINE_BASE_REVISION(1)
// NOT be made "static".
struct limine_framebuffer_request framebuffer_request = {
.id = LIMINE_FRAMEBUFFER_REQUEST,
.revision = 0
};
// 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!
// They CAN be moved to a different .c file.
void *memcpy(void *dest, const void *src, size_t n) {
uint8_t *pdest = (uint8_t *)dest;
const uint8_t *psrc = (const uint8_t *)src;
for (size_t i = 0; i < n; i++) {
pdest[i] = psrc[i];
}
return dest;
}
void *memset(void *s, int c, size_t n) {
uint8_t *p = (uint8_t *)s;
for (size_t i = 0; i < n; i++) {
p[i] = (uint8_t)c;
}
return s;
}
void *memmove(void *dest, const void *src, size_t n) {
uint8_t *pdest = (uint8_t *)dest;
const uint8_t *psrc = (const uint8_t *)src;
if (src > dest) {
for (size_t i = 0; i < n; i++) {
pdest[i] = psrc[i];
}
} else if (src < dest) {
for (size_t i = n; i > 0; i--) {
pdest[i-1] = psrc[i-1];
}
}
return dest;
}
int memcmp(const void *s1, const void *s2, size_t n) {
const uint8_t *p1 = (const uint8_t *)s1;
const uint8_t *p2 = (const uint8_t *)s2;
for (size_t i = 0; i < n; i++) {
if (p1[i] != p2[i]) {
return p1[i] < p2[i] ? -1 : 1;
}
}
return 0;
}
.id = LIMINE_FRAMEBUFFER_REQUEST, .revision = 0};
// Halt and catch fire function.
static void hcf(void) {
asm ("cli");
asm("cli");
for (;;) {
asm ("hlt");
asm("hlt");
}
}
@ -95,13 +39,14 @@ void _start(void) {
}
// Ensure we got a framebuffer.
if (framebuffer_request.response == NULL
|| framebuffer_request.response->framebuffer_count < 1) {
if (framebuffer_request.response == NULL ||
framebuffer_request.response->framebuffer_count < 1) {
hcf();
}
// Fetch the first framebuffer.
struct limine_framebuffer *framebuffer = framebuffer_request.response->framebuffers[0];
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++) {
@ -110,5 +55,14 @@ void _start(void) {
}
// 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 some text
draw_text(framebuffer, "Hello, World!", x, y, fg_color, bg_color);
// Now die.
hcf();
}

192
src/memory.c Normal file
View file

@ -0,0 +1,192 @@
// 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>
#include "memory.h"
size_t align(size_t size);
header_t *head, *tail;
void *memcpy(void *dest, const void *src, size_t n) {
uint8_t *pdest = (uint8_t *)dest;
const uint8_t *psrc = (const uint8_t *)src;
for (size_t i = 0; i < n; i++) {
pdest[i] = psrc[i];
}
return dest;
}
void *memset(void *s, int c, size_t n) {
uint8_t *p = (uint8_t *)s;
for (size_t i = 0; i < n; i++) {
p[i] = (uint8_t)c;
}
return s;
}
void *memmove(void *dest, const void *src, size_t n) {
uint8_t *pdest = (uint8_t *)dest;
const uint8_t *psrc = (const uint8_t *)src;
if (src > dest) {
for (size_t i = 0; i < n; i++) {
pdest[i] = psrc[i];
}
} else if (src < dest) {
for (size_t i = n; i > 0; i--) {
pdest[i - 1] = psrc[i - 1];
}
}
return dest;
}
int memcmp(const void *s1, const void *s2, size_t n) {
const uint8_t *p1 = (const uint8_t *)s1;
const uint8_t *p2 = (const uint8_t *)s2;
for (size_t i = 0; i < n; i++) {
if (p1[i] != p2[i]) {
return p1[i] < p2[i] ? -1 : 1;
}
}
return 0;
}
union header {
struct {
size_t size;
unsigned is_free;
union header *next;
} s;
ALIGN stub; // force alignment to 16 bytes
};
header_t *get_free_block(size_t size) {
header_t *current = head;
while (current) {
if (current->s.is_free && current->s.size >= size) {
return current;
}
current = current->s.next;
}
return NULL;
}
void *malloc(size_t size) {
size_t total_size;
void *block;
header_t *header;
if (!size)
return NULL;
// Align header size to ensure proper alignment
size = align(size);
// Allocate space for the header too
total_size = align(sizeof(header_t)) + size;
// Use total_size to search for a free block that is large enough
header = get_free_block(total_size);
if (header) {
header->s.is_free = 0;
return (void *)(header + 1);
}
// sbrk is called with total_size to increase the memory pool
block = sbrk(total_size);
if (block == (void *)-1) {
return NULL; // sbrk failed to allocate memory
}
header = block;
header->s.size = size; // Set the user's requested size
header->s.is_free = 0;
header->s.next = NULL;
if (!head)
head = header;
if (tail)
tail->s.next = header;
tail = header;
return (void *)(header + 1);
}
void free(void *block) {
header_t *header, *tmp;
void *programbreak;
if (!block)
return;
header = (header_t *)block - 1;
programbreak = sbrk(0);
if ((char *)block + header->s.size == programbreak) {
if (head == tail) {
head = tail = NULL;
} else {
tmp = head;
while (tmp) {
if (tmp->s.next == tail) {
tmp->s.next = NULL;
tail = tmp;
}
tmp = tmp->s.next;
}
}
sbrk(0 - sizeof(header_t) - header->s.size);
return;
}
header->s.is_free = 1;
}
static char
memory_pool[MEMORY_POOL_SIZE]; // Adjust size as necessary for your use
static size_t current_memory_position = 0;
// This function aligns the given size to the defined ALIGNMENT
size_t align(size_t size) {
return (size + (ALIGNMENT - 1)) & ~(ALIGNMENT - 1);
}
void *sbrk(intptr_t increment) {
size_t aligned_increment;
void *previous_position;
// First, align the increment to ensure we provide aligned memory
aligned_increment = align(increment);
// Now, check if we have enough memory left in our pool
if (current_memory_position + aligned_increment > MEMORY_POOL_SIZE ||
current_memory_position + aligned_increment < current_memory_position) {
// Not enough memory left, or we would wrap around
return (void *)-1;
}
// Get the current position to return
previous_position = &memory_pool[current_memory_position];
// Advance the current position by the aligned increment amount
current_memory_position += aligned_increment;
// Return the previous position, which is the start of the newly allocated
// block
return previous_position;
}

8
src/memory.h Normal file
View file

@ -0,0 +1,8 @@
#include <stddef.h>
#define MEMORY_POOL_SIZE 1024 * 1024 * 64 // 64 MB
#define ALIGNMENT 16
typedef char ALIGN[16];
typedef union header header_t;

1
vendor/stb vendored Submodule

@ -0,0 +1 @@
Subproject commit f4a71b13373436a2866c5d68f8f80ac6f0bc1ffe