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>
This commit is contained in:
rajames
2026-10-01 15:40:09 -04:00
co-authored by Junie
parent 3c709c115b
commit a8b70e88d3
630 changed files with 2646 additions and 2036 deletions
+205
View File
@@ -0,0 +1,205 @@
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 2023–2025 Robert A. James
All rights reserved.
This file is part of the StarForth project.
Licensed under the StarForth License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
express or implied, including but not limited to the warranties of
merchantability, fitness for a particular purpose, and noninfringement.
See the License for the specific language governing permissions and
limitations under the License.
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 2023–2025 Robert A. James
All rights reserved.
This file is part of the StarForth project.
Licensed under the StarForth License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
express or implied, including but not limited to the warranties of
merchantability, fitness for a particular purpose, and noninfringement.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "../include/vm.h"
#include "../include/vm_debug.h"
/* No POSIX signals in the kernel — provide stubs and skip the rest. */
#if defined(STARFORTH_MINIMAL)
void vm_debug_set_current_vm(VM *vm) { (void)vm; }
void vm_debug_dump_state(const VM *vm, const char *reason) { (void)vm; (void)reason; }
void vm_debug_install_signal_handlers(void) {}
#else
#include <signal.h>
#include <stdio.h>
#include <string.h>
static VM *g_vm = NULL;
/* @brief Sets the current VM instance for debug operations
* @param vm Pointer to the VM instance to be monitored
*/
void vm_debug_set_current_vm(VM *vm) { g_vm = vm; }
/* Safe BASE read (never touches vm->error). Adjust if you have a helper. */
/** @brief Safely retrieves the current number base from VM
* @param vm Pointer to the VM instance
* @return Current number base (2-36) or 10 if invalid
*/
static unsigned dbg_get_base(const VM *vm) {
if (!vm) return 10u;
size_t idx = (size_t) vm->base_addr;
if (idx < (size_t) VM_MEMORY_SIZE) {
cell_t v = vm->memory[idx];
if (v >= 2 && v <= 36) return (unsigned) v;
}
if (vm->base >= 2 && vm->base <= 36) return (unsigned) vm->base;
return 10u;
}
/** @brief Converts VM mode to string representation
* @param m The VM mode value
* @return String representing the mode
*/
static const char *mode_str(int m) {
switch (m) {
case MODE_INTERPRET: return "INTERPRET";
case MODE_COMPILE: return "COMPILE";
default: return "UNKNOWN";
}
}
/** @brief Dumps preview of a block's contents
* @param vm Pointer to the VM instance
* @param scr Block number to preview
*/
static void dump_block_preview(const VM *vm, cell_t scr) {
if (!vm) return;
if (scr < 1 || scr >= (cell_t) MAX_BLOCKS) return;
vaddr_t addr = (vaddr_t)((unsigned long long) scr * (unsigned long long) BLOCK_SIZE);
if (!vm_addr_ok((VM *) vm, addr, BLOCK_SIZE)) return;
const unsigned char *p = vm_ptr((VM *) vm, addr);
char line[81];
size_t n = 0;
while (n < 80 && n < BLOCK_SIZE) {
unsigned char ch = p[n];
if (ch == 0 || ch == '\n' || ch == '\r') break;
line[n++] = (ch < 32 || ch > 126) ? ' ' : (char) ch;
}
line[n] = '\0';
fprintf(stderr, " block[%ld]: \"%s\"%s\n", (long) scr, line, (n == 80 ? "..." : ""));
}
/* @brief Dumps the current state of the VM for debugging
* @param vm Pointer to the VM instance to dump
* @param reason Optional string describing the reason for the dump
*/
void vm_debug_dump_state(const VM *vm, const char *reason) {
if (!vm) {
fprintf(stderr, "===== VM STATE DUMP (no VM) [%s] =====\n", reason ? reason : "");
return;
}
unsigned base = dbg_get_base(vm);
cell_t scr = 0;
if ((size_t) vm->scr_addr < (size_t) VM_MEMORY_SIZE) scr = vm->memory[(size_t) vm->scr_addr];
fprintf(stderr, "\n===== VM STATE DUMP ===== [%s]\n", reason ? reason : "");
fprintf(stderr, " mode=%s error=%d\n", mode_str(vm->mode), vm->error);
fprintf(stderr, " HERE=%ld BASE=%u SCR=%ld\n", (long) vm->here, base, (long) scr);
fprintf(stderr, " dsp=%d rsp=%d\n", vm->dsp, vm->rsp);
/* If your VM struct exposes stacks, show top few items (guarded). */
#ifdef DATA_STACK_SIZE
if (vm->stack) {
fprintf(stderr, " DATA(top=%d):", vm->dsp);
int shown = 0;
for (int i = vm->dsp; i >= 0 && shown < 16; --i, ++shown)
fprintf(
stderr, " [%d]=%ld", i, (long) vm->stack[i]);
if (vm->dsp >= 16) fprintf(stderr, " ...");
fprintf(stderr, "\n");
}
#endif
#ifdef RETURN_STACK_SIZE
if (vm->rstack) {
fprintf(stderr, " RETURN(top=%d):", vm->rsp);
int shown = 0;
for (int i = vm->rsp; i >= 0 && shown < 16; --i, ++shown)
fprintf(
stderr, " [%d]=%ld", i, (long) vm->rstack[i]);
if (vm->rsp >= 16) fprintf(stderr, " ...");
fprintf(stderr, "\n");
}
#endif
/* If you track current word/ip, print them (guard with your flags/macros). */
#ifdef VM_HAS_CURRENT_ENTRY
if (vm->current_executing_entry) fprintf(stderr, " current=%s\n", vm->current_executing_entry->name);
#endif
#ifdef VM_HAS_IP
fprintf(stderr, " ip=%ld\n", (long) vm->ip);
#endif
dump_block_preview(vm, scr);
fprintf(stderr, "===== END VM STATE DUMP =====\n");
}
/** @brief Signal handler for crash debugging
* @param sig Signal number that was caught
*/
static void sig_handler(int sig) {
const char *name = (sig == SIGSEGV) ? "SIGSEGV" : (sig == SIGABRT) ? "SIGABRT" : "SIGNAL";
fprintf(stderr, "\n*** Caught %s ***\n", name);
if (g_vm) vm_debug_dump_state(g_vm, name);
signal(sig, SIG_DFL);
raise(sig);
}
/** @brief Installs signal handlers for crash debugging
* @details Sets up handlers for SIGSEGV and SIGABRT to dump VM state on crash
*/
void vm_debug_install_signal_handlers(void) {
#if defined(__unix__) || defined(__APPLE__)
/* Use sigaction when available */
#ifdef SA_RESETHAND
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = sig_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESETHAND; /* one-shot: reset to default after firing */
sigaction(SIGSEGV, &sa, NULL);
sigaction(SIGABRT, &sa, NULL);
#else
/* Fallback: plain signal() if SA_RESETHAND isn’t defined */
signal(SIGSEGV, sig_handler);
signal(SIGABRT, sig_handler);
#endif
#else
/* Non-POSIX platforms: best-effort */
signal(SIGSEGV, sig_handler);
signal(SIGABRT, sig_handler);
#endif
}
#endif /* !STARFORTH_MINIMAL */