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
+56
-8
@@ -5,7 +5,8 @@
|
||||
\ written here as text for the text assembler (v4/include/v4/text.h).
|
||||
\
|
||||
\ Constants the loader supplies: N-1 (cell bits - 1), NODE-ERROR, CONSOLE-TX,
|
||||
\ CONSOLE-RX, CONSOLE-STATUS, BASE, (EMIT-HOOK).
|
||||
\ CONSOLE-RX, CONSOLE-STATUS, BASE, (EMIT-HOOK), (MSG), (REPLY), (ME), (OUT),
|
||||
\ (OUT^).
|
||||
\
|
||||
\ ERRORS (D-18). A word that finds an error takes its arguments off the stack
|
||||
\ and stores a code in NODE-ERROR. On a node with a prompt that store is a
|
||||
@@ -91,16 +92,63 @@ header CMOVE
|
||||
DONE: drop drop drop ;
|
||||
|
||||
\ ---- section 5.10: the console -------------------------------------------
|
||||
\ EMIT sends the character to the console, unless (EMIT-HOOK) holds the xt
|
||||
\ of a word: then that word is given it instead ( c -- ), and returns to
|
||||
\ EMIT's caller. Everything the node prints goes through EMIT, so a hook
|
||||
\ sees all of it. The prompt takes the hook off at the end of every line
|
||||
\ (quit.v4), so that nothing can leave the console silent. POST uses it
|
||||
\ to compare what a case prints (docs/v4.0.0/NUCLEUS.md 6.3).
|
||||
\ ---- what a node prints (docs/v4.0.0/MESH.md section 6) ------------------------
|
||||
\ A node has no console of its own. It does what a message asks, and what
|
||||
\ it prints while doing it goes back, as a message, to whoever sent that one,
|
||||
\ on the port it came on. A message is seven words and then its text, four
|
||||
\ characters to a word:
|
||||
\ to, from, type, heat and TTL, ACL tag, sequence, length in characters
|
||||
\ The types: 1 text to be interpreted 2 what a node printed 3 how the
|
||||
\ text ended (one word of text: 0 QUIT, 1 completed, 2 an error).
|
||||
\ The header of the message being served is in the seven cells (MSG); (REPLY)
|
||||
\ is the address of the port it came on; (ME) is this node's number.
|
||||
\
|
||||
\ Characters are kept in (OUT), 256 of them, a character to a cell, and sent
|
||||
\ when it is full and when the text has been done with (quit.v4). (OUT^)
|
||||
\ is where the next goes.
|
||||
\
|
||||
\ THE STACK. What prints may be run with the data stack all but full: EMIT
|
||||
\ has always needed one free cell and no more, and still does -- it keeps
|
||||
\ the character and A on the return stack while it works. Sending needs
|
||||
\ two.
|
||||
|
||||
\ ( type -- ) the first six words of a message to the sender of the one
|
||||
\ being served; its length, and its text, are the caller's to send. Each
|
||||
\ write waits for the neighbour to take it. B is left at the port.
|
||||
: (HDR)
|
||||
(REPLY) a! @ b!
|
||||
(MSG)+1 a! @ !b \ to
|
||||
(ME) a! @ !b \ from
|
||||
!b \ type
|
||||
0 !b 0 !b 0 !b ; \ heat and TTL, ACL tag, sequence: carried, not used yet
|
||||
|
||||
\ ( -- ) send what has been printed: its length, then its characters, four
|
||||
\ to a word, the first lowest
|
||||
: (FLUSH-OUT)
|
||||
(OUT^) a! @ (OUT) xor if NONE drop
|
||||
2 (HDR)
|
||||
(OUT^) a! @ (OUT) - dup !b
|
||||
3 + 2/ 2/ -1 + (OUT) a!
|
||||
FOR @+ @+ 8* + @+ 8* 8* + @+ 8* 8* 8* + !b NEXT
|
||||
(OUT) (OUT^) a! ! ;
|
||||
NONE: drop ;
|
||||
|
||||
\ EMIT puts the character with what has been printed, unless (EMIT-HOOK)
|
||||
\ holds the xt of a word: then that word is given it instead ( c -- ), and
|
||||
\ returns to EMIT's caller. Everything the node prints goes through EMIT,
|
||||
\ so a hook sees all of it. The hook is taken off when the text being
|
||||
\ interpreted ends (quit.v4), so that nothing can leave a node silent.
|
||||
\ POST uses it to compare what a case prints (docs/v4.0.0/NUCLEUS.md 6.3).
|
||||
\ A is kept: the words that print have always been free to use it across
|
||||
\ an EMIT. As v3, only the low 8 bits of the character are printed.
|
||||
header EMIT
|
||||
: EMIT ( c -- )
|
||||
(EMIT-HOOK) b! @b if NONE push ;
|
||||
NONE: drop CONSOLE-TX b! !b ;
|
||||
NONE: drop
|
||||
255 and a push push \ A, and then the character, to the return stack
|
||||
(OUT^) b! @b a! pop !+ a !b \ the character goes where (OUT^) points, which moves on
|
||||
a (OUT)+256 xor if FULL drop pop a! ;
|
||||
FULL: drop (FLUSH-OUT) pop a! ;
|
||||
header KEY
|
||||
: KEY ( -- c )
|
||||
L: CONSOLE-STATUS b! @b if WAIT drop CONSOLE-RX b! @b ;
|
||||
|
||||
+66
-26
@@ -4,17 +4,17 @@
|
||||
\ DECOMPOSITION.md 5.15 and 5.10: QUIT ABORT ABORT" (ABORT") ." (."). Part of
|
||||
\ the compiler capsule. Rests on all the files before it.
|
||||
\
|
||||
\ A NODE IS HANDED A LINE (docs/v4.0.0/ENGINE.md 3.1). It does not read its
|
||||
\ own command line and prints no prompt: that is its host's, as it is the
|
||||
\ kernel's REPL's for a v3 VM. The host puts the text in TIB and starts
|
||||
\ (LINE); the node interprets it and stops at (IDLE), having left in the
|
||||
\ variable (LINE-STATUS) how the line ended:
|
||||
\ 1 it completed the host says " ok"
|
||||
\ A NODE IS SENT TEXT (docs/v4.0.0/MESH.md section 6). It does not read its
|
||||
\ own command line and prints no prompt. A node with nothing to do waits at
|
||||
\ its ports; a neighbour writes it a message; if that is text for this node
|
||||
\ it is interpreted; what it printed goes back as a message, and then a
|
||||
\ message saying how the text ended:
|
||||
\ 1 it completed a console says " ok"
|
||||
\ 2 it ended in an error the message is already printed; " ERROR"
|
||||
\ 0 QUIT nothing is said
|
||||
\ What the console then shows is v3's:
|
||||
\ ok> 65 EMIT the host's prompt, and the line
|
||||
\ A ok what the line printed, then the host's " ok"
|
||||
\ What a console then shows is v3's:
|
||||
\ ok> 65 EMIT the console's prompt, and the line
|
||||
\ A ok what the line printed, then " ok"
|
||||
\ ok> NOSUCH
|
||||
\ UNKNOWN WORD: 'NOSUCH'
|
||||
\ ERROR
|
||||
@@ -22,7 +22,8 @@
|
||||
\ Address out of range an address fault (D-14), from any depth
|
||||
\ ERROR
|
||||
\ KEY, EXPECT and QUERY are FORTH-79 words that read characters, as before;
|
||||
\ they are not how a line reaches the node.
|
||||
\ they are not how text reaches the node. They still read the console's
|
||||
\ registers, CONSOLE-RX and CONSOLE-STATUS, and not a message.
|
||||
\
|
||||
\ THE STACKS are guarded (D-16): each counts what it holds, and a push onto
|
||||
\ a full one or a pop from an empty one is a fault. So QUIT and ABORT, which
|
||||
@@ -30,7 +31,10 @@
|
||||
\ the data stack too. A store to RSTACK-DEPTH or DSTACK-DEPTH does that.
|
||||
\
|
||||
\ Constants the loader supplies:
|
||||
\ (LINE-STATUS) word address of the variable: how the last line ended
|
||||
\ (LINE-STATUS) word address of the variable: how the last text ended
|
||||
\ (MSG) (REPLY) (ME) (OUT) (OUT^) see core.v4, "what a node prints"
|
||||
\ (PORT) word address of the node's port 0; (PORT)+8 is "any port" and
|
||||
\ (PORT)+9 the port the last read from that came on
|
||||
\ (Q) word address of four cells of scratch, which system.v4 and
|
||||
\ blocks.v4 use too
|
||||
\ DSTACK-DEPTH RSTACK-DEPTH word addresses of the stack registers
|
||||
@@ -52,26 +56,62 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
|
||||
\ (docs/v4.0.0/NUCLEUS.md 6.3). (EMIT-HOOK), core.v4, is set to 0 here at
|
||||
\ the end of every line, caught or not.
|
||||
\
|
||||
\ ( k -- ) the line is over. It is entered with how -- 0 QUIT, 1 completed,
|
||||
\ anything else an error -- and never returns: it leaves that in
|
||||
\ (LINE-STATUS) and stops at (IDLE), where the host finds the node. It is
|
||||
\ always jumped to, never called, by something that has emptied the return
|
||||
\ stack (or by a fault, which empties it).
|
||||
: (IDLE) L: jump L
|
||||
\ ( s -- ) the text is done with. What it printed is sent, then a message
|
||||
\ saying how it ended -- 0 QUIT, 1 completed, 2 an error -- and the node waits
|
||||
\ for the next. (DONE) is entered with the same number and decides whether
|
||||
\ an error was one to catch; it is always jumped to, never called, by
|
||||
\ something that has emptied the return stack (or by a fault, which empties
|
||||
\ it). (LINE-STATUS) keeps how the last text ended.
|
||||
: (FINISH) ( s -- )
|
||||
(LINE-STATUS) b! dup !b
|
||||
(FLUSH-OUT)
|
||||
3 (HDR) 4 !b !b \ a message of type 3, four characters long: how it ended
|
||||
jump (IDLE)
|
||||
|
||||
: (DONE)
|
||||
(EMIT-HOOK) b! 0 !b \ the console is the console again
|
||||
(EMIT-HOOK) b! 0 !b \ what is printed is printed again
|
||||
if GO -1 + if OK
|
||||
drop (RESET)
|
||||
(CATCH) b! @b if LOUD drop -1 !b 1 jump (DONE) \ caught: the line completed
|
||||
LOUD: drop 2 (LINE-STATUS) b! !b jump (IDLE)
|
||||
OK: drop 1 (LINE-STATUS) b! !b jump (IDLE)
|
||||
GO: drop 0 (LINE-STATUS) b! !b jump (IDLE)
|
||||
(CATCH) b! @b if LOUD drop -1 !b 1 jump (DONE) \ caught: the text completed
|
||||
LOUD: drop 2 jump (FINISH)
|
||||
OK: drop 1 jump (FINISH)
|
||||
GO: drop 0 jump (FINISH)
|
||||
|
||||
\ ( -- ) THE LINE ENTRY. The host has put a line of text in TIB, a zero
|
||||
\ after it, and its length in SPAN, has emptied the return stack and set P
|
||||
\ here. The line is interpreted with one return entry under it, this
|
||||
\ word's call of INTERPRET; whatever is on the data stack stays there.
|
||||
\ ( -- ) WAITING. A node with nothing to do is blocked reading its ports
|
||||
\ (docs/v4.0.0/MESH.md 4.1): it executes nothing until a neighbour writes.
|
||||
\ What arrives is a message (core.v4). Its first word is taken from any
|
||||
\ port and the rest from the port that came on. Its text goes into TIB,
|
||||
\ which holds 1024 characters, the most a message carries; a longer one is
|
||||
\ read to its end, so that what follows it is not taken for a message, and
|
||||
\ is error 12. A message of type 1 for this node is text to interpret: it
|
||||
\ is done as a line was, and (FINISH) reports how it ended. Any other is
|
||||
\ let go: passing a message on is MESH.md step 4.
|
||||
: (IDLE)
|
||||
(PORT)+8 b! @b (MSG) a! !+ \ to, from any port
|
||||
(PORT)+9 b! @b (PORT) + dup b! \ B: the port it came on
|
||||
5 FOR @b !+ UNEXT \ from, type, heat and TTL, ACL tag, sequence, length
|
||||
(REPLY) a! !
|
||||
(MSG)+6 a! @ -if SIZED jump BAD \ a length below zero: no telling what follows
|
||||
SIZED: dup -1025 + -if LONG drop jump FITS
|
||||
LONG: drop 3 + 2/ 2/ -1 + FOR @b drop UNEXT 0 \ too long: read it and let it go
|
||||
BAD: drop 0 (MSG)+6 a! ! 0 SPAN a! ! NODE-ERROR b! 12 !b
|
||||
FITS:
|
||||
3 + 2/ 2/ if EMPTY
|
||||
-1 + TIB 2/ 2/ a! FOR @b !+ UNEXT
|
||||
jump READ
|
||||
EMPTY: drop
|
||||
READ:
|
||||
0 (MSG)+6 a! @ TIB + C! \ a zero after the text
|
||||
(MSG)+6 a! @ SPAN a! !
|
||||
(MSG) a! @ (ME) a! @ xor if MINE drop jump (IDLE)
|
||||
MINE: drop
|
||||
(MSG)+2 a! @ -1 + if TEXT drop jump (IDLE)
|
||||
TEXT: drop jump (LINE)
|
||||
|
||||
\ ( -- ) THE TEXT, INTERPRETED. It is in TIB with a zero after it and its
|
||||
\ length in SPAN, and the return stack is empty. It is interpreted with one
|
||||
\ return entry under it, this word's call of INTERPRET; whatever is on the
|
||||
\ data stack stays there.
|
||||
: (LINE)
|
||||
0 BLK a! ! \ the terminal, whatever was being loaded
|
||||
TIB (SRC) a! ! 0 >IN a! !
|
||||
|
||||
Reference in New Issue
Block a user