feat(v4): the ROM assembles the system's .f18 to the nucleus, word for word

capsules/v4/sys: the nucleus's fourteen sources as .f18 blobs (made once by
v4/tools/v4tof18.py: header becomes the ROM's directives, every other token
and line kept), the memory map as map-<width>.f18 (mkmap sys, from one
table in host_map.h that host_load reads too), and end.f18, the switch-on
state mkimage.c set in C, the image record, and start (IDLE).

test_host_rom_nucleus.c: the ROM, fed those blobs through the console,
builds RAM identical word for word to mkimage.c's, outside its own state
and the image record, and starts at (IDLE), at both widths.  The cost:
60.7 million instructions, 1.5 s on the C model.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-15 12:27:50 -04:00
co-authored by Claude Opus 5.5
parent 084c520a54
commit 1bf19d6224
22 changed files with 4058 additions and 117 deletions
+11 -1
View File
@@ -114,7 +114,11 @@ ROM_DIR := $(ROOT)/capsules/v4/rom
ROM_FILES := $(wildcard $(addprefix $(ROM_DIR)/,main.f18 in.f18 num.f18 sym.f18 pack.f18 word.f18 dir.f18))
MKROM_WORDS := $(shell expr $(HOST_WORDS) + $(HOST_ROM_WORDS))
rom_src = $(if $(findstring /test_host_rom_,$(1)),$(BINDIR)/rom-$(2).c)
rom_sym = $(if $(findstring /test_host_rom_,$(1)),-DV4_ROM_SYM='"$(BINDIR)/rom-$(2).sym"')
rom_sym = $(if $(findstring /test_host_rom_,$(1)),-DV4_ROM_SYM='"$(BINDIR)/rom-$(2).sym"' -DV4_SYS_DIR='"$(SYS_DIR)"')
# THE SYSTEM'S SOURCE as the ROM reads it (MACHINE.md 3): capsules/v4/sys. Its
# map, map-<width>.f18, is written by tools/mkmap.c from tests/host_map.h
# (`make sysmap`) and kept in the tree, as capsules/BLOCK_MAP.md is.
SYS_DIR := $(ROOT)/capsules/v4/sys
define ROM_RULE
$(BINDIR)/mkmap-$(1): $(HERE)/tools/mkmap.c $$(SRCS) $$(wildcard $(HERE)/include/v4/*.h) $(HERE)/tests/host_map.h $(HERE)/Makefile
@@ -124,6 +128,9 @@ $(BINDIR)/mkmap-$(1): $(HERE)/tools/mkmap.c $$(SRCS) $$(wildcard $(HERE)/include
$(BINDIR)/rom-map-$(1).f18: $(BINDIR)/mkmap-$(1)
$(BINDIR)/mkmap-$(1) > $$@
$(SYS_DIR)/map-$(1).f18: $(BINDIR)/mkmap-$(1)
$(BINDIR)/mkmap-$(1) sys > $$@
$(BINDIR)/mkrom-$(1): $(HERE)/tools/mkrom.c $$(SRCS) $$(wildcard $(HERE)/include/v4/*.h) $(HERE)/Makefile
@mkdir -p $(BINDIR)
$$(CC) $$(CFLAGS) -I$(HERE)/include -DV4_CELL_BITS=$(1) -DV4_NODE_WORDS=$(MKROM_WORDS) -DV4_DATA_RING=$(HOST_DATA_RING) -DV4_RET_RING=$(HOST_RET_RING) \
@@ -137,6 +144,9 @@ $(foreach w,$(WIDTHS),$(eval $(call ROM_RULE,$(w))))
.PHONY: rom
rom: $(foreach w,$(WIDTHS),$(BINDIR)/rom-$(w).c)
.PHONY: sysmap
sysmap: $(foreach w,$(WIDTHS),$(SYS_DIR)/map-$(w).f18)
define IMAGE_RULE
$(BINDIR)/mkimage-$(1): $(HERE)/tools/mkimage.c $$(SRCS) $$(wildcard $(HERE)/include/v4/*.h) $(HERE)/tests/host_map.h $(HERE)/Makefile
@mkdir -p $(BINDIR)
+125 -115
View File
@@ -114,7 +114,10 @@
#define ROM_PI (BUF0_W - 122) /* working cells */
#define ROM_RS (BUF0_W - 121)
#define ROM_RN (BUF0_W - 120)
/* FREE, plain memory: BUF0_W - 22 .. - 21; - 119 .. - 113; BUF0_W - 144 .. - 141 (what a flush makes zero after
/* THE IMAGE RECORD: where the system's source says, with `store`, the addresses a host needs (v4/image.h): where the
* node waits for a message, its fault table, and KEY's code. */
#define ROM_IMG (BUF0_W - 119) /* idle, fault table, KEY, the word after KEY: 4 cells */
/* FREE, plain memory: BUF0_W - 22 .. - 21; - 115 .. - 113; BUF0_W - 144 .. - 141 (what a flush makes zero after
* a full output buffer); and OUT_W - 144 .. OUT_W - 65. They and
* the cells above held the messages waiting, the refusals owed and what went with them (MESH.md 7a, 7b), which a
* node no longer keeps. */
@@ -261,6 +264,124 @@ static unsigned host_branch_slots(void)
* assemble the `count` files named. Returns 1 if all assembled; otherwise
* prints which did not and why. The caller may assemble more text and must
* call v4_text_finish. */
/* THE CONSTANTS the system's source is assembled with: names for the addresses and numbers of this map. One
* list, read by host_load (here) and by tools/mkmap.c, which writes it as the system's map blob for the ROM. */
#define HOST_CONSTANTS(X) \
X("N-1", V4_CELL_BITS - 1) \
X("NODE-ERROR", NODE_ERROR) \
X("CONSOLE-TX", CONSOLE_TX) \
X("CONSOLE-RX", CONSOLE_RX) \
X("CONSOLE-STATUS", CONSOLE_ST) \
X("BASE", BASE) \
X("TIB", TIB) \
X(">IN", TO_IN) \
X("SPAN", SPAN) \
X("WBUF", WBUF) \
X("(P)", PVARS) \
X("DP", DP) \
X("(LATEST)", LATEST) \
X("DBASE", DICT_W * 4) \
X("DLIMIT", DICT_END_W * 4) \
X("PAD", PAD) \
X("(D)", DVARS) \
X("(CG)", CGVARS) \
X("CG-BSLOTS", (v4_cell)host_branch_slots()) \
X("STATE", STATE) \
X("(C)", CVARS) \
X("(CFP)", CFP) \
X("(Q)", QVARS) \
X("(F)", FVARS) \
X("(S)", SVARS) \
X("(W)", WVARS) \
X("(X)", XVARS) \
X("(BOOT)", BOOT_CELLS) \
X("(LOG-LEVEL)", LOG_LEVEL) \
X("(ACL-HOOK)", ACL_HOOK) \
X("SCR", SCR) \
X("BLK", BLK) \
X("(SRC)", SRC) \
X("(SRC-HOOK)", SRC_HOOK) \
X("(LINE-STATUS)", LINE_STATUS) \
X("(PORT)", PORT) \
X("(ME)", ME) \
X("(CONSOLE)", CONSOLE) \
X("(ROUTE#)", ROUTE_COUNT) \
X("(ROUTE-DEFAULT)", ROUTE_DEFAULT) \
X("(LOST)", LOST) \
X("(PRINT-TO)", PRINT_TO) \
X("(PRINT-PORT)", PRINT_PORT) \
X("(DONE-PORT)", DONE_PORT) \
X("(ROUTES)", ROUTES) \
X("-PORTS", -(v4_cell)V4_PORTS) \
X("(NEAR)", NEAR) \
X("(AWAIT-FROM)", AWAIT_FROM) \
X("(WIRE)", WIRE) \
X("(WIRE-A)", WIRE_A) \
X("(WIRE-B)", WIRE_B) \
X("(WIRE-DO)", WIRE_DO) \
X("(WIRE-HOW)", WIRE_HOW) \
X("(WIRE-HAVE)", WIRE_HAVE) \
X("(S-HAVE)", S_HAVE) \
X("(S-MASK)", S_MASK) \
X("(S-WIRE)", S_WIRE) \
X("(S-ON)", S_ON) \
X("(KEEP)", KEEP) \
X("(DEFER)", DEFER) \
X("(D-MASK)", D_MASK) \
X("(IN)", IN_HDR) \
X("(ONE)", ONE) \
X("(OUT-HDR)", OUT_HDR) \
X("(W-NEED)", W_NEED) \
X("(W-PORT)", W_PORT) \
X("(P-WAY)", P_WAY) \
X("(P-NEED)", P_NEED) \
X("(H-PORT)", H_PORT) \
X("(H-NEED)", H_NEED) \
X("(H-MASK)", H_MASK) \
X("(A-SELF)", A_SELF) \
X("(SEND^)", SEND_PTR) \
X("(OUT-TEXT)", OUT_TEXT) \
X("(SEND-TEXT)", SEND_TEXT) \
X("(QUIET)", QUIET) \
X("(REFUSED)", REFUSED) \
X("(OUT^)", OUT_PTR) \
X("(REPLY)", REPLY) \
X("(MSG)", MSG) \
X("(OUT)", OUT_W) \
X("(WORD-DEFINED)", WORD_DEFINED) \
X("(WORD-FORGOTTEN)", WORD_FORGOTTEN) \
X("BLOCK#", V4_REQ_BLOCK) /* the kernel's block requests (v4/blocks.h) */ \
X("BUFFER#", V4_REQ_BUFFER) \
X("UPDATE#", V4_REQ_UPDATE) \
X("SAVE-BUFFERS#", V4_REQ_SAVE_BUFFERS) \
X("EMPTY-BUFFERS#", V4_REQ_EMPTY_BUFFERS) \
X("(Q/)", QBASE) /* 5 cells */ \
X("(QE)", QBASE + 5) /* 8 */ \
X("(QR)", QBASE + 13) /* 5 */ \
X("(QL)", QBASE + 18) /* 7 */ \
X("(QT)", QBASE + 25) /* 6 */ \
X("(QP)", QBASE + 31) /* 4 */ \
X("(DP)", QBASE + 35) /* 4 */ \
X("ADDR-DIGITS", V4_CELL_BITS / 4) \
X("MSB", (v4_cell)V4_MSB) \
X("MAXHI", (v4_cell)(V4_MSB - 1u)) \
X("HIMASK", (v4_cell)(((v4_ucell)1 << (V4_CELL_BITS - 16)) - 1u)) \
X("2N+15", 2 * V4_CELL_BITS + 15) \
X("N-18", V4_CELL_BITS - 18) \
X("N-17", V4_CELL_BITS - 17) \
X("MAX-INT", (v4_cell)(V4_MSB - 1u)) \
X("HLD", HLD) \
X("FENCE", FENCE) \
X("CONTEXT", CONTEXT) \
X("CURRENT", CURRENT) \
X("VOC-LINK", VOC_LINK) \
X("HEND", HEND) \
X("-HFLOOR", -(HEND - 62)) \
X("DSTACK-DEPTH", DSTACK_REG) \
X("RSTACK-DEPTH", RSTACK_REG) \
X("CFBASE", CFS_W) \
X("CFEND", CFS_W + CFS_CELLS)
static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned count)
{
char path[512];
@@ -269,121 +390,10 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned
v4_node_reset(n);
HOST_MESSAGES_EMPTY(n); /* nothing has been printed and no message waits (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);
v4_text_constant(tx, "CONSOLE-TX", CONSOLE_TX);
v4_text_constant(tx, "CONSOLE-RX", CONSOLE_RX);
v4_text_constant(tx, "CONSOLE-STATUS", CONSOLE_ST);
v4_text_constant(tx, "BASE", BASE);
v4_text_constant(tx, "TIB", TIB);
v4_text_constant(tx, ">IN", TO_IN);
v4_text_constant(tx, "SPAN", SPAN);
v4_text_constant(tx, "WBUF", WBUF);
v4_text_constant(tx, "(P)", PVARS);
v4_text_constant(tx, "DP", DP);
v4_text_constant(tx, "(LATEST)", LATEST);
v4_text_constant(tx, "DBASE", DICT_W * 4);
v4_text_constant(tx, "DLIMIT", DICT_END_W * 4);
v4_text_constant(tx, "PAD", PAD);
v4_text_constant(tx, "(D)", DVARS);
v4_text_constant(tx, "(CG)", CGVARS);
v4_text_constant(tx, "CG-BSLOTS", (v4_cell)host_branch_slots());
v4_text_constant(tx, "STATE", STATE);
v4_text_constant(tx, "(C)", CVARS);
v4_text_constant(tx, "(CFP)", CFP);
v4_text_constant(tx, "(Q)", QVARS);
v4_text_constant(tx, "(F)", FVARS);
v4_text_constant(tx, "(S)", SVARS);
v4_text_constant(tx, "(W)", WVARS);
v4_text_constant(tx, "(X)", XVARS);
v4_text_constant(tx, "(BOOT)", BOOT_CELLS);
v4_text_constant(tx, "(LOG-LEVEL)", LOG_LEVEL);
v4_text_constant(tx, "(ACL-HOOK)", ACL_HOOK);
v4_text_constant(tx, "SCR", SCR);
v4_text_constant(tx, "BLK", BLK);
v4_text_constant(tx, "(SRC)", SRC);
v4_text_constant(tx, "(SRC-HOOK)", SRC_HOOK);
v4_text_constant(tx, "(LINE-STATUS)", LINE_STATUS);
v4_text_constant(tx, "(PORT)", PORT);
v4_text_constant(tx, "(ME)", ME);
v4_text_constant(tx, "(CONSOLE)", CONSOLE);
v4_text_constant(tx, "(ROUTE#)", ROUTE_COUNT);
v4_text_constant(tx, "(ROUTE-DEFAULT)", ROUTE_DEFAULT);
v4_text_constant(tx, "(LOST)", LOST);
v4_text_constant(tx, "(PRINT-TO)", PRINT_TO);
v4_text_constant(tx, "(PRINT-PORT)", PRINT_PORT);
v4_text_constant(tx, "(DONE-PORT)", DONE_PORT);
v4_text_constant(tx, "(ROUTES)", ROUTES);
v4_text_constant(tx, "-PORTS", -(v4_cell)V4_PORTS);
v4_text_constant(tx, "(NEAR)", NEAR);
v4_text_constant(tx, "(AWAIT-FROM)", AWAIT_FROM);
v4_text_constant(tx, "(WIRE)", WIRE);
v4_text_constant(tx, "(WIRE-A)", WIRE_A);
v4_text_constant(tx, "(WIRE-B)", WIRE_B);
v4_text_constant(tx, "(WIRE-DO)", WIRE_DO);
v4_text_constant(tx, "(WIRE-HOW)", WIRE_HOW);
v4_text_constant(tx, "(WIRE-HAVE)", WIRE_HAVE);
v4_text_constant(tx, "(S-HAVE)", S_HAVE);
v4_text_constant(tx, "(S-MASK)", S_MASK);
v4_text_constant(tx, "(S-WIRE)", S_WIRE);
v4_text_constant(tx, "(S-ON)", S_ON);
v4_text_constant(tx, "(KEEP)", KEEP);
v4_text_constant(tx, "(DEFER)", DEFER);
v4_text_constant(tx, "(D-MASK)", D_MASK);
v4_text_constant(tx, "(IN)", IN_HDR);
v4_text_constant(tx, "(ONE)", ONE);
v4_text_constant(tx, "(OUT-HDR)", OUT_HDR);
v4_text_constant(tx, "(W-NEED)", W_NEED);
v4_text_constant(tx, "(W-PORT)", W_PORT);
v4_text_constant(tx, "(P-WAY)", P_WAY);
v4_text_constant(tx, "(P-NEED)", P_NEED);
v4_text_constant(tx, "(H-PORT)", H_PORT);
v4_text_constant(tx, "(H-NEED)", H_NEED);
v4_text_constant(tx, "(H-MASK)", H_MASK);
v4_text_constant(tx, "(A-SELF)", A_SELF);
v4_text_constant(tx, "(SEND^)", SEND_PTR);
v4_text_constant(tx, "(OUT-TEXT)", OUT_TEXT);
v4_text_constant(tx, "(SEND-TEXT)", SEND_TEXT);
v4_text_constant(tx, "(QUIET)", QUIET);
v4_text_constant(tx, "(REFUSED)", REFUSED);
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#", V4_REQ_BLOCK); /* the kernel's block requests (v4/blocks.h) */
v4_text_constant(tx, "BUFFER#", V4_REQ_BUFFER);
v4_text_constant(tx, "UPDATE#", V4_REQ_UPDATE);
v4_text_constant(tx, "SAVE-BUFFERS#", V4_REQ_SAVE_BUFFERS);
v4_text_constant(tx, "EMPTY-BUFFERS#", V4_REQ_EMPTY_BUFFERS);
v4_text_constant(tx, "(Q/)", QBASE); /* 5 cells */
v4_text_constant(tx, "(QE)", QBASE + 5); /* 8 */
v4_text_constant(tx, "(QR)", QBASE + 13); /* 5 */
v4_text_constant(tx, "(QL)", QBASE + 18); /* 7 */
v4_text_constant(tx, "(QT)", QBASE + 25); /* 6 */
v4_text_constant(tx, "(QP)", QBASE + 31); /* 4 */
v4_text_constant(tx, "(DP)", QBASE + 35); /* 4 */
v4_text_constant(tx, "ADDR-DIGITS", V4_CELL_BITS / 4);
v4_text_constant(tx, "MSB", (v4_cell)V4_MSB);
v4_text_constant(tx, "MAXHI", (v4_cell)(V4_MSB - 1u));
v4_text_constant(tx, "HIMASK", (v4_cell)(((v4_ucell)1 << (V4_CELL_BITS - 16)) - 1u));
v4_text_constant(tx, "2N+15", 2 * V4_CELL_BITS + 15);
v4_text_constant(tx, "N-18", V4_CELL_BITS - 18);
v4_text_constant(tx, "N-17", V4_CELL_BITS - 17);
v4_text_constant(tx, "MAX-INT", (v4_cell)(V4_MSB - 1u));
v4_text_constant(tx, "HLD", HLD);
v4_text_constant(tx, "FENCE", FENCE);
v4_text_constant(tx, "CONTEXT", CONTEXT);
v4_text_constant(tx, "CURRENT", CURRENT);
v4_text_constant(tx, "VOC-LINK", VOC_LINK);
v4_text_constant(tx, "HEND", HEND);
v4_text_constant(tx, "-HFLOOR", -(HEND - 62));
v4_text_constant(tx, "DSTACK-DEPTH", DSTACK_REG);
v4_text_constant(tx, "RSTACK-DEPTH", RSTACK_REG);
#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);
v4_text_constant(tx, "CFBASE", CFS_W);
v4_text_constant(tx, "CFEND", CFS_W + CFS_CELLS);
for (i = 0; i < count; i++) {
snprintf(path, sizeof path, "%s/%s", V4_CAPSULE_DIR, files[i]);
if (!v4_text_assemble_file(tx, path)) {
+141
View File
@@ -0,0 +1,141 @@
/* test_host_rom_nucleus.c -- the proof: the ROM rebuilds today's nucleus, 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:
* instructions retired and seconds (Captain Bob: watch the startup cost).
*/
#include "rom_harness.h"
#include "v4/text.h"
#include <stdlib.h>
#include <time.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)
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 */
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;
}
/* the ROM's own cells, and the image record: 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;
}
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)");
/* 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,
(long)b.mem[ROM_FAILED], (long)b.mem[ROM_ERRLINE] + 1);
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.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");
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]);
break;
}
CHECK(w == (v4_cell)V4_NODE_WORDS, "every word of RAM is the same");
printf("test_host_rom_nucleus: %d checks, %d failures\n", checks, failures);
return failures != 0;
}
+18 -1
View File
@@ -1,6 +1,7 @@
/* mkmap.c -- the memory map, as constants the ROM is assembled with.
*
* mkmap > rom-map-<width>.f18
* mkmap sys > capsules/v4/sys/map-<width>.f18
*
* Runs on the build machine, built for the host node (HOST_WORDS and the
* host stack sizes) at one cell width. It writes, as `constant` lines in
@@ -17,10 +18,26 @@ static void constant(const char *name, long value)
printf("constant %s %ld\n", name, value);
}
int main(void)
/* `mkmap sys`: the system's map, the blob the ROM reads before the system's
* source (capsules/v4/sys/map-<width>.f18): every constant host_load gives
* the nucleus, and the two its end (capsules/v4/sys/end.f18) needs besides. */
static int system_map(void)
{
printf("\\ map-%d.f18 -- the memory map the system's source is assembled with, %d-bit cells.\n", V4_CELL_BITS, V4_CELL_BITS);
printf("\\ Generated by v4/tools/mkmap.c (mkmap sys) from v4/tests/host_map.h -- do not edit.\n");
#define SYS_CONSTANT(name, value) constant(name, (long)(v4_cell)(value));
HOST_CONSTANTS(SYS_CONSTANT)
#undef SYS_CONSTANT
constant("DICT-W", (long)DICT_W);
constant("(IMG)", (long)ROM_IMG);
return 0;
}
int main(int argc, char **argv)
{
(void)host_load; /* host_map.h's loader is not this tool's */
(void)host_branch_slots;
if (argc == 2 && argv[1][0] == 's') return system_map();
printf("\\ Generated by v4/tools/mkmap.c from v4/tests/host_map.h -- do not edit.\n");
constant("CONSOLE-RX", (long)CONSOLE_RX);
constant("CONSOLE-STATUS", (long)CONSOLE_ST);
+110
View File
@@ -0,0 +1,110 @@
#!/usr/bin/env python3
"""v4tof18.py -- turn the nucleus's .v4 sources into the system's .f18 blobs.
v4/tools/v4tof18.py
Run once (MACHINE.md step 2; plan Task 8): reads v4/capsule/*.v4 and writes
capsules/v4/sys/*.f18. Everything is kept token for token and line for
line except `header NAME flags :`, which text.c lays down itself and the
ROM's language says with its directives:
set HDR-NFA here ", NAME #, F #, HDR-NFA #, HDR-LINK set HDR-LINK here :
-- the name as a counted string, the flags (immediate 1, data 4, inline 8,
compile-only 16), the name's address and the link to the header before;
HDR-LINK is then the new entry's code address, as text.c's latest is. The
first file begins `set HDR-LINK 0`. The tokens are found as text.c's
next_token finds them: `\\` and `(` as whole tokens begin comments; a
header's name is the next run of characters, whatever they are.
"""
import os
import sys
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
SRC = os.path.join(ROOT, "v4", "capsule")
OUT = os.path.join(ROOT, "capsules", "v4", "sys")
FILES = ["core", "input", "dict", "codegen", "compile", "quit", "forth", "numout",
"words", "system", "qmath", "blocks", "log", "acl"]
FLAGS = {"immediate": 1, "data": 4, "inline": 8, "compile-only": 16}
BLANK = " \t\r\n"
def tokens(text):
"""(token, start, end) for each token text.c's next_token would return,
and for each header its raw name."""
i, n = 0, len(text)
raw_next = False
while True:
while i < n and text[i] in BLANK:
i += 1
if i >= n:
return
j = i
while j < n and text[j] not in BLANK:
j += 1
tok = text[i:j]
if raw_next:
raw_next = False
yield tok, i, j
i = j
continue
if tok == "\\":
while j < n and text[j] != "\n":
j += 1
i = j
continue
if tok == "(":
k = text.find(")", j)
if k < 0:
sys.exit("comment not closed")
i = k + 1
continue
yield tok, i, j
if tok == "header":
raw_next = True
i = j
def convert(text, first):
out, last = [], 0
toks = list(tokens(text))
k = 0
while k < len(toks):
tok, s, e = toks[k]
if tok != "header":
k += 1
continue
name = toks[k + 1][0]
flags, m = 0, k + 2
while toks[m][0] != ":":
if toks[m][0] not in FLAGS:
sys.exit("header %s: %s is not a flag" % (name, toks[m][0]))
flags |= FLAGS[toks[m][0]]
m += 1
colon = toks[m][1]
span = text[s:colon]
directive = ('set HDR-NFA here ", %s #, %d #, HDR-NFA #, HDR-LINK set HDR-LINK here '
% (name, flags))
out.append(text[last:s])
out.append(directive + "\n" * span.count("\n"))
last = colon
k = m
out.append(text[last:])
body = "".join(out)
if first:
body = "set HDR-LINK 0 \\ the newest dictionary entry: none yet\n" + body
return body
def main():
os.makedirs(OUT, exist_ok=True)
for i, f in enumerate(FILES):
with open(os.path.join(SRC, f + ".v4")) as h:
text = h.read()
with open(os.path.join(OUT, f + ".f18"), "w") as h:
h.write(convert(text, i == 0))
print("v4tof18: %d files -> %s" % (len(FILES), OUT))
if __name__ == "__main__":
main()