Compare commits

..

8 Commits

Author SHA1 Message Date
haxala1r 53b4cff56a fix: remove redundant .bss zeroing that destroyed page tables
Co-authored-by: aider (openrouter/moonshotai/kimi-k2.6) <aider@aider.chat>
2026-05-04 11:34:31 +03:00
haxala1r 278a524bbb fix: zero .bss before enabling paging to prevent triple fault
Co-authored-by: aider (openrouter/moonshotai/kimi-k2.6) <aider@aider.chat>
2026-05-04 11:28:55 +03:00
haxala1r efcc54ed4b fix: add Xen PVH ELF note to enable QEMU -kernel boot
Co-authored-by: aider (openrouter/moonshotai/kimi-k2.6) <aider@aider.chat>
2026-05-04 11:15:50 +03:00
haxala1r 0d49827035 Reorganized 2026-05-04 10:56:02 +03:00
haxala1r fcb533ff24 fix: disable PIE for kernel build to fix linker error
Co-authored-by: aider (openrouter/moonshotai/kimi-k2.6) <aider@aider.chat>
2026-05-04 10:55:02 +03:00
haxala1r 23451c8e85 build: replace hosted target with static linux and add kernel target
Co-authored-by: aider (openrouter/moonshotai/kimi-k2.6) <aider@aider.chat>
2026-05-04 10:48:51 +03:00
haxala1r fcbc810db8 fix: replace stdlib calls with forth_* and fix UB
Co-authored-by: aider (openrouter/moonshotai/kimi-k2.6) <aider@aider.chat>
2026-05-04 10:34:47 +03:00
haxala1r dbf4eb5d0e feat: abstract I/O and strings for freestanding compilation
Co-authored-by: aider (openrouter/moonshotai/kimi-k2.6) <aider@aider.chat>
2026-05-04 10:17:43 +03:00
10 changed files with 479 additions and 54 deletions
+14 -10
View File
@@ -1,19 +1,23 @@
CC = gcc
CFLAGS = -Wall -Wextra -g -std=c11 -D_POSIX_C_SOURCE=200809L
LDFLAGS =
SRCS = forth_core.c forth_dict.c forth_words.c forth_interp.c main.c
OBJS = $(SRCS:.c=.o)
CC = clang
CFLAGS = -Wall -Wextra -g -std=c11
COMMON_SRCS = forth_core.c forth_dict.c forth_words.c forth_interp.c main.c
LINUX_SRCS = $(COMMON_SRCS) platform_linux.c
KERNEL_SRCS = $(COMMON_SRCS) platform_kernel.c kernel_entry.S
KERNEL_LD = kernel.ld
TARGET = forth
KERNEL_TARGET = forth.kernel
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
$(TARGET): $(LINUX_SRCS) forth.h
$(CC) $(CFLAGS) -static -nostdlib -ffreestanding -fno-stack-protector -o $@ $(LINUX_SRCS)
%.o: %.c forth.h
$(CC) $(CFLAGS) -c $< -o $@
$(KERNEL_TARGET): $(KERNEL_SRCS) forth.h $(KERNEL_LD)
$(CC) $(CFLAGS) -ffreestanding -nostdlib -fno-stack-protector -mno-red-zone -fno-pie -no-pie -o $@ $(KERNEL_SRCS) -T$(KERNEL_LD) -Wl,--build-id=none
clean:
rm -f $(OBJS) $(TARGET)
rm -f $(TARGET) $(KERNEL_TARGET)
.PHONY: all clean
+37 -17
View File
@@ -1,29 +1,46 @@
#ifndef FORTH_H
#define FORTH_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdint.h>
#include <inttypes.h>
#include <stddef.h>
#include <stdarg.h>
// Configuration (all hard limits removed)
#define MAX_NAME_LEN 31
#define FORTH_EOF (-1)
/* Portable panic hook. Override for bare-metal reset/handling. */
#ifndef forth_panic
#define forth_panic() do { for (;;) ; } while (0)
#endif
/* Platform interface -- must be provided by the target platform layer. */
void forth_putchar(char c);
int forth_getchar(void);
void forth_printf(const char* fmt, ...);
void forth_fflush(void);
void forth_panic(void);
/* Portable I/O hooks. Define FORTH_CUSTOM_IO before including this header
* to provide bare-metal UART (or other) implementations. */
#ifndef FORTH_CUSTOM_IO
#define forth_putchar(c) putchar(c)
#define forth_getchar() getchar()
#define forth_printf(...) printf(__VA_ARGS__)
#define forth_fflush() fflush(stdout)
#endif
/* Platform string-to-number (base 10 is required). */
int64_t forth_strtoll(const char* str, char** endptr, int base);
/* Portable character classification. */
static inline int forth_isspace(int c) {
return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
}
/* Portable string operations. */
static inline size_t forth_strlen(const char* s) {
size_t n = 0;
while (s[n]) n++;
return n;
}
static inline void forth_memcpy(void* dst, const void* src, size_t n) {
char* d = dst;
const char* s = src;
while (n--) *d++ = *s++;
}
static inline int forth_strcmp(const char* a, const char* b) {
while (*a && *a == *b) { a++; b++; }
return (unsigned char)*a - (unsigned char)*b;
}
/* Static memory sizes (override before including forth.h) */
#ifndef FORTH_DATA_STACK_SIZE
@@ -105,6 +122,9 @@ extern Word* w_branch;
extern Word* w_zbranch;
extern Word* w_dot_quote_inner;
// Entry point
void forth_run(void);
// Core function prototypes
void data_push(int64_t val);
int64_t data_pop(void);
+4 -4
View File
@@ -5,10 +5,10 @@ Word* add_primitive(const char* name, void (*code)(Word*), uint8_t flags) {
w->prev = dict_head;
dict_head = w;
size_t len = strlen(name);
size_t len = forth_strlen(name);
if (len > MAX_NAME_LEN) len = MAX_NAME_LEN;
w->flags = flags | (uint8_t)len;
memcpy(w->name, name, len);
forth_memcpy(w->name, name, len);
w->name[len] = '\0';
w->code = code;
w->body = NULL;
@@ -18,14 +18,14 @@ Word* add_primitive(const char* name, void (*code)(Word*), uint8_t flags) {
Word* lookup_word(const char* name) {
for (Word* w = dict_head; w != NULL; w = w->prev) {
if (w->flags & (1 << 6)) continue;
if (strcmp(w->name, name) == 0) return w;
if (forth_strcmp(w->name, name) == 0) return w;
}
return NULL;
}
Word* lookup_word_internal(const char* name) {
for (Word* w = dict_head; w != NULL; w = w->prev) {
if (strcmp(w->name, name) == 0) return w;
if (forth_strcmp(w->name, name) == 0) return w;
}
return NULL;
}
+6 -6
View File
@@ -3,12 +3,12 @@
// Input tokenizer (unchanged)
char* next_token(void) {
if (input_ptr == NULL) return NULL;
while (*input_ptr != '\0' && isspace((unsigned char)*input_ptr)) {
while (*input_ptr != '\0' && forth_isspace((unsigned char)*input_ptr)) {
input_ptr++;
}
if (*input_ptr == '\0') return NULL;
char* start = input_ptr;
while (*input_ptr != '\0' && !isspace((unsigned char)*input_ptr)) {
while (*input_ptr != '\0' && !forth_isspace((unsigned char)*input_ptr)) {
input_ptr++;
}
if (*input_ptr != '\0') {
@@ -63,10 +63,10 @@ void process_token(const char* token) {
}
} else { // Try to parse as number
char* end;
long long v = strtoll(token, &end, 10);
int64_t v = forth_strtoll(token, &end, 10);
if (end != token && *end == '\0') {
if (state == 0) {
data_push((int64_t)v);
data_push(v);
} else { // Compile lit + number
if (!w_lit) {
forth_printf("Fatal: lit word not found\n");
@@ -91,13 +91,13 @@ void outer_interpreter(void) {
int c;
while (i < sizeof(line_buf) - 1) {
c = forth_getchar();
if (c == EOF || c == '\n' || c == '\r') {
if (c == FORTH_EOF || c == '\n' || c == '\r') {
break;
}
line_buf[i++] = (char)c;
}
line_buf[i] = '\0';
if (i == 0 && c == EOF) {
if (i == 0 && c == FORTH_EOF) {
break;
}
input_buf = line_buf;
+17 -15
View File
@@ -309,7 +309,7 @@ void do_zero_gt(Word* w) {
void do_dot(Word* w) {
(void)w;
if (data_sp < 0) return;
forth_printf("%" PRId64 " ", data_pop());
forth_printf("%lld ", (long long)data_pop());
forth_fflush();
}
@@ -329,7 +329,7 @@ void do_emit(Word* w) {
void do_key(Word* w) {
(void)w;
int c = forth_getchar();
data_push(c == EOF ? -1 : c);
data_push(c == FORTH_EOF ? -1 : c);
}
void do_dot_quote(Word* w) {
@@ -337,7 +337,7 @@ void do_dot_quote(Word* w) {
if (state == 0) {
// Interpret mode: print immediately
if (input_ptr == NULL) { forth_printf("Missing string\n"); return; }
while (*input_ptr && isspace((unsigned char)*input_ptr)) input_ptr++;
while (*input_ptr && forth_isspace((unsigned char)*input_ptr)) input_ptr++;
if (*input_ptr != '"') { forth_printf("Expected \" to start string\n"); return; }
input_ptr++;
char* start = input_ptr;
@@ -349,7 +349,7 @@ void do_dot_quote(Word* w) {
} else {
// Compile mode: compile string for runtime
if (input_ptr == NULL) { forth_printf("Missing string\n"); return; }
while (*input_ptr && isspace((unsigned char)*input_ptr)) input_ptr++;
while (*input_ptr && forth_isspace((unsigned char)*input_ptr)) input_ptr++;
if (*input_ptr != '"') { forth_printf("Expected \" to start string\n"); return; }
input_ptr++;
char* start = input_ptr;
@@ -495,10 +495,10 @@ void do_variable(Word* w) {
new_w->prev = dict_head;
dict_head = new_w;
size_t len = strlen(name);
size_t len = forth_strlen(name);
if (len > MAX_NAME_LEN) len = MAX_NAME_LEN;
new_w->flags = (uint8_t)len;
memcpy(new_w->name, name, len);
forth_memcpy(new_w->name, name, len);
new_w->name[len] = '\0';
new_w->code = do_do_var;
new_w->body = var_cell; // body points directly to the data cell in user_mem
@@ -524,10 +524,10 @@ void do_constant(Word* w) {
new_w->prev = dict_head;
dict_head = new_w;
size_t len = strlen(name);
size_t len = forth_strlen(name);
if (len > MAX_NAME_LEN) len = MAX_NAME_LEN;
new_w->flags = (uint8_t)len;
memcpy(new_w->name, name, len);
forth_memcpy(new_w->name, name, len);
new_w->name[len] = '\0';
new_w->code = do_do_const;
new_w->body = val_cell; // body points to the cell that holds the value
@@ -583,9 +583,9 @@ void do_colon(Word* w) {
(void)w;
char* name = next_token();
if (!name) { forth_printf("':' expects a name\n"); return; }
size_t len = strlen(name);
size_t len = forth_strlen(name);
if (len > MAX_NAME_LEN) len = MAX_NAME_LEN;
memcpy(compiling_name, name, len);
forth_memcpy(compiling_name, name, len);
compiling_name[len] = '\0';
state = 1;
compile_idx = 0;
@@ -603,7 +603,7 @@ void do_semicolon(Word* w) {
// Create body copy of compiled cells
Cell* body_copy = forth_alloc_body(compile_idx);
if (!body_copy) { forth_printf("Out of memory\n"); return; }
memcpy(body_copy, compile_buf, compile_idx * sizeof(Cell));
forth_memcpy(body_copy, compile_buf, compile_idx * sizeof(Cell));
// Create new word entry
Word* new_w = forth_alloc_word();
@@ -611,10 +611,10 @@ void do_semicolon(Word* w) {
new_w->prev = dict_head;
dict_head = new_w;
size_t len = strlen(compiling_name);
size_t len = forth_strlen(compiling_name);
if (len > MAX_NAME_LEN) len = MAX_NAME_LEN;
new_w->flags = (uint8_t)len; // no hidden, no immediate
memcpy(new_w->name, compiling_name, len);
forth_memcpy(new_w->name, compiling_name, len);
new_w->name[len] = '\0';
new_w->code = do_docolon;
new_w->body = body_copy;
@@ -692,7 +692,8 @@ void do_until(Word* w) {
if (!w_zbranch) { forth_printf("Fatal: 0branch not found\n"); return; }
if (!ensure_compile_cap(2)) return;
compile_buf[compile_idx++] = (Cell){.word = w_zbranch};
compile_buf[compile_idx++] = (Cell){.num = begin_idx - compile_idx};
compile_buf[compile_idx] = (Cell){.num = begin_idx - compile_idx};
compile_idx++;
}
void do_while(Word* w) {
@@ -722,7 +723,8 @@ void do_repeat(Word* w) {
if (!w_branch) { forth_printf("Fatal: branch not found\n"); return; }
if (!ensure_compile_cap(2)) return;
compile_buf[compile_idx++] = (Cell){.word = w_branch};
compile_buf[compile_idx++] = (Cell){.num = begin_idx - compile_idx};
compile_buf[compile_idx] = (Cell){.num = begin_idx - compile_idx};
compile_idx++;
compile_buf[while_offset_idx].num = compile_idx - while_offset_idx;
}
+35
View File
@@ -0,0 +1,35 @@
ENTRY(_start)
SECTIONS
{
. = 0x100000;
.text : ALIGN(4K)
{
*(.multiboot2)
*(.text)
}
.rodata : ALIGN(4K)
{
*(.rodata)
}
.data : ALIGN(4K)
{
*(.data)
}
.bss : ALIGN(4K)
{
__bss_start = .;
*(.bss)
*(COMMON)
__bss_end = .;
}
.note : ALIGN(4)
{
*(.note.Xen)
}
}
+108
View File
@@ -0,0 +1,108 @@
# Multiboot2 header and x86_64 long-mode bootstrap
.section .multiboot2, "a"
.align 8
multiboot_header:
.long 0xe85250d6
.long 0
.long multiboot_header_end - multiboot_header
.long -(0xe85250d6 + 0 + (multiboot_header_end - multiboot_header))
.align 8
.word 0
.word 0
.long 8
multiboot_header_end:
.section .bss, "aw", @nobits
.align 4096
pml4:
.space 4096
pdpt:
.space 4096
pd:
.space 4096
.align 16
stack_bottom:
.space 16384
stack_top:
.section .text
.code32
.global _start
.type _start, @function
_start:
movl $stack_top, %esp
cld
movl $__bss_start, %edi
movl $__bss_end, %ecx
subl %edi, %ecx
xorl %eax, %eax
rep stosb
movl $pdpt, %eax
orl $0x03, %eax
movl %eax, pml4
movl $pd, %eax
orl $0x03, %eax
movl %eax, pdpt
movl $0x00000083, %eax
movl $pd, %edi
movl $512, %ecx
1:
movl %eax, (%edi)
addl $0x200000, %eax
addl $8, %edi
loop 1b
movl $pml4, %eax
movl %eax, %cr3
movl %cr4, %eax
orl $0x20, %eax
movl %eax, %cr4
movl $0xC0000080, %ecx
rdmsr
orl $0x100, %eax
wrmsr
movl %cr0, %eax
orl $0x80000000, %eax
movl %eax, %cr0
lgdt gdt64_pointer
ljmp $0x08, $long_mode_start
.code64
long_mode_start:
movabsq $stack_top, %rax
movq %rax, %rsp
call kernel_main
cli
1:
hlt
jmp 1b
.section .note.Xen, "a", @note
.align 4
.long 4
.long 4
.long 18
.asciz "Xen"
.align 4
.long _start
.section .rodata
.align 8
gdt64:
.quad 0x0000000000000000
.quad 0x00209A0000000000
gdt64_pointer:
.word gdt64_pointer - gdt64 - 1
.quad gdt64
+1 -2
View File
@@ -1,6 +1,6 @@
#include "forth.h"
int main(void) {
void forth_run(void) {
here = user_mem;
// Hidden words first
@@ -104,5 +104,4 @@ int main(void) {
// Start outer interpreter
outer_interpreter();
return 0;
}
+143
View File
@@ -0,0 +1,143 @@
#include "forth.h"
#include <stdint.h>
#include <stdarg.h>
// VGA text-mode buffer
static uint16_t* vga_buffer = (uint16_t*)0xB8000;
static int vga_row = 0;
static int vga_col = 0;
static void vga_putchar(char c) {
if (c == '\n') {
vga_col = 0;
vga_row++;
} else {
int idx = vga_row * 80 + vga_col;
vga_buffer[idx] = (uint16_t)((0x0F << 8) | (unsigned char)c);
vga_col++;
if (vga_col >= 80) {
vga_col = 0;
vga_row++;
}
}
if (vga_row >= 25) {
vga_row = 0;
vga_col = 0;
}
}
static inline void outb(uint16_t port, uint8_t val) {
__asm__ volatile ("outb %0, %1" : : "a"(val), "Nd"(port));
}
static inline uint8_t inb(uint16_t port) {
uint8_t ret;
__asm__ volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port));
return ret;
}
static void serial_init(void) {
outb(0x3F8 + 1, 0x00);
outb(0x3F8 + 3, 0x80);
outb(0x3F8 + 0, 0x01);
outb(0x3F8 + 1, 0x00);
outb(0x3F8 + 3, 0x03);
outb(0x3F8 + 2, 0xC7);
outb(0x3F8 + 4, 0x0B);
}
void forth_putchar(char c) {
vga_putchar(c);
while ((inb(0x3F8 + 5) & 0x20) == 0) { }
outb(0x3F8, (uint8_t)c);
}
int forth_getchar(void) {
if ((inb(0x3F8 + 5) & 1) == 0) {
return FORTH_EOF;
}
return inb(0x3F8);
}
void forth_fflush(void) {
}
void forth_panic(void) {
__asm__ volatile ("cli; hlt");
__builtin_unreachable();
}
int64_t forth_strtoll(const char* str, char** endptr, int base) {
const char* s = str;
int neg = 0;
int64_t val = 0;
while (forth_isspace(*s)) s++;
if (*s == '-') { neg = 1; s++; }
else if (*s == '+') { s++; }
while (*s >= '0' && *s <= '9') {
val = val * base + (*s - '0');
s++;
}
if (endptr) *endptr = (char*)s;
return neg ? -val : val;
}
static void print_str(const char* s) {
while (*s) forth_putchar(*s++);
}
static void print_int(long long v) {
char buf[32];
int i = 30;
int neg = 0;
if (v < 0) { neg = 1; v = -v; }
buf[31] = '\0';
if (v == 0) {
buf[i--] = '0';
} else {
while (v > 0 && i >= 0) {
buf[i--] = '0' + (v % 10);
v /= 10;
}
}
if (neg) buf[i--] = '-';
print_str(&buf[i + 1]);
}
void forth_printf(const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
while (*fmt) {
if (*fmt == '%' && *(fmt + 1)) {
fmt++;
if (*fmt == 's') {
print_str(va_arg(ap, const char*));
} else if (*fmt == 'c') {
forth_putchar((char)va_arg(ap, int));
} else if (*fmt == '%') {
forth_putchar('%');
} else if (*fmt == 'l') {
if (*(fmt + 1) == 'l' && (*(fmt + 2) == 'd' || *(fmt + 2) == 'i' || *(fmt + 2) == 'u')) {
fmt += 2;
print_int(va_arg(ap, long long));
}
} else if (*fmt == 'd' || *fmt == 'i' || *fmt == 'u') {
print_int(va_arg(ap, int));
} else {
forth_putchar('%');
forth_putchar(*fmt);
}
} else {
forth_putchar(*fmt);
}
fmt++;
}
va_end(ap);
}
void kernel_main(void) {
serial_init();
forth_printf("Kernel started\n");
forth_run();
forth_panic();
}
+114
View File
@@ -0,0 +1,114 @@
#include "forth.h"
#include <stdarg.h>
static inline long syscall3(long n, long a1, long a2, long a3) {
long ret;
__asm__ volatile ("syscall"
: "=a"(ret)
: "a"(n), "D"(a1), "S"(a2), "d"(a3)
: "rcx", "r11", "memory");
return ret;
}
static inline long syscall1(long n, long a1) {
long ret;
__asm__ volatile ("syscall"
: "=a"(ret)
: "a"(n), "D"(a1)
: "rcx", "r11", "memory");
return ret;
}
void forth_putchar(char c) {
char ch = c;
syscall3(1, 1, (long)&ch, 1);
}
void forth_fflush(void) {
}
int forth_getchar(void) {
unsigned char c;
long n = syscall3(0, 0, (long)&c, 1);
if (n <= 0) return FORTH_EOF;
return (int)c;
}
void forth_panic(void) {
syscall1(60, 1);
__builtin_unreachable();
}
int64_t forth_strtoll(const char* str, char** endptr, int base) {
const char* s = str;
int neg = 0;
int64_t val = 0;
while (forth_isspace(*s)) s++;
if (*s == '-') { neg = 1; s++; }
else if (*s == '+') { s++; }
while (*s >= '0' && *s <= '9') {
val = val * base + (*s - '0');
s++;
}
if (endptr) *endptr = (char*)s;
return neg ? -val : val;
}
static void print_str(const char* s) {
while (*s) forth_putchar(*s++);
}
static void print_int(long long v) {
char buf[32];
int i = 30;
int neg = 0;
if (v < 0) { neg = 1; v = -v; }
buf[31] = '\0';
if (v == 0) {
buf[i--] = '0';
} else {
while (v > 0 && i >= 0) {
buf[i--] = '0' + (v % 10);
v /= 10;
}
}
if (neg) buf[i--] = '-';
print_str(&buf[i + 1]);
}
void forth_printf(const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
while (*fmt) {
if (*fmt == '%' && *(fmt + 1)) {
fmt++;
if (*fmt == 's') {
print_str(va_arg(ap, const char*));
} else if (*fmt == 'c') {
forth_putchar((char)va_arg(ap, int));
} else if (*fmt == '%') {
forth_putchar('%');
} else if (*fmt == 'l') {
if (*(fmt + 1) == 'l' && (*(fmt + 2) == 'd' || *(fmt + 2) == 'i' || *(fmt + 2) == 'u')) {
fmt += 2;
print_int(va_arg(ap, long long));
}
} else if (*fmt == 'd' || *fmt == 'i' || *fmt == 'u') {
print_int(va_arg(ap, int));
} else {
forth_putchar('%');
forth_putchar(*fmt);
}
} else {
forth_putchar(*fmt);
}
fmt++;
}
va_end(ap);
}
void _start(void) {
forth_run();
syscall1(60, 0);
__builtin_unreachable();
}