MESH.md step 3. The ports are the transport; the message is what is transported: to, from, type, heat and TTL, ACL tag, sequence, length, then text four characters to a word. - quit.v4: a node with nothing to do is blocked reading "any port"; text for it is interpreted; (FINISH) sends what it printed and then how the text ended, and it waits again - core.v4: EMIT keeps what is printed, (FLUSH-OUT) and (HDR) send it to the sender on the port the message came on. EMIT still needs one free data cell and no more; it works on the return stack and in A and B - message.h/.c: the same format for whatever is on a port and is not a node - boot.c: the boot is the node's console on port 1 and its kernel on port 0 - the prompt tests are a console that speaks messages - gone: v4_line_begin, v4_line_done, v4_line_status; writing a node's input buffer and setting its P from outside; any use of CONSOLE-TX Verified: make -C v4 test (test_host_quit.c 1283 checks, the full-stack figures unchanged) and make -C v4 sanitize pass; hosted-check passes on three ISAs with POST 550 of 550; clean qemu with STARFORTH_V4=1 passes POST and answers lines typed at each prompt on amd64, aarch64 and riscv64 (logs/20261006-110551, -111621, -111341). -110837 is an aarch64 run ended by the test wrapper's limit while still in UEFI firmware; it shows nothing about v4. Not done: KEY, EXPECT and QUERY still read the console's input registers; a message not for this node is let go (step 4). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
124 lines
5.4 KiB
C
124 lines
5.4 KiB
C
/* mkimage.c -- build the capsule image (include/v4/image.h).
|
|
*
|
|
* mkimage DESCRIPTION.c CAPSULE.f18
|
|
*
|
|
* Runs on the build machine. It assembles capsule/ *.v4 -- the nucleus,
|
|
* docs/v4.0.0/NUCLEUS.md -- onto a host node with the memory map of
|
|
* tests/host_map.h. It runs nothing. It writes the nucleus as a capsule
|
|
* of F18 code (v4/include/v4/capsule.h) -- what is sent to an empty node's
|
|
* port to make it a StarForth node -- and the addresses a host needs, as a
|
|
* C file (v4/include/v4/image.h). No FORTH source is compiled here: what
|
|
* is not in the nucleus is loaded from capsules when the system boots.
|
|
*
|
|
* The image is the same on every machine it is built on: nothing in it
|
|
* depends on the build machine but the cell width this tool was compiled
|
|
* for.
|
|
*/
|
|
#include "v4/image.h"
|
|
#include "v4/capsule.h"
|
|
#include "v4/text.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "../tests/host_map.h"
|
|
|
|
static v4_node n;
|
|
static v4_text tx;
|
|
static v4_cell w_key, w_key_end;
|
|
|
|
static void die(const char *what, const char *detail)
|
|
{
|
|
fprintf(stderr, "mkimage: %s%s%s\n", what, detail ? ": " : "", detail ? detail : "");
|
|
exit(1);
|
|
}
|
|
|
|
/* one word of the capsule, to the file */
|
|
static void put_word(void *self, v4_cell word)
|
|
{
|
|
v4_ucell u = (v4_ucell)word;
|
|
unsigned i;
|
|
for (i = 0; i < V4_CELL_BITS / 8; i++) fputc((int)((u >> (8 * i)) & 0xFFu), (FILE *)self);
|
|
}
|
|
|
|
/* every entry from xt back: no access control fields set */
|
|
static void clear_acl(v4_cell xt)
|
|
{
|
|
for (; xt != 0; xt = n.mem[xt - 1]) n.mem[xt - 3] &= 31;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
static const char *const files[] = { "core.v4", "input.v4", "dict.v4", "codegen.v4", "compile.v4", "quit.v4", "forth.v4",
|
|
"numout.v4", "words.v4", "system.v4", "qmath.v4", "blocks.v4", "log.v4", "acl.v4" };
|
|
FILE *out;
|
|
v4_cell k, idle, faults, voc;
|
|
unsigned count = 0;
|
|
|
|
if (argc != 3) die("usage: mkimage DESCRIPTION.c CAPSULE.f18", NULL);
|
|
|
|
if (!host_load(&tx, &n, files, (unsigned)(sizeof files / sizeof files[0]))) die("the capsule does not assemble", NULL);
|
|
if (!v4_text_finish(&tx)) die("not everything is defined", v4_text_error(&tx));
|
|
if (v4_text_here(&tx) >= DICT_W) die("the capsule's code runs into the dictionary space", NULL);
|
|
idle = v4_text_word(&tx, "(IDLE)");
|
|
faults = v4_text_word(&tx, "(FAULTS)");
|
|
w_key = v4_text_word(&tx, "KEY");
|
|
w_key_end = v4_text_word(&tx, "CR");
|
|
|
|
/* the variables, as at switch-on */
|
|
n.mem[DP] = DICT_W * 4;
|
|
n.mem[LATEST] = v4_text_latest(&tx);
|
|
n.mem[STATE] = 0;
|
|
n.mem[CFP] = CFS_W;
|
|
n.mem[BASE] = 10;
|
|
n.mem[FENCE] = DICT_W;
|
|
n.mem[LOG_LEVEL] = 2;
|
|
n.mem[CONTEXT] = LATEST;
|
|
n.mem[CURRENT] = LATEST;
|
|
n.mem[SRC] = TIB;
|
|
|
|
/* The nucleus is the system as COLD finds it: COLD comes back to here,
|
|
* and FORGET will not go below it. What the capsules add at boot
|
|
* (v4/system/boot.c) is above it. */
|
|
n.mem[BOOT_CELLS] = n.mem[DP];
|
|
n.mem[BOOT_CELLS + 1] = n.mem[LATEST];
|
|
n.mem[FENCE] = (n.mem[DP] + 3) / 4;
|
|
/* and nothing of the building is left behind: the terminal is the input,
|
|
* no block is in a buffer, no word has an access control field set */
|
|
n.mem[NODE_ERROR] = 0;
|
|
n.mem[BLK] = 0; n.mem[SCR] = 0; n.mem[SRC] = TIB; n.mem[SRC_HOOK] = 0;
|
|
for (k = BVARS; k < BVARS + 6; k++) n.mem[k] = 0;
|
|
for (k = STORAGE_REG; k < STORAGE_REG + 4; k++) n.mem[k] = 0;
|
|
for (k = BUF0_W; k < BUF0_W + 2 * 256; k++) n.mem[k] = 0;
|
|
for (k = TIB_W; k < TIB_W + 260; k++) n.mem[k] = 0;
|
|
for (k = WBUF_W; k < WBUF_W + WBUF_CELLS; k++) n.mem[k] = 0;
|
|
for (k = PAD_W; k < PAD_W + 21; k++) n.mem[k] = 0;
|
|
n.mem[TO_IN] = 0; n.mem[SPAN] = 0;
|
|
clear_acl(n.mem[LATEST]);
|
|
for (voc = n.mem[VOC_LINK]; voc != 0; voc = n.mem[voc + 1]) clear_acl(n.mem[voc]);
|
|
if (!v4_node_guards_intact(&n)) die("the node's guards are damaged", NULL);
|
|
|
|
n.mem[LINE_STATUS] = 1; /* nothing has ended badly */
|
|
n.mem[OUT_PTR] = OUT_W; /* nothing has been printed */
|
|
|
|
/* the nucleus as a capsule of F18 code: the words a neighbour writes to
|
|
* an empty node's port, each in V4_CELL_BITS / 8 bytes, low byte first */
|
|
out = fopen(argv[2], "wb");
|
|
if (!out) die("cannot write", argv[2]);
|
|
count = v4_capsule_write(&n, idle, put_word, out);
|
|
if (fclose(out) != 0) die("cannot write", argv[2]);
|
|
|
|
out = fopen(argv[1], "w");
|
|
if (!out) die("cannot write", argv[1]);
|
|
fprintf(out, "/* Generated by v4/tools/mkimage.c from v4/capsule -- do not edit. */\n#include \"v4/image.h\"\n\n");
|
|
fprintf(out, "const v4_image v4_capsule_image = {\n %uu, %uu, %uu, %uu,\n",
|
|
(unsigned)V4_CELL_BITS, (unsigned)V4_NODE_WORDS, (unsigned)V4_DATA_RING, (unsigned)V4_RET_RING);
|
|
fprintf(out, " %ld,\n %ld, %ld,\n %ld, %ld,\n", (long)idle, (long)faults, (long)PORT, (long)w_key, (long)w_key_end);
|
|
fprintf(out, " %ld, %ld, %ld,\n %ld, %ld,\n %ld,\n %ld,\n %ld, %ld,\n %ld, %ld\n};\n", (long)CONSOLE_TX, (long)CONSOLE_RX, (long)CONSOLE_ST,
|
|
(long)DSTACK_REG, (long)RSTACK_REG, (long)NODE_ERROR, (long)STORAGE_REG, (long)DP, (long)LATEST,
|
|
(long)BOOT_CELLS, (long)FENCE);
|
|
if (fclose(out) != 0) die("cannot write", argv[1]);
|
|
fprintf(stderr, "mkimage: a capsule of %u words, dictionary to word %ld, %d-bit cells -> %s, %s\n", count, (long)((n.mem[DP] + 3) / 4), V4_CELL_BITS, argv[2], argv[1]);
|
|
return 0;
|
|
}
|