Compare commits

...

28 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
haxala1r 3c292cfc18 fix: define missing global state variable
Co-authored-by: aider (openrouter/moonshotai/kimi-k2.6) <aider@aider.chat>
2026-05-03 22:41:11 +03:00
haxala1r dcc88bc8ca feat: add portable panic, I/O hooks and configurable memory sizes
Co-authored-by: aider (openrouter/moonshotai/kimi-k2.6) <aider@aider.chat>
2026-05-03 22:38:29 +03:00
haxala1r f033187c6c refactor: make interpreter portable with static pools and I/O hooks
Co-authored-by: aider (openrouter/moonshotai/kimi-k2.6) <aider@aider.chat>
2026-05-03 22:35:21 +03:00
haxala1r b115744991 fix: qdup/tuck/2swap bugs, getline, and cached word pointers
Co-authored-by: aider (openrouter/moonshotai/kimi-k2.6) <aider@aider.chat>
2026-05-03 21:39:52 +03:00
haxala1r bf3c15ec27 chore: remove forth.c 2026-05-03 19:42:30 +03:00
haxala1r 46e43961fd feat: add depth, pick, roll, qdup, 2dup, 2drop, 2swap words
Co-authored-by: aider (openrouter/deepseek/deepseek-v4-pro) <aider@aider.chat>
2026-05-03 19:40:33 +03:00
haxala1r 5916a92a4f feat: add depth, pick, roll, ?dup, 2dup, 2drop, and 2swap words
Co-authored-by: aider (openrouter/deepseek/deepseek-v4-pro) <aider@aider.chat>
2026-05-03 19:37:02 +03:00
haxala1r a0b535f0a3 fix: correct argument order for store operations
Co-authored-by: aider (openrouter/deepseek/deepseek-v4-pro) <aider@aider.chat>
2026-05-03 19:19:41 +03:00
haxala1r 145035fb67 fix: allocate VARIABLE and CONSTANT cells in user memory
Co-authored-by: aider (openrouter/deepseek/deepseek-v4-pro) <aider@aider.chat>
2026-05-03 18:54:09 +03:00
haxala1r afd0a80dcb feat: switch to 64-bit integers and dynamic memory
Co-authored-by: aider (openrouter/deepseek/deepseek-v4-pro) <aider@aider.chat>
2026-05-03 18:45:42 +03:00
14 changed files with 1713 additions and 1151 deletions
+14 -10
View File
@@ -1,19 +1,23 @@
CC = gcc
CFLAGS = -Wall -Wextra -g
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
+3 -3
View File
@@ -10,7 +10,7 @@ This is a toy/subset Forth interpreter designed for educational purposes. It pro
- **Threaded Code**: The interpreter uses an inner/outer interpreter model. Colon definitions are sequences of word addresses (threaded code) traversed by an instruction pointer.
- **Architecture**:
- 32-bit signed integer cells (`int32_t`)
- 64-bit signed integer cells (`int64_t`)
- Separate data and return stacks
- Fixed-size dictionary, user memory, and compile buffer
- Dictionary is a singly-linked list searched linearly
@@ -23,8 +23,8 @@ This is a toy/subset Forth interpreter designed for educational purposes. It pro
- Memory access (`@`, `!`, `C@`, `C!`, `HERE`, `ALLOT`)
- Simple string output (`."`)
- **Limitations**:
- Fixed memory limits (dictionary, stacks, user memory)
- 32-bit signed integers only; no floating-point support
- Fixed user memory limit (1M cells); dictionary and stacks grow dynamically
- 64-bit signed integers only; no floating-point support
- No file I/O or operating system interface beyond stdin/stdout
- No immediate user-defined words or advanced introspection
- Single-threaded execution
+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 */
};
-384
View File
@@ -1,384 +0,0 @@
/*
* Small Forth Implementation in C
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdint.h>
// Configuration
#define DATA_STACK_SIZE 256
#define RET_STACK_SIZE 256
#define DICT_SIZE 256
#define BODY_SIZE 1024
#define COMPILE_BUF_SIZE 1024
#define INPUT_BUF_SIZE 256
#define MAX_NAME_LEN 31
// Core types
typedef struct Word Word;
typedef union Cell {
Word* word;
int32_t num;
} Cell;
struct Word {
Word* prev;
uint8_t flags; // Bit7=immediate, Bit6=hidden, Bits0-5=name length
char name[MAX_NAME_LEN + 1];
void (*code)(Word*);
Cell* body;
};
// Globals
int32_t data_stack[DATA_STACK_SIZE];
int sp = -1;
Cell* ret_stack[RET_STACK_SIZE];
int rp = -1;
Cell* ip = NULL;
Word dict[DICT_SIZE];
int dict_idx = 0;
Word* dict_head = NULL;
Cell dict_bodies[BODY_SIZE];
int body_idx = 0;
int state = 0; // 0=interpret, 1=compile
Cell compile_buf[COMPILE_BUF_SIZE];
int compile_idx = 0;
char compiling_name[MAX_NAME_LEN + 1];
char input_buf[INPUT_BUF_SIZE];
char* input_ptr = NULL;
// Stack helpers
void data_push(int32_t val) {
if (sp < DATA_STACK_SIZE - 1) {
data_stack[++sp] = val;
} else {
printf("Data stack overflow\n");
}
}
int32_t data_pop(void) {
if (sp >= 0) {
return data_stack[sp--];
} else {
printf("Data stack underflow\n");
return 0;
}
}
void ret_push(Cell* val) {
if (rp < RET_STACK_SIZE - 1) {
ret_stack[++rp] = val;
} else {
printf("Return stack overflow\n");
}
}
Cell* ret_pop(void) {
if (rp >= 0) {
return ret_stack[rp--];
} else {
printf("Return stack underflow\n");
return NULL;
}
}
// Dictionary helpers
Word* add_primitive(const char* name, void (*code)(Word*), uint8_t flags) {
if (dict_idx >= DICT_SIZE) {
printf("Dictionary full\n");
return NULL;
}
Word* w = &dict[dict_idx++];
w->prev = dict_head;
dict_head = w;
size_t len = strlen(name);
if (len > MAX_NAME_LEN) len = MAX_NAME_LEN;
w->flags = flags | (uint8_t)len;
strncpy(w->name, name, len);
w->name[len] = '\0';
w->code = code;
w->body = NULL;
return w;
}
Word* lookup_word(const char* name) {
for (Word* w = dict_head; w != NULL; w = w->prev) {
if (w->flags & (1 << 6)) continue; // Skip hidden words
if (strcmp(w->name, name) == 0) return w;
}
return NULL;
}
// Input tokenizer
char* next_token(void) {
if (input_ptr == NULL) return NULL;
while (*input_ptr != '\0' && 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)) {
input_ptr++;
}
if (*input_ptr != '\0') {
*input_ptr = '\0';
input_ptr++;
}
return start;
}
// Primitive word implementations
void do_dup(Word* w) {
if (sp < 0) return;
int32_t v = data_stack[sp];
data_push(v);
}
void do_drop(Word* w) {
data_pop();
}
void do_swap(Word* w) {
if (sp < 1) return;
int32_t a = data_stack[sp-1];
int32_t b = data_stack[sp];
data_stack[sp-1] = b;
data_stack[sp] = a;
}
void do_over(Word* w) {
if (sp < 1) return;
data_push(data_stack[sp-1]);
}
void do_add(Word* w) {
if (sp < 1) return;
int32_t b = data_pop();
int32_t a = data_pop();
data_push(a + b);
}
void do_sub(Word* w) {
if (sp < 1) return;
int32_t b = data_pop();
int32_t a = data_pop();
data_push(a - b);
}
void do_mul(Word* w) {
if (sp < 1) return;
int32_t b = data_pop();
int32_t a = data_pop();
data_push(a * b);
}
void do_div(Word* w) {
if (sp < 1) return;
int32_t b = data_pop();
int32_t a = data_pop();
if (b == 0) {
printf("Division by zero\n");
data_push(a);
data_push(b);
return;
}
data_push(a / b);
}
void do_dot(Word* w) {
if (sp < 0) return;
printf("%d ", data_pop());
fflush(stdout);
}
void do_cr(Word* w) {
printf("\n");
fflush(stdout);
}
void do_exit(Word* w) {
Cell* ret_addr = ret_pop();
ip = ret_addr;
}
void do_docolon(Word* w) {
// Push current ip (return address) onto return stack
ret_push(ip);
// Set ip to this word's body
ip = w->body;
}
void do_lit(Word* w) {
// ip points to the number cell (inner interpreter already incremented past lit word)
data_push(ip->num);
ip++; // Move past number cell
}
void do_colon(Word* w) {
char* name = next_token();
if (name == NULL) {
printf("':' expects a name\n");
return;
}
strncpy(compiling_name, name, MAX_NAME_LEN);
compiling_name[MAX_NAME_LEN] = '\0';
state = 1; // Enter compile mode
compile_idx = 0; // Reset compile buffer
}
void do_semicolon(Word* w) {
if (state != 1) {
printf("';' is only valid in compile mode\n");
return;
}
Word* exit_w = lookup_word("exit");
if (exit_w == NULL) {
printf("Fatal: exit word not found\n");
return;
}
if (compile_idx >= COMPILE_BUF_SIZE) {
printf("Compile buffer overflow\n");
return;
}
compile_buf[compile_idx++] = (Cell){.word = exit_w};
// Copy compiled body to dictionary body storage
if (body_idx + compile_idx > BODY_SIZE) {
printf("Dictionary body storage full\n");
return;
}
memcpy(&dict_bodies[body_idx], compile_buf, compile_idx * sizeof(Cell));
// Create new word entry
if (dict_idx >= DICT_SIZE) {
printf("Dictionary full\n");
return;
}
Word* new_w = &dict[dict_idx++];
new_w->prev = dict_head;
dict_head = new_w;
size_t len = strlen(compiling_name);
if (len > MAX_NAME_LEN) len = MAX_NAME_LEN;
new_w->flags = (uint8_t)len; // No hidden, no immediate
strncpy(new_w->name, compiling_name, len);
new_w->name[len] = '\0';
new_w->code = do_docolon;
new_w->body = &dict_bodies[body_idx];
body_idx += compile_idx;
state = 0; // Back to interpret mode
}
// Interpreter functions
void inner_interpreter(void) {
while (ip != NULL) {
Cell current = *ip;
ip++; // Move to next cell
current.word->code(current.word);
}
}
void process_token(const char* token) {
Word* w = lookup_word(token);
if (w != NULL) {
if (state == 0) { // Interpret mode
if (w->code == do_docolon) { // Colon definition
ret_push(NULL); // Return address to stop interpreter
ip = w->body;
inner_interpreter();
} else { // Primitive word
w->code(w);
}
} else { // Compile mode
if (w->flags & (1 << 7)) { // Immediate word: execute now
if (w->code == do_docolon) {
ret_push(NULL);
ip = w->body;
inner_interpreter();
} else {
w->code(w);
}
} else { // Normal word: compile into current definition
if (compile_idx >= COMPILE_BUF_SIZE) {
printf("Compile buffer full\n");
return;
}
compile_buf[compile_idx++] = (Cell){.word = w};
}
}
} else { // Not a known word: try to parse as number
char* end;
long v = strtol(token, &end, 10);
if (end != token && *end == '\0') { // Valid integer
if (state == 0) { // Interpret mode: push number
data_push((int32_t)v);
} else { // Compile mode: compile lit + number
Word* lit_w = lookup_word("lit");
if (lit_w == NULL) {
printf("Fatal: lit word not found\n");
return;
}
if (compile_idx + 2 > COMPILE_BUF_SIZE) {
printf("Compile buffer full\n");
return;
}
compile_buf[compile_idx++] = (Cell){.word = lit_w};
compile_buf[compile_idx++] = (Cell){.num = (int32_t)v};
}
} else {
printf("Unknown word: '%s'\n", token);
}
}
}
void outer_interpreter(void) {
while (1) {
printf("ok ");
fflush(stdout);
if (fgets(input_buf, INPUT_BUF_SIZE, stdin) == NULL) {
break; // EOF
}
input_ptr = input_buf;
char* tok;
while ((tok = next_token()) != NULL) {
process_token(tok);
}
}
printf("\n");
}
int main(void) {
// Register primitive words
// Hidden words first
add_primitive("exit", do_exit, 0);
add_primitive("docolon", do_docolon, 1 << 6); // Hidden
add_primitive("lit", do_lit, 1 << 6); // Hidden
// Public primitives
add_primitive("dup", do_dup, 0);
add_primitive("drop", do_drop, 0);
add_primitive("swap", do_swap, 0);
add_primitive("over", do_over, 0);
add_primitive("+", do_add, 0);
add_primitive("-", do_sub, 0);
add_primitive("*", do_mul, 0);
add_primitive("/", do_div, 0);
add_primitive(".", do_dot, 0);
add_primitive("cr", do_cr, 0);
// Compilation words
add_primitive(":", do_colon, 0);
add_primitive(";", do_semicolon, 1 << 7); // Immediate word
// Start outer interpreter
outer_interpreter();
return 0;
}
+130 -45
View File
@@ -1,88 +1,155 @@
#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
#define DATA_STACK_SIZE 256
#define RET_STACK_SIZE 256
#define DICT_SIZE 256
#define BODY_SIZE 1024
#define COMPILE_BUF_SIZE 1024
#define INPUT_BUF_SIZE 256
// Configuration (all hard limits removed)
#define MAX_NAME_LEN 31
#define COMPILE_STACK_SIZE 256
#define USER_MEMORY_SIZE 1024
#define FORTH_EOF (-1)
/* 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);
/* 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
#define FORTH_DATA_STACK_SIZE 128
#endif
#ifndef FORTH_RET_STACK_SIZE
#define FORTH_RET_STACK_SIZE 128
#endif
#ifndef FORTH_COMPILE_STACK_SIZE
#define FORTH_COMPILE_STACK_SIZE 64
#endif
#ifndef FORTH_COMPILE_BUF_SIZE
#define FORTH_COMPILE_BUF_SIZE 256
#endif
#ifndef FORTH_USER_MEM_CELLS
#define FORTH_USER_MEM_CELLS (1024 * 1024)
#endif
#ifndef FORTH_MAX_WORDS
#define FORTH_MAX_WORDS 256
#endif
#ifndef FORTH_MAX_WORD_BODY_CELLS
#define FORTH_MAX_WORD_BODY_CELLS 4096
#endif
#define F_IMMEDIATE (1 << 7)
#define F_HIDDEN (1 << 6)
// Core types
typedef struct Word Word;
typedef union Cell {
Word* word;
int32_t num;
union Cell* cell_ptr; // Use union tag here, as typedef Cell is not available yet
int64_t num;
void* ptr; // used for return stack (holds Cell*)
} Cell;
struct Word {
Word* prev;
uint8_t flags; // Bit7=immediate, Bit6=hidden, Bits0-5=name length
uint8_t flags; // Bit7=immediate, Bit6=hidden, Bits0-5=name length
char name[MAX_NAME_LEN + 1];
void (*code)(Word*);
Cell* body;
Cell* body; // points to the words body (allocated dynamically)
};
// Globals
extern int32_t data_stack[DATA_STACK_SIZE];
extern int sp;
extern Cell ret_stack[RET_STACK_SIZE];
extern int rp;
extern Cell* ip;
// Globals (all dynamic)
extern int64_t *data_stack;
extern int32_t data_sp; // stack indices are small enough for int32_t
extern int32_t data_cap;
extern Cell *ret_stack;
extern int32_t rp;
extern int32_t ret_cap;
extern Cell* ip; // instruction pointer
extern Word dict[DICT_SIZE];
extern int dict_idx;
extern Word* dict_head;
extern Cell dict_bodies[BODY_SIZE];
extern int body_idx;
extern int state; // 0=interpret, 1=compile
extern Cell compile_buf[COMPILE_BUF_SIZE];
extern int compile_idx;
extern int state;
extern Cell *compile_buf;
extern int32_t compile_idx;
extern int32_t compile_cap;
extern char compiling_name[MAX_NAME_LEN + 1];
extern char input_buf[INPUT_BUF_SIZE];
extern char* input_buf; // line buffer (dynamic, managed by getline)
extern size_t input_buf_cap;
extern char* input_ptr;
extern Cell* compile_stack[COMPILE_STACK_SIZE];
extern int compile_sp;
extern int64_t *compile_stack; // holds indices into compile_buf
extern int32_t compile_sp;
extern int32_t compile_stack_cap;
extern int32_t user_mem[USER_MEMORY_SIZE];
extern int32_t* here;
extern Cell *user_mem;
extern int64_t user_mem_size; // in cells
extern Cell* here;
// Pointers to critical hidden/primitive words (set during init)
extern Word* w_exit;
extern Word* w_docolon;
extern Word* w_lit;
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(int32_t val);
int32_t data_pop(void);
void data_push(int64_t val);
int64_t data_pop(void);
void ret_push_ip(Cell* val);
Cell* ret_pop_ip(void);
void ret_push_num(int32_t val);
int32_t ret_pop_num(void);
void compile_push(Cell* addr);
Cell* compile_pop(void);
void ret_push_num(int64_t val);
int64_t ret_pop_num(void);
void compile_push(int64_t idx);
int64_t compile_pop(void);
Word* add_primitive(const char* name, void (*code)(Word*), uint8_t flags);
Word* lookup_word(const char* name);
Word* lookup_word_internal(const char* name);
char* next_token(void);
void inner_interpreter(void);
void process_token(const char* token);
void outer_interpreter(void);
// Primitive word prototypes
// Ensure compile_buf has at least 'needed' free cells (returns 0 on overflow)
int ensure_compile_cap(int32_t needed);
Word* forth_alloc_word(void);
Cell* forth_alloc_body(int32_t cells);
// Stack ops
void do_dup(Word* w);
void do_drop(Word* w);
@@ -137,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);
@@ -149,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);
@@ -171,4 +247,13 @@ void do_until(Word* w);
void do_while(Word* w);
void do_repeat(Word* w);
#endif // FORTH_H
/* Additional stack words */
void do_depth(Word* w);
void do_pick(Word* w);
void do_roll(Word* w);
void do_qdup(Word* w);
void do_2dup(Word* w);
void do_2drop(Word* w);
void do_2swap(Word* w);
#endif
+118 -63
View File
@@ -1,103 +1,158 @@
#include "forth.h"
// Global variables
int32_t data_stack[DATA_STACK_SIZE];
int sp = -1;
Cell ret_stack[RET_STACK_SIZE];
int rp = -1;
// 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;
Word dict[DICT_SIZE];
int dict_idx = 0;
Word* dict_head = NULL;
Cell dict_bodies[BODY_SIZE];
int body_idx = 0;
int state = 0;
Cell compile_buf[COMPILE_BUF_SIZE];
int compile_idx = 0;
char compiling_name[MAX_NAME_LEN + 1];
char input_buf[INPUT_BUF_SIZE];
#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};
char* input_buf = NULL;
size_t input_buf_cap = 0;
char* input_ptr = NULL;
Cell* compile_stack[COMPILE_STACK_SIZE];
int compile_sp = -1;
#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;
int32_t user_mem[USER_MEMORY_SIZE];
int32_t* here = &user_mem[0];
#ifndef FORTH_CUSTOM_MEMORY
static Cell user_mem_storage[FORTH_USER_MEM_CELLS];
Cell *user_mem = user_mem_storage;
Cell* here = user_mem_storage;
#else
Cell *user_mem;
Cell* here;
#endif
int64_t user_mem_size = FORTH_USER_MEM_CELLS;
// Stack helpers
void data_push(int32_t val) {
if (sp < DATA_STACK_SIZE - 1) {
data_stack[++sp] = val;
} else {
printf("Data stack overflow\n");
Word* w_exit = NULL;
Word* w_docolon = NULL;
Word* w_lit = NULL;
Word* w_branch = NULL;
Word* w_zbranch = NULL;
Word* w_dot_quote_inner = NULL;
static Word word_pool[FORTH_MAX_WORDS];
static int32_t word_pool_used = 0;
static Cell word_body_storage[FORTH_MAX_WORD_BODY_CELLS];
static int32_t word_body_used = 0;
Word* forth_alloc_word(void) {
if (word_pool_used >= FORTH_MAX_WORDS) {
forth_printf("Dictionary full\n");
forth_panic();
}
return &word_pool[word_pool_used++];
}
int32_t data_pop(void) {
if (sp >= 0) {
return data_stack[sp--];
} else {
printf("Data stack underflow\n");
Cell* forth_alloc_body(int32_t cells) {
if (word_body_used + cells > FORTH_MAX_WORD_BODY_CELLS) {
forth_printf("Word body pool exhausted\n");
forth_panic();
}
Cell* p = &word_body_storage[word_body_used];
word_body_used += cells;
return p;
}
// ---------- Data stack ----------
void data_push(int64_t val) {
if (data_sp + 1 >= data_cap) {
forth_printf("Data stack overflow\n");
return;
}
data_stack[++data_sp] = val;
}
int64_t data_pop(void) {
if (data_sp < 0) {
forth_printf("Data stack underflow\n");
return 0;
}
return data_stack[data_sp--];
}
// ---------- Return stack (holds Cell values) ----------
void ret_push_ip(Cell* val) {
if (rp < RET_STACK_SIZE - 1) {
rp++;
ret_stack[rp].cell_ptr = val;
} else {
printf("Return stack overflow\n");
if (rp + 1 >= ret_cap) {
forth_printf("Return stack overflow\n");
return;
}
rp++;
ret_stack[rp].ptr = val; // store pointer in the 'ptr' member
}
Cell* ret_pop_ip(void) {
if (rp >= 0) {
Cell* res = ret_stack[rp].cell_ptr;
rp--;
return res;
} else {
printf("Return stack underflow\n");
if (rp < 0) {
forth_printf("Return stack underflow\n");
return NULL;
}
return ret_stack[rp--].ptr;
}
void ret_push_num(int32_t val) {
if (rp < RET_STACK_SIZE - 1) {
rp++;
ret_stack[rp].num = val;
} else {
printf("Return stack overflow\n");
void ret_push_num(int64_t val) {
if (rp + 1 >= ret_cap) {
forth_printf("Return stack overflow\n");
return;
}
rp++;
ret_stack[rp].num = val;
}
int32_t ret_pop_num(void) {
if (rp >= 0) {
int32_t res = ret_stack[rp].num;
rp--;
return res;
} else {
printf("Return stack underflow\n");
int64_t ret_pop_num(void) {
if (rp < 0) {
forth_printf("Return stack underflow (num)\n");
return 0;
}
return ret_stack[rp--].num;
}
void compile_push(Cell* addr) {
if (compile_sp < COMPILE_STACK_SIZE - 1) {
compile_stack[++compile_sp] = addr;
} else {
printf("Compile stack overflow\n");
// ---------- Compile stack (indices) ----------
void compile_push(int64_t idx) {
if (compile_sp + 1 >= compile_stack_cap) {
forth_printf("Compile stack overflow\n");
return;
}
compile_stack[++compile_sp] = idx;
}
Cell* compile_pop(void) {
if (compile_sp >= 0) {
return compile_stack[compile_sp--];
} else {
printf("Compile stack underflow\n");
return NULL;
int64_t compile_pop(void) {
if (compile_sp < 0) {
forth_printf("Compile stack underflow\n");
return -1;
}
return compile_stack[compile_sp--];
}
+6 -10
View File
@@ -1,18 +1,14 @@
#include "forth.h"
Word* add_primitive(const char* name, void (*code)(Word*), uint8_t flags) {
if (dict_idx >= DICT_SIZE) {
printf("Dictionary full\n");
return NULL;
}
Word* w = &dict[dict_idx++];
Word* w = forth_alloc_word();
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;
@@ -21,15 +17,15 @@ 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; // Skip hidden words
if (strcmp(w->name, name) == 0) return w;
if (w->flags & (1 << 6)) continue;
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;
}
+50 -35
View File
@@ -1,14 +1,14 @@
#include "forth.h"
// Input tokenizer
// 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') {
@@ -18,11 +18,20 @@ char* next_token(void) {
return start;
}
// Interpreter functions
// Ensure compile_buf has room for at least 'needed' more cells
int ensure_compile_cap(int32_t needed) {
if (compile_idx + needed > compile_cap) {
forth_printf("Compile buffer overflow\n");
return 0;
}
return 1;
}
// Inner interpreter (unchanged)
void inner_interpreter(void) {
while (ip != NULL) {
Cell current = *ip;
ip++; // Move to next cell
ip++;
current.word->code(current.word);
}
}
@@ -31,15 +40,15 @@ void process_token(const char* token) {
Word* w = lookup_word(token);
if (w != NULL) {
if (state == 0) { // Interpret mode
if (w->code == do_docolon) { // Colon definition
ret_push_ip(NULL); // Return address to stop interpreter
if (w->code == do_docolon) {
ret_push_ip(NULL);
ip = w->body;
inner_interpreter();
} else { // Primitive word
} else {
w->code(w);
}
} else { // Compile mode
if (w->flags & (1 << 7)) { // Immediate word: execute now
if (w->flags & (1 << 7)) { // Immediate word
if (w->code == do_docolon) {
ret_push_ip(NULL);
ip = w->body;
@@ -47,51 +56,57 @@ void process_token(const char* token) {
} else {
w->code(w);
}
} else { // Normal word: compile into current definition
if (compile_idx >= COMPILE_BUF_SIZE) {
printf("Compile buffer full\n");
return;
}
} else { // Compile normal word
if (!ensure_compile_cap(1)) return;
compile_buf[compile_idx++] = (Cell){.word = w};
}
}
} else { // Not a known word: try to parse as number
} else { // Try to parse as number
char* end;
long v = strtol(token, &end, 10);
if (end != token && *end == '\0') { // Valid integer
if (state == 0) { // Interpret mode: push number
data_push((int32_t)v);
} else { // Compile mode: compile lit + number
Word* lit_w = lookup_word_internal("lit");
if (lit_w == NULL) {
printf("Fatal: lit word not found\n");
int64_t v = forth_strtoll(token, &end, 10);
if (end != token && *end == '\0') {
if (state == 0) {
data_push(v);
} else { // Compile lit + number
if (!w_lit) {
forth_printf("Fatal: lit word not found\n");
return;
}
if (compile_idx + 2 > COMPILE_BUF_SIZE) {
printf("Compile buffer full\n");
return;
}
compile_buf[compile_idx++] = (Cell){.word = lit_w};
compile_buf[compile_idx++] = (Cell){.num = (int32_t)v};
if (!ensure_compile_cap(2)) return;
compile_buf[compile_idx++] = (Cell){.word = w_lit};
compile_buf[compile_idx++] = (Cell){.num = (int64_t)v};
}
} else {
printf("Unknown word: '%s'\n", token);
forth_printf("Unknown word: '%s'\n", token);
}
}
}
void outer_interpreter(void) {
static char line_buf[256];
while (1) {
printf("ok ");
fflush(stdout);
if (fgets(input_buf, INPUT_BUF_SIZE, stdin) == NULL) {
break; // EOF
forth_printf("ok ");
forth_fflush();
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);
input_ptr = input_buf;
char* tok;
while ((tok = next_token()) != NULL) {
process_token(tok);
}
}
printf("\n");
forth_printf("\n");
}
+482 -546
View File
File diff suppressed because it is too large Load Diff
+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
+65 -47
View File
@@ -1,31 +1,32 @@
#include "forth.h"
int main(void) {
// Register primitive words
void forth_run(void) {
here = user_mem;
// Hidden words first
add_primitive("exit", do_exit, 0);
add_primitive("docolon", do_docolon, 1 << 6); // Hidden
add_primitive("lit", do_lit, 1 << 6); // Hidden
add_primitive("do_dot_quote_inner", do_dot_quote_inner, 1 << 6); // Hidden
add_primitive("0branch", do_zero_branch, 1 << 6); // Hidden (for IF, UNTIL, WHILE)
add_primitive("branch", do_branch, 1 << 6); // Hidden (for ELSE, REPEAT)
w_exit = add_primitive("exit", do_exit, 0);
w_docolon = add_primitive("docolon", do_docolon, F_HIDDEN);
w_lit = add_primitive("lit", do_lit, F_HIDDEN);
w_dot_quote_inner = add_primitive("do_dot_quote_inner", do_dot_quote_inner, F_HIDDEN);
w_zbranch = add_primitive("0branch", do_zero_branch, F_HIDDEN);
w_branch = add_primitive("branch", do_branch, F_HIDDEN);
// Public primitives
// Stack ops
add_primitive("dup", do_dup, 0);
add_primitive("drop", do_drop, 0);
add_primitive("swap", do_swap, 0);
add_primitive("over", do_over, 0);
add_primitive("rot", do_rot, 0);
add_primitive("minus-rot", do_minus_rot, 0);
add_primitive("nip", do_nip, 0);
add_primitive("tuck", do_tuck, 0);
add_primitive("dup", do_dup, 0);
add_primitive("drop", do_drop, 0);
add_primitive("swap", do_swap, 0);
add_primitive("over", do_over, 0);
add_primitive("rot", do_rot, 0);
add_primitive("-rot", do_minus_rot, 0);
add_primitive("nip", do_nip, 0);
add_primitive("tuck", do_tuck, 0);
// Arithmetic
add_primitive("+", do_add, 0);
add_primitive("-", do_sub, 0);
add_primitive("*", do_mul, 0);
add_primitive("/", do_div, 0);
add_primitive("+", do_add, 0);
add_primitive("-", do_sub, 0);
add_primitive("*", do_mul, 0);
add_primitive("/", do_div, 0);
add_primitive("mod", do_mod, 0);
add_primitive("/mod", do_slash_mod, 0);
add_primitive("1+", do_one_plus, 0);
@@ -33,23 +34,23 @@ int main(void) {
add_primitive("2+", do_two_plus, 0);
add_primitive("2-", do_two_minus, 0);
add_primitive("negate", do_negate, 0);
add_primitive("abs", do_abs, 0);
add_primitive("min", do_min, 0);
add_primitive("max", do_max, 0);
add_primitive("abs", do_abs, 0);
add_primitive("min", do_min, 0);
add_primitive("max", do_max, 0);
// Logic
add_primitive("and", do_and, 0);
add_primitive("or", do_or, 0);
add_primitive("xor", do_xor, 0);
add_primitive("and", do_and, 0);
add_primitive("or", do_or, 0);
add_primitive("xor", do_xor, 0);
add_primitive("invert", do_invert, 0);
add_primitive("lshift", do_lshift, 0);
add_primitive("rshift", do_rshift, 0);
// Comparison
add_primitive("=", do_eq, 0);
add_primitive("=", do_eq, 0);
add_primitive("<>", do_neq, 0);
add_primitive("<", do_lt, 0);
add_primitive(">", do_gt, 0);
add_primitive("<", do_lt, 0);
add_primitive(">", do_gt, 0);
add_primitive("<=", do_lte, 0);
add_primitive(">=", do_gte, 0);
add_primitive("0=", do_zero_eq, 0);
@@ -57,23 +58,32 @@ int main(void) {
add_primitive("0>", do_zero_gt, 0);
// I/O
add_primitive(".", do_dot, 0);
add_primitive("cr", do_cr, 0);
add_primitive("emit", do_emit, 0);
add_primitive("key", do_key, 0);
add_primitive(".\"", do_dot_quote, 1 << 7); // Immediate
add_primitive(".", do_dot, 0);
add_primitive("cr", do_cr, 0);
add_primitive("emit", do_emit, 0);
add_primitive("key", do_key, 0);
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);
add_primitive("@", do_fetch, 0);
add_primitive("!", do_store, 0);
add_primitive("+!", do_plus_store, 0);
add_primitive("c@", do_cfetch, 0);
add_primitive("c!", do_cstore, 0);
add_primitive("variable", do_variable, 0);
add_primitive("constant", do_constant, 0);
add_primitive("here", do_here, 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);
@@ -81,17 +91,25 @@ int main(void) {
add_primitive("r@", do_r_fetch, 0);
// Compilation / control flow
add_primitive(":", do_colon, 0);
add_primitive(";", do_semicolon, 1 << 7); // Immediate
add_primitive("if", do_if, 1 << 7); // Immediate
add_primitive("else", do_else, 1 << 7); // Immediate
add_primitive("then", do_then, 1 << 7); // Immediate
add_primitive("begin", do_begin, 1 << 7); // Immediate
add_primitive("until", do_until, 1 << 7); // Immediate
add_primitive("while", do_while, 1 << 7); // Immediate
add_primitive("repeat", do_repeat, 1 << 7); // Immediate
add_primitive(":", do_colon, 0);
add_primitive(";", do_semicolon, F_IMMEDIATE);
add_primitive("if", do_if, F_IMMEDIATE);
add_primitive("else", do_else, F_IMMEDIATE);
add_primitive("then", do_then, F_IMMEDIATE);
add_primitive("begin", do_begin, F_IMMEDIATE);
add_primitive("until", do_until, F_IMMEDIATE);
add_primitive("while", do_while, F_IMMEDIATE);
add_primitive("repeat", do_repeat, F_IMMEDIATE);
/* Additional words */
add_primitive("depth", do_depth, 0);
add_primitive("pick", do_pick, 0);
add_primitive("roll", do_roll, 0);
add_primitive("?dup", do_qdup, 0);
add_primitive("2dup", do_2dup, 0);
add_primitive("2drop", do_2drop, 0);
add_primitive("2swap", do_2swap, 0);
// 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();
}