diff --git a/forth.h b/forth.h index 56b6ab6..608cbbc 100644 --- a/forth.h +++ b/forth.h @@ -7,7 +7,6 @@ #include #include #include -#include // Configuration (all hard limits removed) #define MAX_NAME_LEN 31 @@ -88,8 +87,11 @@ void inner_interpreter(void); void process_token(const char* token); void outer_interpreter(void); -// Ensure compile_buf has at least 'needed' free cells -void ensure_compile_cap(int32_t needed); +// 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); diff --git a/forth_core.c b/forth_core.c index a7adc58..954c0b9 100644 --- a/forth_core.c +++ b/forth_core.c @@ -1,34 +1,38 @@ #include "forth.h" -// Dynamic storage -int64_t *data_stack = NULL; +// Static storage for bare-metal portability +static int64_t data_stack_storage[FORTH_DATA_STACK_SIZE]; +int64_t *data_stack = data_stack_storage; int32_t data_sp = -1; -int32_t data_cap = 0; +int32_t data_cap = FORTH_DATA_STACK_SIZE; -Cell *ret_stack = NULL; +static Cell ret_stack_storage[FORTH_RET_STACK_SIZE]; +Cell *ret_stack = ret_stack_storage; int32_t rp = -1; -int32_t ret_cap = 0; +int32_t ret_cap = FORTH_RET_STACK_SIZE; Cell* ip = NULL; Word* dict_head = NULL; -int state = 0; -Cell *compile_buf = NULL; +static Cell compile_buf_storage[FORTH_COMPILE_BUF_SIZE]; +Cell *compile_buf = compile_buf_storage; int32_t compile_idx = 0; -int32_t compile_cap = 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; -int64_t *compile_stack = NULL; +static int64_t compile_stack_storage[FORTH_COMPILE_STACK_SIZE]; +int64_t *compile_stack = compile_stack_storage; int32_t compile_sp = -1; -int32_t compile_stack_cap = 0; +int32_t compile_stack_cap = FORTH_COMPILE_STACK_SIZE; -Cell *user_mem = NULL; -int64_t user_mem_size = 0; -Cell* here = NULL; +static Cell user_mem_storage[FORTH_USER_MEM_CELLS]; +Cell *user_mem = user_mem_storage; +int64_t user_mem_size = FORTH_USER_MEM_CELLS; +Cell* here = user_mem_storage; Word* w_exit = NULL; Word* w_docolon = NULL; @@ -37,17 +41,35 @@ 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++]; +} + +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) { - int32_t new_cap = data_cap ? data_cap * 2 : 128; - int64_t *tmp = realloc(data_stack, new_cap * sizeof(int64_t)); - if (!tmp) { - fprintf(stderr, "Out of memory\n"); - exit(1); - } - data_stack = tmp; - data_cap = new_cap; + forth_printf("Data stack overflow\n"); + return; } data_stack[++data_sp] = val; } @@ -61,69 +83,52 @@ int64_t data_pop(void) { } // ---------- Return stack (holds Cell values) ---------- -static void ensure_ret_stack(void) { - if (rp + 1 >= ret_cap) { - int32_t new_cap = ret_cap ? ret_cap * 2 : 128; - Cell *tmp = realloc(ret_stack, new_cap * sizeof(Cell)); - if (!tmp) { - fprintf(stderr, "Out of memory\n"); - exit(1); - } - ret_stack = tmp; - ret_cap = new_cap; - } -} - void ret_push_ip(Cell* val) { - ensure_ret_stack(); + 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) { - fprintf(stderr, "Return stack underflow\n"); + forth_printf("Return stack underflow\n"); return NULL; } return ret_stack[rp--].ptr; } void ret_push_num(int64_t val) { - ensure_ret_stack(); + if (rp + 1 >= ret_cap) { + forth_printf("Return stack overflow\n"); + return; + } rp++; ret_stack[rp].num = val; } int64_t ret_pop_num(void) { if (rp < 0) { - fprintf(stderr, "Return stack underflow (num)\n"); + forth_printf("Return stack underflow (num)\n"); return 0; } return ret_stack[rp--].num; } // ---------- Compile stack (indices) ---------- -static void ensure_compile_stack(void) { - if (compile_sp + 1 >= compile_stack_cap) { - int32_t new_cap = compile_stack_cap ? compile_stack_cap * 2 : 64; - int64_t *tmp = realloc(compile_stack, new_cap * sizeof(int64_t)); - if (!tmp) { - fprintf(stderr, "Out of memory\n"); - exit(1); - } - compile_stack = tmp; - compile_stack_cap = new_cap; - } -} - void compile_push(int64_t idx) { - ensure_compile_stack(); + if (compile_sp + 1 >= compile_stack_cap) { + forth_printf("Compile stack overflow\n"); + return; + } compile_stack[++compile_sp] = idx; } int64_t compile_pop(void) { if (compile_sp < 0) { - fprintf(stderr, "Compile stack underflow\n"); + forth_printf("Compile stack underflow\n"); return -1; } return compile_stack[compile_sp--]; diff --git a/forth_dict.c b/forth_dict.c index bf70764..22f3e40 100644 --- a/forth_dict.c +++ b/forth_dict.c @@ -1,11 +1,7 @@ #include "forth.h" Word* add_primitive(const char* name, void (*code)(Word*), uint8_t flags) { - Word* w = malloc(sizeof(Word)); - if (!w) { - fprintf(stderr, "Out of memory\n"); - exit(1); - } + Word* w = forth_alloc_word(); w->prev = dict_head; dict_head = w; diff --git a/forth_interp.c b/forth_interp.c index 980f0ec..d582f4c 100644 --- a/forth_interp.c +++ b/forth_interp.c @@ -19,17 +19,12 @@ char* next_token(void) { } // Ensure compile_buf has room for at least 'needed' more cells -void ensure_compile_cap(int32_t needed) { - while (compile_idx + needed > compile_cap) { - int32_t new_cap = compile_cap ? compile_cap * 2 : 256; - Cell *tmp = realloc(compile_buf, new_cap * sizeof(Cell)); - if (!tmp) { - fprintf(stderr, "Out of memory\n"); - exit(1); - } - compile_buf = tmp; - compile_cap = new_cap; +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) @@ -62,7 +57,7 @@ void process_token(const char* token) { w->code(w); } } else { // Compile normal word - ensure_compile_cap(1); + if (!ensure_compile_cap(1)) return; compile_buf[compile_idx++] = (Cell){.word = w}; } } @@ -74,27 +69,30 @@ void process_token(const char* token) { data_push((int64_t)v); } else { // Compile lit + number if (!w_lit) { - fprintf(stderr, "Fatal: lit word not found\n"); + forth_printf("Fatal: lit word not found\n"); return; } - ensure_compile_cap(2); + 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); - ssize_t n = getline(&input_buf, &input_buf_cap, stdin); - if (n < 0) { + forth_printf("ok "); + forth_fflush(); + if (fgets(line_buf, sizeof(line_buf), stdin) == NULL) { break; } + input_buf = line_buf; + input_buf_cap = sizeof(line_buf); + size_t n = strlen(input_buf); if (n > 0 && input_buf[n - 1] == '\n') { input_buf[n - 1] = '\0'; } @@ -104,5 +102,5 @@ void outer_interpreter(void) { process_token(tok); } } - printf("\n"); + forth_printf("\n"); } diff --git a/forth_words.c b/forth_words.c index 3b7058c..cac73f1 100644 --- a/forth_words.c +++ b/forth_words.c @@ -98,7 +98,7 @@ void do_div(Word* w) { int64_t b = data_pop(); int64_t a = data_pop(); if (b == 0) { - fprintf(stderr, "Division by zero\n"); + forth_printf("Division by zero\n"); data_push(a); data_push(b); return; @@ -112,7 +112,7 @@ void do_mod(Word* w) { int64_t b = data_pop(); int64_t a = data_pop(); if (b == 0) { - fprintf(stderr, "Modulo by zero\n"); + forth_printf("Modulo by zero\n"); data_push(a); data_push(b); return; @@ -126,7 +126,7 @@ void do_slash_mod(Word* w) { int64_t b = data_pop(); int64_t a = data_pop(); if (b == 0) { - fprintf(stderr, "Modulo by zero\n"); + forth_printf("Modulo by zero\n"); data_push(a); data_push(b); return; @@ -309,26 +309,26 @@ void do_zero_gt(Word* w) { void do_dot(Word* w) { (void)w; if (data_sp < 0) return; - printf("%" PRId64 " ", data_pop()); - fflush(stdout); + forth_printf("%" PRId64 " ", data_pop()); + forth_fflush(); } void do_cr(Word* w) { (void)w; - printf("\n"); - fflush(stdout); + forth_printf("\n"); + forth_fflush(); } void do_emit(Word* w) { (void)w; if (data_sp < 0) return; - putchar((char)data_pop()); - fflush(stdout); + forth_putchar((char)data_pop()); + forth_fflush(); } void do_key(Word* w) { (void)w; - int c = getchar(); + int c = forth_getchar(); data_push(c == EOF ? -1 : c); } @@ -336,28 +336,28 @@ void do_dot_quote(Word* w) { (void)w; if (state == 0) { // Interpret mode: print immediately - if (input_ptr == NULL) { fprintf(stderr, "Missing string\n"); return; } + if (input_ptr == NULL) { forth_printf("Missing string\n"); return; } while (*input_ptr && isspace((unsigned char)*input_ptr)) input_ptr++; - if (*input_ptr != '"') { fprintf(stderr, "Expected \" to start string\n"); return; } + if (*input_ptr != '"') { forth_printf("Expected \" to start string\n"); return; } input_ptr++; char* start = input_ptr; while (*input_ptr && *input_ptr != '"') input_ptr++; - if (*input_ptr != '"') { fprintf(stderr, "Unterminated string\n"); return; } - while (start < input_ptr) putchar(*start++); + if (*input_ptr != '"') { forth_printf("Unterminated string\n"); return; } + while (start < input_ptr) forth_putchar(*start++); input_ptr++; - fflush(stdout); + forth_fflush(); } else { // Compile mode: compile string for runtime - if (input_ptr == NULL) { fprintf(stderr, "Missing string\n"); return; } + if (input_ptr == NULL) { forth_printf("Missing string\n"); return; } while (*input_ptr && isspace((unsigned char)*input_ptr)) input_ptr++; - if (*input_ptr != '"') { fprintf(stderr, "Expected \" to start string\n"); return; } + if (*input_ptr != '"') { forth_printf("Expected \" to start string\n"); return; } input_ptr++; char* start = input_ptr; while (*input_ptr && *input_ptr != '"') input_ptr++; - if (*input_ptr != '"') { fprintf(stderr, "Unterminated string\n"); return; } + if (*input_ptr != '"') { forth_printf("Unterminated string\n"); return; } size_t len = input_ptr - start; - if (!w_dot_quote_inner) { fprintf(stderr, "Fatal: do_dot_quote_inner not found\n"); return; } - ensure_compile_cap(2 + (int32_t)len); + if (!w_dot_quote_inner) { forth_printf("Fatal: do_dot_quote_inner not found\n"); return; } + if (!ensure_compile_cap(2 + (int32_t)len)) return; compile_buf[compile_idx++] = (Cell){.word = w_dot_quote_inner}; compile_buf[compile_idx++] = (Cell){.num = (int64_t)len}; for (size_t i = 0; i < len; i++) { @@ -372,21 +372,21 @@ void do_dot_quote_inner(Word* w) { int64_t len = ip->num; ip++; for (int64_t i = 0; i < len; i++) { - putchar((char)ip->num); + forth_putchar((char)ip->num); ip++; } - fflush(stdout); + forth_fflush(); } void do_words(Word* w) { (void)w; - printf("Dictionary words:\n"); + forth_printf("Dictionary words:\n"); for (Word* cur = dict_head; cur != NULL; cur = cur->prev) { if (cur->flags & (1 << 6)) continue; - printf("%s ", cur->name); + forth_printf("%s ", cur->name); } - printf("\n"); - fflush(stdout); + forth_printf("\n"); + forth_fflush(); } // Memory operations @@ -394,7 +394,7 @@ void do_fetch(Word* w) { (void)w; int64_t addr = data_pop(); if (addr < 0 || addr >= user_mem_size) { - fprintf(stderr, "Address out of bounds\n"); + forth_printf("Address out of bounds\n"); return; } data_push(user_mem[addr].num); @@ -405,7 +405,7 @@ void do_store(Word* w) { int64_t addr = data_pop(); int64_t val = data_pop(); if (addr < 0 || addr >= user_mem_size) { - fprintf(stderr, "Address out of bounds\n"); + forth_printf("Address out of bounds\n"); return; } user_mem[addr].num = val; @@ -416,7 +416,7 @@ void do_plus_store(Word* w) { int64_t addr = data_pop(); int64_t val = data_pop(); if (addr < 0 || addr >= user_mem_size) { - fprintf(stderr, "Address out of bounds\n"); + forth_printf("Address out of bounds\n"); return; } user_mem[addr].num += val; @@ -427,7 +427,7 @@ void do_cfetch(Word* w) { int64_t addr = data_pop(); // byte offset int64_t max_byte = user_mem_size * (int64_t)sizeof(Cell); if (addr < 0 || addr >= max_byte) { - fprintf(stderr, "Address out of bounds\n"); + forth_printf("Address out of bounds\n"); return; } uint8_t* base = (uint8_t*)user_mem; @@ -440,7 +440,7 @@ void do_cstore(Word* w) { int64_t val = data_pop(); int64_t max_byte = user_mem_size * (int64_t)sizeof(Cell); if (addr < 0 || addr >= max_byte) { - fprintf(stderr, "Address out of bounds\n"); + forth_printf("Address out of bounds\n"); return; } uint8_t* base = (uint8_t*)user_mem; @@ -457,7 +457,7 @@ void do_allot(Word* w) { (void)w; int64_t n = data_pop(); if (here + n > user_mem + user_mem_size) { - fprintf(stderr, "User memory overflow\n"); + forth_printf("User memory overflow\n"); return; } here += n; @@ -467,11 +467,11 @@ void do_allot(Word* w) { void do_variable(Word* w) { (void)w; char* name = next_token(); - if (!name) { fprintf(stderr, "VARIABLE expects a name\n"); return; } + if (!name) { forth_printf("VARIABLE expects a name\n"); return; } // allocate one cell in user memory for the variable's data if (here + 1 > user_mem + user_mem_size) { - fprintf(stderr, "User memory overflow\n"); + forth_printf("User memory overflow\n"); return; } Cell* var_cell = here; // address of the data cell @@ -479,8 +479,7 @@ void do_variable(Word* w) { here++; // create dictionary entry - Word* new_w = malloc(sizeof(Word)); - if (!new_w) { printf("Out of memory\n"); exit(1); } + Word* new_w = forth_alloc_word(); new_w->prev = dict_head; dict_head = new_w; @@ -497,11 +496,11 @@ void do_constant(Word* w) { (void)w; int64_t val = data_pop(); char* name = next_token(); - if (!name) { fprintf(stderr, "CONSTANT expects a name\n"); data_push(val); return; } + if (!name) { forth_printf("CONSTANT expects a name\n"); data_push(val); return; } // allocate a cell in user memory to hold the constant value if (here + 1 > user_mem + user_mem_size) { - fprintf(stderr, "User memory overflow\n"); + forth_printf("User memory overflow\n"); data_push(val); // restore the value (optional) return; } @@ -509,8 +508,7 @@ void do_constant(Word* w) { val_cell->num = val; here++; - Word* new_w = malloc(sizeof(Word)); - if (!new_w) { printf("Out of memory\n"); exit(1); } + Word* new_w = forth_alloc_word(); new_w->prev = dict_head; dict_head = new_w; @@ -547,7 +545,7 @@ void do_r_from(Word* w) { void do_r_fetch(Word* w) { (void)w; - if (rp < 0) { fprintf(stderr, "Return stack underflow\n"); return; } + if (rp < 0) { forth_printf("Return stack underflow\n"); return; } data_push(ret_stack[rp].num); } @@ -572,7 +570,7 @@ void do_lit(Word* w) { void do_colon(Word* w) { (void)w; char* name = next_token(); - if (!name) { fprintf(stderr, "':' expects a name\n"); return; } + if (!name) { forth_printf("':' expects a name\n"); return; } size_t len = strlen(name); if (len > MAX_NAME_LEN) len = MAX_NAME_LEN; memcpy(compiling_name, name, len); @@ -584,20 +582,20 @@ void do_colon(Word* w) { void do_semicolon(Word* w) { (void)w; - if (state != 1) { fprintf(stderr, "';' only valid in compile mode\n"); return; } - if (!w_exit) { fprintf(stderr, "Fatal: exit word not found\n"); return; } + if (state != 1) { forth_printf("';' only valid in compile mode\n"); return; } + if (!w_exit) { forth_printf("Fatal: exit word not found\n"); return; } - ensure_compile_cap(1); + if (!ensure_compile_cap(1)) return; compile_buf[compile_idx++] = (Cell){.word = w_exit}; // Create body copy of compiled cells - Cell* body_copy = malloc(compile_idx * sizeof(Cell)); - if (!body_copy) { printf("Out of memory\n"); exit(1); } + Cell* body_copy = forth_alloc_body(compile_idx); + if (!body_copy) { forth_printf("Out of memory\n"); return; } memcpy(body_copy, compile_buf, compile_idx * sizeof(Cell)); // Create new word entry - Word* new_w = malloc(sizeof(Word)); - if (!new_w) { printf("Out of memory\n"); free(body_copy); exit(1); } + Word* new_w = forth_alloc_word(); + if (!new_w) { forth_printf("Out of memory\n"); return; } new_w->prev = dict_head; dict_head = new_w; @@ -632,9 +630,9 @@ void do_zero_branch(Word* w) { // Control flow using compile stack (indices) void do_if(Word* w) { (void)w; - if (state != 1) { fprintf(stderr, "IF only valid in compile mode\n"); return; } - if (!w_zbranch) { fprintf(stderr, "Fatal: 0branch not found\n"); return; } - ensure_compile_cap(2); + if (state != 1) { forth_printf("IF only valid in compile mode\n"); return; } + if (!w_zbranch) { forth_printf("Fatal: 0branch not found\n"); return; } + if (!ensure_compile_cap(2)) return; compile_buf[compile_idx++] = (Cell){.word = w_zbranch}; // compile_push current index (where the offset will be placed) compile_push(compile_idx); @@ -643,7 +641,7 @@ void do_if(Word* w) { void do_then(Word* w) { (void)w; - if (state != 1) { fprintf(stderr, "THEN only valid in compile mode\n"); return; } + if (state != 1) { forth_printf("THEN only valid in compile mode\n"); return; } int64_t offset_idx = compile_pop(); if (offset_idx < 0) return; compile_buf[offset_idx].num = compile_idx - offset_idx; @@ -651,12 +649,12 @@ void do_then(Word* w) { void do_else(Word* w) { (void)w; - if (state != 1) { fprintf(stderr, "ELSE only valid in compile mode\n"); return; } + if (state != 1) { forth_printf("ELSE only valid in compile mode\n"); return; } int64_t if_offset_idx = compile_pop(); if (if_offset_idx < 0) return; - if (!w_branch) { fprintf(stderr, "Fatal: branch not found\n"); return; } - ensure_compile_cap(2); + if (!w_branch) { forth_printf("Fatal: branch not found\n"); return; } + if (!ensure_compile_cap(2)) return; // resolve IF offset to skip the ELSE branch compile_buf[if_offset_idx].num = (compile_idx + 2) - if_offset_idx; @@ -669,30 +667,30 @@ void do_else(Word* w) { void do_begin(Word* w) { (void)w; - if (state != 1) { fprintf(stderr, "BEGIN only valid in compile mode\n"); return; } + if (state != 1) { forth_printf("BEGIN only valid in compile mode\n"); return; } compile_push(compile_idx); } void do_until(Word* w) { (void)w; - if (state != 1) { fprintf(stderr, "UNTIL only valid in compile mode\n"); return; } + if (state != 1) { forth_printf("UNTIL only valid in compile mode\n"); return; } int64_t begin_idx = compile_pop(); if (begin_idx < 0) return; - if (!w_zbranch) { fprintf(stderr, "Fatal: 0branch not found\n"); return; } - ensure_compile_cap(2); + if (!w_zbranch) { forth_printf("Fatal: 0branch not found\n"); return; } + if (!ensure_compile_cap(2)) return; compile_buf[compile_idx++] = (Cell){.word = w_zbranch}; compile_buf[compile_idx++] = (Cell){.num = begin_idx - compile_idx}; } void do_while(Word* w) { (void)w; - if (state != 1) { fprintf(stderr, "WHILE only valid in compile mode\n"); return; } + if (state != 1) { forth_printf("WHILE only valid in compile mode\n"); return; } int64_t begin_idx = compile_pop(); if (begin_idx < 0) return; - if (!w_zbranch) { fprintf(stderr, "Fatal: 0branch not found\n"); return; } - ensure_compile_cap(2); + if (!w_zbranch) { forth_printf("Fatal: 0branch not found\n"); return; } + if (!ensure_compile_cap(2)) return; compile_buf[compile_idx++] = (Cell){.word = w_zbranch}; int64_t while_offset_idx = compile_idx; compile_idx++; // reserve offset @@ -703,14 +701,14 @@ void do_while(Word* w) { void do_repeat(Word* w) { (void)w; - if (state != 1) { fprintf(stderr, "REPEAT only valid in compile mode\n"); return; } + if (state != 1) { forth_printf("REPEAT only valid in compile mode\n"); return; } int64_t begin_idx = compile_pop(); if (begin_idx < 0) return; int64_t while_offset_idx = compile_pop(); if (while_offset_idx < 0) return; - if (!w_branch) { fprintf(stderr, "Fatal: branch not found\n"); return; } - ensure_compile_cap(2); + if (!w_branch) { forth_printf("Fatal: branch not found\n"); return; } + if (!ensure_compile_cap(2)) return; compile_buf[compile_idx++] = (Cell){.word = w_branch}; compile_buf[compile_idx++] = (Cell){.num = begin_idx - compile_idx}; diff --git a/main.c b/main.c index 13cfae6..72a14bf 100644 --- a/main.c +++ b/main.c @@ -1,13 +1,6 @@ #include "forth.h" int main(void) { - // Allocate user memory - user_mem_size = 1024 * 1024; // 1 Mega cells - user_mem = calloc((size_t)user_mem_size, sizeof(Cell)); - if (!user_mem) { - fprintf(stderr, "Failed to allocate user memory\n"); - return 1; - } here = user_mem; // Hidden words first