feat(v4): the machine boots from its ROM; the prebuilt nucleus is gone
The host node starts in its ROM (entry 12), and the boot feeds it the system's source through the console: capsules/v4/sys/map-64.f18, the fourteen .f18 blobs and end.f18, each found, hash-checked and signature-checked as any capsule, then EOT. The ROM assembles the system and starts it at (IDLE); the host reads the image record (v4_image_from_node) for what only the assembled system knows. A ROM error is reported with its blob, line and meaning, then PARITY:FAIL. Deleted: v4/tools/mkimage.c, capsules/v4/nucleus-64.f18, v4/capsule/*.v4, v4/tools/v4tof18.py. The .f18 files are the source now. - text.c speaks the rest of the ROM's language (set, here, store, start; v4_text_value, v4_text_start), so the oracle reads the same source: test_host_rom_nucleus compares the ROM against text.c on the .f18 files, word for word, image record included. Host tests load the .f18 files. - mkmap image writes v4_map_image (the map's description of the system); hosted and the kernel link the ROM and the map instead of v4_image_64.c. - Hera (Captain Bob's ruling, option 1): births stream the system the ROM assembled, as assembled code; hera.4th is unchanged. test_host_unit's host builds the system with the ROM. - New parity lines: PARITY:V4_F18 per blob, PARITY:V4_ROM words=2049 rom_hash=0xa6c792b5821010b7 instructions=60725216. Acceptance: amd64, aarch64, riscv64 boot to ok> with PARITY:V4_POST tests=538 pass=538 fail=0, POST: PASSED, and PARITY:V4_SYSTEM word_count=317 dict_hash=0xe5a57f3eb72ff94a on all three -- the same hash as the prebuilt nucleus gave. make -C v4 test test-32 hosted-check: all pass. Logs: logs/20261015-141648, -141938, -142350. Startup cost under QEMU TCG, the ROM's assembly (last V4_F18 line to V4_ROM): amd64 26.7 s of 132 s to ok>; aarch64 41.0 s of 195 s; riscv64 20.5 s of 101 s. Hosted native: +0.85 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
1bf19d6224
commit
a73580e949
+20
-14
@@ -5,7 +5,7 @@
|
||||
* one. Every test_host_*.c uses this one, so that the capsule's variables
|
||||
* and buffers are in the same place in all of them:
|
||||
*
|
||||
* 16 .. the capsule's code, assembled from capsule/ *.v4
|
||||
* 16 .. the capsule's code, assembled from capsules/v4/sys/ *.f18
|
||||
* DICT_W .. DICT_END_W dictionary space for what is defined at run time
|
||||
* top of memory buffers, then variables, as below
|
||||
*/
|
||||
@@ -34,13 +34,13 @@
|
||||
#define RSTACK_REG (TOP - 16) /* RSTACK-DEPTH */
|
||||
|
||||
/* each capsule file's scratch cells */
|
||||
#define PVARS (TOP - 24) /* (P): input.v4, 8 cells */
|
||||
#define DVARS (TOP - 32) /* (D): dict.v4, 5 cells */
|
||||
#define CGVARS (TOP - 48) /* (CG): codegen.v4, 14 cells */
|
||||
#define CVARS (TOP - 62) /* (C): compile.v4, 10 cells */
|
||||
#define FVARS (TOP - 105) /* (F): forth.v4, 7 cells */
|
||||
#define PVARS (TOP - 24) /* (P): input.f18, 8 cells */
|
||||
#define DVARS (TOP - 32) /* (D): dict.f18, 5 cells */
|
||||
#define CGVARS (TOP - 48) /* (CG): codegen.f18, 14 cells */
|
||||
#define CVARS (TOP - 62) /* (C): compile.f18, 10 cells */
|
||||
#define FVARS (TOP - 105) /* (F): forth.f18, 7 cells */
|
||||
#define SVARS (TOP - 540 - (v4_cell)V4_DATA_DEPTH) /* (S): where PICK and ROLL set stack values aside, one cell for each cell of the stack */
|
||||
#define WVARS (TOP - 99) /* (W): numout.v4, 3 cells */
|
||||
#define WVARS (TOP - 99) /* (W): numout.f18, 3 cells */
|
||||
#define CONTEXT (TOP - 64) /* the vocabulary names are looked up in */
|
||||
#define CURRENT (TOP - 63) /* the vocabulary new words go into */
|
||||
#define VOC_LINK (TOP - 50) /* the newest vocabulary */
|
||||
@@ -57,14 +57,14 @@
|
||||
#define SBUF_W (TOP - 540) /* 64 cells for the tests' own strings */
|
||||
#define HBUF_W (SVARS - 16) /* the hold buffer: 16 cells = 64 bytes */
|
||||
#define HEND ((HBUF_W + 16) * 4)
|
||||
#define XVARS (HBUF_W - 4) /* (X): words.v4, 4 cells */
|
||||
#define XVARS (HBUF_W - 4) /* (X): words.f18, 4 cells */
|
||||
#define QBASE (XVARS - 40) /* the Q words' and DUMP's scratch cells, 39 in all */
|
||||
#define BVARS (QBASE - 16) /* blocks.v4: 6 cells not used now, then SCR BLK (SRC) (SRC-HOOK) */
|
||||
#define BVARS (QBASE - 16) /* blocks.f18: 6 cells not used now, then SCR BLK (SRC) (SRC-HOOK) */
|
||||
#define SCR (BVARS + 6)
|
||||
#define BLK (BVARS + 7)
|
||||
#define SRC (BVARS + 8)
|
||||
#define SRC_HOOK (BVARS + 9)
|
||||
#define QUIET (BUF0_W - 10) /* 1 while the node does something that is no text's doing: an error then is not told (quit.v4, (RAISED)) */
|
||||
#define QUIET (BUF0_W - 10) /* 1 while the node does something that is no text's doing: an error then is not told (quit.f18, (RAISED)) */
|
||||
#define REFUSED (BUF0_W - 7) /* how many of this node's messages it has been told were refused */
|
||||
#define ACL_HOOK (BUF0_W - 6) /* the xt of the access control recheck word, or 0 */
|
||||
#define LINE_STATUS (BUF0_W - 9) /* how the last line ended: 0 QUIT, 1 completed, 2 an error */
|
||||
@@ -76,7 +76,7 @@
|
||||
#define PRINT_TO (BUF0_W - 20) /* where what the text being served prints is to go: the node, */
|
||||
#define PRINT_PORT (BUF0_W - 33) /* and the port, by its number, that leads there */
|
||||
#define DONE_PORT (BUF0_W - 34) /* the port, by its number, that leads back to whoever sent the text being served */
|
||||
/* WHOLE MESSAGES (MESH.md 7d.4; core.v4). A node keeps no message but the one it is doing: these are what it
|
||||
/* WHOLE MESSAGES (MESH.md 7d.4; core.f18). A node keeps no message but the one it is doing: these are what it
|
||||
* builds one in and looks at one with, and where it has got to in going through its wires. */
|
||||
#define WIRE (PORT + (v4_cell)V4_PORT_WIRE) /* the six addresses after the ports (node.h): the wire an operation is about, */
|
||||
#define WIRE_A (PORT + (v4_cell)V4_PORT_A) /* what it needs, */
|
||||
@@ -235,7 +235,7 @@
|
||||
typedef char host_map_ports_fit[(PORT >= OUT_W + OUT_CELLS && PORT + (v4_cell)V4_PORT_HAVE < OUT_HDR) ? 1 : -1];
|
||||
/* Nothing printed: a node's variable for it, as at switch-on. */
|
||||
#define HOST_MESSAGES_EMPTY(n) do { (n)->mem[OUT_PTR] = OUT_W; } while (0)
|
||||
#define QVARS (BUF0_W - 4) /* (Q): quit.v4, system.v4 and blocks.v4, 4 cells */
|
||||
#define QVARS (BUF0_W - 4) /* (Q): quit.f18, system.f18 and blocks.f18, 4 cells */
|
||||
#define WBUF (WBUF_W * 4)
|
||||
#define TIB (TIB_W * 4)
|
||||
#define PAD (PAD_W * 4)
|
||||
@@ -388,19 +388,25 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned
|
||||
unsigned i;
|
||||
|
||||
v4_node_reset(n);
|
||||
HOST_MESSAGES_EMPTY(n); /* nothing has been printed and no message waits (core.v4) */
|
||||
HOST_MESSAGES_EMPTY(n); /* nothing has been printed and no message waits (core.f18) */
|
||||
v4_text_begin(tx, n, 16);
|
||||
#define HOST_CONSTANT(name, value) v4_text_constant(tx, name, (v4_cell)(value));
|
||||
HOST_CONSTANTS(HOST_CONSTANT)
|
||||
#undef HOST_CONSTANT
|
||||
v4_node_stack_regs_attach(n, DSTACK_REG, RSTACK_REG);
|
||||
for (i = 0; i < count; i++) {
|
||||
snprintf(path, sizeof path, "%s/%s", V4_CAPSULE_DIR, files[i]);
|
||||
snprintf(path, sizeof path, "%s/%s", V4_SYS_DIR, files[i]);
|
||||
if (!v4_text_assemble_file(tx, path)) {
|
||||
printf(" %s does not assemble: %s\n", files[i], v4_text_error(tx));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* the newest entry the source laid down (its HDR-LINK) is the
|
||||
* assembler's latest: a `header` in a test's own text links to it */
|
||||
{
|
||||
v4_cell newest;
|
||||
if (v4_text_value(tx, "HDR-LINK", &newest)) tx->latest = newest;
|
||||
}
|
||||
/* one vocabulary, FORTH, whose head cell is LATEST */
|
||||
n->mem[CONTEXT] = LATEST;
|
||||
n->mem[CURRENT] = LATEST;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "v4/testcode.h"
|
||||
#include "host_map.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if V4_CELL_BITS == 64
|
||||
@@ -84,9 +85,13 @@ static int rom_in(const v4_node *n, const char *name)
|
||||
return (long)n->p >= start && (long)n->p < next;
|
||||
}
|
||||
|
||||
static char *rom_system_text(size_t *len);
|
||||
static int rom_build_system(v4_node *n, long *words);
|
||||
|
||||
/* Reset `n`, attach the ROM and the console's receive registers. */
|
||||
static void rom_boot(v4_node *n)
|
||||
{
|
||||
(void)rom_build_system;
|
||||
(void)host_load; /* host_map.h's loader is not used here */
|
||||
(void)host_branch_slots;
|
||||
(void)rom_call; /* not every test uses every helper */
|
||||
@@ -99,4 +104,61 @@ static void rom_boot(v4_node *n)
|
||||
v4_node_console_input_attach(n, CONSOLE_RX, CONSOLE_ST);
|
||||
}
|
||||
|
||||
/* THE SYSTEM, AS THE MACHINE BUILDS IT (MACHINE.md section 6, step 1). The
|
||||
* system's source, capsules/v4/sys (V4_SYS_DIR): the map, the fourteen and
|
||||
* the end, each followed by a new line, then EOT -- what v4/system/boot.c
|
||||
* feeds the console. rom_system_text gives it whole, *len its length;
|
||||
* rom_build_system has `n`, booted with its ROM, assemble it, and runs it to
|
||||
* its start: 1 if it started, with *words the instruction words it took. */
|
||||
static const char *const rom_system_files[] = { "core", "input", "dict", "codegen", "compile", "quit", "forth",
|
||||
"numout", "words", "system", "qmath", "blocks", "log", "acl" };
|
||||
|
||||
static char *rom_system_text(size_t *len)
|
||||
{
|
||||
size_t total = 0, cap = 1u << 20;
|
||||
char *text = malloc(cap);
|
||||
unsigned i;
|
||||
if (!text) return NULL;
|
||||
for (i = 0; i < 16; i++) {
|
||||
char path[512];
|
||||
FILE *f;
|
||||
long n;
|
||||
if (i == 0) snprintf(path, sizeof path, "%s/map-%d.f18", V4_SYS_DIR, V4_CELL_BITS);
|
||||
else if (i <= 14) snprintf(path, sizeof path, "%s/%s.f18", V4_SYS_DIR, rom_system_files[i - 1]);
|
||||
else snprintf(path, sizeof path, "%s/end.f18", V4_SYS_DIR);
|
||||
f = fopen(path, "rb");
|
||||
if (!f) { printf(" cannot read %s\n", path); free(text); return NULL; }
|
||||
fseek(f, 0, SEEK_END); n = ftell(f); fseek(f, 0, SEEK_SET);
|
||||
if (n < 0 || total + (size_t)n + 2 > cap || fread(text + total, 1, (size_t)n, f) != (size_t)n) {
|
||||
fclose(f); free(text); return NULL;
|
||||
}
|
||||
fclose(f);
|
||||
total += (size_t)n;
|
||||
text[total++] = '\n'; /* a blob that does not end with a new line still ends a token */
|
||||
}
|
||||
text[total++] = 4; /* EOT */
|
||||
*len = total;
|
||||
return text;
|
||||
}
|
||||
|
||||
static int rom_build_system(v4_node *n, long *words)
|
||||
{
|
||||
size_t total, fed = 0;
|
||||
char *text = rom_system_text(&total);
|
||||
long w = 0;
|
||||
if (!text) return 0;
|
||||
rom_boot(n);
|
||||
v4_node_stack_regs_attach(n, DSTACK_REG, RSTACK_REG);
|
||||
n->p = V4_ROM_BASE + 12; /* entry 12, (ROM): where the machine starts */
|
||||
while (n->p >= V4_ROM_BASE && w < 400000000L && !n->stopped) {
|
||||
if (fed < total && n->input_pos == n->input_len) fed += v4_node_console_feed(n, text + fed, (unsigned)(total - fed));
|
||||
(void)v4_exec_step_word(n, &rom_es, &rom_h);
|
||||
w++;
|
||||
if (w % 1000 == 0 && n->mem[ROM_FAILED] != 0) break;
|
||||
}
|
||||
free(text);
|
||||
if (words) *words = w;
|
||||
return n->p < V4_ROM_BASE && n->mem[ROM_FAILED] == 0;
|
||||
}
|
||||
|
||||
#endif /* V4_TESTS_ROM_HARNESS_H */
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* test_host_codegen.c -- the code generator, executed on the host node.
|
||||
*
|
||||
* capsule/codegen.v4 packs opcodes, literals and branches into instruction
|
||||
* capsules/v4/sys/codegen.f18 packs opcodes, literals and branches into instruction
|
||||
* words at HERE; it is what the compiling words will be built on. It is the
|
||||
* third thing in this tree that does that job -- after the slot packer in C
|
||||
* (asm.h) and the text assembler on top of it (text.h) -- and the first that
|
||||
@@ -107,7 +107,7 @@ static int lay_by_text(void)
|
||||
return v4_text_assemble(&rtx, src) && v4_text_finish(&rtx);
|
||||
}
|
||||
|
||||
/* By the code generator, on `n`: one call to a word of codegen.v4 for each
|
||||
/* By the code generator, on `n`: one call to a word of codegen.f18 for each
|
||||
* item, as the compiling words will make them. */
|
||||
static int lay_by_generator(void)
|
||||
{
|
||||
@@ -179,7 +179,7 @@ int main(void)
|
||||
CHECK(bslots == 3, "at this node size a branch may sit in slots 0 .. 2: %u", bslots);
|
||||
|
||||
{
|
||||
static const char *const files[] = { "core.v4", "input.v4", "dict.v4", "codegen.v4" };
|
||||
static const char *const files[] = { "core.f18", "input.f18", "dict.f18", "codegen.f18" };
|
||||
CHECK(host_load(&tx, &n, files, 4), "the capsule assembles");
|
||||
}
|
||||
CHECK(v4_text_finish(&tx), "everything is defined: %s", v4_text_error(&tx));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* test_host_compile.c -- the defining and compiling words, executed on the
|
||||
* host node.
|
||||
*
|
||||
* capsule/compile.v4 and capsule/forth.v4 on top of the earlier layers: for
|
||||
* capsules/v4/sys/compile.f18 and capsules/v4/sys/forth.f18 on top of the earlier layers: for
|
||||
* the first time FORTH source text goes in and running code comes out. Each
|
||||
* test hands lines of source to INTERPRET, which defines words with : and ;
|
||||
* and the rest, runs them, and leaves results on the stack.
|
||||
@@ -195,7 +195,7 @@ int main(void)
|
||||
printf("v4 host compiler tests: V4_CELL_BITS=%d, V4_NODE_WORDS=%u\n", V4_CELL_BITS, (unsigned)V4_NODE_WORDS);
|
||||
|
||||
{
|
||||
static const char *const files[] = { "core.v4", "input.v4", "dict.v4", "codegen.v4", "compile.v4", "quit.v4", "forth.v4" };
|
||||
static const char *const files[] = { "core.f18", "input.f18", "dict.f18", "codegen.f18", "compile.f18", "quit.f18", "forth.f18" };
|
||||
CHECK(host_load(&tx, &n, files, 7), "the capsule assembles");
|
||||
}
|
||||
/* two in-line words of the test's own, to exercise the in-liner: one with
|
||||
|
||||
@@ -375,15 +375,15 @@ int main(void)
|
||||
|
||||
printf("v4 mesh depth tests: V4_CELL_BITS=%d, V4_NODE_WORDS=%u\n", V4_CELL_BITS, (unsigned)V4_NODE_WORDS);
|
||||
{
|
||||
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" };
|
||||
static const char *const files[] = { "core.f18", "input.f18", "dict.f18", "codegen.f18", "compile.f18", "quit.f18", "forth.f18",
|
||||
"numout.f18", "words.f18", "system.f18", "qmath.f18", "blocks.f18", "log.f18", "acl.f18" };
|
||||
CHECK(host_load(&tx, &nucleus, files, 14), "the nucleus assembles");
|
||||
}
|
||||
CHECK(v4_text_finish(&tx), "everything is defined: %s", v4_text_error(&tx));
|
||||
if (failures) { printf(" %d checks, %d failures\n", checks, failures); return 1; }
|
||||
w_idle = v4_text_word(&tx, "(IDLE)");
|
||||
w_fault = v4_text_word(&tx, "(FAULTS)");
|
||||
nucleus.mem[DP] = DICT_W * 4; /* the variables, as at switch-on (tools/mkimage.c) */
|
||||
nucleus.mem[DP] = DICT_W * 4; /* the variables, as at switch-on (capsules/v4/sys/end.f18) */
|
||||
nucleus.mem[LATEST] = v4_text_latest(&tx);
|
||||
nucleus.mem[CFP] = CFS_W;
|
||||
nucleus.mem[BASE] = 10;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
*
|
||||
* DECOMPOSITION.md 5.12 and 5.13: HERE ALIGN ALLOT , C, 2, PAD LATEST, FIND
|
||||
* and ', and the entry-field words. The definitions are text,
|
||||
* capsule/dict.v4, on top of core.v4 and input.v4.
|
||||
* capsules/v4/sys/dict.f18, on top of core.f18 and input.f18.
|
||||
*
|
||||
* v4's dictionary is new: v3's was a C structure and its words handed out
|
||||
* host pointers. What carries over is what the words mean:
|
||||
@@ -192,7 +192,7 @@ int main(void)
|
||||
printf("v4 host dictionary tests: V4_CELL_BITS=%d, V4_NODE_WORDS=%u\n", V4_CELL_BITS, (unsigned)V4_NODE_WORDS);
|
||||
|
||||
{
|
||||
static const char *const files[] = { "core.v4", "input.v4", "dict.v4" };
|
||||
static const char *const files[] = { "core.f18", "input.f18", "dict.f18" };
|
||||
CHECK(host_load(&tx, &n, files, 3), "the capsule assembles");
|
||||
}
|
||||
capsule_latest = v4_text_latest(&tx);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* DECOMPOSITION.md 5.9 and 5.15: TIB >IN SPAN SOURCE BL EXPECT QUERY WORD
|
||||
* ENCLOSE CONVERT NUMBER and the comment words. The definitions are text,
|
||||
* capsule/core.v4 and capsule/input.v4, assembled here by the text assembler.
|
||||
* capsules/v4/sys/core.f18 and capsules/v4/sys/input.f18, assembled here by the text assembler.
|
||||
*
|
||||
* These are FORTH-79 words and follow the standard (ruled 2026-10-04) where
|
||||
* v3 did something else:
|
||||
@@ -211,7 +211,7 @@ int main(void)
|
||||
printf("v4 host input tests: V4_CELL_BITS=%d, V4_NODE_WORDS=%u\n", V4_CELL_BITS, (unsigned)V4_NODE_WORDS);
|
||||
|
||||
{
|
||||
static const char *const files[] = { "core.v4", "input.v4" };
|
||||
static const char *const files[] = { "core.f18", "input.f18" };
|
||||
CHECK(host_load(&tx, &n, files, 2), "the capsule assembles");
|
||||
}
|
||||
/* the in-line names, as words the test can call */
|
||||
@@ -263,7 +263,7 @@ 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");
|
||||
/* a node has no console of its own: what it prints is kept to be sent (core.v4) */
|
||||
/* a node has no console of its own: what it prints is kept to be sent (core.f18) */
|
||||
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");
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* console --1 [10] 2--2 [11] 3--2 [12]
|
||||
*
|
||||
* Each node is the nucleus (v4/capsule/ *.v4) and nothing else. They start
|
||||
* Each node is the nucleus (capsules/v4/sys/ *.f18) and nothing else. They start
|
||||
* alike, numbered 0 and knowing no way anywhere; everything they come to
|
||||
* know they are told by text sent from the console, as whoever wires a node
|
||||
* tells it. The console is a device on a port and speaks whole messages
|
||||
@@ -343,15 +343,15 @@ int main(void)
|
||||
|
||||
printf("v4 mesh tests: V4_CELL_BITS=%d, V4_NODE_WORDS=%u\n", V4_CELL_BITS, (unsigned)V4_NODE_WORDS);
|
||||
{
|
||||
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" };
|
||||
static const char *const files[] = { "core.f18", "input.f18", "dict.f18", "codegen.f18", "compile.f18", "quit.f18", "forth.f18",
|
||||
"numout.f18", "words.f18", "system.f18", "qmath.f18", "blocks.f18", "log.f18", "acl.f18" };
|
||||
CHECK(host_load(&tx, &nucleus, files, 14), "the nucleus assembles");
|
||||
}
|
||||
CHECK(v4_text_finish(&tx), "everything is defined: %s", v4_text_error(&tx));
|
||||
if (failures) { printf(" %d checks, %d failures\n", checks, failures); return 1; }
|
||||
w_idle = v4_text_word(&tx, "(IDLE)");
|
||||
w_fault = v4_text_word(&tx, "(FAULTS)");
|
||||
nucleus.mem[DP] = DICT_W * 4; /* the variables, as at switch-on (tools/mkimage.c) */
|
||||
nucleus.mem[DP] = DICT_W * 4; /* the variables, as at switch-on (capsules/v4/sys/end.f18) */
|
||||
nucleus.mem[LATEST] = v4_text_latest(&tx);
|
||||
nucleus.mem[CFP] = CFS_W;
|
||||
nucleus.mem[BASE] = 10;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* test_host_quit.c -- the prompt, executed on the host node.
|
||||
*
|
||||
* capsule/quit.v4 on top of the earlier layers: QUIT, ABORT, ABORT" and ." .
|
||||
* capsules/v4/sys/quit.f18 on top of the earlier layers: QUIT, ABORT, ABORT" and ." .
|
||||
* For the first time the node is not handed a line: it is started at QUIT and
|
||||
* left running, characters are fed to its console, and what it prints is
|
||||
* read back. Nothing here calls INTERPRET or pokes TIB.
|
||||
@@ -558,7 +558,7 @@ static const transcript script[] = {
|
||||
{ "2 BASE ! 5 . DECIMAL\n", "UNKNOWN WORD: '5'\n ERROR\nok> ", 1 },
|
||||
{ ": T 10 0 DO I . LOOP ; T\n", "0 1 2 3 4 5 6 7 8 9 ok\nok> ", 1 },
|
||||
{ "DEPTH . 1 2 DEPTH . . .\n", "0 2 2 1 ok\nok> ", 1 },
|
||||
/* qmath.v4: every result printed by Q.PRINT, which shows all sixteen bits of the fraction */
|
||||
/* qmath.f18: every result printed by Q.PRINT, which shows all sixteen bits of the fraction */
|
||||
{ "1 Q.FROM-INT 1 Q.FROM-INT Q./ Q.PRINT\n", "1.00000 ok\nok> ", 1 },
|
||||
{ "1 Q.FROM-INT 1 Q.FROM-INT Q./ Q.SQRT Q.PRINT\n", "1.00000 ok\nok> ", 1 },
|
||||
{ "1 Q.FROM-INT 1 Q.FROM-INT Q./ Q.LOG Q.ABS Q.PRINT\n", "0.00000 ok\nok> ", 1 },
|
||||
@@ -697,7 +697,7 @@ static const transcript script[] = {
|
||||
{ "Q.0 Q.LOG 65 EMIT\n-1 Q.FROM-INT Q.LOG\n", "Argument out of range\n ERROR\nok> Argument out of range\n ERROR\nok> ", 0 },
|
||||
{ "7 PAD -1 DUMP 65 EMIT\n.S\n", "Negative count\n ERROR\nok> <1> 7 \n ok\nok> ", 0 },
|
||||
{ "PAD 0 DUMP\n", " ok\nok> ", 1 },
|
||||
/* acl.v4: the fields of an entry, and the check, as v3 */
|
||||
/* acl.f18: the fields of an entry, and the check, as v3 */
|
||||
{ ": FOO 65 EMIT ; ' FOO ACL-MODE@ . ' FOO ACL-TTL@ . ' FOO ACL-ALLOW@ .\n' FOO ACL-PINNED? .\n", "0 0 -1 ok\nok> 0 ok\nok> ", 1 },
|
||||
{ ": FOO 65 EMIT ; 1 ' FOO ACL-MODE! 77 ' FOO ACL-TTL! ' FOO ACL-MODE@ .\n' FOO ACL-TTL@ . FOO ' FOO ACL-TTL@ .\n", "1 ok\nok> 77 A76 ok\nok> ", 1 },
|
||||
{ ": FOO 65 EMIT ; ' FOO ACL-PIN ' FOO ACL-PINNED? . 5 ' FOO ACL-TTL!\n0 ' FOO ACL-ALLOW! 1 ' FOO ACL-MODE!\n' FOO ACL-TTL@ . ' FOO ACL-ALLOW@ . ' FOO ACL-MODE@ .\n",
|
||||
@@ -720,7 +720,7 @@ static const transcript script[] = {
|
||||
{ ": IM 65 EMIT ; IMMEDIATE 3 ' IM ACL-TTL! 0 ' IM ACL-ALLOW! : U IM ;\n", "\033[33mWARN: \033[0mACL: denied 'IM'\n ERROR\nok> ", 0 },
|
||||
{ ": P1 ; : P2 ; VOCABULARY VV VV DEFINITIONS : P3 ; FORTH DEFINITIONS\n9 ' P1 ACL-TTL! 1 ' P2 ACL-MODE! ' P2 ACL-PIN VV 9 ' P3 ACL-TTL!\nACL-INIT-PRIMITIVES ' P1 ACL-TTL@ . ' P2 ACL-MODE@ . ' P3 ACL-TTL@ . FORTH\n",
|
||||
" ok\nok> ok\nok> 0 1 0 ok\nok> ", 0 },
|
||||
/* log.v4: v3's lines, colours and all, without its time of day */
|
||||
/* log.f18: v3's lines, colours and all, without its time of day */
|
||||
{ "LOG-LEVEL@ . LOG-ERROR . LOG-WARN . LOG-INFO . LOG-TEST . LOG-DEBUG .\n",
|
||||
"2 0 1 2 3 4 ok\nok> ", 1 },
|
||||
{ "LOG-INFO\" hello info\"\nLOG-WARN\" careful\"\nLOG-ERROR\" bad\"\nLOG-DEBUG\" dbg\"\nLOG-TEST\" tst\"\n",
|
||||
@@ -750,7 +750,7 @@ static const transcript script[] = {
|
||||
"\033[32mINFO: \033[0m\n ok\nok> ", 1 },
|
||||
{ ": W LOG-ERROR\" e\" LOG-WARN\" w\" LOG-TEST\" t\" ; W 3 LOG-LEVEL! W\n",
|
||||
"\033[31mERROR: \033[0me\n\033[33mWARN: \033[0mw\n\033[31mERROR: \033[0me\n\033[33mWARN: \033[0mw\n\033[35mTEST: \033[0mt\n ok\nok> ", 1 },
|
||||
/* blocks.v4 */
|
||||
/* blocks.f18 */
|
||||
{ "20 BLOCK 1024 BLANK 65 20 BLOCK C! UPDATE SAVE-BUFFERS 20 BLOCK C@ .\n",
|
||||
"65 ok\nok> ", 1 },
|
||||
{ "20 BLOCK 1024 BLANK 20 BLOCK 16 65 FILL UPDATE 20 LIST\nSCR @ .\n",
|
||||
@@ -789,7 +789,7 @@ static const transcript script[] = {
|
||||
{ "7 FORGET DUP 65 EMIT\nFORGET NOSUCH\nDUP . .\n", "Protected word\n ERROR\nok> UNKNOWN WORD: 'NOSUCH'\n ERROR\nok> 7 7 ok\nok> ", 0 },
|
||||
{ ": S3 S\" \" SWAP DROP . S\" ab\" S\" cde\" TYPE TYPE ; S3\n", "0 cdeab ok\nok> ", 0 },
|
||||
{ ": T3 ['] NOSUCH ;\n['] DUP\n", "UNKNOWN WORD: 'NOSUCH'\n ERROR\nok> [']: compile-only\n ERROR\nok> ", 0 },
|
||||
/* words.v4 */
|
||||
/* words.f18 */
|
||||
{ "1 2 3 4 2SWAP . . . .\n", "2 1 4 3 ok\nok> ", 1 },
|
||||
{ "1 2 3 4 2OVER . . . . . .\n", "2 1 4 3 2 1 ok\nok> ", 1 },
|
||||
{ "1 2 3 4 5 6 2ROT . . . . . .\n", "2 1 6 5 4 3 ok\nok> ", 1 },
|
||||
@@ -918,7 +918,7 @@ int main(void)
|
||||
CHECK(storage_made(), "the chains behind the storage port are made");
|
||||
|
||||
{
|
||||
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" };
|
||||
static const char *const files[] = { "core.f18", "input.f18", "dict.f18", "codegen.f18", "compile.f18", "quit.f18", "forth.f18", "numout.f18", "words.f18", "system.f18", "qmath.f18", "blocks.f18", "log.f18", "acl.f18" };
|
||||
CHECK(host_load(&tx, &n, files, 14), "the capsule assembles");
|
||||
}
|
||||
CHECK(v4_text_finish(&tx), "everything is defined: %s", v4_text_error(&tx));
|
||||
@@ -927,7 +927,7 @@ int main(void)
|
||||
if (failures) { printf(" %d checks, %d failures\n", checks, failures); return 1; }
|
||||
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_key_end = v4_text_word(&tx, "CR"); /* the word after KEY in core.f18 */
|
||||
w_fault = v4_text_word(&tx, "(FAULTS)");
|
||||
capsule_latest = v4_text_latest(&tx);
|
||||
CHECK(w_key_end > w_key && w_key_end - w_key <= 12, "KEY is the few words before CR");
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/* test_host_rom_nucleus.c -- the proof: the ROM rebuilds today's nucleus, word for word.
|
||||
/* test_host_rom_nucleus.c -- the proof: the ROM assembles the system as text.c does, word for word.
|
||||
*
|
||||
* Plan Task 8; MACHINE.md section 6, step 1. Node A is built as
|
||||
* v4/tools/mkimage.c builds the nucleus: text.c assembles v4/capsule/ *.v4,
|
||||
* then the switch-on state is set in C. Node B is the machine with its ROM,
|
||||
* fed the system's blobs -- capsules/v4/sys/map-<width>.f18, the fourteen
|
||||
* .f18 files made from the .v4 ones, and end.f18 -- through the console.
|
||||
* When the ROM starts the system, every word of B's RAM must be A's, except
|
||||
* the ROM's own state area and the image record (which A does not have, and
|
||||
* which must hold A's addresses). It also reports what the assembly cost:
|
||||
* Plan Task 8 and 9; MACHINE.md section 6, step 1. The system's source is
|
||||
* capsules/v4/sys: map-<width>.f18, the fourteen .f18 files and end.f18.
|
||||
* Node A is that source assembled off the node by text.c, the oracle (it
|
||||
* speaks the ROM's language: constant, set, here, #, ", store, start).
|
||||
* Node B is the machine with its ROM, fed the same source through the
|
||||
* console. When the ROM starts the system, every word of B's RAM must be
|
||||
* A's, except the ROM's own state area (which A does not have), and B must
|
||||
* start where A's start says. It also reports what the assembly cost:
|
||||
* instructions retired and seconds (Captain Bob: watch the startup cost).
|
||||
*/
|
||||
#include "rom_harness.h"
|
||||
@@ -20,118 +20,55 @@ static int failures = 0, checks = 0;
|
||||
|
||||
static v4_node a, b;
|
||||
static v4_text tx;
|
||||
static const char *const files[] = { "core", "input", "dict", "codegen", "compile", "quit", "forth",
|
||||
"numout", "words", "system", "qmath", "blocks", "log", "acl" };
|
||||
|
||||
/* node A: mkimage.c's main, as it is */
|
||||
/* node A: text.c assembles the system's source, map first and end last */
|
||||
static int build_a(void)
|
||||
{
|
||||
static const char *const v4files[] = { "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" };
|
||||
v4_cell k;
|
||||
if (!host_load(&tx, &a, v4files, 14) || !v4_text_finish(&tx)) return 0;
|
||||
a.mem[DP] = DICT_W * 4;
|
||||
a.mem[LATEST] = v4_text_latest(&tx);
|
||||
a.mem[STATE] = 0;
|
||||
a.mem[CFP] = CFS_W;
|
||||
a.mem[BASE] = 10;
|
||||
a.mem[FENCE] = DICT_W;
|
||||
a.mem[LOG_LEVEL] = 2;
|
||||
a.mem[CONTEXT] = LATEST;
|
||||
a.mem[CURRENT] = LATEST;
|
||||
a.mem[SRC] = TIB;
|
||||
a.mem[BOOT_CELLS] = a.mem[DP];
|
||||
a.mem[BOOT_CELLS + 1] = a.mem[LATEST];
|
||||
a.mem[FENCE] = (a.mem[DP] + 3) / 4;
|
||||
a.mem[NODE_ERROR] = 0;
|
||||
a.mem[BLK] = 0; a.mem[SCR] = 0; a.mem[SRC] = TIB; a.mem[SRC_HOOK] = 0;
|
||||
for (k = BVARS; k < BVARS + 6; k++) a.mem[k] = 0;
|
||||
for (k = BUF0_W; k < BUF0_W + (v4_cell)(V4_BLOCK_SLOTS * V4_BLOCK_CELLS); k++) a.mem[k] = 0;
|
||||
for (k = TIB_W; k < TIB_W + 260; k++) a.mem[k] = 0;
|
||||
for (k = WBUF_W; k < WBUF_W + WBUF_CELLS; k++) a.mem[k] = 0;
|
||||
for (k = PAD_W; k < PAD_W + 21; k++) a.mem[k] = 0;
|
||||
a.mem[TO_IN] = 0; a.mem[SPAN] = 0;
|
||||
a.mem[LINE_STATUS] = 1;
|
||||
HOST_MESSAGES_EMPTY(&a);
|
||||
return 1;
|
||||
char path[512];
|
||||
unsigned i;
|
||||
v4_node_reset(&a);
|
||||
v4_text_begin(&tx, &a, ROM_ORIGIN);
|
||||
for (i = 0; i < 16; i++) {
|
||||
if (i == 0) snprintf(path, sizeof path, "%s/map-%d.f18", V4_SYS_DIR, V4_CELL_BITS);
|
||||
else if (i <= 14) snprintf(path, sizeof path, "%s/%s.f18", V4_SYS_DIR, rom_system_files[i - 1]);
|
||||
else snprintf(path, sizeof path, "%s/end.f18", V4_SYS_DIR);
|
||||
if (!v4_text_assemble_file(&tx, path)) return 0;
|
||||
}
|
||||
return v4_text_start(&tx) >= 0;
|
||||
}
|
||||
|
||||
/* the ROM's own cells, and the image record: not compared */
|
||||
/* the ROM's own cells: not compared */
|
||||
static int excluded(v4_cell w)
|
||||
{
|
||||
return (w >= OUT_W - 144 && w < OUT_W - 64)
|
||||
|| (w >= BUF0_W - 32 && w < BUF0_W - 22)
|
||||
|| (w >= BUF0_W - 122 && w < BUF0_W - 119)
|
||||
|| (w >= ROM_IMG && w < ROM_IMG + 4);
|
||||
}
|
||||
|
||||
static char *slurp(const char *name, size_t *len)
|
||||
{
|
||||
char path[512];
|
||||
FILE *f;
|
||||
char *t;
|
||||
long n;
|
||||
snprintf(path, sizeof path, "%s/%s", V4_SYS_DIR, name);
|
||||
f = fopen(path, "rb");
|
||||
if (!f) { printf(" cannot read %s\n", path); return NULL; }
|
||||
fseek(f, 0, SEEK_END); n = ftell(f); fseek(f, 0, SEEK_SET);
|
||||
t = malloc((size_t)n + 2);
|
||||
if (fread(t, 1, (size_t)n, f) != (size_t)n) { fclose(f); free(t); return NULL; }
|
||||
fclose(f);
|
||||
t[n] = '\n'; t[n + 1] = 0; /* a blob that does not end with a new line still ends a token */
|
||||
*len = (size_t)n + 1;
|
||||
return t;
|
||||
|| (w >= BUF0_W - 122 && w < BUF0_W - 119);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
static char *text;
|
||||
size_t total = 0, cap = 1u << 20, len, fed = 0;
|
||||
char name[32];
|
||||
unsigned i;
|
||||
long words = 0;
|
||||
clock_t t0, t1;
|
||||
v4_cell w, idle_a;
|
||||
|
||||
CHECK(build_a(), "node A builds as mkimage.c builds it: %s", v4_text_error(&tx));
|
||||
idle_a = v4_text_word(&tx, "(IDLE)");
|
||||
CHECK(build_a(), "text.c assembles the system's source to its start: %s", v4_text_error(&tx));
|
||||
idle_a = v4_text_start(&tx);
|
||||
CHECK(idle_a == v4_text_word(&tx, "(IDLE)"), "the system starts at (IDLE)");
|
||||
|
||||
/* the system's source, as the loader will feed it: map, the fourteen, end, EOT */
|
||||
text = malloc(cap);
|
||||
snprintf(name, sizeof name, "map-%d.f18", V4_CELL_BITS);
|
||||
for (i = 0; i < 16; i++) {
|
||||
char *t;
|
||||
if (i == 0) t = slurp(name, &len);
|
||||
else if (i <= 14) { char f18[32]; snprintf(f18, sizeof f18, "%s.f18", files[i - 1]); t = slurp(f18, &len); }
|
||||
else t = slurp("end.f18", &len);
|
||||
if (!t) { CHECK(0, "a blob is missing"); return 1; }
|
||||
memcpy(text + total, t, len); total += len; free(t);
|
||||
}
|
||||
text[total++] = 4;
|
||||
|
||||
rom_boot(&b);
|
||||
v4_node_stack_regs_attach(&b, DSTACK_REG, RSTACK_REG);
|
||||
b.p = V4_ROM_BASE + 12;
|
||||
t0 = clock();
|
||||
while (b.p >= V4_ROM_BASE && words < 400000000L && !b.stopped) {
|
||||
if (fed < total) fed += v4_node_console_feed(&b, text + fed, (unsigned)(total - fed));
|
||||
(void)v4_exec_step_word(&b, &rom_es, &rom_h);
|
||||
words++;
|
||||
if (words % 1000 == 0 && rom_in(&b, "(ROM-FAILED)")) break;
|
||||
}
|
||||
t1 = clock();
|
||||
CHECK(b.p < V4_ROM_BASE, "the ROM started the system: P %ld, failed %ld at line %ld", (long)b.p,
|
||||
CHECK(rom_build_system(&b, &words), "the ROM started the system: P %ld, failed %ld at line %ld", (long)b.p,
|
||||
(long)b.mem[ROM_FAILED], (long)b.mem[ROM_ERRLINE] + 1);
|
||||
t1 = clock();
|
||||
printf(" the ROM assembled the system: %lu instructions, %lu instruction words, %.2f s (%d-bit cells)\n",
|
||||
(unsigned long)rom_es.anticlock, (unsigned long)words, (double)(t1 - t0) / CLOCKS_PER_SEC, V4_CELL_BITS);
|
||||
|
||||
CHECK(b.p == idle_a, "it starts at (IDLE): %ld, mkimage's %ld", (long)b.p, (long)idle_a);
|
||||
CHECK(b.p == idle_a, "it starts where text.c's start says: %ld, text.c's %ld", (long)b.p, (long)idle_a);
|
||||
CHECK(b.mem[ROM_IMG] == idle_a && b.mem[ROM_IMG + 1] == v4_text_word(&tx, "(FAULTS)")
|
||||
&& b.mem[ROM_IMG + 2] == v4_text_word(&tx, "KEY") && b.mem[ROM_IMG + 3] == v4_text_word(&tx, "CR"),
|
||||
"the image record holds mkimage's addresses");
|
||||
"the image record holds text.c's addresses");
|
||||
for (w = 0; w < (v4_cell)V4_NODE_WORDS; w++) {
|
||||
if (excluded(w) || a.mem[w] == b.mem[w]) continue;
|
||||
CHECK(0, "RAM word %ld: mkimage %lx, ROM %lx", (long)w, (unsigned long)a.mem[w], (unsigned long)b.mem[w]);
|
||||
CHECK(0, "RAM word %ld: text.c %lx, ROM %lx", (long)w, (unsigned long)a.mem[w], (unsigned long)b.mem[w]);
|
||||
break;
|
||||
}
|
||||
CHECK(w == (v4_cell)V4_NODE_WORDS, "every word of RAM is the same");
|
||||
|
||||
+14
-28
@@ -7,8 +7,12 @@
|
||||
* kernel --0 | / \ | square; the corners across from each
|
||||
* [13]---[14] other are not wired
|
||||
*
|
||||
* Hera is born empty and is given the nucleus through the port her requests
|
||||
* go to, as the boot gives it (v4/system/boot.c). She is told who she is,
|
||||
* The host builds the system as the machine does: a node with its ROM
|
||||
* assembles capsules/v4/sys (MACHINE.md section 6, step 1; tests/
|
||||
* rom_harness.h). That system, assembled, is the nucleus capsule: a node
|
||||
* that is born is given assembled code, never the source (Captain Bob,
|
||||
* 2026-10-15). Hera is born empty and is given it through the port her
|
||||
* requests go to. She is told who she is,
|
||||
* given the words that ask for nodes (v4/include/v4/manage.h), FORTH-79,
|
||||
* POST, and her own capsule, capsules/v4/hera.4th. Then she is told
|
||||
* `10 UNIT`, and everything after that is hers: she asks for four nodes,
|
||||
@@ -16,7 +20,7 @@
|
||||
* joins them and then FORTH-79 and POST a line at a time, and joins them.
|
||||
*
|
||||
* What stands in here for the host: the capsules are read from the files
|
||||
* the build bakes in, and the nucleus is assembled as mkimage assembles it.
|
||||
* the build bakes in.
|
||||
*
|
||||
* STORAGE (MESH.md section 8, acceptance 3). Every node asks its kernel
|
||||
* for its blocks, on its port 0, and the kernel's block subsystem
|
||||
@@ -38,14 +42,13 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "host_map.h"
|
||||
#include "rom_harness.h"
|
||||
|
||||
static int failures = 0, checks = 0;
|
||||
#define CHECK(c,...) do{checks++; if(!(c)){failures++; printf("FAIL %s:%d: ",__FILE__,__LINE__); printf(__VA_ARGS__); printf("\n");}}while(0)
|
||||
|
||||
#define PLACES 10u
|
||||
static v4_text tx;
|
||||
static v4_node nucleus; /* the nucleus, assembled: what the capsule of F18 code is written from */
|
||||
static v4_node nucleus; /* the system, as the ROM assembled it: what the capsule of F18 code is written from */
|
||||
static v4_cell w_idle, w_fault;
|
||||
static v4_fabric_node pool[PLACES];
|
||||
static int pool_used[PLACES];
|
||||
@@ -369,28 +372,11 @@ int main(void)
|
||||
return 0;
|
||||
}
|
||||
CHECK(storage_made(), "the kernel's blocks are there before any node is");
|
||||
{
|
||||
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" };
|
||||
CHECK(host_load(&tx, &nucleus, files, 14), "the nucleus assembles");
|
||||
}
|
||||
CHECK(v4_text_finish(&tx), "everything is defined: %s", v4_text_error(&tx));
|
||||
CHECK(rom_build_system(&nucleus, 0), "the host's machine assembles the system with its ROM");
|
||||
if (failures) { printf(" %d checks, %d failures\n", checks, failures); return 1; }
|
||||
w_idle = v4_text_word(&tx, "(IDLE)");
|
||||
w_fault = v4_text_word(&tx, "(FAULTS)");
|
||||
nucleus.mem[DP] = DICT_W * 4; /* the variables, as at switch-on (tools/mkimage.c) */
|
||||
nucleus.mem[LATEST] = v4_text_latest(&tx);
|
||||
nucleus.mem[CFP] = CFS_W;
|
||||
nucleus.mem[BASE] = 10;
|
||||
nucleus.mem[FENCE] = DICT_W;
|
||||
nucleus.mem[BOOT_CELLS] = nucleus.mem[DP];
|
||||
nucleus.mem[BOOT_CELLS + 1] = nucleus.mem[LATEST];
|
||||
nucleus.mem[LOG_LEVEL] = 2;
|
||||
nucleus.mem[CONTEXT] = LATEST;
|
||||
nucleus.mem[CURRENT] = LATEST;
|
||||
nucleus.mem[SRC] = TIB;
|
||||
nucleus.mem[LINE_STATUS] = 1;
|
||||
HOST_MESSAGES_EMPTY(&nucleus);
|
||||
w_idle = nucleus.mem[ROM_IMG]; /* the image record (capsules/v4/sys/end.f18) */
|
||||
w_fault = nucleus.mem[ROM_IMG + 1];
|
||||
CHECK(nucleus.p == w_idle, "and it has started where it waits");
|
||||
(void)v4_capsule_write(&nucleus, w_idle, f18_put, 0);
|
||||
CHECK(f18_count > 1000 && f18_count < sizeof f18 / sizeof f18[0], "the nucleus is a capsule of F18 code: %u cells", f18_count);
|
||||
memset(&im, 0, sizeof im);
|
||||
@@ -437,7 +423,7 @@ int main(void)
|
||||
CHECK(hera->mem[DP] == before[DP] && hera->mem[LATEST] == before[LATEST], "HERE and LATEST are where they were before POST");
|
||||
for (k = 0; k < here; k++) if (hera->mem[k] != before[k]) { if (first < 0) first = k; changed++; }
|
||||
/* What differs is the TTL in the entry of each word POST had interpreted: a count of uses left before
|
||||
* the word's access is checked again, which every use counts down (compile.v4, (ACL?)), as in v3. */
|
||||
* the word's access is checked again, which every use counts down (compile.f18, (ACL?)), as in v3. */
|
||||
printf(" after POST and COLD: %ld of the %ld cells below HERE differ from before POST (the first at %ld); dict_hash %s\n",
|
||||
(long)changed, (long)here, (long)first, v4_image_dict_hash(hera, &im) == hash ? "the same" : "differs");
|
||||
}
|
||||
|
||||
+31
-1
@@ -424,7 +424,7 @@ int main(void)
|
||||
CHECK(refused(": T &VAR ;", "already defined", 1), "and be a word");
|
||||
CHECK(refused(": &X 1 ;", "cannot be used as a name", 1), "a name cannot begin with &");
|
||||
|
||||
/* headers: a dictionary entry in front of a word (capsule/dict.v4) */
|
||||
/* headers: a dictionary entry in front of a word (capsules/v4/sys/dict.f18) */
|
||||
CHECK(ok("header DUP : (DUP) dup + ;"), "a header assembles: %s", v4_text_error(&tx));
|
||||
{
|
||||
v4_cell xt = v4_text_word(&tx, "(DUP)");
|
||||
@@ -521,6 +521,36 @@ int main(void)
|
||||
"#, and \", are reserved");
|
||||
}
|
||||
|
||||
/* ---- `set`, `here`, `store` and `start`: the rest of the ROM's language ---- */
|
||||
{
|
||||
v4_cell v = 0;
|
||||
CHECK(ok("set V 3 set V 4 #, V") && nb.mem[ORIGIN] == 4 && v4_text_value(&tx, "V", &v) && v == 4,
|
||||
"a set variable may be set again; the newest value is its value: %s", v4_text_error(&tx));
|
||||
CHECK(ok("set V 7 : T V ;") && result("T", 0, 0, 0) == 7, "a set variable in code is a literal");
|
||||
CHECK(ok("constant K 2 set V K+1 #, V") && nb.mem[ORIGIN] == 3, "set takes a VALUE");
|
||||
CHECK(ok(": T ; set H here #, H set H here #, H") && nb.mem[ORIGIN + 1] == ORIGIN + 1 && nb.mem[ORIGIN + 2] == ORIGIN + 2,
|
||||
"here is the next word address, the word before ended");
|
||||
CHECK(ok(": T dup set H here ; #, H") && nb.mem[ORIGIN + 2] == ORIGIN + 1, "here inside a word ends the instruction word");
|
||||
CHECK(ok("set V 1 #, V+1") == 0, "CONST+N is for constants only");
|
||||
CHECK(refused("constant K 1 set K 2", "already defined", 1), "set over a constant is refused");
|
||||
CHECK(refused("set V 1 constant V 2", "already defined", 1), "a constant over a set variable is refused");
|
||||
CHECK(refused("set V", "set without a value", 1), "set needs a value");
|
||||
CHECK(refused(": T here ;", "here is only a value", 1), "here alone is not something to assemble");
|
||||
CHECK(ok("constant A 300 store A 5 store A+1 &T : T ;") == 0, "store's &NAME needs a word already defined");
|
||||
CHECK(ok(": T ; constant A 300 store A 5 store A+1 &T") && nb.mem[300] == 5 && nb.mem[301] == ORIGIN,
|
||||
"store puts a VALUE anywhere in RAM");
|
||||
CHECK(refused("store -1 5", "address out of range", 1), "store outside RAM is refused");
|
||||
CHECK(refused("store 5", "store without a value", 1), "store needs a value");
|
||||
CHECK(ok(": A ; : B ; start B") && v4_text_start(&tx) == ORIGIN + 1, "start names the word the system starts at");
|
||||
CHECK(ok(": A ;") && v4_text_start(&tx) == -1, "no start: -1");
|
||||
CHECK(refused(": A C ; start A", "never defined", 1), "at start every branch has its target");
|
||||
CHECK(refused("constant K 1 start K", "start of something not a word", 1), "start of a constant is refused");
|
||||
CHECK(refused(": A ; start A : B ;", "text after start", 1), "nothing comes after start");
|
||||
CHECK(refused(": set ;", "cannot be used as a name", 1) && refused(": here ;", "cannot be used as a name", 1)
|
||||
&& refused(": store ;", "cannot be used as a name", 1) && refused(": start ;", "cannot be used as a name", 1),
|
||||
"set, here, store and start are reserved");
|
||||
}
|
||||
|
||||
CHECK(v4_node_guards_intact(&na) && v4_node_guards_intact(&nb), "guards intact");
|
||||
|
||||
printf(" %d checks, %d failures\n", checks, failures);
|
||||
|
||||
Reference in New Issue
Block a user