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>
79 lines
2.5 KiB
C
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;
|
|
}
|