Files
LithosAnanke/v3/src/word_source/defer_words.c
T
rajamesandJunie a8b70e88d3 Reorganize source tree: kernel/, v3/, v4/ split and board infrastructure
Source tree reorganization:
- Move StarForth v3 engine to v3/ (src/, include/, Makefile)
- Move kernel to kernel/ (src/, include/, linker/, Makefile)
- Create v4/ skeleton for F18-ISA golden model (DECOMPOSITION.md, JUSTIFICATION.md)
- Move FABRIC-0..4.md to docs/fabric/
- Move ONTOLOGY.md and ROADMAP.md to docs/

Board infrastructure:
- Add boards/ser5/, boards/raspi/, boards/milkv/, boards/zynq7020/
- Each board has board.mk (ISA, CPU flags, boot recipe) and README.md
- Root Makefile becomes thin dispatcher: boot_image, all, clean, docs take TARGET
- make boot_image TARGET=SER5|RASPI|MILKV builds one GPT/MBR image per board
- ZYNQ7020 target exists but stops with clear error (ARMv7 port not built yet)
- scripts/mkdiskimage.sh builds disk images for all boards

Docs pipeline:
- docs/book/ with LaTeX master (main.tex) and Makefile
- pandoc converts Markdown to LaTeX at build time
- Two Lua filters: table-widths.lua (wide tables wrap), code-breaks.lua (inline code breaks)
- make docs builds single PDF (754 pages, 0 missing characters)
- make docs TARGET=<board> adds board appendix
- build/docs/<book|board>/meta.tex stamps git commit into PDF

Bug fixes:
- 42 include paths that only worked by accident now use correct relative paths
- clang-18 hardcode replaced with configurable CC variable (fixed aarch64 build)
- Pi 5: kernel_2712.img linked at 0x80000, .bss zeroed, memory reserved
- Doxyfile, .clang-tidy, README.md, Kconfig paths updated

Verified:
- Hosted v3 build passes 1012 tests, 0 failures
- SER5 image boots in QEMU (OVMF), POST passes, K exact (65536 = Q48_ONE)
- Milk-V image boots in QEMU (OpenSBI + U-Boot + bootefi), POST passes
- make clean TARGET=<board> removes only that board and its ISA objects
- make all builds all boards, hosted v3, and docs in one run

Co-authored-by: Junie <junie@jetbrains.com>
2026-10-01 15:40:09 -04:00

