Hera still polls xHCI and sig-checks attached drives, but the storage
registration step (blk_subsys_attach_device(), now wrapped as the
BLK-ATTACH primitive) moves to Artemis's own dictionary, reached via
HERA-BLK-ATTACH-REQ/BLK-ATTACH-ACK (VM-EXEC, since Hera can't load her
own messaging.4th -- see the doc comment in repl.c). Identity birth
(Zuse genesis / WIREBIND) is deferred until the ack confirms storage
actually succeeded, instead of running synchronously underneath a
storage call that might fail ("wait for ack, safer for identity data").
Caught and fixed a real bug live during acceptance testing: Artemis's
ACK-APPEND-NUM fed a single-cell value into <# #S #> (which expects a
double-cell pair), causing a stack underflow the first time
HERA-BLK-ATTACH-REQ ran. Fixed with the same `0 SWAP` convention every
other numeric-append helper in this codebase already uses.
Verified booting clean to (zuse) ok> with no VM-EXEC errors on all
three architectures (amd64/aarch64/riscv64).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Ec88YKxxhZGG1RNnune78
188 lines
7.3 KiB
C
188 lines
7.3 KiB
C
/*
|
||
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 */
|