feat(v4.0.0): a node is sent text as a message and sends back what it prints
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>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
42610e844f
commit
a041b401ea
@@ -69,6 +69,12 @@
|
||||
#define CATCH (BUF0_W - 8) /* non-zero: a line's error is recorded here, -1, and the line ends " ok" */
|
||||
#define LINE_STATUS (BUF0_W - 9) /* how the last line ended: 0 QUIT, 1 completed, 2 an error */
|
||||
#define PORT (BUF0_W - 32) /* the node's ports (node.h): V4_PORTS of them, then "any port" and "which port". Port 0 is where its requests go. */
|
||||
#define ME (BUF0_W - 13) /* this node's number: a message is for it when its first word is this */
|
||||
#define OUT_PTR (BUF0_W - 14) /* where the next character printed goes: a cell of the output buffer */
|
||||
#define REPLY (BUF0_W - 15) /* the address of the port the message being served came on */
|
||||
#define MSG (BUF0_W - 48) /* the header of the message being served, 7 cells */
|
||||
#define OUT_W (BUF0_W - 400) /* the output buffer: 256 cells, a character to a cell */
|
||||
#define OUT_CELLS 256
|
||||
#define WORD_DEFINED (BUF0_W - 11) /* the xt of the word that is told of each new entry, or 0 */
|
||||
#define WORD_FORGOTTEN (BUF0_W - 12) /* the xt of the word that is told when entries go, or 0 */
|
||||
#define LOG_LEVEL (BUF0_W - 5) /* the level at or below which a message is printed */
|
||||
@@ -108,6 +114,7 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned
|
||||
unsigned i;
|
||||
|
||||
v4_node_reset(n);
|
||||
n->mem[OUT_PTR] = OUT_W; /* nothing has been printed: what is, is kept from here (core.v4) */
|
||||
v4_text_begin(tx, n, 16);
|
||||
v4_text_constant(tx, "N-1", V4_CELL_BITS - 1);
|
||||
v4_text_constant(tx, "NODE-ERROR", NODE_ERROR);
|
||||
@@ -148,6 +155,11 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned
|
||||
v4_text_constant(tx, "(CATCH)", CATCH);
|
||||
v4_text_constant(tx, "(LINE-STATUS)", LINE_STATUS);
|
||||
v4_text_constant(tx, "(PORT)", PORT);
|
||||
v4_text_constant(tx, "(ME)", ME);
|
||||
v4_text_constant(tx, "(OUT^)", OUT_PTR);
|
||||
v4_text_constant(tx, "(REPLY)", REPLY);
|
||||
v4_text_constant(tx, "(MSG)", MSG);
|
||||
v4_text_constant(tx, "(OUT)", OUT_W);
|
||||
v4_text_constant(tx, "(WORD-DEFINED)", WORD_DEFINED);
|
||||
v4_text_constant(tx, "(WORD-FORGOTTEN)", WORD_FORGOTTEN);
|
||||
v4_text_constant(tx, "BLOCK-NUMBER", STORAGE_REG);
|
||||
|
||||
@@ -263,7 +263,10 @@ int main(void)
|
||||
}
|
||||
n.mem[BASE] = 10;
|
||||
CHECK(v4_node_console_feed(&n, "Z", 1) == 1 && run("KEY", 0, 0, 0, 0) && pop() == 'Z' && clean(), "KEY");
|
||||
CHECK(run("EMIT", 1, 'q', 0, 0) && clean() && n.console_len == 1 && n.console[0] == 'q', "EMIT");
|
||||
/* a node has no console of its own: what it prints is kept to be sent (core.v4) */
|
||||
n.mem[OUT_PTR] = OUT_W;
|
||||
CHECK(run("EMIT", 1, 'q', 0, 0) && clean() && n.mem[OUT_PTR] == OUT_W + 1 && n.mem[OUT_W] == 'q', "EMIT");
|
||||
CHECK(run("EMIT", 1, 0x141, 0, 0) && clean() && n.mem[OUT_PTR] == OUT_W + 2 && n.mem[OUT_W + 1] == 'A', "EMIT prints the low 8 bits");
|
||||
}
|
||||
|
||||
/* ---- the in-line names ---- */
|
||||
|
||||
+75
-38
@@ -41,7 +41,7 @@
|
||||
* 3 ROLL is ROT. v3's count from zero.
|
||||
*/
|
||||
#include "v4/text.h"
|
||||
#include "v4/image.h"
|
||||
#include "v4/message.h"
|
||||
#include "v4/testcode.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
@@ -59,7 +59,7 @@ static v4_exec_state es;
|
||||
static v4_heat h;
|
||||
static v4_text tx;
|
||||
static v4_cell w_key, w_key_end, w_fault, capsule_latest;
|
||||
static v4_image img; /* where a line is handed to the node: v4/include/v4/image.h */
|
||||
static v4_cell w_idle; /* (IDLE): where the node waits for a message */
|
||||
static char out[V4_CONSOLE_CAP + 1];
|
||||
|
||||
/* The storage device's blocks: zeros at every switch-on. */
|
||||
@@ -73,16 +73,6 @@ static void put_block(unsigned num, const char *text)
|
||||
}
|
||||
static long last_steps;
|
||||
|
||||
/* THE HOST. A node is handed a line and prints no prompt (ENGINE.md 3.1);
|
||||
* these tests are its host, and do what tools/hosted.c does: take what is
|
||||
* typed a line at a time, hand each line over, and say " ok" or " ERROR" and
|
||||
* the next prompt. What is typed after a line is there for a word in it
|
||||
* that reads the keyboard, and what such a word does not take is the next
|
||||
* line. */
|
||||
static char typed[8192]; /* typed and not yet taken */
|
||||
static unsigned typed_len;
|
||||
static int line_open; /* a line has been handed over and has not ended */
|
||||
|
||||
/* THE KERNEL. A node asks its kernel by writing to its port (node.h), and
|
||||
* these tests are that too. Request 1 is "an entry has been made" ( xt -- )
|
||||
* and request 2 "the entries from here up have gone" ( w -- ): the kernel's
|
||||
@@ -149,7 +139,31 @@ static const char *node_name(v4_cell xt)
|
||||
return name;
|
||||
}
|
||||
|
||||
/* Run until the line ends, or until it has been inside KEY with nothing to
|
||||
/* THE CONSOLE. A node is sent text as a message and sends back what it
|
||||
* prints (MESH.md section 6); these tests are its console, on its port 1,
|
||||
* and do what tools/hosted.c does: take what is typed a line at a time,
|
||||
* send each line as a message, show what comes back, and say " ok" or
|
||||
* " ERROR" and the next prompt. What is typed after a line is there for a
|
||||
* word in it that reads the keyboard, and what such a word does not take is
|
||||
* the next line. */
|
||||
#define CONSOLE_PORT 1u
|
||||
static char typed[8192]; /* typed and not yet taken */
|
||||
static unsigned typed_len;
|
||||
static int line_open; /* a line has been sent and has not ended */
|
||||
static v4_message to_node, from_node; /* the message going in, and the one coming out */
|
||||
static unsigned to_sent; /* how many words of to_node the node has taken */
|
||||
static char shown[V4_CONSOLE_CAP]; /* what the node has printed since the console last looked */
|
||||
static unsigned shown_len;
|
||||
static int shown_dropped;
|
||||
static int text_ended; /* how the text ended, when it has */
|
||||
|
||||
/* 1 if the node is where it waits for a message, with nothing given it. */
|
||||
static int node_idle(void)
|
||||
{
|
||||
return n.reading && !n.given && n.read_port == V4_PORT_ANY && n.p > w_idle && n.p <= w_idle + 3;
|
||||
}
|
||||
|
||||
/* Run until the text ends, or until it has been inside KEY with nothing to
|
||||
* read for 64 instruction words; or until `max` words. 1: ended, 2: waiting
|
||||
* for a character, 0: neither. */
|
||||
static int run_line(long max)
|
||||
@@ -157,14 +171,36 @@ static int run_line(long max)
|
||||
long steps = 0;
|
||||
unsigned idle = 0;
|
||||
while (steps < max && idle < 64) {
|
||||
if (v4_line_done(&n, &img)) { last_steps = steps; return 1; }
|
||||
(void)v4_exec_step_word(&n, &es, &h);
|
||||
steps++;
|
||||
if (n.asking) { kernel_serve(); v4_node_port_served(&n); } /* blocked at its port: the kernel's turn */
|
||||
if (n.asking && n.ask_port == 0) { kernel_serve(); v4_node_port_served(&n); continue; } /* a request */
|
||||
if (n.asking && n.ask_port == CONSOLE_PORT) { /* a word of a message from the node */
|
||||
v4_cell word = n.request;
|
||||
v4_node_port_served(&n);
|
||||
if (v4_message_word(&from_node, word)) {
|
||||
unsigned i, chars = v4_message_length(&from_node);
|
||||
if (v4_message_type(&from_node) == V4_MSG_OUTPUT) {
|
||||
for (i = 0; i < chars; i++) {
|
||||
if (shown_len < sizeof shown) shown[shown_len++] = v4_message_char(&from_node, i);
|
||||
else shown_dropped = 1;
|
||||
}
|
||||
} else if (v4_message_type(&from_node) == V4_MSG_DONE) {
|
||||
text_ended = (int)from_node.word[V4_MSG_HEADER];
|
||||
from_node.count = 0;
|
||||
last_steps = steps;
|
||||
return 1;
|
||||
}
|
||||
from_node.count = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (n.reading && !n.given && to_sent < to_node.count && (n.read_port == CONSOLE_PORT || n.read_port == V4_PORT_ANY)) {
|
||||
v4_node_port_give(&n, CONSOLE_PORT, to_node.word[to_sent++]); /* a word of the message to the node */
|
||||
continue;
|
||||
}
|
||||
if (n.input_pos == n.input_len && n.p >= w_key && n.p < w_key_end) idle++; else idle = 0;
|
||||
}
|
||||
last_steps = steps;
|
||||
if (v4_line_done(&n, &img)) return 1;
|
||||
return idle >= 64 ? 2 : 0;
|
||||
}
|
||||
|
||||
@@ -193,7 +229,6 @@ static void boot_with(unsigned depth)
|
||||
for (v4_cell k = BVARS; k < BVARS + 14; k++) if (k != SRC) n.mem[k] = 0; /* empty buffers, SCR and BLK 0, no hook */
|
||||
memset(disk, 0, sizeof disk);
|
||||
v4_node_storage_attach(&n, STORAGE_REG, disk, DISK_BLOCKS);
|
||||
v4_node_console_attach(&n, CONSOLE_TX);
|
||||
v4_node_console_input_attach(&n, CONSOLE_RX, CONSOLE_ST);
|
||||
v4_node_fault_attach(&n, w_fault);
|
||||
v4_node_error_attach(&n, NODE_ERROR);
|
||||
@@ -208,10 +243,16 @@ static void boot_with(unsigned depth)
|
||||
n.mem[WORD_FORGOTTEN] = 0;
|
||||
known_n = 0;
|
||||
last_request = 0;
|
||||
n.mem[LINE_STATUS] = V4_LINE_COMPLETED;
|
||||
n.p = img.idle; /* idle, waiting to be handed a line */
|
||||
n.mem[LINE_STATUS] = 1;
|
||||
n.mem[ME] = 0; /* it has no number yet: a message to 0 is for it */
|
||||
n.mem[OUT_PTR] = OUT_W;
|
||||
n.mem[REPLY] = PORT + CONSOLE_PORT;
|
||||
n.p = w_idle; /* waiting for a message */
|
||||
typed_len = 0;
|
||||
line_open = 0;
|
||||
to_node.count = 0; to_sent = 0; from_node.count = 0;
|
||||
shown_len = 0; shown_dropped = 0;
|
||||
(void)v4_exec_step_word(&n, &es, &h); /* it reads its ports, and is blocked */
|
||||
}
|
||||
static void boot(void) { boot_with(0); }
|
||||
/* The same with nothing at all on the data stack. */
|
||||
@@ -252,27 +293,27 @@ static const char *say(const char *input)
|
||||
unsigned len;
|
||||
if (!nl) break;
|
||||
len = (unsigned)(nl - typed);
|
||||
if (!v4_line_begin(&n, &img, typed, len)) return "(line too long)";
|
||||
if (!v4_message_text(&to_node, 0, 1, V4_MSG_TEXT, typed, len)) return "(line too long)";
|
||||
to_sent = 0;
|
||||
memmove(typed, nl + 1, typed_len - len - 1);
|
||||
typed_len -= len + 1;
|
||||
line_open = 1;
|
||||
}
|
||||
v4_node_console_attach(&n, CONSOLE_TX); /* empties the capture */
|
||||
v4_node_console_input_attach(&n, CONSOLE_RX, CONSOLE_ST); /* and the keyboard */
|
||||
shown_len = 0; shown_dropped = 0;
|
||||
v4_node_console_input_attach(&n, CONSOLE_RX, CONSOLE_ST); /* empties the keyboard */
|
||||
if (v4_node_console_feed(&n, typed, typed_len) != typed_len) return "(input queue full)";
|
||||
ended = run_line(40000000);
|
||||
took = n.input_pos; /* what a word in the line read */
|
||||
memmove(typed, typed + took, typed_len - took);
|
||||
typed_len -= took;
|
||||
if (n.console_dropped || olen + n.console_len + 16 > sizeof out) return "(too much output)";
|
||||
memcpy(out + olen, n.console, n.console_len);
|
||||
olen += n.console_len;
|
||||
if (shown_dropped || olen + shown_len + 16 > sizeof out) return "(too much output)";
|
||||
memcpy(out + olen, shown, shown_len);
|
||||
olen += shown_len;
|
||||
out[olen] = 0;
|
||||
if (ended == 0) return "(still running)";
|
||||
if (ended == 2) break;
|
||||
line_open = 0;
|
||||
tail = v4_line_status(&n, &img) == V4_LINE_COMPLETED ? " ok\nok> "
|
||||
: v4_line_status(&n, &img) == V4_LINE_ERROR ? " ERROR\nok> " : "ok> ";
|
||||
tail = text_ended == V4_TEXT_COMPLETED ? " ok\nok> " : text_ended == V4_TEXT_ERROR ? " ERROR\nok> " : "ok> ";
|
||||
memcpy(out + olen, tail, strlen(tail) + 1);
|
||||
olen += (unsigned)strlen(tail);
|
||||
}
|
||||
@@ -739,12 +780,7 @@ int main(void)
|
||||
CHECK(v4_text_here(&tx) < DICT_W, "code stays below the dictionary space");
|
||||
printf(" capsule: %ld words\n", (long)v4_text_here(&tx) - 16);
|
||||
if (failures) { printf(" %d checks, %d failures\n", checks, failures); return 1; }
|
||||
img.line = v4_text_word(&tx, "(LINE)");
|
||||
img.idle = v4_text_word(&tx, "(IDLE)");
|
||||
img.line_status = LINE_STATUS;
|
||||
img.tib = TIB;
|
||||
img.tib_bytes = 1025u;
|
||||
img.span = SPAN;
|
||||
w_idle = v4_text_word(&tx, "(IDLE)");
|
||||
w_key = v4_text_word(&tx, "KEY");
|
||||
w_key_end = v4_text_word(&tx, "CR"); /* the word after KEY in core.v4 */
|
||||
w_fault = v4_text_word(&tx, "(FAULTS)");
|
||||
@@ -753,11 +789,12 @@ int main(void)
|
||||
|
||||
/* ---- switch-on ---- */
|
||||
boot();
|
||||
CHECK(v4_line_done(&n, &img) && n.console_len == 0, "the node is idle and has printed nothing: the prompt is its host's");
|
||||
CHECK(run_line(5000) == 1 && last_steps == 0, "and goes on waiting to be handed a line");
|
||||
CHECK(node_idle() && n.mem[OUT_PTR] == OUT_W, "the node is waiting at its ports and has printed nothing: the prompt is its console's");
|
||||
{ v4_uheat_t clock = es.anticlock;
|
||||
CHECK(run_line(5000) == 0 && es.anticlock == clock, "and goes on waiting, executing nothing"); }
|
||||
CHECK(canary_under_expect(), "with the data stack as it found it");
|
||||
(void)v4_exec_step_word(&n, &es, &h);
|
||||
CHECK(v4_line_done(&n, &img), "it stays idle however long it is run");
|
||||
CHECK(node_idle(), "it stays waiting however long it is run");
|
||||
|
||||
/* ---- the sessions ---- */
|
||||
for (i = 0; i < NSCRIPT; i++) {
|
||||
@@ -883,7 +920,7 @@ int main(void)
|
||||
memcpy(line, "65 EMIT", 7);
|
||||
line[1025] = '\n'; line[1026] = 0;
|
||||
CHECK(is(say(line), "(line too long)"), "one of 1025 is not taken: the node's input buffer holds a block");
|
||||
CHECK(v4_line_done(&n, &img) && n.console_len == 0, "and nothing of it was run");
|
||||
CHECK(node_idle() && n.mem[OUT_PTR] == OUT_W, "and nothing of it was sent");
|
||||
}
|
||||
|
||||
/* ---- ABORT and QUIT from deep in a programme, again and again ---- */
|
||||
@@ -1247,7 +1284,7 @@ int main(void)
|
||||
boot_bare();
|
||||
CHECK(load_source("tools.fth"), "tools.fth compiles, every line of it");
|
||||
CHECK(is(say(": SQ DUP * ; SEE SQ\n"), ": SQ\n dup call * \n ; \n ok\nok> "), "a definition: an in-line word, a call by name, the return");
|
||||
CHECK(is(say("SEE DUP\n"), ": DUP\n dup ; \n ok\nok> ") && is(say("SEE EMIT\n"), out) && strncmp(out, ": EMIT\n @p ", 12) == 0 && strstr(out, " b! !b ; \n"),
|
||||
CHECK(is(say("SEE DUP\n"), ": DUP\n dup ; \n ok\nok> ") && is(say("SEE EMIT\n"), out) && strncmp(out, ": EMIT\n @p ", 12) == 0 && strstr(out, " b! @b \n if ") && strstr(out, " pop a! ; \n"),
|
||||
"the capsule's own words; a literal's value follows its @p");
|
||||
CHECK(is(say("VARIABLE V 7 V ! SEE V\n"), ": V\n data: 7 \n ok\nok> ") && is(say("5 CONSTANT K SEE K\n"), ": K\n data: 5 \n ok\nok> "), "a variable and a constant show what they hold");
|
||||
CHECK(is(say(": IM 1 ; IMMEDIATE SEE IM\n"), ": IM\n @p 1 ; \nIMMEDIATE\n ok\nok> "), "an immediate word says so");
|
||||
|
||||
Reference in New Issue
Block a user