Compare commits

..

18 Commits

Author SHA1 Message Date
haxala1r b50883b3ed fix: use correct multiboot2 framebuffer info tag type (8 not 5)
Co-authored-by: aider (openrouter/anthropic/claude-opus-4.7) <aider@aider.chat>
2026-05-05 22:28:09 +03:00
haxala1r 1d16c23fc5 fix: correct multiboot2 framebuffer tag layout
Co-authored-by: aider (openrouter/anthropic/claude-opus-4.7) <aider@aider.chat>
2026-05-05 22:27:02 +03:00
haxala1r 29ad4c2835 fix: identity-map first 4 GiB to cover framebuffer MMIO
Co-authored-by: aider (openrouter/anthropic/claude-opus-4.7) <aider@aider.chat>
2026-05-05 22:23:46 +03:00
haxala1r d8fad1765e feat: add multiboot2 framebuffer and Forth pixel primitives
Co-authored-by: aider (openrouter/moonshotai/kimi-k2.6) <aider@aider.chat>
2026-05-05 22:18:56 +03:00
haxala1r d73ace71fb fix: correct GDT, segment loads, and ISR stack alignment
Co-authored-by: aider (openrouter/anthropic/claude-opus-4.7) <aider@aider.chat>
2026-05-05 21:30:47 +03:00
haxala1r 9c242187ea fix: correct 64-bit IDT gate descriptor layout to prevent triple fault on keypress
Co-authored-by: aider (openrouter/anthropic/claude-sonnet-4.6) <aider@aider.chat>
2026-05-05 21:28:27 +03:00
haxala1r 75ddd75ab9 fix: mask all PIC IRQs after remap to prevent triple fault
Co-authored-by: aider (openrouter/moonshotai/kimi-k2.6) <aider@aider.chat>
2026-05-04 12:33:32 +03:00
haxala1r 47b6a1eb82 feat: add interrupt-driven PS/2 keyboard input
Co-authored-by: aider (openrouter/moonshotai/kimi-k2.6) <aider@aider.chat>
2026-05-04 12:15:33 +03:00
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
haxala1r 19c8608c76 feat: add comma, fix allot, and support bare-metal custom memory
Co-authored-by: aider (openrouter/moonshotai/kimi-k2.6) <aider@aider.chat>
2026-05-03 23:07:26 +03:00
haxala1r 1c0c6daf4f fix: replace direct stdio calls with portable I/O hooks
Co-authored-by: aider (openrouter/moonshotai/kimi-k2.6) <aider@aider.chat>
2026-05-03 22:51:46 +03:00
12 changed files with 980 additions and 61 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 font8x8.c
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
+100
View File
@@ -0,0 +1,100 @@
#include <stdint.h>
const uint8_t font8x8[96][8] = {
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, /* 32 ' ' */
{0x18,0x18,0x18,0x18,0x18,0x00,0x18,0x00}, /* 33 '!' */
{0x66,0x66,0x66,0x00,0x00,0x00,0x00,0x00}, /* 34 '"' */
{0x66,0x66,0xFF,0x66,0xFF,0x66,0x66,0x00}, /* 35 '#' */
{0x18,0x3E,0x60,0x3C,0x06,0x7C,0x18,0x00}, /* 36 '$' */
{0x62,0x66,0x0C,0x18,0x30,0x66,0x46,0x00}, /* 37 '%' */
{0x3C,0x66,0x3C,0x38,0x67,0x66,0x3F,0x00}, /* 38 '&' */
{0x06,0x0C,0x18,0x00,0x00,0x00,0x00,0x00}, /* 39 ''' */
{0x0C,0x18,0x30,0x30,0x30,0x18,0x0C,0x00}, /* 40 '(' */
{0x30,0x18,0x0C,0x0C,0x0C,0x18,0x30,0x00}, /* 41 ')' */
{0x00,0x66,0x3C,0xFF,0x3C,0x66,0x00,0x00}, /* 42 '*' */
{0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00}, /* 43 '+' */
{0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x30}, /* 44 ',' */
{0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00}, /* 45 '-' */
{0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00}, /* 46 '.' */
{0x00,0x03,0x06,0x0C,0x18,0x30,0x60,0x00}, /* 47 '/' */
{0x3C,0x66,0x6E,0x76,0x66,0x66,0x3C,0x00}, /* 48 '0' */
{0x18,0x18,0x38,0x18,0x18,0x18,0x7E,0x00}, /* 49 '1' */
{0x3C,0x66,0x06,0x0C,0x30,0x60,0x7E,0x00}, /* 50 '2' */
{0x3C,0x66,0x06,0x1C,0x06,0x66,0x3C,0x00}, /* 51 '3' */
{0x06,0x0E,0x1E,0x66,0x7F,0x06,0x06,0x00}, /* 52 '4' */
{0x7E,0x60,0x7C,0x06,0x06,0x66,0x3C,0x00}, /* 53 '5' */
{0x3C,0x60,0x60,0x7C,0x66,0x66,0x3C,0x00}, /* 54 '6' */
{0x7E,0x06,0x0C,0x18,0x30,0x30,0x30,0x00}, /* 55 '7' */
{0x3C,0x66,0x66,0x3C,0x66,0x66,0x3C,0x00}, /* 56 '8' */
{0x3C,0x66,0x66,0x3E,0x06,0x06,0x3C,0x00}, /* 57 '9' */
{0x00,0x00,0x18,0x00,0x00,0x18,0x00,0x00}, /* 58 ':' */
{0x00,0x00,0x18,0x00,0x00,0x18,0x18,0x30}, /* 59 ';' */
{0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x00}, /* 60 '<' */
{0x00,0x00,0x7E,0x00,0x7E,0x00,0x00,0x00}, /* 61 '=' */
{0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x00}, /* 62 '>' */
{0x3C,0x66,0x06,0x0C,0x18,0x00,0x18,0x00}, /* 63 '?' */
{0x3C,0x66,0x6E,0x6E,0x60,0x62,0x3C,0x00}, /* 64 '@' */
{0x18,0x3C,0x66,0x7E,0x66,0x66,0x66,0x00}, /* 65 'A' */
{0x7C,0x66,0x66,0x7C,0x66,0x66,0x7C,0x00}, /* 66 'B' */
{0x3C,0x66,0x60,0x60,0x60,0x66,0x3C,0x00}, /* 67 'C' */
{0x78,0x6C,0x66,0x66,0x66,0x6C,0x78,0x00}, /* 68 'D' */
{0x7E,0x60,0x60,0x78,0x60,0x60,0x7E,0x00}, /* 69 'E' */
{0x7E,0x60,0x60,0x78,0x60,0x60,0x60,0x00}, /* 70 'F' */
{0x3C,0x66,0x60,0x6E,0x66,0x66,0x3C,0x00}, /* 71 'G' */
{0x66,0x66,0x66,0x7E,0x66,0x66,0x66,0x00}, /* 72 'H' */
{0x3C,0x18,0x18,0x18,0x18,0x18,0x3C,0x00}, /* 73 'I' */
{0x1E,0x0C,0x0C,0x0C,0x0C,0x6C,0x38,0x00}, /* 74 'J' */
{0x66,0x6C,0x78,0x70,0x78,0x6C,0x66,0x00}, /* 75 'K' */
{0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00}, /* 76 'L' */
{0x63,0x77,0x7F,0x6B,0x63,0x63,0x63,0x00}, /* 77 'M' */
{0x66,0x76,0x7E,0x7E,0x6E,0x66,0x66,0x00}, /* 78 'N' */
{0x3C,0x66,0x66,0x66,0x66,0x66,0x3C,0x00}, /* 79 'O' */
{0x7C,0x66,0x66,0x7C,0x60,0x60,0x60,0x00}, /* 80 'P' */
{0x3C,0x66,0x66,0x66,0x66,0x3C,0x0E,0x00}, /* 81 'Q' */
{0x7C,0x66,0x66,0x7C,0x78,0x6C,0x66,0x00}, /* 82 'R' */
{0x3C,0x66,0x60,0x3C,0x06,0x66,0x3C,0x00}, /* 83 'S' */
{0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x00}, /* 84 'T' */
{0x66,0x66,0x66,0x66,0x66,0x66,0x3C,0x00}, /* 85 'U' */
{0x66,0x66,0x66,0x66,0x66,0x3C,0x18,0x00}, /* 86 'V' */
{0x63,0x63,0x63,0x6B,0x7F,0x77,0x63,0x00}, /* 87 'W' */
{0x66,0x66,0x3C,0x18,0x3C,0x66,0x66,0x00}, /* 88 'X' */
{0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x00}, /* 89 'Y' */
{0x7E,0x06,0x0C,0x18,0x30,0x60,0x7E,0x00}, /* 90 'Z' */
{0x3C,0x30,0x30,0x30,0x30,0x30,0x3C,0x00}, /* 91 '[' */
{0x00,0x60,0x30,0x18,0x0C,0x06,0x03,0x00}, /* 92 '\' */
{0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00}, /* 93 ']' */
{0x18,0x3C,0x66,0x00,0x00,0x00,0x00,0x00}, /* 94 '^' */
{0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00}, /* 95 '_' */
{0x18,0x18,0x0C,0x00,0x00,0x00,0x00,0x00}, /* 96 '`' */
{0x00,0x00,0x3C,0x06,0x3E,0x66,0x3E,0x00}, /* 97 'a' */
{0x00,0x60,0x60,0x7C,0x66,0x66,0x7C,0x00}, /* 98 'b' */
{0x00,0x00,0x3C,0x60,0x60,0x60,0x3C,0x00}, /* 99 'c' */
{0x00,0x06,0x06,0x3E,0x66,0x66,0x3E,0x00}, /* 100 'd' */
{0x00,0x00,0x3C,0x66,0x7E,0x60,0x3C,0x00}, /* 101 'e' */
{0x00,0x0E,0x18,0x3E,0x18,0x18,0x18,0x00}, /* 102 'f' */
{0x00,0x00,0x3E,0x66,0x66,0x3E,0x06,0x7C}, /* 103 'g' */
{0x00,0x60,0x60,0x7C,0x66,0x66,0x66,0x00}, /* 104 'h' */
{0x00,0x18,0x00,0x38,0x18,0x18,0x3C,0x00}, /* 105 'i' */
{0x00,0x06,0x00,0x06,0x06,0x06,0x06,0x3C}, /* 106 'j' */
{0x00,0x60,0x60,0x6C,0x78,0x6C,0x66,0x00}, /* 107 'k' */
{0x00,0x38,0x18,0x18,0x18,0x18,0x3C,0x00}, /* 108 'l' */
{0x00,0x00,0x66,0x7F,0x6B,0x63,0x63,0x00}, /* 109 'm' */
{0x00,0x00,0x7C,0x66,0x66,0x66,0x66,0x00}, /* 110 'n' */
{0x00,0x00,0x3C,0x66,0x66,0x66,0x3C,0x00}, /* 111 'o' */
{0x00,0x00,0x7C,0x66,0x66,0x7C,0x60,0x60}, /* 112 'p' */
{0x00,0x00,0x3E,0x66,0x66,0x3E,0x06,0x06}, /* 113 'q' */
{0x00,0x00,0x7C,0x66,0x60,0x60,0x60,0x00}, /* 114 'r' */
{0x00,0x00,0x3E,0x60,0x3C,0x06,0x7C,0x00}, /* 115 's' */
{0x00,0x18,0x18,0x7E,0x18,0x18,0x0C,0x00}, /* 116 't' */
{0x00,0x00,0x66,0x66,0x66,0x66,0x3E,0x00}, /* 117 'u' */
{0x00,0x00,0x66,0x66,0x66,0x3C,0x18,0x00}, /* 118 'v' */
{0x00,0x00,0x63,0x6B,0x7F,0x3E,0x36,0x00}, /* 119 'w' */
{0x00,0x00,0x66,0x3C,0x18,0x3C,0x66,0x00}, /* 120 'x' */
{0x00,0x00,0x66,0x66,0x66,0x3E,0x0C,0x78}, /* 121 'y' */
{0x00,0x00,0x7E,0x0C,0x18,0x30,0x7E,0x00}, /* 122 'z' */
{0x0C,0x18,0x18,0x30,0x18,0x18,0x0C,0x00}, /* 123 '{' */
{0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00}, /* 124 '|' */
{0x30,0x18,0x18,0x0C,0x18,0x18,0x30,0x00}, /* 125 '}' */
{0x00,0x00,0x66,0x3C,0x00,0x00,0x00,0x00}, /* 126 '~' */
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, /* 127 DEL */
};
+46 -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);
@@ -184,6 +204,14 @@ void do_dot_quote(Word* w);
void do_dot_quote_inner(Word* w);
void do_words(Word* w);
// Framebuffer
void do_fb_addr(Word* w);
void do_fb_width(Word* w);
void do_fb_height(Word* w);
void do_fb_pitch(Word* w);
void do_fb_bpp(Word* w);
void do_fb_plot(Word* w);
// Memory
void do_fetch(Word* w);
void do_store(Word* w);
@@ -196,6 +224,7 @@ void do_do_var(Word* w);
void do_do_const(Word* w);
void do_here(Word* w);
void do_allot(Word* w);
void do_comma(Word* w);
// Return stack
void do_to_r(Word* w);
+23 -2
View File
@@ -1,13 +1,21 @@
#include "forth.h"
// Static storage for bare-metal portability
#ifndef FORTH_CUSTOM_MEMORY
static int64_t data_stack_storage[FORTH_DATA_STACK_SIZE];
int64_t *data_stack = data_stack_storage;
#else
int64_t *data_stack;
#endif
int32_t data_sp = -1;
int32_t data_cap = FORTH_DATA_STACK_SIZE;
#ifndef FORTH_CUSTOM_MEMORY
static Cell ret_stack_storage[FORTH_RET_STACK_SIZE];
Cell *ret_stack = ret_stack_storage;
#else
Cell *ret_stack;
#endif
int32_t rp = -1;
int32_t ret_cap = FORTH_RET_STACK_SIZE;
Cell* ip = NULL;
@@ -16,8 +24,12 @@ Word* dict_head = NULL;
int state = 0;
#ifndef FORTH_CUSTOM_MEMORY
static Cell compile_buf_storage[FORTH_COMPILE_BUF_SIZE];
Cell *compile_buf = compile_buf_storage;
#else
Cell *compile_buf;
#endif
int32_t compile_idx = 0;
int32_t compile_cap = FORTH_COMPILE_BUF_SIZE;
char compiling_name[MAX_NAME_LEN + 1] = {0};
@@ -26,15 +38,24 @@ char* input_buf = NULL;
size_t input_buf_cap = 0;
char* input_ptr = NULL;
#ifndef FORTH_CUSTOM_MEMORY
static int64_t compile_stack_storage[FORTH_COMPILE_STACK_SIZE];
int64_t *compile_stack = compile_stack_storage;
#else
int64_t *compile_stack;
#endif
int32_t compile_sp = -1;
int32_t compile_stack_cap = FORTH_COMPILE_STACK_SIZE;
#ifndef FORTH_CUSTOM_MEMORY
static Cell user_mem_storage[FORTH_USER_MEM_CELLS];
Cell *user_mem = user_mem_storage;
int64_t user_mem_size = FORTH_USER_MEM_CELLS;
Cell* here = user_mem_storage;
#else
Cell *user_mem;
Cell* here;
#endif
int64_t user_mem_size = FORTH_USER_MEM_CELLS;
Word* w_exit = NULL;
Word* w_docolon = NULL;
@@ -78,7 +99,7 @@ void data_push(int64_t val) {
int64_t data_pop(void) {
if (data_sp < 0) {
fprintf(stderr, "Data stack underflow\n");
forth_printf("Data stack underflow\n");
return 0;
}
return data_stack[data_sp--];
+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;
}
+15 -9
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");
@@ -87,15 +87,21 @@ void outer_interpreter(void) {
while (1) {
forth_printf("ok ");
forth_fflush();
if (fgets(line_buf, sizeof(line_buf), stdin) == NULL) {
size_t i = 0;
int c;
while (i < sizeof(line_buf) - 1) {
c = forth_getchar();
if (c == FORTH_EOF || c == '\n' || c == '\r') {
break;
}
line_buf[i++] = (char)c;
}
line_buf[i] = '\0';
if (i == 0 && c == FORTH_EOF) {
break;
}
input_buf = line_buf;
input_buf_cap = sizeof(line_buf);
size_t n = strlen(input_buf);
if (n > 0 && input_buf[n - 1] == '\n') {
input_buf[n - 1] = '\0';
}
input_ptr = input_buf;
char* tok;
while ((tok = next_token()) != NULL) {
+31 -17
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;
@@ -456,13 +456,25 @@ void do_here(Word* w) {
void do_allot(Word* w) {
(void)w;
int64_t n = data_pop();
if (here + n > user_mem + user_mem_size) {
forth_printf("User memory overflow\n");
int64_t idx = here - user_mem;
if (idx + n < 0 || idx + n > user_mem_size) {
forth_printf("User memory out of bounds\n");
return;
}
here += n;
}
void do_comma(Word* w) {
(void)w;
int64_t val = data_pop();
if (here + 1 > user_mem + user_mem_size) {
forth_printf("User memory overflow\n");
return;
}
here->num = val;
here++;
}
// Variable and constant
void do_variable(Word* w) {
(void)w;
@@ -483,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
@@ -512,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
@@ -571,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;
@@ -591,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();
@@ -599,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;
@@ -680,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) {
@@ -710,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)
}
}
+184
View File
@@ -0,0 +1,184 @@
# 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))
/* Framebuffer tag: type=5, size=20, width=height=0 (any), depth=32.
* Layout per multiboot2 spec:
* u16 type, u16 flags, u32 size, u32 width, u32 height, u32 depth
* Padded to 8-byte alignment. */
.align 8
.word 5
.word 0
.long 20
.long 0
.long 0
.long 32
.long 0 /* padding to 8-byte alignment */
.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 * 4
.align 16
stack_bottom:
.space 16384
stack_top:
.align 8
mboot_info_ptr:
.space 8
.section .text
.code32
.global _start
.type _start, @function
_start:
movl $stack_top, %esp
movl %ebx, mboot_info_ptr
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
/* Populate 4 PDPT entries, each pointing to one of 4 PDs.
* This identity-maps the first 4 GiB using 2 MiB pages,
* which covers typical framebuffer/MMIO regions. */
movl $pd, %eax
orl $0x03, %eax
movl %eax, pdpt + 0
addl $4096, %eax
movl %eax, pdpt + 8
addl $4096, %eax
movl %eax, pdpt + 16
addl $4096, %eax
movl %eax, pdpt + 24
movl $0x00000083, %eax
movl $pd, %edi
movl $2048, %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:
/* Load data segment registers with the data descriptor (0x10).
* In long mode most of these are ignored for access checks, but
* %ss must be a valid (or null) selector so that iretq from an
* interrupt can safely reload it. */
movw $0x10, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movw %ax, %ss
movabsq $stack_top, %rax
movq %rax, %rsp
/* Ensure 16-byte alignment for the System V AMD64 ABI. */
andq $-16, %rsp
movq mboot_info_ptr, %rdi
call kernel_main
cli
1:
hlt
jmp 1b
.code64
.global isr_keyboard
.align 16
isr_keyboard:
/* Save all caller-saved registers used by the SysV AMD64 ABI. */
pushq %rax
pushq %rcx
pushq %rdx
pushq %rsi
pushq %rdi
pushq %r8
pushq %r9
pushq %r10
pushq %r11
/* On interrupt entry the CPU pushed 5 qwords (40 bytes), and we
* pushed 9 more (72 bytes) = 112 bytes total. That leaves %rsp
* misaligned by 8 relative to a 16-byte boundary after the
* upcoming `call`. Push one dummy qword to realign. */
subq $8, %rsp
cld
call keyboard_handler
addq $8, %rsp
popq %r11
popq %r10
popq %r9
popq %r8
popq %rdi
popq %rsi
popq %rdx
popq %rcx
popq %rax
iretq
.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 /* 0x00: null descriptor */
.quad 0x00209A0000000000 /* 0x08: 64-bit code, DPL=0 */
.quad 0x0000920000000000 /* 0x10: data, writable, present */
gdt64_end:
gdt64_pointer:
.word gdt64_end - gdt64 - 1
.quad gdt64
+10 -2
View File
@@ -1,6 +1,6 @@
#include "forth.h"
int main(void) {
void forth_run(void) {
here = user_mem;
// Hidden words first
@@ -65,6 +65,14 @@ int main(void) {
add_primitive(".\"", do_dot_quote, F_IMMEDIATE); // immediate
add_primitive("words", do_words, 0);
// Framebuffer
add_primitive("fb-addr", do_fb_addr, 0);
add_primitive("fb-width", do_fb_width, 0);
add_primitive("fb-height", do_fb_height, 0);
add_primitive("fb-pitch", do_fb_pitch, 0);
add_primitive("fb-bpp", do_fb_bpp, 0);
add_primitive("fb-plot", do_fb_plot, 0);
// Memory
add_primitive("@", do_fetch, 0);
add_primitive("!", do_store, 0);
@@ -75,6 +83,7 @@ int main(void) {
add_primitive("constant", do_constant, 0);
add_primitive("here", do_here, 0);
add_primitive("allot", do_allot, 0);
add_primitive(",", do_comma, 0);
// Return stack
add_primitive(">r", do_to_r, 0);
@@ -103,5 +112,4 @@ int main(void) {
// Start outer interpreter
outer_interpreter();
return 0;
}
+397
View File
@@ -0,0 +1,397 @@
#include "forth.h"
#include <stdint.h>
#include <stdarg.h>
extern const uint8_t font8x8[96][8];
struct mboot_tag {
uint32_t type;
uint32_t size;
};
struct mboot_tag_framebuffer {
uint32_t type;
uint32_t size;
uint64_t framebuffer_addr;
uint32_t framebuffer_pitch;
uint32_t framebuffer_width;
uint32_t framebuffer_height;
uint8_t framebuffer_bpp;
uint8_t framebuffer_type;
uint16_t reserved;
};
static uint64_t fb_addr = 0;
static uint32_t fb_pitch = 0;
static uint32_t fb_width = 0;
static uint32_t fb_height = 0;
static uint8_t fb_bpp = 0;
static int fb_cols = 0;
static int fb_rows = 0;
static int fb_col = 0;
static int fb_row = 0;
static void fb_draw_pixel(int x, int y, uint32_t color) {
if (x < 0 || y < 0 || (uint32_t)x >= fb_width || (uint32_t)y >= fb_height) return;
if (!fb_addr) return;
if (fb_bpp == 32) {
uint32_t* p = (uint32_t*)(fb_addr + y * fb_pitch + x * 4);
*p = color;
} else if (fb_bpp == 24) {
uint8_t* p = (uint8_t*)(fb_addr + y * fb_pitch + x * 3);
p[0] = color & 0xFF;
p[1] = (color >> 8) & 0xFF;
p[2] = (color >> 16) & 0xFF;
}
}
static void fb_scroll(void) {
int line_height = 8;
uint32_t bytes_per_pixel = (fb_bpp + 7) / 8;
uint32_t copy_bytes = fb_width * bytes_per_pixel;
for (uint32_t y = 0; y < fb_height - line_height; y++) {
uint8_t* dst = (uint8_t*)(fb_addr + y * fb_pitch);
uint8_t* src = (uint8_t*)(fb_addr + (y + line_height) * fb_pitch);
for (uint32_t i = 0; i < copy_bytes; i++) {
dst[i] = src[i];
}
}
for (uint32_t y = fb_height - line_height; y < fb_height; y++) {
uint8_t* row = (uint8_t*)(fb_addr + y * fb_pitch);
for (uint32_t i = 0; i < copy_bytes; i++) {
row[i] = 0;
}
}
}
static void fb_draw_char(char c) {
if (!fb_addr || fb_cols == 0) return;
if (c == '\n') {
fb_col = 0;
fb_row++;
} else if (c == '\r') {
fb_col = 0;
} else if (c == '\b') {
if (fb_col > 0) fb_col--;
} else {
unsigned char ch = (unsigned char)c;
if (ch < 32 || ch > 126) ch = '?';
const uint8_t* glyph = font8x8[ch - 32];
int start_x = fb_col * 8;
int start_y = fb_row * 8;
for (int dy = 0; dy < 8; dy++) {
uint8_t row = glyph[dy];
for (int dx = 0; dx < 8; dx++) {
if (row & (0x80 >> dx)) {
fb_draw_pixel(start_x + dx, start_y + dy, 0xFFFFFF);
} else {
fb_draw_pixel(start_x + dx, start_y + dy, 0x000000);
}
}
}
fb_col++;
}
if (fb_col >= fb_cols) {
fb_col = 0;
fb_row++;
}
if (fb_row >= fb_rows) {
fb_scroll();
fb_row = fb_rows - 1;
}
}
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 inline void io_wait(void) {
__asm__ volatile ("outb %0, %1" : : "a"((uint8_t)0), "Nd"((uint16_t)0x80));
}
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);
}
/* Each 64-bit IDT entry is 16 bytes = two uint64_t words.
*
* Word 0 layout:
* [15: 0] offset[15:0]
* [31:16] segment selector
* [34:32] IST (0 = no IST)
* [39:35] reserved (0)
* [47:40] type+attr (0x8E = present, DPL=0, 64-bit interrupt gate)
* [63:48] offset[31:16]
*
* Word 1 layout:
* [31: 0] offset[63:32]
* [63:32] reserved (0)
*/
typedef struct {
uint64_t low;
uint64_t high;
} __attribute__((packed)) IdtEntry;
static IdtEntry idt_entries[256];
static void idt_set_gate(int vector, uint64_t offset, uint16_t selector, uint8_t type_attr) {
uint64_t low =
(offset & 0x000000000000FFFFULL) | /* offset[15:0] */
((uint64_t)selector << 16) | /* selector */
/* bits 34:32 = IST=0, bits 39:35 = 0 */
((uint64_t)type_attr << 40) | /* type+attr */
((offset & 0x00000000FFFF0000ULL) << 32); /* offset[31:16] */
uint64_t high = offset >> 32; /* offset[63:32] */
idt_entries[vector].low = low;
idt_entries[vector].high = high;
}
static struct {
uint16_t limit;
uint64_t base;
} __attribute__((packed)) idt_ptr;
static void pic_remap(void) {
uint8_t a1 = inb(0x21);
uint8_t a2 = inb(0xA1);
outb(0x20, 0x11);
io_wait();
outb(0xA0, 0x11);
io_wait();
outb(0x21, 0x20);
io_wait();
outb(0xA1, 0x28);
io_wait();
outb(0x21, 0x04);
io_wait();
outb(0xA1, 0x02);
io_wait();
outb(0x21, 0x01);
io_wait();
outb(0xA1, 0x01);
io_wait();
outb(0x21, a1);
outb(0xA1, a2);
}
static void pic_unmask_keyboard(void) {
uint8_t mask = inb(0x21);
mask &= ~(1 << 1);
outb(0x21, mask);
}
#define KEYBUF_SIZE 256
static volatile char keybuf[KEYBUF_SIZE];
static volatile int keybuf_head = 0;
static volatile int keybuf_tail = 0;
static const char scancode_map[] = {
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b',
'\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',
0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`',
0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0,
'*', 0, ' ', 0
};
void keyboard_handler(void) {
uint8_t scancode = inb(0x60);
if (scancode < sizeof(scancode_map)) {
char c = scancode_map[scancode];
if (c) {
int next = (keybuf_tail + 1) % KEYBUF_SIZE;
if (next != keybuf_head) {
keybuf[keybuf_tail] = c;
keybuf_tail = next;
forth_putchar(c);
}
}
}
outb(0x20, 0x20);
}
void forth_putchar(char c) {
fb_draw_char(c);
while ((inb(0x3F8 + 5) & 0x20) == 0) { }
outb(0x3F8, (uint8_t)c);
}
int forth_getchar(void) {
while (keybuf_head == keybuf_tail) {
__asm__ volatile ("pause");
}
char c = keybuf[keybuf_head];
keybuf_head = (keybuf_head + 1) % KEYBUF_SIZE;
return (int)c;
}
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(uint64_t mboot_ptr) {
extern void isr_keyboard(void);
/* Multiboot2 info tag types:
* 5 = BIOS boot device, 6 = memory map, 8 = framebuffer info, ...
* Note: in the *header* (request side), type 5 means "framebuffer
* request", but the bootloader's *response* tag uses type 8. */
struct mboot_tag* tag = (struct mboot_tag*)(mboot_ptr + 8);
while (tag->type != 0) {
if (tag->type == 8) {
struct mboot_tag_framebuffer* fb = (struct mboot_tag_framebuffer*)tag;
fb_addr = fb->framebuffer_addr;
fb_pitch = fb->framebuffer_pitch;
fb_width = fb->framebuffer_width;
fb_height = fb->framebuffer_height;
fb_bpp = fb->framebuffer_bpp;
}
tag = (struct mboot_tag*)((uint8_t*)tag + ((tag->size + 7) & ~7));
}
if (fb_addr && fb_bpp >= 24) {
fb_cols = fb_width / 8;
fb_rows = fb_height / 8;
for (uint32_t y = 0; y < fb_height; y++) {
for (uint32_t x = 0; x < fb_width; x++) {
fb_draw_pixel(x, y, 0);
}
}
}
serial_init();
idt_set_gate(0x21, (uint64_t)isr_keyboard, 0x08, 0x8E);
idt_ptr.limit = (uint16_t)(sizeof(idt_entries) - 1);
idt_ptr.base = (uint64_t)(uintptr_t)idt_entries;
__asm__ volatile ("lidt %0" : : "m"(idt_ptr));
pic_remap();
outb(0x21, 0xFF); // mask all IRQs on master
outb(0xA1, 0xFF); // mask all IRQs on slave
pic_unmask_keyboard();
__asm__ volatile ("sti");
forth_printf("Kernel started\n");
forth_run();
forth_panic();
}
void do_fb_addr(Word* w) {
(void)w;
data_push((int64_t)fb_addr);
}
void do_fb_width(Word* w) {
(void)w;
data_push((int64_t)fb_width);
}
void do_fb_height(Word* w) {
(void)w;
data_push((int64_t)fb_height);
}
void do_fb_pitch(Word* w) {
(void)w;
data_push((int64_t)fb_pitch);
}
void do_fb_bpp(Word* w) {
(void)w;
data_push((int64_t)fb_bpp);
}
void do_fb_plot(Word* w) {
(void)w;
int64_t color = data_pop();
int64_t y = data_pop();
int64_t x = data_pop();
fb_draw_pixel((int)x, (int)y, (uint32_t)color);
}
+121
View File
@@ -0,0 +1,121 @@
#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 do_fb_addr(Word* w) { (void)w; data_push(0); }
void do_fb_width(Word* w) { (void)w; data_push(0); }
void do_fb_height(Word* w) { (void)w; data_push(0); }
void do_fb_pitch(Word* w) { (void)w; data_push(0); }
void do_fb_bpp(Word* w) { (void)w; data_push(0); }
void do_fb_plot(Word* w) { (void)w; data_pop(); data_pop(); data_pop(); }
void _start(void) {
forth_run();
syscall1(60, 0);
__builtin_unreachable();
}