Compare commits
28 Commits
a882bf6888
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
b50883b3ed
|
|||
|
1d16c23fc5
|
|||
|
29ad4c2835
|
|||
|
d8fad1765e
|
|||
|
d73ace71fb
|
|||
|
9c242187ea
|
|||
|
75ddd75ab9
|
|||
|
47b6a1eb82
|
|||
|
53b4cff56a
|
|||
|
278a524bbb
|
|||
|
efcc54ed4b
|
|||
|
0d49827035
|
|||
|
fcb533ff24
|
|||
|
23451c8e85
|
|||
|
fcbc810db8
|
|||
|
dbf4eb5d0e
|
|||
|
19c8608c76
|
|||
|
1c0c6daf4f
|
|||
|
3c292cfc18
|
|||
|
dcc88bc8ca
|
|||
|
f033187c6c
|
|||
|
b115744991
|
|||
|
bf3c15ec27
|
|||
|
46e43961fd
|
|||
|
5916a92a4f
|
|||
|
a0b535f0a3
|
|||
|
145035fb67
|
|||
|
afd0a80dcb
|
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 word’s 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
@@ -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
@@ -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
@@ -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");
|
||||
}
|
||||
|
||||
+490
-554
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
Reference in New Issue
Block a user