Files
LithosAnanke/v4/src/image.c
T
rajamesandClaude Opus 5.5 25fc5fd5e3 feat(v4.0.0): the node tells its kernel of its words; the boot seals the system
ENGINE.md 3b, the node's side of ruling A (a word's code is the node's, its
accounts the kernel's).

- dict.v4, system.v4: (WORD-DEFINED) ( xt -- ) is run when an entry is
  made, (WORD-FORGOTTEN) ( w -- ) when FORGET or COLD removes entries; with
  0 there no one is told, as on the hosted product
- test_host_quit.c: a kernel that keeps the list of words and is checked to
  hold exactly the node's dictionary after definitions, a vocabulary, an
  abandoned definition, FORGET, a refused FORGET and COLD; KERNEL-WORD
  called from the prompt and from a definition

Fixed, found while writing that test: since the capsules moved from build
time to boot time (294e6946), what COLD returns to and FORGET protects was
still the nucleus alone, so COLD lost U*, U/MOD and BYE and FORGET U* was
allowed.  The boot now seals the system when it has loaded it
(v4_image_seal), and hosted-check checks COLD, the capsule word after it,
the refused FORGET and BYE.

Verified: make -C v4 test passes at both widths (1283 checks in
test_host_quit.c); hosted-check passes on three ISAs; clean qemu with
STARFORTH_V4=1 on amd64, aarch64 and riscv64 passes POST, and COLD, U*
after it, FORGET U* (refused), an unserved kernel word and BYE typed at
each prompt are answered correctly (logs/20261005-193045, -193307, -193636).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 19:38:40 -04:00

79 lines
2.5 KiB
C

/* image.c -- start a node from a capsule image. See image.h.
*
* Nothing here uses the C library: the bare-metal kernel links this file.
*/
#include "v4/image.h"
int v4_image_boot(v4_node *n, v4_exec_state *es, v4_heat *h, const v4_image *im,
unsigned char *disk, unsigned blocks)
{
unsigned i;
if (im->cell_bits != (unsigned)V4_CELL_BITS || im->node_words != (unsigned)V4_NODE_WORDS
|| im->data_ring != (unsigned)V4_DATA_RING || im->ret_ring != (unsigned)V4_RET_RING)
return 0;
v4_node_reset(n);
v4_exec_reset(es);
v4_heat_reset(h);
for (i = 0; i < im->count; i++)
if (v4_node_addr_ok(im->cells[i].addr)) n->mem[im->cells[i].addr] = im->cells[i].value;
v4_node_console_attach(n, im->console_tx);
v4_node_console_input_attach(n, im->console_rx, im->console_status);
v4_node_stack_regs_attach(n, im->dstack_reg, im->rstack_reg);
v4_node_error_attach(n, im->node_error);
v4_node_fault_attach(n, im->fault_table);
v4_node_port_attach(n, im->port);
if (disk && blocks) v4_node_storage_attach(n, im->storage_reg, disk, blocks);
n->mem[im->line_status] = V4_LINE_COMPLETED;
n->p = im->idle;
return 1;
}
int v4_image_waiting(const v4_node *n, const v4_image *im)
{
return n->input_pos == n->input_len && n->p >= im->key_start && n->p < im->key_end;
}
/* One byte of the node's memory: four to a cell, the first lowest, at either
* cell width -- as C! stores them (v4/capsule/core.v4). */
static void put_byte(v4_node *n, v4_cell baddr, unsigned c)
{
v4_cell word = baddr / 4;
unsigned shift = 8u * (unsigned)(baddr % 4);
v4_ucell mask = (v4_ucell)0xFFu << shift;
n->mem[word] = (v4_cell)(((v4_ucell)n->mem[word] & ~mask) | ((v4_ucell)(c & 0xFFu) << shift));
}
int v4_line_begin(v4_node *n, const v4_image *im, const char *text, unsigned len)
{
unsigned i;
if (len + 1u > im->tib_bytes) return 0;
for (i = 0; i < len; i++) put_byte(n, im->tib + (v4_cell)i, (unsigned char)text[i]);
put_byte(n, im->tib + (v4_cell)len, 0);
n->mem[im->span] = (v4_cell)len;
v4_rstack_reset(&n->rs);
n->p = im->line;
return 1;
}
int v4_line_done(const v4_node *n, const v4_image *im)
{
return n->p == im->idle;
}
int v4_line_status(const v4_node *n, const v4_image *im)
{
return (int)n->mem[im->line_status];
}
void v4_image_seal(v4_node *n, const v4_image *im)
{
n->mem[im->boot_cells] = n->mem[im->dp];
n->mem[im->boot_cells + 1] = n->mem[im->latest];
n->mem[im->fence] = (n->mem[im->dp] + 3) / 4;
}