/* hosted.c -- StarForth v4 as a Linux program: the nucleus image on the * golden model, its capsules loaded as the system comes up (v4/include/v4/ * boot.h, the same boot the bare-metal kernel runs), the console on stdin * and stdout. * * It runs until its input ends. Blocks are kept in memory and are gone * when it stops. */ #include "v4/boot.h" #include "block_subsystem.h" #include #include static v4_node n; static v4_exec_state es; static v4_heat h; /* STORAGE. The node asks its kernel for its blocks (v4/blocks.h), and the * kernel's block subsystem has them (docs/v4.0.0/MESH.md section 8). A * Linux program is given what hosted v3 has when it is started with no * disk: the chain's fast RAM, blocks 1 to 2047, which is gone when the * program ends. */ static unsigned char block_ram[(size_t)BLK_RAM_BLOCKS * BLK_FORTH_SIZE]; extern void sf_time_init(void); static void console_out(const char *text, unsigned len) { (void)fwrite(text, 1, len, stdout); } /* a character for a line that reads the keyboard */ static int console_key(void) { int c; fflush(stdout); c = getchar(); return c == EOF ? V4_BOOT_KEY_END : c; } /* THE KERNEL'S WORDS, for a node that is a Linux program: the words that * are C in hosted v3 because only the host can do them. */ static const char *const kernel_words[] = { "BYE" }; static int leaving; static void serve(v4_node *node, unsigned request) { (void)node; if (request == 1) { /* BYE ( -- ): as hosted v3, say so and leave */ fputs("Goodbye!\n", stdout); leaving = 1; } } /* 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) { static char line[1026]; v4_boot boot; boot.n = &n; boot.es = &es; boot.h = &h; boot.im = &v4_capsule_image; boot.out = console_out; boot.key = console_key; boot.words = kernel_words; boot.word_count = sizeof kernel_words / sizeof kernel_words[0]; boot.serve = serve; sf_time_init(); if (blk_subsys_init(block_ram, sizeof block_ram) != BLK_OK) { fputs("V4: no block storage\n", stderr); return 1; } if (!v4_boot_run(&boot)) { fflush(stdout); return 1; } for (;;) { size_t 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--; 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--; 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); else if (how == V4_TEXT_COMPLETED) fputs(" ok\nok> ", stdout); else break; how = v4_boot_line(&boot, 0, 0); } if (how == V4_TEXT_COMPLETED) fputs(" ok\n", stdout); if (leaving) { fflush(stdout); return 0; } else if (how == V4_TEXT_ERROR) fputs(" ERROR\n", stdout); else if (how == V4_BOOT_LINE_TOO_LONG) fputs("Line too long\n ERROR\n", stdout); else if (how == V4_BOOT_LINE_NO_INPUT) { putchar('\n'); return 0; } else if (how == V4_BOOT_LINE_STOPPED) { fflush(stdout); fprintf(stderr, "starforth4: the node stopped on a fault\n"); return 1; } } }