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
+187
View File
@@ -0,0 +1,187 @@
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 2023–2025 Robert A. James
All rights reserved.
Licensed under the StarForth License, Version 1.0
*/
/**
* repl.h - Emergency FORTH REPL for LithosAnanke kernel
*/
#ifndef STARKERNEL_REPL_H
#define STARKERNEL_REPL_H
#include "vm.h"
#include "starkernel/homeblocks_sig.h"
struct blkio_dev;
#ifdef __cplusplus
extern "C" {
#endif
/**
* sk_repl - Run the emergency FORTH REPL on the serial console.
*
* Blocks until vm->halted is set (BYE word) or the VM encounters a halt.
* Runs with interrupts enabled; the APIC heartbeat continues to fire.
*
* @param vm Mama VM instance (must be fully initialised)
*/
void sk_repl(VM *vm);
/**
* sk_repl_run - Bare REPL loop (no banner).
*
* Same as sk_repl but skips the version/welcome banner. Used by START
* to enter a child VM's interpreter loop without reprinting the header.
*
* @param vm Fully initialised VM instance
*/
void sk_repl_run(VM *vm);
/**
* sk_repl_step - Execute one REPL turn on a VM and return.
*
* Prints the VM's prompt, reads one line, interprets it, prints ok/ERROR,
* then returns. Used by the Compudynamics VM-STEP primitive so Hera can
* give a single REPL quantum to a child VM without surrendering control
* for the full sk_repl_run() loop.
*
* @param vm Fully initialised VM instance
* @return 1 if the VM is still running, 0 if it halted during this turn
*/
int sk_repl_step(VM *vm);
/**
* sk_repl_set_active_vm - Redirect REPL input to a different VM (USE word).
*
* Pass NULL to restore default dispatch (Mama's VM).
* The change takes effect on the next REPL iteration.
*
* @param vm Target VM, or NULL for default
*/
void sk_repl_set_active_vm(VM *vm);
/**
* sk_repl_get_active_vm - Return the current USE-redirected VM, or NULL.
*/
VM *sk_repl_get_active_vm(void);
/**
* sk_repl_get_homeblocks_dev / sk_repl_get_homeblocks_sig - The currently
* attached home-blocks USB drive, or NULL if none is attached / the
* attached drive didn't check out as HOMEBLOCKS_SIG_OK (FABRIC-2.md
* §F.6/§F.9/§F.18). Both return NULL together; never one without the
* other.
*/
struct blkio_dev *sk_repl_get_homeblocks_dev(void);
const homeblocks_sig_t *sk_repl_get_homeblocks_sig(void);
/**
* sk_repl_get_attached_blk_dev - The currently attached USB block
* device, regardless of home-blocks recognition (FABRIC-2.md
* §F.8/§F.19) -- MINT's own target, since a blank/unminted drive never
* sets sk_repl_get_homeblocks_dev() above. NULL if nothing is attached.
*/
struct blkio_dev *sk_repl_get_attached_blk_dev(void);
/**
* sk_repl_register_words - Registers repl.c's own FORTH-visible words
* (currently just BLK-ATTACH-ACK, Artemis's storage-attach reply target --
* see sk_word_blk_attach_ack()'s doc comment in repl.c). Call once from
* register_forth79_words() alongside the other __STARKERNEL__-only
* registration calls.
*/
void sk_repl_register_words(VM *vm);
/**
* sk_console_getkey - Real body of the standard dictionary's KEY word
* (called from shim.c's getchar()). Blocks until a key is available from
* either input source (serial console or the PS2/virtio keyboard-event
* bridge), servicing the heartbeat/idle loop while waiting so a KEY call
* from inside any word never stalls the heartbeat. No echo -- that's the
* caller's responsibility, same as any standard KEY.
*
* @param active_vm VM whose idle dispatch runs while waiting (see
* sk_repl_idle()'s own doc comment on why this is a
* parameter rather than read via sk_repl_get_active_vm())
* @return the key read, as an unsigned byte value
*/
int sk_console_getkey(VM *active_vm);
/**
* sk_console_key_available - Real body of the standard dictionary's
* ?TERMINAL word (called from sf_terminal_ready()). Non-blocking peek:
* returns 1 if a key is ready without consuming it (a following
* sk_console_getkey() returns that exact key), 0 otherwise.
*/
int sk_console_key_available(void);
/**
* sk_console_readline - Real body of the standard dictionary's
* QUERY/EXPECT words (called from shim.c's fgets()). Reads one line from
* the console with echo and backspace support, servicing the heartbeat/
* idle loop while waiting -- the same line editor the REPL's own prompt
* uses internally, so a mid-word EXPECT behaves identically to typing at
* "ok>" itself.
*
* @param buf Destination buffer
* @param size Buffer capacity, including the NUL terminator
* @param active_vm VM whose idle dispatch runs while waiting
* @param reanchor_prompt Nonzero to re-print the "ok> " prompt whenever
* an idle bottom half (heartbeat, USB attach/detach) writes
* to the console while this readline blocks at a bare,
* untyped prompt -- keeps the top-level prompt as the last
* thing shown once the chatter dies down. Callers whose
* prompt line is their own (shim.c's fgets(), i.e.
* QUERY/EXPECT/ACCEPT) pass 0 so "ok> " never gets stamped
* onto their mid-word input context.
* @return number of characters placed in buf, not counting the NUL, or -1
* (2026-09-06) when reanchor_prompt is nonzero and the attached
* identity logged out while this call was blocked waiting for
* input with nothing typed yet -- see repl.c's own doc comment
* on this function for what a caller must do with -1.
*/
int sk_console_readline(char* buf, int size, VM* active_vm, int reanchor_prompt);
/**
* sk_repl_headless_wait - Idle-service loop with no interactive surface
* at all: no banner, no prompt, no console_getc()/readline. Runs
* heartbeat_service() and the same SK_IDLE_BEAT_INTERVAL-gated
* sk_repl_idle(mama) cadence sk_console_readline()'s own idle branch
* uses -- so USB/WIREBIND/Zuse-attach detection, the heartbeat, and all
* other idle-tick subsystems keep running -- until a real identity is
* currently attached (Zuse's own session, or a WIREBIND user), at which
* point it returns.
*
* Revised 2026-09-06: originally exited on a one-way sticky "has anyone
* ever logged in this boot" flag (sk_console_mark_login()/sk_console_
* login_occurred(), both retired) -- that let a real gap through, found
* live: once the flag tripped once, it never reset, so a later full
* logout (nobody attached at all) fell through to a bare, unauthenticated
* prompt instead of going silent again. This now checks live attach
* state instead (repl.c's own sk_console_identity_present()), and is
* called from two places: once from kernel_main.c in place of an
* immediate sk_repl(mama) call when EMERGENCY_CONSOLE_ENABLED is off (the
* default, 2026-09-05) -- no thumbdrive, no prompt, at boot -- and again
* from inside sk_repl_run()'s own main loop, every time nobody is
* currently attached, so the same silence re-engages after any later
* logout mid-boot too. When EMERGENCY_CONSOLE_ENABLED is on (the debug/
* recovery escape hatch), neither call site applies -- the console shows
* immediately and stays visible regardless of attach state, exactly as
* before this change.
*
* @param mama Hera's own VM instance -- the idle-dispatch target,
* same as every other sk_repl_idle() caller uses.
*/
void sk_repl_headless_wait(VM *mama);
#ifdef __cplusplus
}
#endif
#endif /* STARKERNEL_REPL_H */