Code review fixes, all compile clean (hosted gcc + aarch64/riscv64 kernel flags):
- repl.c (H1): reentrancy guards on the MSG-TICK idle pump. sk_repl_idle()
now defers when Hera is mid-interpret (g_mama_interpreting) or when its
own vm_interpret is on the stack (g_idle_pump_active), so a blocking
KEY/EXPECT/QUERY inside a dispatched line can no longer re-enter the
interpreter and clobber the in-flight input buffer.
- virtio_rng.c: clamp device-returned used_len to VRNG_BUF_SIZE before the
caller's data_buf copy, closing a device-controlled OOB read.
- block_subsystem.c: first-write path now keys off created_time==0 instead
of dead magic==0 so fresh blocks get a real created_time stamp; first_free/
last_allocated fixed to absolute Forth LBNs (set in blk_compute_fresh_geometry
from slot->start_lbn, no longer the wrong physical-BAM-index values from
compute_totals_from_B); physical-bounds guard on blk_meta_zone_read/write
prevents unsigned underflow on a corrupt fence >= device size.
- capsule_zuse_boot.c / capsule_wirebind.c: identity seed validated magic ->
version -> CRC-64 (compute_crc64 over offsetof(crc)) before trusting it,
so a corrupt/format-mismatched record is refused, never loaded.
- log.h / starkernel/log.h: unused LOG_LINE_MAX 256 renamed LOG_MSG_LINE_MAX
to lift the include-order collision with vm.h's LOG_LINE_MAX 64; stale
include-order comments dropped (kernel_main.c, shim.c, capsule_birth.c).
- FABRIC-3.md: three stale-doc carry-forward items closed [x] with cbe7b49
notes.
Real KEY/?TERMINAL/QUERY/EXPECT bodies (console WIP):
- repl.h/repl.c: sk_console_getkey()/sk_console_key_available()/
sk_console_readline() public bodies; non-destructive peek buffers the
found byte so a following KEY returns it.
- shim.c: getchar()/fgetc()/fgets()/sf_terminal_ready() routed through the
real console paths instead of stubs; sf_terminal_ready() in platform_io.h
with sf_terminal_ready() implemented for the hosted build (linux/io.c,
POSIX select on fd 0) wired into Makefile.
- io_words.c: ?TERMINAL now returns actual terminal-readiness, not constant 0.
Artifacts: minted disk/artemis.img + rebuilt lfs kernel; BLOCK_MAP.md,
doe csv + qemu log regenerated.
259 lines
7.8 KiB
C
259 lines
7.8 KiB
C
/*
|
||
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.
|
||
|
||
*/
|
||
|
||
/* io_words.c - FORTH-79 I/O & Terminal Words */
|
||
#include "include/io_words.h"
|
||
#include "../../include/word_registry.h"
|
||
#include "../../include/log.h"
|
||
#include "../../include/platform_io.h"
|
||
#include <stdio.h>
|
||
|
||
|
||
/**
|
||
* @brief FORTH word EMIT - Output character to terminal
|
||
* @param vm Pointer to the VM structure
|
||
* @details Stack effect: ( c -- )
|
||
* Outputs the character from top of stack to the terminal
|
||
*/
|
||
static void io_word_emit(VM *vm) {
|
||
if (vm->dsp < 0) {
|
||
vm->error = 1;
|
||
return;
|
||
}
|
||
|
||
char c = (char) vm->data_stack[vm->dsp--];
|
||
putchar(c);
|
||
fflush(stdout);
|
||
}
|
||
|
||
/**
|
||
* @brief FORTH word CR - Output carriage return
|
||
* @param vm Pointer to the VM structure
|
||
* @details Stack effect: ( -- )
|
||
* Outputs a newline character to the terminal
|
||
*/
|
||
static void io_word_cr(VM *vm) {
|
||
(void)vm;
|
||
putchar('\n');
|
||
fflush(stdout);
|
||
}
|
||
|
||
/**
|
||
* @brief FORTH word KEY - Input character from terminal
|
||
* @param vm Pointer to the VM structure
|
||
* @details Stack effect: ( -- c )
|
||
* Reads one character from terminal and pushes it to stack
|
||
*/
|
||
static void io_word_key(VM *vm) {
|
||
if (vm->dsp >= STACK_SIZE - 1) {
|
||
vm->error = 1;
|
||
return;
|
||
}
|
||
|
||
int c = getchar();
|
||
vm->data_stack[++vm->dsp] = (cell_t) c;
|
||
}
|
||
|
||
/**
|
||
* @brief FORTH word ?TERMINAL - Check if input is available
|
||
* @param vm Pointer to the VM structure
|
||
* @details Stack effect: ( -- flag )
|
||
* Pushes true if input is available, false otherwise
|
||
*/
|
||
static void io_word_question_terminal(VM *vm) {
|
||
if (vm->dsp >= STACK_SIZE - 1) {
|
||
vm->error = 1;
|
||
return;
|
||
}
|
||
|
||
vm->data_stack[++vm->dsp] = sf_terminal_ready() ? -1 : 0;
|
||
}
|
||
|
||
/**
|
||
* @brief FORTH word TYPE - Output string of characters
|
||
* @param vm Pointer to the VM structure
|
||
* @details Stack effect: ( addr u -- )
|
||
* Outputs u characters from memory starting at addr
|
||
*/
|
||
static void io_word_type(VM *vm) {
|
||
if (vm->dsp < 1) {
|
||
log_message(LOG_ERROR, "TYPE: Data stack underflow");
|
||
vm->error = 1;
|
||
return;
|
||
}
|
||
|
||
cell_t count = vm_pop(vm);
|
||
cell_t addr = vm_pop(vm);
|
||
|
||
// Add bounds checking
|
||
if (addr < 0 || count < 0 || (addr + count) > VM_MEMORY_SIZE) {
|
||
log_message(LOG_ERROR, "TYPE: Invalid range [%ld, %ld)", (long) addr, (long) (addr + count));
|
||
vm->error = 1;
|
||
return;
|
||
}
|
||
|
||
// Output characters from VM memory
|
||
for (cell_t i = 0; i < count; i++) {
|
||
putchar(vm->memory[addr + i]);
|
||
}
|
||
fflush(stdout);
|
||
|
||
log_message(LOG_DEBUG, "TYPE: Output %ld characters from address %ld", (long) count, (long) addr);
|
||
}
|
||
|
||
/**
|
||
* @brief FORTH word SPACE - Output one space character
|
||
* @param vm Pointer to the VM structure
|
||
* @details Stack effect: ( -- )
|
||
* Outputs a single space character to terminal
|
||
*/
|
||
static void io_word_space(VM *vm) {
|
||
(void)vm;
|
||
putchar(' ');
|
||
fflush(stdout);
|
||
}
|
||
|
||
/**
|
||
* @brief FORTH word SPACES - Output multiple spaces
|
||
* @param vm Pointer to the VM structure
|
||
* @details Stack effect: ( n -- )
|
||
* Outputs n space characters to terminal
|
||
*/
|
||
static void io_word_spaces(VM *vm) {
|
||
if (vm->dsp < 0) {
|
||
vm->error = 1;
|
||
return;
|
||
}
|
||
|
||
cell_t count = vm->data_stack[vm->dsp--];
|
||
|
||
if (count < 0) return;
|
||
|
||
for (cell_t i = 0; i < count; i++) {
|
||
putchar(' ');
|
||
}
|
||
fflush(stdout);
|
||
}
|
||
|
||
/* (do-string) — runtime called by compiled ." ; reads inline string from
|
||
* threaded code via the return-stack IP, prints it, then advances IP past
|
||
* the padded inline block. NOT immediate.
|
||
*
|
||
* Inline layout immediately after the (do-string) cell in the thread:
|
||
* [1 length byte][n string bytes][padding to next cell boundary]
|
||
*/
|
||
static void io_runtime_do_string(VM *vm) {
|
||
if (vm->rsp < 0) { vm->error = 1; return; }
|
||
/* return_stack[rsp] holds ip+1 as a raw C pointer — points at inline data */
|
||
uint8_t *data = (uint8_t *)(uintptr_t)vm->return_stack[vm->rsp];
|
||
if (!data) { vm->error = 1; return; }
|
||
uint8_t n = data[0];
|
||
for (uint8_t i = 0; i < n; i++)
|
||
putchar((unsigned char)data[1 + i]);
|
||
fflush(stdout);
|
||
/* advance IP past inline block (length byte + string bytes, cell-aligned) */
|
||
size_t skip = 1 + (size_t)n;
|
||
size_t padded = (skip + (sizeof(cell_t) - 1)) & ~(sizeof(cell_t) - 1);
|
||
vm->return_stack[vm->rsp] = (cell_t)(uintptr_t)(data + padded);
|
||
}
|
||
|
||
/* ." ( "ccc<quote>" -- )
|
||
* Immediate. Interpretation: parse and print the string directly.
|
||
* Compilation: compile (do-string) + inline [len][chars][pad].
|
||
*/
|
||
static void io_word_dot_quote(VM *vm) {
|
||
const char *src = vm->input_buffer;
|
||
size_t pos = vm->input_pos;
|
||
size_t end = vm->input_length;
|
||
|
||
if (!src || pos > end) {
|
||
vm->error = 1;
|
||
return;
|
||
}
|
||
if (pos < end && src[pos] == ' ') pos++;
|
||
|
||
size_t start = pos;
|
||
while (pos < end && src[pos] != '"') pos++;
|
||
if (pos >= end) { vm->error = 1; return; }
|
||
|
||
size_t n = pos - start;
|
||
if (n > 255) n = 255; /* single length byte — clamp silently */
|
||
vm->input_pos = pos + 1;
|
||
|
||
if (vm->mode == MODE_INTERPRET) {
|
||
for (size_t i = 0; i < n; i++)
|
||
putchar((unsigned char)src[start + i]);
|
||
fflush(stdout);
|
||
return;
|
||
}
|
||
|
||
/* Compilation: emit (do-string) cell then inline [len][chars][pad] */
|
||
DictEntry *do_str = vm_find_word(vm, "(do-string)", 11);
|
||
if (!do_str) { vm->error = 1; return; }
|
||
vm_compile_word(vm, do_str);
|
||
|
||
size_t skip = 1 + n;
|
||
size_t padded = (skip + (sizeof(cell_t) - 1)) & ~(sizeof(cell_t) - 1);
|
||
uint8_t *raw = (uint8_t *)vm_allot(vm, padded);
|
||
if (!raw) { vm->error = 1; return; }
|
||
raw[0] = (uint8_t)n;
|
||
for (size_t i = 0; i < n; i++) raw[1 + i] = (uint8_t)src[start + i];
|
||
for (size_t i = 1 + n; i < padded; i++) raw[i] = 0;
|
||
}
|
||
|
||
/*
|
||
* @brief Register all I/O words with the VM
|
||
* @param vm Pointer to the VM structure
|
||
* @details Registers all FORTH-79 I/O and terminal words with the virtual machine
|
||
*/
|
||
void register_io_words(VM *vm) {
|
||
register_word(vm, "EMIT", io_word_emit);
|
||
register_word(vm, "CR", io_word_cr);
|
||
register_word(vm, "KEY", io_word_key);
|
||
register_word(vm, "?TERMINAL", io_word_question_terminal);
|
||
register_word(vm, "TYPE", io_word_type);
|
||
register_word(vm, "SPACE", io_word_space);
|
||
register_word(vm, "SPACES", io_word_spaces);
|
||
register_word(vm, "(do-string)", io_runtime_do_string);
|
||
register_word(vm, ".\"", io_word_dot_quote);
|
||
vm_make_immediate(vm);
|
||
} |