diff --git a/kernel/src/v4/sk_v4.c b/kernel/src/v4/sk_v4.c index 8838fe6e..b0ba43a6 100644 --- a/kernel/src/v4/sk_v4.c +++ b/kernel/src/v4/sk_v4.c @@ -29,7 +29,7 @@ #include "v4/boot.h" #include "block_subsystem.h" -#define SK_V4_LINE 255u /* what the line editor here takes */ +#define SK_V4_LINE 1024u /* what the line editor here takes: a line, as the boot takes one (v4/system/boot.c) */ /* v3's prompt (repl.c's SK_PROMPT_TEXT): "ok> " in bright cyan, after the * console's own "[zuse@Hera] " line prefix. */ @@ -82,12 +82,15 @@ static void sk_v4_serve(v4_node *node, unsigned request) * Zuse's session has ended (her drive was pulled): the caller goes headless * (not in a build with EMERGENCY_CONSOLE_ENABLED, whose console needs no * login: v3's sk_console_readline() the same). While nothing is typed the - * idle beat runs, as v3's. */ + * idle beat runs, as v3's. A line typed past SK_V4_LINE characters is not + * cut short and run: what is typed after that is not kept, and + * SK_V4_LINE + 1 is returned, which the boot refuses as too long + * (V4_BOOT_LINE_TOO_LONG), running nothing. */ #define SK_V4_CURSOR_BLINK 50u /* ticks between blinks, as v3's (500 ms at 100 Hz) */ static int sk_v4_readline(void) { - int len = 0; + int len = 0, over = 0; int cursor_on = 1; uint64_t blink = heartbeat_ticks(); @@ -113,11 +116,16 @@ static int sk_v4_readline(void) arch_relax(); continue; } - if (c == '\r' || c == '\n') { console_fb_erase_cursor(); console_putc('\n'); return len; } + if (c == '\r' || c == '\n') { + console_fb_erase_cursor(); console_putc('\n'); + return over ? (int)SK_V4_LINE + 1 : len; + } if (c == 8 || c == 127) { - if (len > 0) { len--; console_putc(8); console_putc(' '); console_putc(8); } - } else if (c >= 32 && c < 127 && len < (int)SK_V4_LINE) { - sk_v4_text[len++] = (char)c; + if (over > 0) { over--; console_putc(8); console_putc(' '); console_putc(8); } + else if (len > 0) { len--; console_putc(8); console_putc(' '); console_putc(8); } + } else if (c >= 32 && c < 127) { + if (len < (int)SK_V4_LINE) sk_v4_text[len++] = (char)c; + else over++; console_putc((char)c); } cursor_on = 1; /* typing shows a solid cursor, as v3's */ @@ -196,6 +204,7 @@ void sk_v4_run(void (*chain)(void), const BootDisk *boot_disk) break; } how = v4_boot_line(&boot, sk_v4_text, (unsigned)len); + if (how == V4_BOOT_LINE_TOO_LONG) { console_puts("Line too long\n ERROR\n"); how = V4_BOOT_LINE_WAITING; continue; } if (how == V4_TEXT_ERROR) console_puts(" ERROR\n" SK_V4_PROMPT); else if (how == V4_TEXT_COMPLETED) console_puts(" ok\n" SK_V4_PROMPT); else break; @@ -204,6 +213,7 @@ void sk_v4_run(void (*chain)(void), const BootDisk *boot_disk) if (logged_out && (how == V4_TEXT_ERROR || how == V4_TEXT_COMPLETED)) continue; if (how == V4_TEXT_COMPLETED) console_puts(" ok\n"); else if (how == V4_TEXT_ERROR) console_puts(" ERROR\n"); + else if (how == V4_BOOT_LINE_TOO_LONG) console_puts("Line too long\n ERROR\n"); else if (how == V4_BOOT_LINE_REFUSED) console_puts("Message refused\n ERROR\n"); /* the node's wire is full: error 19's words */ else if (how == V4_BOOT_LINE_STOPPED) { console_println("StarForth v4: the node stopped on a fault"); diff --git a/v4/src/exec.c b/v4/src/exec.c index ead41dd5..7c3461a2 100644 --- a/v4/src/exec.c +++ b/v4/src/exec.c @@ -107,6 +107,11 @@ void v4_exec_op(v4_node *n, v4_exec_state *es, v4_heat *h, v4_dstack *ds = &n->ds; v4_rstack *rs = &n->rs; v4_cell x; + /* A store can be refused by the node itself -- to "any port", "which + * port", How or Have (node.c) -- or raise an error (D-18). Refused, the + * opcode did nothing more (node.h, FAULTS); raised, the store was made, + * but P is the handler's. */ + unsigned faults = n->faults; if (op <= V4_OP_BANG_A && stack_faulted(n, op)) return; @@ -180,20 +185,27 @@ void v4_exec_op(v4_node *n, v4_exec_state *es, v4_heat *h, case V4_OP_STORE_P: if (faulted(n, n->p)) return; v4_node_store(n, n->p, v4_dstack_pop(ds)); + if (n->faults != faults) { /* P is the handler now: it is not advanced */ + if (n->fault_kind != V4_FAULT_RAISED) return; + break; + } n->p = add_wrap(n->p, 1); break; case V4_OP_STORE_INC: if (faulted(n, n->a)) return; v4_node_store(n, n->a, v4_dstack_pop(ds)); + if (n->faults != faults && n->fault_kind != V4_FAULT_RAISED) return; /* refused: A is untouched */ n->a = add_wrap(n->a, 1); break; case V4_OP_STORE_B: if (faulted(n, n->b)) return; v4_node_store(n, n->b, v4_dstack_pop(ds)); + if (n->faults != faults && n->fault_kind != V4_FAULT_RAISED) return; break; case V4_OP_STORE_A: if (faulted(n, n->a)) return; v4_node_store(n, n->a, v4_dstack_pop(ds)); + if (n->faults != faults && n->fault_kind != V4_FAULT_RAISED) return; break; case V4_OP_MUL_STEP: diff --git a/v4/tests/test_port.c b/v4/tests/test_port.c index 60f0b8cc..131d782f 100644 --- a/v4/tests/test_port.c +++ b/v4/tests/test_port.c @@ -133,6 +133,42 @@ int main(void) v4_node_fault(&n, V4_FAULT_RAISED, 12); /* no handler attached: the node stops */ CHECK(v4_exec_step_word(&n, &es, &h) == 0 && n.stopped, "a node its neighbour faults does not go on with the word"); + /* ---- a store the node refuses moves neither A nor P (node.h, FAULTS) ---- */ + { + const v4_cell table = 900, any = PORT + (v4_cell)V4_PORT_ANY; + v4_cell w_inc, w_storep; + + v4_asm_begin(&as, &n, 200); + w_inc = v4_asm_label(&as); + LIT(any); O(BANG_A); LIT(9); O(STORE_INC); O(SEMI); + w_storep = v4_asm_label(&as); /* a word a neighbour sends: !p ; */ + O(STORE_P); O(SEMI); + CHECK(v4_asm_ok(&as), "the refused-store words assemble"); + + v4_node_fault_attach(&n, table); + start(w_inc); + { + unsigned words; /* to the fault and no further: the table is empty */ + for (words = 0; words < 100 && n.faults == 0; words++) (void)v4_exec_step_word(&n, &es, &h); + } + CHECK(n.faults == 1 && n.fault_kind == V4_FAULT_ADDRESS && n.fault_addr == any, + "!+ to \"any port\" is an address fault"); + CHECK(n.a == any, "and A is untouched: %ld", (long)n.a); + CHECK(n.p == table + (v4_cell)V4_FAULT_ADDRESS, "P is the address fault's handler: %ld", (long)n.p); + + /* P at "any port", as a node newly born executes what it is sent + * (node.h, v4_node_born): the `!p` it is sent stores at P */ + v4_node_fault_attach(&n, table); + start(any); + v4_dstack_push(&n.ds, 9); + v4_node_port_give(&n, 0, n.mem[w_storep]); + (void)v4_exec_step_word(&n, &es, &h); + CHECK(n.faults == 1 && n.fault_kind == V4_FAULT_ADDRESS && n.fault_addr == any, + "!p with P at \"any port\" is an address fault"); + CHECK(n.p == table + (v4_cell)V4_FAULT_ADDRESS, + "and P is the address fault's handler, not the next one's: %ld", (long)n.p); + } + /* ---- detaching ---- */ v4_node_reset(&n); CHECK(n.port == -1 && !n.asking, "reset detaches the port"); diff --git a/v4/tools/hosted.c b/v4/tools/hosted.c index 3a3898e2..38539dbf 100644 --- a/v4/tools/hosted.c +++ b/v4/tools/hosted.c @@ -52,6 +52,24 @@ static void serve(v4_node *node, unsigned request) } } +/* A line from stdin, its new line taken off. Returns its length, or -1 when + * the input has ended. A line longer than the buffer is read to its end and + * the rest thrown away, so that no part of it is run as a line of its own: + * the length returned is then the buffer's, which is more than a line may be, + * and the boot refuses it as too long. */ +static long read_line(char *line, unsigned cap) +{ + size_t len; + int c; + + if (!fgets(line, (int)cap, stdin)) return -1; + len = strlen(line); + if (len && line[len - 1] == '\n') return (long)(len - 1); + if (len == cap - 1u) + while ((c = getchar()) != EOF && c != '\n') { } + return (long)len; +} + /* The prompt is the host's, as it is the kernel's REPL's for a v3 VM: read a * line, hand it to the node, say how it ended. */ int main(void) @@ -68,23 +86,19 @@ int main(void) if (!v4_boot_run(&boot)) { fflush(stdout); return 1; } for (;;) { - size_t len; + long len; int how; fputs("ok> ", stdout); fflush(stdout); - if (!fgets(line, sizeof line, stdin)) { putchar('\n'); return 0; } - len = strlen(line); - if (len && line[len - 1] == '\n') len--; + if ((len = read_line(line, sizeof line)) < 0) { putchar('\n'); return 0; } how = v4_boot_line(&boot, line, (unsigned)len); /* The line is waiting for an answer no one here can send. The next * line typed breaks the wait: the line that was waiting ends in an * error, and then the line just typed is done (boot.h). */ while (how == V4_BOOT_LINE_WAITING) { fflush(stdout); - if (!fgets(line, sizeof line, stdin)) { putchar('\n'); return 0; } - len = strlen(line); - if (len && line[len - 1] == '\n') len--; + if ((len = read_line(line, sizeof line)) < 0) { putchar('\n'); return 0; } how = v4_boot_line(&boot, line, (unsigned)len); if (how == V4_BOOT_LINE_TOO_LONG) { fputs("Line too long\n ERROR\n", stdout); how = V4_BOOT_LINE_WAITING; continue; } if (how == V4_TEXT_ERROR) fputs(" ERROR\nok> ", stdout);