#include "forth.h" // Stack operations void do_dup(Word* w) { (void)w; if (sp < 0) return; int32_t v = data_stack[sp]; data_push(v); } void do_drop(Word* w) { (void)w; data_pop(); } void do_swap(Word* w) { (void)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) { (void)w; if (sp < 1) return; data_push(data_stack[sp-1]); } void do_rot(Word* w) { (void)w; if (sp < 2) return; int32_t a = data_stack[sp-2]; int32_t b = data_stack[sp-1]; int32_t c = data_stack[sp]; data_stack[sp-2] = c; data_stack[sp-1] = a; data_stack[sp] = b; } void do_minus_rot(Word* w) { (void)w; if (sp < 2) return; int32_t a = data_stack[sp-2]; int32_t b = data_stack[sp-1]; int32_t c = data_stack[sp]; data_stack[sp-2] = b; data_stack[sp-1] = c; data_stack[sp] = a; } void do_nip(Word* w) { (void)w; if (sp < 1) return; data_stack[sp-1] = data_stack[sp]; sp--; } void do_tuck(Word* w) { (void)w; if (sp < 1) return; int32_t a = data_stack[sp-1]; int32_t b = data_stack[sp]; data_push(a); data_stack[sp-2] = b; } // Arithmetic operations void do_add(Word* w) { (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); data_push(a + b); } void do_sub(Word* w) { (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); data_push(a - b); } void do_mul(Word* w) { (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); data_push(a * b); } void do_div(Word* w) { (void)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_mod(Word* w) { (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); if (b == 0) { printf("Modulo by zero\n"); data_push(a); data_push(b); return; } data_push(a % b); } void do_slash_mod(Word* w) { (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); if (b == 0) { printf("Modulo by zero\n"); data_push(a); data_push(b); return; } data_push(a / b); data_push(a % b); } void do_one_plus(Word* w) { (void)w; if (sp < 0) return; data_stack[sp]++; } void do_one_minus(Word* w) { (void)w; if (sp < 0) return; data_stack[sp]--; } void do_two_plus(Word* w) { (void)w; if (sp < 0) return; data_stack[sp] += 2; } void do_two_minus(Word* w) { (void)w; if (sp < 0) return; data_stack[sp] -= 2; } void do_negate(Word* w) { (void)w; if (sp < 0) return; data_stack[sp] = -data_stack[sp]; } void do_abs(Word* w) { (void)w; if (sp < 0) return; int32_t v = data_pop(); data_push(v < 0 ? -v : v); } void do_min(Word* w) { (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); data_push(a < b ? a : b); } void do_max(Word* w) { (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); data_push(a > b ? a : b); } // Logic operations void do_and(Word* w) { (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); data_push(a & b); } void do_or(Word* w) { (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); data_push(a | b); } void do_xor(Word* w) { (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); data_push(a ^ b); } void do_invert(Word* w) { (void)w; if (sp < 0) return; data_stack[sp] = ~data_stack[sp]; } void do_lshift(Word* w) { (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); data_push(a << b); } void do_rshift(Word* w) { (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); data_push((int32_t)((uint32_t)a >> b)); } // Comparison operations void do_eq(Word* w) { (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); data_push(a == b ? -1 : 0); } void do_neq(Word* w) { (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); data_push(a != b ? -1 : 0); } void do_lt(Word* w) { (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); data_push(a < b ? -1 : 0); } void do_gt(Word* w) { (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); data_push(a > b ? -1 : 0); } void do_lte(Word* w) { (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); data_push(a <= b ? -1 : 0); } void do_gte(Word* w) { (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); data_push(a >= b ? -1 : 0); } void do_zero_eq(Word* w) { (void)w; if (sp < 0) return; int32_t a = data_pop(); data_push(a == 0 ? -1 : 0); } void do_zero_lt(Word* w) { (void)w; if (sp < 0) return; int32_t a = data_pop(); data_push(a < 0 ? -1 : 0); } void do_zero_gt(Word* w) { (void)w; if (sp < 0) return; int32_t a = data_pop(); data_push(a > 0 ? -1 : 0); } // I/O operations void do_dot(Word* w) { (void)w; if (sp < 0) return; printf("%d ", data_pop()); fflush(stdout); } void do_cr(Word* w) { (void)w; printf("\n"); fflush(stdout); } void do_emit(Word* w) { (void)w; if (sp < 0) return; putchar((char)data_pop()); fflush(stdout); } void do_key(Word* w) { (void)w; int c = getchar(); data_push(c == EOF ? -1 : c); } void do_dot_quote(Word* w) { (void)w; // Immediate word: parse string until " and print/compile if (state == 0) { // Interpret mode: print immediately if (input_ptr == NULL) { printf("Missing string\n"); return; } // Skip whitespace before opening " while (*input_ptr != '\0' && isspace((unsigned char)*input_ptr)) { input_ptr++; } if (*input_ptr != '"') { printf("Expected \" to start string\n"); return; } input_ptr++; // Skip opening " char* start = input_ptr; // Find closing " while (*input_ptr != '\0' && *input_ptr != '"') { input_ptr++; } if (*input_ptr != '"') { printf("Unterminated string\n"); return; } // Print the string while (start < input_ptr) { putchar(*start++); } input_ptr++; // Skip closing " fflush(stdout); } else { // Compile mode: compile string for runtime if (input_ptr == NULL) { printf("Missing string\n"); return; } // Skip whitespace before opening " while (*input_ptr != '\0' && isspace((unsigned char)*input_ptr)) { input_ptr++; } if (*input_ptr != '"') { printf("Expected \" to start string\n"); return; } input_ptr++; // Skip opening " char* start = input_ptr; // Find closing " while (*input_ptr != '\0' && *input_ptr != '"') { input_ptr++; } if (*input_ptr != '"') { printf("Unterminated string\n"); return; } size_t len = input_ptr - start; // Compile do_dot_quote_inner Word* inner_w = lookup_word_internal("do_dot_quote_inner"); if (inner_w == NULL) { printf("Fatal: do_dot_quote_inner not found\n"); return; } if (compile_idx + 2 + (int32_t)len > COMPILE_BUF_SIZE) { printf("Compile buffer full\n"); return; } compile_buf[compile_idx++] = (Cell){.word = inner_w}; compile_buf[compile_idx++] = (Cell){.num = (int32_t)len}; // Store string characters in compile buffer (each as a num cell) for (size_t i = 0; i < len; i++) { compile_buf[compile_idx++] = (Cell){.num = (int32_t)start[i]}; } input_ptr++; // Skip closing " } } void do_dot_quote_inner(Word* w) { (void)w; // Runtime: ip points to length cell, followed by string characters int32_t len = ip->num; ip++; for (int32_t i = 0; i < len; i++) { putchar((char)ip->num); ip++; } fflush(stdout); } void do_words(Word* w) { (void)w; printf("Dictionary words:\n"); for (Word* cur = dict_head; cur != NULL; cur = cur->prev) { if (cur->flags & (1 << 6)) continue; // Skip hidden printf("%s ", cur->name); } printf("\n"); fflush(stdout); } // Control flow operations void do_exit(Word* w) { (void)w; Cell* ret_addr = ret_pop_ip(); ip = ret_addr; } void do_docolon(Word* w) { // Push current ip (return address) onto return stack ret_push_ip(ip); // Set ip to this word's body ip = w->body; } void do_lit(Word* w) { (void)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) { (void)w; char* name = next_token(); if (name == NULL) { 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); compiling_name[len] = '\0'; state = 1; // Enter compile mode compile_idx = 0; // Reset compile buffer } void do_semicolon(Word* w) { (void)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 memcpy(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 } void do_branch(Word* w) { (void)w; // Unconditional branch: ip points to offset cell int32_t offset = ip->num; ip += offset; // Jump offset cells (relative to offset cell itself) } void do_zero_branch(Word* w) { (void)w; // Conditional branch: if top of stack is 0, branch int32_t cond = data_pop(); if (cond == 0) { int32_t offset = ip->num; ip += offset; } else { ip++; // Skip offset cell } } // Memory operations void do_fetch(Word* w) { (void)w; int32_t addr = data_pop(); if (addr < 0 || addr >= USER_MEMORY_SIZE) { printf("Address out of bounds\n"); return; } data_push(user_mem[addr]); } void do_store(Word* w) { (void)w; int32_t val = data_pop(); int32_t addr = data_pop(); if (addr < 0 || addr >= USER_MEMORY_SIZE) { printf("Address out of bounds\n"); return; } user_mem[addr] = val; } void do_plus_store(Word* w) { (void)w; int32_t val = data_pop(); int32_t addr = data_pop(); if (addr < 0 || addr >= USER_MEMORY_SIZE) { printf("Address out of bounds\n"); return; } user_mem[addr] += val; } void do_cfetch(Word* w) { (void)w; int32_t addr = data_pop(); int32_t max_addr = USER_MEMORY_SIZE * (int32_t)sizeof(int32_t); if (addr < 0 || addr >= max_addr) { printf("Address out of bounds\n"); return; } int32_t cell = addr / (int32_t)sizeof(int32_t); int32_t byte = addr % (int32_t)sizeof(int32_t); uint8_t* p = (uint8_t*)&user_mem[cell]; data_push((int32_t)p[byte]); } void do_cstore(Word* w) { (void)w; int32_t val = data_pop(); int32_t addr = data_pop(); int32_t max_addr = USER_MEMORY_SIZE * (int32_t)sizeof(int32_t); if (addr < 0 || addr >= max_addr) { printf("Address out of bounds\n"); return; } int32_t cell = addr / (int32_t)sizeof(int32_t); int32_t byte = addr % (int32_t)sizeof(int32_t); uint8_t* p = (uint8_t*)&user_mem[cell]; p[byte] = (uint8_t)val; } void do_here(Word* w) { (void)w; data_push((int32_t)(here - user_mem)); } void do_allot(Word* w) { (void)w; int32_t n = data_pop(); here += n; } void do_variable(Word* w) { (void)w; char* name = next_token(); if (name == NULL) { printf("VARIABLE expects a name\n"); return; } if (dict_idx >= DICT_SIZE) { printf("Dictionary full\n"); return; } if (body_idx >= BODY_SIZE) { printf("Dictionary body storage full\n"); return; } Word* new_w = &dict[dict_idx++]; new_w->prev = dict_head; dict_head = new_w; size_t len = strlen(name); if (len > MAX_NAME_LEN) len = MAX_NAME_LEN; new_w->flags = (uint8_t)len; memcpy(new_w->name, name, len); new_w->name[len] = '\0'; new_w->code = do_do_var; // Allocate body cell to hold the address (cell index in user_mem) new_w->body = &dict_bodies[body_idx++]; int32_t addr = (int32_t)(here - user_mem); new_w->body->num = addr; // Allocate one cell in user_mem here++; } void do_constant(Word* w) { (void)w; int32_t val = data_pop(); char* name = next_token(); if (name == NULL) { printf("CONSTANT expects a name\n"); data_push(val); return; } if (dict_idx >= DICT_SIZE) { printf("Dictionary full\n"); return; } if (body_idx >= BODY_SIZE) { printf("Dictionary body storage full\n"); return; } Word* new_w = &dict[dict_idx++]; new_w->prev = dict_head; dict_head = new_w; size_t len = strlen(name); if (len > MAX_NAME_LEN) len = MAX_NAME_LEN; new_w->flags = (uint8_t)len; memcpy(new_w->name, name, len); new_w->name[len] = '\0'; new_w->code = do_do_const; // Allocate body cell to hold the constant value new_w->body = &dict_bodies[body_idx++]; new_w->body->num = val; } void do_do_var(Word* w) { data_push(w->body->num); // Push the user_mem cell index } void do_do_const(Word* w) { data_push(w->body->num); // Push the constant value } // Return stack operations void do_to_r(Word* w) { (void)w; int32_t val = data_pop(); ret_push_num(val); } void do_r_from(Word* w) { (void)w; int32_t val = ret_pop_num(); data_push(val); } void do_r_fetch(Word* w) { (void)w; if (rp < 0) { printf("Return stack underflow\n"); return; } data_push(ret_stack[rp].num); } // Control flow: IF, ELSE, THEN, BEGIN, UNTIL, WHILE, REPEAT void do_if(Word* w) { (void)w; if (state != 1) { printf("IF only valid in compile mode\n"); return; } Word* zbranch = lookup_word_internal("0branch"); if (zbranch == NULL) { printf("Fatal: 0branch not found\n"); return; } if (compile_idx + 2 > COMPILE_BUF_SIZE) { printf("Compile buffer full\n"); return; } compile_buf[compile_idx++] = (Cell){.word = zbranch}; compile_push((Cell*)&compile_buf[compile_idx]); compile_idx++; // Reserve offset cell } void do_then(Word* w) { (void)w; if (state != 1) { printf("THEN only valid in compile mode\n"); return; } Cell* offset_addr = compile_pop(); if (offset_addr == NULL) return; int32_t offset_idx = (int32_t)(offset_addr - compile_buf); int32_t target_idx = compile_idx; offset_addr->num = target_idx - offset_idx; } void do_else(Word* w) { (void)w; if (state != 1) { printf("ELSE only valid in compile mode\n"); return; } Cell* if_offset = compile_pop(); if (if_offset == NULL) return; Word* branch_w = lookup_word_internal("branch"); if (branch_w == NULL) { printf("Fatal: branch not found\n"); return; } if (compile_idx + 2 > COMPILE_BUF_SIZE) { printf("Compile buffer full\n"); return; } // Resolve IF offset to jump to after this branch int32_t if_offset_idx = (int32_t)(if_offset - compile_buf); int32_t target_idx = compile_idx + 2; // after branch + its offset if_offset->num = target_idx - if_offset_idx; // Compile unconditional branch for ELSE part compile_buf[compile_idx++] = (Cell){.word = branch_w}; compile_push((Cell*)&compile_buf[compile_idx]); compile_idx++; // Reserve offset cell } void do_begin(Word* w) { (void)w; if (state != 1) { printf("BEGIN only valid in compile mode\n"); return; } compile_push((Cell*)&compile_buf[compile_idx]); } void do_until(Word* w) { (void)w; if (state != 1) { printf("UNTIL only valid in compile mode\n"); return; } Cell* begin_addr = compile_pop(); if (begin_addr == NULL) return; Word* zbranch = lookup_word_internal("0branch"); if (zbranch == NULL) { printf("Fatal: 0branch not found\n"); return; } if (compile_idx + 2 > COMPILE_BUF_SIZE) { printf("Compile buffer full\n"); return; } int32_t begin_idx = (int32_t)(begin_addr - compile_buf); compile_buf[compile_idx++] = (Cell){.word = zbranch}; int32_t offset_idx = compile_idx; compile_buf[compile_idx++] = (Cell){.num = begin_idx - offset_idx}; } void do_while(Word* w) { (void)w; if (state != 1) { printf("WHILE only valid in compile mode\n"); return; } Cell* begin_addr = compile_pop(); if (begin_addr == NULL) return; Word* zbranch = lookup_word_internal("0branch"); if (zbranch == NULL) { printf("Fatal: 0branch not found\n"); return; } if (compile_idx + 2 > COMPILE_BUF_SIZE) { printf("Compile buffer full\n"); return; } compile_buf[compile_idx++] = (Cell){.word = zbranch}; Cell* while_offset = (Cell*)&compile_buf[compile_idx]; compile_idx++; // Reserve offset cell compile_push(while_offset); compile_push(begin_addr); } void do_repeat(Word* w) { (void)w; if (state != 1) { printf("REPEAT only valid in compile mode\n"); return; } Cell* begin_addr = compile_pop(); if (begin_addr == NULL) return; Cell* while_offset = compile_pop(); if (while_offset == NULL) return; Word* branch_w = lookup_word_internal("branch"); if (branch_w == NULL) { printf("Fatal: branch not found\n"); return; } if (compile_idx + 2 > COMPILE_BUF_SIZE) { printf("Compile buffer full\n"); return; } // Compile backward branch to begin int32_t begin_idx = (int32_t)(begin_addr - compile_buf); compile_buf[compile_idx++] = (Cell){.word = branch_w}; int32_t offset_idx = compile_idx; compile_buf[compile_idx++] = (Cell){.num = begin_idx - offset_idx}; // Resolve WHILE offset to point after this backward branch int32_t while_offset_idx = (int32_t)(while_offset - compile_buf); int32_t target_idx = compile_idx; while_offset->num = target_idx - while_offset_idx; }