From 7702949534a2bcf4113b69d55282685745302ba1 Mon Sep 17 00:00:00 2001 From: nub31 Date: Sun, 28 Dec 2025 02:29:42 +0100 Subject: [PATCH] ... --- .clang-format | 7 ++++ .clangd | 2 + makefile | 16 ++++---- src/boot.asm | 19 +++++++-- src/console.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++ src/console.h | 27 +++++++++++++ src/def.h | 56 +++++++++++++++++++++++++++ src/linker.ld | 2 +- src/main.c | 8 ++++ src/print.c | 73 +++++++++++++++++++++++++++++++++++ src/print.h | 3 ++ src/string.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/string.h | 10 +++++ 13 files changed, 414 insertions(+), 12 deletions(-) create mode 100644 .clang-format create mode 100644 .clangd create mode 100644 src/console.c create mode 100644 src/console.h create mode 100644 src/def.h create mode 100644 src/main.c create mode 100644 src/print.c create mode 100644 src/print.h create mode 100644 src/string.c create mode 100644 src/string.h diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..b0f3436 --- /dev/null +++ b/.clang-format @@ -0,0 +1,7 @@ +IndentWidth: 4 +ColumnLimit: 120 + +AllowShortFunctionsOnASingleLine: None +AllowShortBlocksOnASingleLine: Never +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false \ No newline at end of file diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..6c5daf0 --- /dev/null +++ b/.clangd @@ -0,0 +1,2 @@ +CompileFlags: + Add: [ -m32, -ffreestanding, -fno-stack-protector, -nostdlib, -nostdinc, -Wall, -Wextra, -std=c23 ] diff --git a/makefile b/makefile index 482a282..5441db0 100644 --- a/makefile +++ b/makefile @@ -1,18 +1,18 @@ -CC = x86_64-elf-gcc -LD = x86_64-elf-ld +CC = i686-elf-gcc +LD = i686-elf-ld AS = nasm # Modify these settings here for defines and debug info -# CFLAGS = -g -D DEBUG -# LDFLAGS = -g -# ASFLAGS = -g -F dwarf +CFLAGS = -g -D DEBUG +LDFLAGS = -g +ASFLAGS = -g -F dwarf # Do not modify -CFLAGS += -m64 -ffreestanding -nostdinc -nostdlib -Wall -Wextra -Wshadow -std=c23 +CFLAGS += -m32 -ffreestanding -fno-stack-protector -nostdlib -nostdinc -Wall -Wextra -std=c23 LDFLAGS += -ASFLAGS += -f elf64 +ASFLAGS += -f elf32 -SRC_C := $(shell find src -name '*.c') +SRC_C := $(shell find src -name '*.c') SRC_ASM := $(shell find src -name '*.asm') OBJS := $(patsubst src/%.c, .build/%.o, $(SRC_C)) $(patsubst src/%.asm, .build/%.o, $(SRC_ASM)) diff --git a/src/boot.asm b/src/boot.asm index 5ffd136..a8fe1ec 100644 --- a/src/boot.asm +++ b/src/boot.asm @@ -1,4 +1,5 @@ -global entry +global _start +extern kmain %define MAGIC 0xe85250d6 %define ARCH 0x0 @@ -18,7 +19,19 @@ header: dd 8 .end: +section .bss +align 4096 +stack: + .bottom: + resb 1024 * 16 + .top: + section .text bits 32 -entry: - jmp $ \ No newline at end of file +_start: + mov esp, stack.top + call kmain +.halt: + cli + hlt + jmp .halt \ No newline at end of file diff --git a/src/console.c b/src/console.c new file mode 100644 index 0000000..ea5d789 --- /dev/null +++ b/src/console.c @@ -0,0 +1,98 @@ +#include "console.h" + +#define ROWS 25 +#define COLUMNS 80 + +typedef struct { + u8 character; + u8 color; +} vga_char_t; + +static vga_char_t *vga_buffer = (vga_char_t *)0xb8000; +static u8 cursor_row = 0; +static u8 cursor_col = 0; + +void console_putchar(char c, u8 color) { + switch (c) { + case '\n': { + cursor_row += 1; + cursor_col = 0; + break; + } + case '\r': { + cursor_col = 0; + break; + } + case '\t': { + u8 remainder = 4 - (cursor_col % 4); + cursor_col += remainder; + break; + } + case '\b': { + if (cursor_col > 0) { + cursor_col -= 1; + } else if (cursor_row > 0) { + cursor_row -= 1; + + cursor_col = 0; + for (int col = COLUMNS - 1; col >= 0; col--) { + vga_char_t cell = vga_buffer[cursor_row * COLUMNS + col]; + if (cell.character != ' ') { + cursor_col = col; + break; + } + } + } + + vga_buffer[cursor_row * COLUMNS + cursor_col] = (vga_char_t){ + .character = ' ', + .color = VGA_DEFAULT_COLOR, + }; + break; + } + default: { + vga_buffer[COLUMNS * cursor_row + cursor_col] = (vga_char_t){ + .character = c, + .color = color, + }; + cursor_col += 1; + break; + } + } + + if (cursor_col >= COLUMNS) { + cursor_col = 0; + cursor_row += 1; + } + + if (cursor_row >= ROWS) { + for (size_t row = 1; row < ROWS; row++) { + for (size_t col = 0; col < COLUMNS; col++) { + vga_buffer[COLUMNS * (row - 1) + col] = vga_buffer[COLUMNS * row + col]; + } + } + + for (size_t col = 0; col < COLUMNS; col++) { + vga_buffer[COLUMNS * (ROWS - 1) + col] = (vga_char_t){ + .character = ' ', + .color = VGA_DEFAULT_COLOR, + }; + }; + + cursor_row = ROWS - 1; + } +} + +void console_clear() { + for (size_t row = 0; row < ROWS; row++) { + for (size_t col = 0; col < COLUMNS; col++) { + vga_buffer[COLUMNS * row + col] = (vga_char_t){ + .character = ' ', + .color = VGA_DEFAULT_COLOR, + }; + } + } + + cursor_row = 0; + cursor_col = 0; +} \ No newline at end of file diff --git a/src/console.h b/src/console.h new file mode 100644 index 0000000..f1ba7ce --- /dev/null +++ b/src/console.h @@ -0,0 +1,27 @@ +#pragma once + +#include "def.h" + +#define VGA_BLACK 0 +#define VGA_BLUE 1 +#define VGA_GREEN 2 +#define VGA_CYAN 3 +#define VGA_RED 4 +#define VGA_MAGENTA 5 +#define VGA_BROWN 6 +#define VGA_LIGHT_GRAY 7 +#define VGA_DARK_GRAY 8 +#define VGA_LIGHT_BLUE 9 +#define VGA_LIGHT_GREEN 10 +#define VGA_LIGHT_CYAN 11 +#define VGA_LIGHT_RED 12 +#define VGA_LIGHT_MAGENTA 13 +#define VGA_YELLOW 14 +#define VGA_WHITE 15 + +#define VGA_COLOR(fg, bg) (fg | bg << 4) + +#define VGA_DEFAULT_COLOR VGA_COLOR(VGA_LIGHT_GRAY, VGA_BLACK) + +void console_putchar(char character, u8 color); +void console_clear(); \ No newline at end of file diff --git a/src/def.h b/src/def.h new file mode 100644 index 0000000..2be3332 --- /dev/null +++ b/src/def.h @@ -0,0 +1,56 @@ +#pragma once + +typedef __builtin_va_list va_list; + +#define va_start(ap, last) __builtin_va_start(ap, last) +#define va_arg(ap, type) __builtin_va_arg(ap, type) +#define va_end(ap) __builtin_va_end(ap) +#define va_copy(dest, src) __builtin_va_copy(dest, src) + +#ifndef NULL +#define NULL ((void *)0) +#endif + +typedef unsigned long size_t; +typedef unsigned long uintptr_t; + +#define offsetof(type, member) __builtin_offsetof(type, member) + +typedef unsigned char u8; +typedef signed char i8; +typedef unsigned short u16; +typedef signed short i16; +typedef unsigned int u32; +typedef signed int i32; + +#define I8_MIN (-128) +#define I8_MAX 127 + +#define U8_MIN 0x0 +#define U8_MAX 0xff + +#define I16_MIN (-32768) +#define I16_MAX 32767 + +#define U16_MIN 0x0 +#define U16_MAX 0xffff + +#define I32_MIN (-2147483647 - 1) +#define I32_MAX 2147483647 + +#define U32_MIN 0x0 +#define U32_MAX 0xffffffffU + +#define I8_C(x) x +#define U8_C(x) x##U + +#define I16_C(x) x +#define U16_C(x) x##U + +#define I32_C(x) x +#define U32_C(x) x##U + +#define KiB(count) (U64_C(count) * U64_C(1024)) +#define MiB(count) (U64_C(count) * U64_C(1024) * U64_C(1024)) +#define GiB(count) (U64_C(count) * U64_C(1024) * U64_C(1024) * U64_C(1024)) +#define TiB(count) (U64_C(count) * U64_C(1024) * U64_C(1024) * U64_C(1024) * U64_C(1024)) \ No newline at end of file diff --git a/src/linker.ld b/src/linker.ld index df1f061..1c416ea 100644 --- a/src/linker.ld +++ b/src/linker.ld @@ -1,4 +1,4 @@ -ENTRY(entry) +ENTRY(_start) SECTIONS { diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..1531798 --- /dev/null +++ b/src/main.c @@ -0,0 +1,8 @@ +#include "console.h" +#include "print.h" + +// Multiboot 2 puts us in 32-bit protected mode with interrupts disabled +void kmain(void) { + console_clear(); + kprintf("booting nub-os\n"); +} diff --git a/src/print.c b/src/print.c new file mode 100644 index 0000000..c4b8b86 --- /dev/null +++ b/src/print.c @@ -0,0 +1,73 @@ +#include "print.h" +#include "console.h" +#include "def.h" +#include "string.h" + +// Supported formats: +// - `d`: i32 (decimal) +// - `u`: u32 (decimal) +// - `x`: u32 (hex) +// - `c`: char (ascii) +// - `s`: char* (ascii string) +// +// ```c +// printf( +// "The answer is %d is located at offset %x in file %s", +// 42, +// 0x2000 +// "hitchhiker.txt" +// ); +// ``` +void kprintf(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + + bool should_format = false; + + for (size_t i = 0; fmt[i] != '\0'; i++) { + if (should_format) { + should_format = false; + + if (fmt[i] == '%') { + console_putchar('%', VGA_DEFAULT_COLOR); + } else if (fmt[i] == 's') { + const char *str = va_arg(args, const char *); + for (size_t j = 0; str[j] != '\0'; j++) { + console_putchar(str[j], VGA_DEFAULT_COLOR); + } + } else if (fmt[i] == 'c') { + char character = (char)va_arg(args, u32); + console_putchar(character, VGA_DEFAULT_COLOR); + } else if (fmt[i] == 'd') { + u32 val = va_arg(args, u32); + char buf[21]; + itoa(val, buf); + for (size_t j = 0; buf[j] != '\0'; j++) { + console_putchar(buf[j], VGA_DEFAULT_COLOR); + } + } else if (fmt[i] == 'u') { + u32 val = va_arg(args, u32); + char buf[21]; + uitoa(val, buf); + for (size_t j = 0; buf[j] != '\0'; j++) { + console_putchar(buf[j], VGA_DEFAULT_COLOR); + } + } else if (fmt[i] == 'x') { + u32 val = va_arg(args, u32); + char buf[17]; + uitoa_hex(val, buf); + for (size_t j = 0; buf[j] != '\0'; j++) { + console_putchar(buf[j], VGA_DEFAULT_COLOR); + } + } else { + console_putchar(fmt[i], VGA_DEFAULT_COLOR); + } + } else if (fmt[i] == '%') { + should_format = true; + } else { + console_putchar(fmt[i], VGA_DEFAULT_COLOR); + } + } + + va_end(args); +} \ No newline at end of file diff --git a/src/print.h b/src/print.h new file mode 100644 index 0000000..93e2152 --- /dev/null +++ b/src/print.h @@ -0,0 +1,3 @@ +#pragma once + +void kprintf(const char *fmt, ...); \ No newline at end of file diff --git a/src/string.c b/src/string.c new file mode 100644 index 0000000..dedb8d7 --- /dev/null +++ b/src/string.c @@ -0,0 +1,105 @@ +#include "string.h" + +int strcmp(const char *a, const char *b) { + while ((*a != '\0' && *b != '\0') && *a == *b) { + a++; + b++; + } + + return (*a == *b) ? 0 : (*a > *b) ? 1 : -1; +} + +void reverse(char *str, size_t length) { + int start = 0; + int end = length - 1; + while (start < end) { + char temp = str[start]; + str[start] = str[end]; + str[end] = temp; + start++; + end--; + } +} + +void itoa(i32 value, char *buffer) { + char temp[21]; + int i = 0, j = 0; + int negative = 0; + + if (value < 0) { + negative = 1; + if (value == I32_MIN) { + const char *min_str = "9223372036854775808"; + for (i = 0; min_str[i] != '\0'; i++) + temp[i] = min_str[i]; + i = 19; + } else { + value = -value; + } + } + + if (value == 0 && !negative) { + buffer[0] = '0'; + buffer[1] = '\0'; + return; + } + + if (!(negative && value == I32_MIN)) { + while (value > 0) { + temp[i++] = '0' + (value % 10); + value /= 10; + } + } + + if (negative) { + temp[i++] = '-'; + } + + while (i > 0) { + buffer[j++] = temp[--i]; + } + buffer[j] = '\0'; +} + +void uitoa(u32 value, char *buffer) { + char temp[21]; + int i = 0, j = 0; + + if (value == 0) { + buffer[0] = '0'; + buffer[1] = '\0'; + return; + } + + while (value > 0) { + temp[i++] = '0' + (value % 10); + value /= 10; + } + + while (i > 0) { + buffer[j++] = temp[--i]; + } + buffer[j] = '\0'; +} + +void uitoa_hex(u32 value, char *buffer) { + const char *digits = "0123456789abcdef"; + char temp[17]; + int pos = 0; + + if (value == 0) { + buffer[0] = '0'; + buffer[1] = '\0'; + return; + } + + while (value > 0 && pos < 16) { + temp[pos++] = digits[value & 0xF]; + value >>= 4; + } + + for (int i = 0; i < pos; i++) { + buffer[i] = temp[pos - i - 1]; + } + buffer[pos] = '\0'; +} \ No newline at end of file diff --git a/src/string.h b/src/string.h new file mode 100644 index 0000000..c440d31 --- /dev/null +++ b/src/string.h @@ -0,0 +1,10 @@ +#pragma once + +#include "def.h" + +int strcmp(const char *a, const char *b); +void reverse(char *str, size_t length); + +void itoa(i32 value, char *buffer); +void uitoa(u32 value, char *buffer); +void uitoa_hex(u32 value, char *buffer); \ No newline at end of file