209 lines
6.5 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 2023–2025 Robert A. James
All rights reserved.
Licensed under the StarForth License, Version 1.0.
*/
/*
* DEFER / IS / DEFER@ primitives — late-binding deferred words.
*
* DEFER name ( -- ) Create a deferred word, data field = 0 (unset).
* IS name ( xt -- ) Store xt into the data field of a DEFER word.
* DEFER@ name ( -- xt ) Fetch the current action xt of a DEFER word.
*
* When a deferred word executes, defer_runtime reads its data field
* (a raw DictEntry*) and calls that entry's func directly. This is
* essential because the inner interpreter stores DictEntry* pointers
* in compiled bodies at compile time — redefining a colon word after
* compilation does not update callers. DEFER/IS gives late binding
* without any name-lookup overhead at execution time.
*
* IS enforces that the named word was created by DEFER (func ==
* defer_runtime). Attempting IS on any other word sets vm->error.
*/
#include "include/defer_words.h"
#include "../../include/vm.h"
#include "../../include/word_registry.h"
#include "../../include/log.h"
#include "../../include/dictionary_management.h"
#include <stdint.h>
/* ── defer_runtime ─────────────────────────────────────────────────────
* The func stored in every DEFER-created word.
* Data field = 0 means unset → error. Non-zero = DictEntry* to call.
*/
static void defer_runtime(VM *vm)
{
if (!vm || !vm->current_executing_entry) { if (vm) vm->error = 1; return; }
DictEntry *self = vm->current_executing_entry;
cell_t *df = vm_dictionary_get_data_field(self);
if (!df || *df == 0)
{
log_message(LOG_ERROR, "DEFER runtime: no target set for '%.*s'",
(int)self->name_len, self->name);
vm->error = 1;
return;
}
DictEntry *target = (DictEntry *)(uintptr_t)(*df);
if (!target || !target->func)
{
log_message(LOG_ERROR, "DEFER runtime: invalid target for '%.*s'",
(int)self->name_len, self->name);
vm->error = 1;
return;
}
vm->current_executing_entry = target;
target->func(vm);
}
/* ── DEFER ─────────────────────────────────────────────────────────────
* DEFER name ( -- )
* Creates a new dictionary entry with defer_runtime as its func.
* Data field initialised to 0 (unset). Calling the word before IS
* has been used will set vm->error.
*/
static void word_defer(VM *vm)
{
char name[WORD_NAME_MAX + 1];
int len = vm_parse_word(vm, name, sizeof(name));
if (len <= 0)
{
log_message(LOG_ERROR, "DEFER: missing name");
vm->error = 1;
return;
}
DictEntry *de = vm_create_word(vm, name, (size_t)len, defer_runtime);
if (!de)
{
log_message(LOG_ERROR, "DEFER: vm_create_word failed for '%.*s'", len, name);
vm->error = 1;
return;
}
cell_t *df = vm_dictionary_get_data_field(de);
if (!df)
{
log_message(LOG_ERROR, "DEFER: no data field for '%.*s'", len, name);
vm->error = 1;
return;
}
*df = 0; /* unset — defer_runtime will error if called before IS */
log_message(LOG_DEBUG, "DEFER: created '%.*s' (unset)", len, name);
}
/* ── IS ────────────────────────────────────────────────────────────────
* IS name ( xt -- )
* Pops an execution token (DictEntry*) from the stack, parses the next
* word from input, and stores the xt into that word's data field.
* The named word MUST have been created by DEFER; any other word causes
* an error. This prevents corrupting non-DEFER words' memory.
*/
static void word_is(VM *vm)
{
if (vm->dsp < 0)
{
log_message(LOG_ERROR, "IS: stack underflow");
vm->error = 1;
return;
}
cell_t xt = vm_pop(vm);
char name[WORD_NAME_MAX + 1];
int len = vm_parse_word(vm, name, sizeof(name));
if (len <= 0)
{
log_message(LOG_ERROR, "IS: missing name");
vm->error = 1;
return;
}
DictEntry *de = vm_find_word(vm, name, (size_t)len);
if (!de)
{
log_message(LOG_ERROR, "IS: word '%.*s' not found", len, name);
vm->error = 1;
return;
}
if (de->func != defer_runtime)
{
log_message(LOG_ERROR, "IS: '%.*s' was not created by DEFER", len, name);
vm->error = 1;
return;
}
cell_t *df = vm_dictionary_get_data_field(de);
if (!df)
{
log_message(LOG_ERROR, "IS: no data field for '%.*s'", len, name);
vm->error = 1;
return;
}
*df = xt;
log_message(LOG_DEBUG, "IS: stored xt into '%.*s'", len, name);
}
/* ── DEFER@ ────────────────────────────────────────────────────────────
* DEFER@ name ( -- xt )
* Parses the next word from input and pushes its current action xt.
* The named word must have been created by DEFER.
*/
static void word_defer_fetch(VM *vm)
{
char name[WORD_NAME_MAX + 1];
int len = vm_parse_word(vm, name, sizeof(name));
if (len <= 0)
{
log_message(LOG_ERROR, "DEFER@: missing name");
vm->error = 1;
return;
}
DictEntry *de = vm_find_word(vm, name, (size_t)len);
if (!de)
{
log_message(LOG_ERROR, "DEFER@: word '%.*s' not found", len, name);
vm->error = 1;
return;
}
if (de->func != defer_runtime)
{
log_message(LOG_ERROR, "DEFER@: '%.*s' was not created by DEFER", len, name);
vm->error = 1;
return;
}
cell_t *df = vm_dictionary_get_data_field(de);
if (!df)
{
log_message(LOG_ERROR, "DEFER@: no data field for '%.*s'", len, name);
vm->error = 1;
return;
}
vm_push(vm, *df);
log_message(LOG_DEBUG, "DEFER@: fetched xt from '%.*s'", len, name);
}
/* ── Registration ──────────────────────────────────────────────────────*/
void register_defer_words(VM *vm)
{
register_word(vm, "DEFER", word_defer);
register_word(vm, "IS", word_is);
register_word(vm, "DEFER@", word_defer_fetch);
}