Add first C user program

This commit is contained in:
2026-06-11 20:19:24 +03:00
parent b32c12b311
commit 1c935cf154
55 changed files with 365 additions and 276 deletions
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#define MEMORY_SIZE 0x08000000
#define PAGE_SIZE 4096
#define PAGE_COUNT (MEMORY_SIZE / PAGE_SIZE)
#define HEAP_PAGE_COUNT 256
#define HEAP_SIZE (HEAP_PAGE_COUNT * PAGE_SIZE)
#define KERNEL_VIRTUAL_BASE 0xFFFFFFFF80000000ULL
#define PHYS_TO_VIRT(phys) ((void *)((uint64_t)(phys) + KERNEL_VIRTUAL_BASE))
#define VIRT_TO_PHYS(virt) ((uint64_t)(virt) - KERNEL_VIRTUAL_BASE)
#define KERNEL_VIRTUAL_PML4 (KERNEL_VIRTUAL_BASE + 0x10000)
#define KERNEL_VIRTUAL_CODE (KERNEL_VIRTUAL_PML4 + 0x10000)
#define KERNEL_VIRTUAL_HEAP (KERNEL_VIRTUAL_CODE + 0x100000)
#define KERNEL_VIRTUAL_UNUSED (KERNEL_VIRTUAL_HEAP + HEAP_SIZE)
#define KERNEL_VIRTUAL_STACK (KERNEL_VIRTUAL_BASE + 0xF00000 - PAGE_SIZE)
#define KERNEL_VIRTUAL_STACK_TOP (KERNEL_VIRTUAL_STACK + PAGE_SIZE)
#define USER_VIRTUAL_BASE 0x0000000000000000ULL
#define USER_VIRTUAL_CODE (USER_VIRTUAL_BASE + 0x400000)
#define USER_VIRTUAL_HEAP (USER_VIRTUAL_CODE + 0x100000)
#define USER_VIRTUAL_UNUSED (USER_VIRTUAL_HEAP + HEAP_SIZE)
#define USER_VIRTUAL_STACK (0x0000700000000000ULL - PAGE_SIZE)
#define USER_VIRTUAL_STACK_TOP (USER_VIRTUAL_STACK + PAGE_SIZE)
+60
View File
@@ -0,0 +1,60 @@
#include "src/lib/memory.h"
#include "src/lib/layout.h"
#include "src/lib/util.h"
#ifdef KERNEL
#define HEAP_VIRTUAL_BASE KERNEL_VIRTUAL_HEAP
#else
#define HEAP_VIRTUAL_BASE USER_VIRTUAL_HEAP
#endif
typedef struct free_chunk {
uint64_t size; // header + data
struct free_chunk *next;
} free_chunk_t;
static free_chunk_t *free_list;
void *memory_allocate(uint64_t size) {
if (!free_list) {
free_list = (free_chunk_t *)HEAP_VIRTUAL_BASE;
free_list->size = HEAP_SIZE;
free_list->next = NUL;
}
size = sizeof(free_chunk_t) + (size + 7) & (uint64_t)~7;
free_chunk_t *prev = NUL;
free_chunk_t *curr = free_list;
while (curr) {
if (curr->size >= size + (sizeof(free_chunk_t) + 8)) {
free_chunk_t *remainder = (free_chunk_t *)((uint8_t *)curr + size);
remainder->size = curr->size - size;
remainder->next = curr->next;
curr->size = size;
curr->next = remainder;
}
if (curr->size >= size) {
if (prev) {
prev->next = curr->next;
} else {
free_list = curr->next;
}
void *result = (void *)((uint8_t *)curr + sizeof(free_chunk_t));
memory_set(0, size - sizeof(free_chunk_t), result);
return result;
}
prev = curr;
curr = curr->next;
}
return NUL;
}
void memory_free(void *pointer) {
free_chunk_t *chunk = (free_chunk_t *)((uint8_t *)pointer - sizeof(free_chunk_t));
chunk->next = free_list;
free_list = chunk;
// TODO Merge adjacent free chunks.
}
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include <stdint.h>
void *memory_allocate(uint64_t size);
void memory_free(void *pointer);
static inline void memory_set(uint8_t value, uint64_t bytes, void *to) {
__asm__ volatile("rep stosb" : "=D"(to), "=c"(bytes) : "D"(to), "a"(value), "c"(bytes) : "memory");
}
static inline void memory_move(void *from, uint64_t bytes, void *to) {
if (to == from) {
return;
} else if (to < from) {
__asm__ volatile("rep movsb" : "=D"(to), "=S"(from), "=c"(bytes) : "D"(to), "S"(from), "c"(bytes) : "memory");
} else {
// TODO ASSERT(0, "memory_move: forward overlapping move not implemented");
}
}
static inline void memory_copy(const void *from, uint64_t bytes, void *to) {
// TODO ASSERT((uint64_t)to + bytes <= (uint64_t)from || (uint64_t)to >= (uint64_t)from + bytes, "memory_copy: overlapping backward
// copy");
__asm__ volatile("rep movsb" : "=D"(to), "=S"(from), "=c"(bytes) : "D"(to), "S"(from), "c"(bytes) : "memory");
}
+227
View File
@@ -0,0 +1,227 @@
#include "src/lib/string.h"
#include <stdarg.h>
uint8_t bytes_equal(const char *l, const char *r, uint64_t count) {
while (count--) {
if (*l++ != *r++) {
return 0;
}
}
return 1;
}
uint8_t string_empty(const char *s) {
return *s == '\0';
}
uint64_t string_length(const char *s) {
const char *e = s;
while (*e) {
e++;
}
return (uint64_t)(e - s);
}
uint8_t string_equal(const char *l, const char *r) {
while (*l && *r) {
if (*l != *r) {
return 0;
}
l++;
r++;
}
return *l == *r;
}
uint64_t string_split(char *string, char separator, uint64_t max, char **result) {
uint64_t count = 1;
*result = string;
while (*string && count < max) {
if (*string == separator) {
*string = '\0';
result++;
*result = string + 1;
count++;
}
string++;
}
return count;
}
uint64_t string_byte_to_hex(uint8_t value, uint64_t max, char *result) {
if (max == 0) {
return 0;
}
if (max == 1) {
result[0] = '\0';
return 0;
}
if (max == 2) {
result[0] = '0';
result[1] = '\0';
return 1;
}
char tmp[3] = "00";
uint8_t i = 0;
while (value) {
uint8_t nibble = value & 0xF;
tmp[i++] = nibble < 10 ? '0' + nibble : 'a' + nibble - 10;
value >>= 4;
}
result[0] = '0';
result[1] = 'x';
i = 2;
while (i < 4 && i < max - 1) {
result[i] = tmp[3 - i];
i++;
}
result[i] = '\0';
return i;
}
uint64_t string_uint_to_decimal(uint64_t value, uint64_t max, char *result) {
if (max == 0) {
return 0;
}
char tmp[20];
uint8_t len = 0;
if (value == 0) {
tmp[len++] = '0';
} else {
while (value && len < 20) {
tmp[len++] = '0' + (value % 10);
value /= 10;
}
}
uint8_t i = 0;
while (i < len && i < max - 1) {
result[i] = tmp[len - 1 - i];
i++;
}
result[i] = '\0';
return i;
}
uint64_t string_int_to_decimal(int64_t value, uint64_t max, char *result) {
if (max == 0) {
return 0;
}
if (value >= 0) {
return string_uint_to_decimal((uint64_t)value, max, result);
}
result[0] = '-';
return 1 + string_uint_to_decimal((uint64_t)(-value), max - 1, result + 1);
}
uint64_t string_uint_to_hex(uint64_t value, uint64_t max, char *result) {
if (max == 0) {
return 0;
}
if (max == 1) {
result[0] = '\0';
return 0;
}
if (max == 2) {
result[0] = '0';
result[1] = '\0';
return 1;
}
char tmp[17] = "0000000000000000";
uint8_t i = 0;
while (value) {
uint8_t nibble = value & 0xF;
tmp[i++] = nibble < 10 ? '0' + nibble : 'A' + nibble - 10;
value >>= 4;
}
result[0] = '0';
result[1] = 'x';
i = 2;
while (i < 18 && i < max - 1) {
result[i] = tmp[17 - i];
i++;
}
result[i] = '\0';
return i;
}
uint64_t string_format(const char *format, uint64_t max, char *output, ...) {
va_list args;
va_start(args, output);
uint64_t limit = max - 1;
uint64_t fi = 0, ri = 0;
while (format[fi] && ri < limit) {
if (format[fi] != '%') {
output[ri++] = format[fi++];
} else {
switch (format[fi + 1]) {
case 'c':
output[ri++] = (char)va_arg(args, int);
fi += 2;
break;
case 'd':
string_int_to_decimal(va_arg(args, int64_t), limit - ri, output + ri);
while (output[ri]) {
ri++;
}
fi += 2;
break;
case 'u':
string_uint_to_decimal(va_arg(args, uint64_t), limit - ri, output + ri);
while (output[ri]) {
ri++;
}
fi += 2;
break;
case 'x':
string_uint_to_hex(va_arg(args, uint64_t), limit - ri, output + ri);
while (output[ri]) {
ri++;
}
fi += 2;
break;
case 's': {
char *s = va_arg(args, char *);
while (*s && ri < limit) {
output[ri++] = *s;
s++;
}
fi += 2;
break;
}
case '%': {
output[ri++] = '%';
fi += 2;
break;
}
default:
output[ri++] = format[fi++];
output[ri++] = format[fi++];
break;
}
}
}
output[ri] = '\0';
va_end(args);
return ri;
}
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include <stdint.h>
uint8_t bytes_equal(const char *l, const char *r, uint64_t count);
uint8_t string_empty(const char *s);
uint64_t string_length(const char *s);
uint8_t string_equal(const char *l, const char *r);
uint64_t string_split(char *string, char separator, uint64_t max, char **result);
uint64_t string_byte_to_hex(uint8_t value, uint64_t max, char *result);
uint64_t string_uint_to_decimal(uint64_t value, uint64_t max, char *result);
uint64_t string_int_to_decimal(int64_t value, uint64_t max, char *result);
uint64_t string_uint_to_hex(uint64_t value, uint64_t max, char *result);
uint64_t string_format(const char *format, uint64_t max, char *output, ...);
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#define NUL 0 // TODO Fix VSCode thinking `NULL` conflicts with some other definition.