feat(v4): the shared math in both ROMs, and a mesh node's ROM

MACHINE.md 2.2.  UM*, UM/MOD, D+, DNEGATE, Q.* and Q./ are entries 0-5
of the host ROM and of a mesh node's ROM, from one source (rom/math.f18)
and one entry list (v4/rom_entries.h).  The host ROM starts at entry 18.
The mesh ROM is 277 words; the host ROM 2372.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-15 19:46:00 -04:00
co-authored by Claude Opus 5.5
parent 473e105a69
commit 11b5d402b1
11 changed files with 426 additions and 43 deletions
+17 -14
View File
@@ -4,22 +4,25 @@
\ routines through this table and never to an address inside the ROM, so a
\ rebuilt ROM breaks no payload. It is at the ROM's base, one jump a word,
\ in a fixed order that only ever grows at its end. Entry i is at the ROM's
\ base plus i. DATASHEET-F18.md lists each routine's stack effect.
\ base plus i. Entries 0-5 are the shared math, the same in a mesh node's
\ ROM (mesh.f18, MACHINE.md 2.2). DATASHEET-F18.md lists each routine's
\ stack effect.
: (ENTRIES)
jump (KEY) \ 0 ( -- c )
jump (TOKEN) \ 1 ( -- len )
jump (NUMBER) \ 2 ( -- n flag )
jump (FIND) \ 3 ( -- rec | 0 )
jump (DEFINE) \ 4 ( value kind -- )
jump (OPCODE) \ 5 ( -- op | -1 )
jump (OP) \ 6 ( op -- )
jump (LIT) \ 7 ( v -- )
jump (BRANCH) \ 8 ( op target -- )
jump (BRANCH>) \ 9 ( op -- ref )
jump (RESOLVE) \ 10 ( ref target -- )
jump (LABEL) \ 11 ( -- addr )
jump (ROM) \ 12 the ROM itself: where the machine starts
(MATH-ENTRIES) \ 0-5 the shared math (math.f18; v4/rom_entries.h)
jump (KEY) \ 6 ( -- c )
jump (TOKEN) \ 7 ( -- len )
jump (NUMBER) \ 8 ( -- n flag )
jump (FIND) \ 9 ( -- rec | 0 )
jump (DEFINE) \ 10 ( value kind -- )
jump (OPCODE) \ 11 ( -- op | -1 )
jump (OP) \ 12 ( op -- )
jump (LIT) \ 13 ( v -- )
jump (BRANCH) \ 14 ( op target -- )
jump (BRANCH>) \ 15 ( op -- ref )
jump (RESOLVE) \ 16 ( ref target -- )
jump (LABEL) \ 17 ( -- addr )
jump (ROM) \ 18 the ROM itself: where the machine starts (V4_ROM_START)
\ ( -- ) the state the ROM begins from: code goes to ORIGIN, nothing read,
\ nothing defined
+143
View File
@@ -0,0 +1,143 @@
\ math.f18 -- the ROM's shared math (docs/v4.0.0/MACHINE.md 2.2).
\
\ Six routines a payload calls through the ROM's entry table, as a GA144
\ application calls its node ROM's multiply and divide steps, and their
\ helpers. Both ROMs carry them: the host node's and a mesh node's. They
\ were the system's (capsules/v4/sys: core.f18, numout.f18, qmath.f18) and
\ do exactly what they did there; the system's words of those names now call
\ these. No names, no headers, no FORTH.
\
\ Constants from the map (tools/mkmap.c): N-1 NODE-ERROR MSB MAXHI 2N+15
\ N-18 N-17, and (Q/), Q./'s five scratch cells, fixed in the memory map
\ (MACHINE.md 2.1).
\
\ ERRORS. Q./ by zero leaves the result D-11 gives and stores 11, "Division
\ by zero", into NODE-ERROR (D-18).
macro SWAP over push push drop pop pop endmacro
macro - push inv pop + inv endmacro \ x y -- x-y
macro NIP push drop pop endmacro
macro ROT push SWAP pop SWAP endmacro
macro OR over inv and xor endmacro
\ ( u1 u2 -- ulo uhi ) entry 0
: (UM*)
over 1 and inv 1 + over 2/ and \ u1 u2 t0 t0 = u2 2/ if u1 odd
over a! N-1 push \ A: u2 R: loop count
push over 2/ pop \ u1 u2 s t0 s = u1 2/
L: +* unext \ u1 u2 s hi A: lo
push drop pop \ u1 u2 hi
2* a -if L0 drop 1 + jump L1 L0: drop L1: push
over -if L2 drop dup jump L3 L2: drop 0 L3: push
dup -if L4 drop over 1 and jump L5 L4: drop 0 L5:
pop + pop + push
and 1 and a 2* + pop ;
\ ( ulo uhi ud -- urem uquot ) entry 1. One step a bit, the divisor in
\ A. Exact for uhi < ud.
: (UM/MOD)
a! N-1 FOR
-if L0
2* over -if L1 drop 1 + jump L2
L1: drop L2: push 2* pop
jump SUB
L0:
2* over -if L3 drop 1 + jump L4
L3: drop L4: push 2* pop
dup a xor -if L5
drop -if NOSUB jump SUB
L5: drop dup inv a + inv -if L6
drop jump NOSUB
L6: push drop pop jump SETBIT
SUB: inv a + inv
SETBIT: push 1 + pop
NOSUB:
NEXT
over push push drop pop pop ;
\ ( d1 d2 -- d3 ) entry 2
: (D+)
push over push push drop pop \ al bl R: bh ah
over over xor -if L1
drop + -if C1 jump C0
L1: drop over -if L2
drop + jump C1
L2: drop +
C0: pop pop + ;
C1: pop pop + 1 + ;
\ ( d -- -d ) entry 3
: (DNEGATE)
inv over if L1 drop push inv 1 + pop ;
L1: drop 1 + ;
\ ( q -- n ) the system's Q.TO-INT, which Q.* needs
: (Q>INT) push a! 0 pop 15 FOR +* UNEXT drop drop a ;
\ ( a b -- c ) entry 4
: (Q.*)
SWAP push over over (UM*) drop
push push over pop
-if L1 SWAP jump L2 L1: SWAP drop 0 L2:
pop SWAP - push
push over pop (UM*) pop +
ROT pop dup push
over -if L3 drop dup jump L4 L3: drop 0 L4:
push (UM*) pop - (D+)
ROT pop (UM*)
SWAP push 0 (D+) pop
push over pop SWAP (Q>INT) push (Q>INT) pop SWAP ;
: (QERR11) NODE-ERROR b! 11 !b ;
: (D2*C) ( lo hi cin -- lo' hi' cout )
a! dup -if L1 drop 1 jump L2 L1: drop 0 L2: push
over -if L3 drop 1 jump L4 L3: drop 0 L4:
over + +
push 2* a + pop pop ;
: (UQ/) ( a0 a1 b0 b1 -- q0 q1 )
(Q/)+2 a! SWAP !+ ! (Q/) a! SWAP !+ ! 0 0 0
2N+15 FOR
push (Q/) a! @+ @ pop (D2*C)
push (Q/)+1 a! ! (Q/) a! ! pop (D2*C)
if CMP drop jump TAKE
CMP: drop (Q/)+3 b! @b
over over xor if EQH -if SAMEH
drop drop -if NT1 jump TAKE NT1: jump NOTAKE
SAMEH: drop over SWAP push inv pop + inv
-if T2 drop jump NOTAKE T2: drop jump TAKE
EQH: drop drop over (Q/)+2 b! @b
over over xor -if SAMEL
drop drop -if NT3 drop jump TAKEL NT3: drop jump NOTAKEL
SAMEL: drop push inv pop + inv
-if T4 drop jump NOTAKEL T4: drop jump TAKEL
TAKEL: drop (Q/)+2 b! @b push inv pop + inv 0 1 jump END
NOTAKEL: 0 jump END
TAKE: push (Q/)+2 b! @b over over push inv pop + inv push
over over xor -if SB drop NIP -if NB drop 1 jump BD NB: drop 0 jump BD
SB: drop push inv pop + inv -if NB2 drop 1 jump BD NB2: drop 0
BD: pop pop (Q/)+3 b! @b push inv pop + inv
push SWAP pop SWAP - 1 jump END
NOTAKE: 0
END:
NEXT
push (Q/) a! @+ @ pop (D2*C) drop push push drop drop pop pop ;
: (Q./) ( a b -- q ) \ entry 5
over over OR if ZERO drop
push over pop SWAP over xor (Q/)+4 b! !b
-if L1 (DNEGATE) L1: push push -if L2 (DNEGATE) L2: pop pop
dup if CHK drop jump DIV
CHK: drop over -131072 and if CHK2 drop jump DIV
CHK2: drop push push dup pop dup push N-18 FOR 2* UNEXT
over over xor -if SAME drop drop -if NOOV jump OV
SAME: drop push inv pop + inv -if OV
NOOV: drop pop pop jump DIV
OV: drop drop drop pop pop drop drop
(Q/)+4 b! @b -if OVP drop 0 MSB ; OVP: drop -1 MAXHI ;
DIV: (UQ/) (Q/)+4 b! @b -if L3 drop (DNEGATE) ; L3: drop ;
ZERO: drop drop drop
over over OR if Z0 drop -if ZP drop drop 0 MSB jump (QERR11)
ZP: drop drop -1 MAXHI jump (QERR11)
Z0: drop jump (QERR11)
+10
View File
@@ -0,0 +1,10 @@
\ mesh.f18 -- a mesh node's ROM: its entry table (docs/v4.0.0/MACHINE.md 2.2).
\
\ A mesh node's ROM is the shared math (math.f18) and nothing else. It has
\ no start: a mesh node at reset blocks on its ports and executes what
\ arrives (docs/v4.0.0/MESH.md 4.2 and 5). Its entries are the first of
\ the host ROM's, at the same numbers: (MATH-ENTRIES), which tools/mkmap.c
\ writes from v4/include/v4/rom_entries.h.
: (ENTRIES)
(MATH-ENTRIES)
+12 -6
View File
@@ -99,15 +99,18 @@ ENGINE_SRCS := $(addprefix $(HERE)/src/,node.c exec.c stack.c iword.c heat.c gua
# code in capsules/v4/rom, assembled off the node by tools/mkrom.c, as a
# bitstream is built. tools/mkmap.c writes the memory map's constants it is
# assembled with, from tests/host_map.h. main.f18 comes first: the ROM's
# entry table is at its base. Tests named test_host_rom_*.c, and
# entry table is at its base. math.f18, the shared math, is in both: tools/mkrom.c
# builds mesh-rom-<w>.c from mesh.f18 and math.f18 (MACHINE.md 2.2). Tests named test_host_rom_*.c, and
# test_host_unit.c, whose host builds the system with it, link the ROM of
# their width and read its symbols.
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))
ROM_FILES := $(wildcard $(addprefix $(ROM_DIR)/,main.f18 in.f18 num.f18 sym.f18 pack.f18 word.f18 dir.f18 math.f18))
# A mesh node's ROM (MACHINE.md 2.2): its entry table, then the shared math.
MESH_ROM_FILES := $(wildcard $(addprefix $(ROM_DIR)/,mesh.f18 math.f18))
MKROM_WORDS := $(shell expr $(HOST_WORDS) + $(HOST_ROM_WORDS))
uses_rom = $(or $(findstring /test_host_rom_,$(1)),$(findstring /test_host_unit,$(1)))
rom_src = $(if $(call uses_rom,$(1)),$(BINDIR)/rom-$(2).c)
rom_sym = $(if $(call uses_rom,$(1)),-DV4_ROM_SYM='"$(BINDIR)/rom-$(2).sym"')
rom_src = $(if $(call uses_rom,$(1)),$(BINDIR)/rom-$(2).c $(BINDIR)/mesh-rom-$(2).c)
rom_sym = $(if $(call uses_rom,$(1)),-DV4_ROM_SYM='"$(BINDIR)/rom-$(2).sym"' -DV4_MESH_ROM_SYM='"$(BINDIR)/mesh-rom-$(2).sym"')
# 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.
@@ -132,12 +135,15 @@ $(BINDIR)/mkrom-$(1): $(HERE)/tools/mkrom.c $$(SRCS) $$(wildcard $(HERE)/include
-DROM_ORIGIN=$(HOST_WORDS) -DROM_RANGE=$(HOST_ROM_WORDS) $(HERE)/tools/mkrom.c $$(SRCS) -o $$@
$(BINDIR)/rom-$(1).c $(BINDIR)/rom-$(1).sym: $(BINDIR)/mkrom-$(1) $(BINDIR)/rom-map-$(1).f18 $$(ROM_FILES)
$(BINDIR)/mkrom-$(1) $(BINDIR)/rom-$(1).c $(BINDIR)/rom-$(1).sym $(BINDIR)/rom-map-$(1).f18 $$(ROM_FILES)
$(BINDIR)/mkrom-$(1) rom $(BINDIR)/rom-$(1).c $(BINDIR)/rom-$(1).sym $(BINDIR)/rom-map-$(1).f18 $$(ROM_FILES)
$(BINDIR)/mesh-rom-$(1).c $(BINDIR)/mesh-rom-$(1).sym: $(BINDIR)/mkrom-$(1) $(BINDIR)/rom-map-$(1).f18 $$(MESH_ROM_FILES)
$(BINDIR)/mkrom-$(1) mesh_rom $(BINDIR)/mesh-rom-$(1).c $(BINDIR)/mesh-rom-$(1).sym $(BINDIR)/rom-map-$(1).f18 $$(MESH_ROM_FILES)
endef
$(foreach w,$(WIDTHS),$(eval $(call ROM_RULE,$(w))))
.PHONY: rom
rom: $(foreach w,$(WIDTHS),$(BINDIR)/rom-$(w).c)
rom: $(foreach w,$(WIDTHS),$(BINDIR)/rom-$(w).c $(BINDIR)/mesh-rom-$(w).c)
.PHONY: sysmap
sysmap: $(foreach w,$(WIDTHS),$(SYS_DIR)/map-$(w).f18)
+31
View File
@@ -0,0 +1,31 @@
/* rom_entries.h -- the ROMs' shared entries (docs/v4.0.0/MACHINE.md 2.2).
*
* Both ROMs, the host node's and a mesh node's, begin with these entries, in
* this order: entry i is at V4_ROM_BASE + i, a jump to the routine. A
* payload calls an entry, never an address inside the ROM. This is the one
* list: tools/mkmap.c makes of it the (MATH-ENTRIES) macro both ROMs' entry
* tables begin with (capsules/v4/rom/main.f18, mesh.f18) and the constants
* ROM-UM* ... the system's source calls them by (tests/host_map.h).
*
* X(INDEX, ROUTINE, FORTH, EFFECT): ROUTINE is the ROM's name for the code
* (capsules/v4/rom/math.f18), FORTH the system's word that calls it, EFFECT
* its stack effect. An entry is added only at the end.
*/
#ifndef V4_ROM_ENTRIES_H
#define V4_ROM_ENTRIES_H
#define V4_ROM_MATH_ENTRIES(X) \
X(0, "(UM*)", "UM*", "( u1 u2 -- ulo uhi )") \
X(1, "(UM/MOD)", "UM/MOD", "( ulo uhi ud -- urem uquot )") \
X(2, "(D+)", "D+", "( d1 d2 -- d3 )") \
X(3, "(DNEGATE)", "DNEGATE", "( d -- -d )") \
X(4, "(Q.*)", "Q.*", "( a b -- c )") \
X(5, "(Q./)", "Q./", "( a b -- q )")
#define V4_ROM_MATH_COUNT 6u
/* The host ROM's own entries follow: the assembler's twelve routines, then
* (ROM), where the host machine starts (capsules/v4/rom/main.f18). */
#define V4_ROM_START (V4_ROM_MATH_COUNT + 12u)
#endif
+5 -5
View File
@@ -6,6 +6,7 @@
* which only a whole system has.
*/
#include "v4/post.h"
#include "v4/rom_entries.h"
#include "v4/blocks.h"
#include "v4/boot.h"
#include "v4/rom_errors.h"
@@ -413,9 +414,9 @@ static int load_capsule(const v4_boot *b, const char *name)
/* The ROM (docs/v4.0.0/MACHINE.md section 2): the machine's one binary,
* built by v4/tools/mkrom.c for this cell width. The machine starts at its
* entry 12, (ROM) (capsules/v4/rom/main.f18), which reads the system's
* source from the console, assembles it into RAM and, at the source's
* `start`, runs it. */
* entry V4_ROM_START (v4/rom_entries.h), (ROM) (capsules/v4/rom/main.f18),
* which reads the system's source from the console, assembles it into RAM
* and, at the source's `start`, runs it. */
#if V4_CELL_BITS == 64
extern const v4_cell v4_rom_64[];
extern const unsigned v4_rom_64_words;
@@ -427,7 +428,6 @@ extern const unsigned v4_rom_32_words;
#define BOOT_ROM v4_rom_32
#define BOOT_ROM_WORDS v4_rom_32_words
#endif
#define ROM_START 12u /* the entry where the machine starts */
#define ROM_STEP_LIMIT 400000000ULL /* instruction words the ROM may take: it takes about 16 million */
#define EOT 4 /* the end of the source: the ROM's (KEY) sees no more */
@@ -502,7 +502,7 @@ static int load_system(const v4_boot *b)
}
v4_node_rom_attach(n, BOOT_ROM, BOOT_ROM_WORDS);
n->p = V4_ROM_BASE + (v4_cell)ROM_START;
n->p = V4_ROM_BASE + (v4_cell)V4_ROM_START;
while (n->p >= V4_ROM_BASE) {
/* the console is given more of the source whenever it has taken
* all it had */
+18 -1
View File
@@ -13,6 +13,7 @@
#include "v4/testcode.h"
#include "host_map.h"
#include "v4/sys_files.h"
#include "v4/rom_entries.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -29,11 +30,27 @@ extern const unsigned v4_rom_32_words;
#define ROM_IMAGE_WORDS v4_rom_32_words
#endif
/* A mesh node's ROM (MACHINE.md 2.2): the shared math only. */
#if V4_CELL_BITS == 64
extern const v4_cell v4_mesh_rom_64[];
extern const unsigned v4_mesh_rom_64_words;
#define MESH_ROM v4_mesh_rom_64
#define MESH_ROM_WORDS v4_mesh_rom_64_words
#else
extern const v4_cell v4_mesh_rom_32[];
extern const unsigned v4_mesh_rom_32_words;
#define MESH_ROM v4_mesh_rom_32
#define MESH_ROM_WORDS v4_mesh_rom_32_words
#endif
/* The entry table, in its fixed order: entry i is at V4_ROM_BASE + i. */
#define ROM_MATH_NAME(i, routine, forth, effect) routine,
static const char *const rom_entries[] = {
V4_ROM_MATH_ENTRIES(ROM_MATH_NAME)
"(KEY)", "(TOKEN)", "(NUMBER)", "(FIND)", "(DEFINE)", "(OPCODE)",
"(OP)", "(LIT)", "(BRANCH)", "(BRANCH>)", "(RESOLVE)", "(LABEL)", "(ROM)"
};
#undef ROM_MATH_NAME
#define ROM_ENTRIES (sizeof rom_entries / sizeof rom_entries[0])
static v4_exec_state rom_es;
@@ -153,7 +170,7 @@ static int rom_build_system(v4_node *n, long *words)
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 */
n->p = V4_ROM_BASE + (v4_cell)V4_ROM_START; /* (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);
+1 -1
View File
@@ -69,7 +69,7 @@ static int rom_run(const char *text)
failed_lo = lo; failed_hi = hi < 0 ? (long)V4_ROM_BASE + (long)ROM_IMAGE_WORDS : hi;
}
rom_boot(&b);
b.p = V4_ROM_BASE + 12;
b.p = V4_ROM_BASE + (v4_cell)V4_ROM_START;
while (steps++ < 50000000L) {
if (fed <= len) fed += v4_node_console_feed(&b, text + fed, (unsigned)(len + 1 - fed));
if (b.p < V4_ROM_BASE) return 1;
+156
View File
@@ -0,0 +1,156 @@
/* test_host_rom_math.c -- the ROMs' shared math (docs/v4.0.0/MACHINE.md 2.2).
*
* Both ROMs begin with the same six entries (v4/include/v4/rom_entries.h):
* UM*, UM/MOD, D+, DNEGATE, Q.* and Q./. Each is called through its entry,
* on the host ROM and on the mesh ROM, with the values below, and must give
* what the system's words of those names gave before they moved into the
* ROM. The tables are those words' results as text.c assembled the system
* on 2026-10-15, before the move, at each width.
*/
#include "v4/rom_entries.h"
#include "v4/iword.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 M ((v4_cell)V4_MSB) /* the most negative cell */
#define X ((v4_cell)~(v4_ucell)V4_MSB) /* the largest */
#define ONES (~(v4_ucell)0)
#define CANARY ((v4_cell)0x0C0FFEE5)
/* entry, arguments (deepest first), results (deepest first), NODE-ERROR after */
typedef struct { unsigned entry, argc; v4_cell in[4]; unsigned outc; v4_cell out[2]; v4_cell error; } math_case;
static const math_case cases[] = {
{ 0, 2, {0, 0}, 2, {0, 0}, 0 },
{ 0, 2, {1, -1}, 2, {-1, 0}, 0 },
{ 0, 2, {-1, -1}, 2, {1, -2}, 0 },
{ 0, 2, {M, 2}, 2, {0, 1}, 0 },
{ 0, 2, {12345, 67890}, 2, {838102050, 0}, 0 },
{ 0, 2, {-1, M}, 2, {M, X}, 0 },
{ 0, 2, {X, X}, 2, {1, X / 2}, 0 },
{ 1, 3, {0, 0, 1}, 2, {0, 0}, 0 },
{ 1, 3, {100, 0, 7}, 2, {2, 14}, 0 },
{ 1, 3, {-1, -2, -1}, 2, {-2, -1}, 0 },
{ 1, 3, {0, 1, 3}, 2, {1, (v4_cell)(ONES / 3u)}, 0 },
{ 1, 3, {-1, 0, 10}, 2, {5, (v4_cell)(ONES / 10u)}, 0 },
#if V4_CELL_BITS == 64
{ 1, 3, {12345, 6, 7}, 2, {2, -2635249153387077039LL}, 0 },
#else
{ 1, 3, {12345, 6, 7}, 2, {0, -613564993L}, 0 },
#endif
{ 1, 3, {M, X - 1, X}, 2, {X - 1, -2}, 0 },
{ 1, 3, {-1, X, M}, 2, {X, -1}, 0 },
{ 2, 4, {-1, 0, 1, 0}, 2, {0, 1}, 0 },
{ 2, 4, {-1, -1, 1, 0}, 2, {0, 0}, 0 },
{ 2, 4, {M, 0, M, 0}, 2, {0, 1}, 0 },
{ 2, 4, {5, -1, -6, 0}, 2, {-1, -1}, 0 },
{ 2, 4, {0, X, 0, 1}, 2, {0, M}, 0 },
{ 2, 4, {123, 4, -200, -5}, 2, {-77, -1}, 0 },
{ 3, 2, {0, 0}, 2, {0, 0}, 0 },
{ 3, 2, {1, 0}, 2, {-1, -1}, 0 },
{ 3, 2, {0, 1}, 2, {0, -1}, 0 },
{ 3, 2, {-1, -1}, 2, {1, 0}, 0 },
{ 3, 2, {0, M}, 2, {0, M}, 0 },
{ 3, 2, {5, 0}, 2, {-5, -1}, 0 },
{ 4, 4, {65536, 0, 65536, 0}, 2, {65536, 0}, 0 },
{ 4, 4, {163840, 0, -196608, -1}, 2, {-491520, -1}, 0 },
{ 4, 4, {-65536, -1, -65536, -1}, 2, {65536, 0}, 0 },
{ 4, 4, {32768, 0, 32768, 0}, 2, {16384, 0}, 0 },
{ 4, 4, {0, 0, 65536, 0}, 2, {0, 0}, 0 },
{ 4, 4, {0, 1, 0, 1}, 2, {0, (v4_cell)1 << (V4_CELL_BITS - 16)}, 0 },
{ 4, 4, {-1, X, 131072, 0}, 2, {-2, -1}, 0 },
{ 4, 4, {1, 0, 1, 0}, 2, {0, 0}, 0 },
{ 5, 4, {65536, 0, 196608, 0}, 2, {21845, 0}, 0 },
{ 5, 4, {-491520, -1, 131072, 0}, 2, {-245760, -1}, 0 },
{ 5, 4, {65536, 0, 0, 0}, 2, {-1, X}, 11 },
{ 5, 4, {-65536, -1, 0, 0}, 2, {0, M}, 11 },
{ 5, 4, {0, 0, 0, 0}, 2, {0, 0}, 11 },
{ 5, 4, {0, 1, 1, 0}, 2, {0, 65536}, 0 },
{ 5, 4, {-1, X, 32768, 0}, 2, {-1, X}, 0 },
{ 5, 4, {M, -1, -65536, -1}, 2, {M, 0}, 0 },
{ 5, 4, {1, 0, 65536, 0}, 2, {1, 0}, 0 },
};
#define CASES (sizeof cases / sizeof cases[0])
static v4_node n;
/* Call entry c->entry of `rom`. 1 if it returned with the canary under its
* results; got[] the results, deepest first, *err NODE-ERROR. */
static int run(const v4_cell *rom, unsigned words, const math_case *c, v4_cell *got, v4_cell *err)
{
unsigned i;
rom_boot(&n);
v4_node_rom_attach(&n, rom, words);
v4_dstack_push(&n.ds, CANARY);
for (i = 0; i < c->argc; i++) v4_dstack_push(&n.ds, c->in[i]);
if (v4_test_call(&n, &rom_es, &rom_h, V4_ROM_BASE + (v4_cell)c->entry, 10000000L) <= 0) return 0;
for (i = c->outc; i-- > 0; ) got[i] = v4_dstack_pop(&n.ds);
*err = n.mem[NODE_ERROR];
return v4_dstack_pop(&n.ds) == CANARY;
}
static void cases_on(const char *which, const v4_cell *rom, unsigned words)
{
unsigned k;
for (k = 0; k < CASES; k++) {
const math_case *c = &cases[k];
v4_cell got[2] = { 0, 0 }, err = -99;
int ok = run(rom, words, c, got, &err);
CHECK(ok && got[0] == c->out[0] && got[1] == c->out[1] && err == c->error,
"%s ROM, entry %u, case %u: got %lld %lld error %lld, want %lld %lld error %lld",
which, c->entry, k, (long long)got[0], (long long)got[1], (long long)err,
(long long)c->out[0], (long long)c->out[1], (long long)c->error);
}
}
/* The address of `name` in a ROM's symbol file, or -1. */
static long sym_addr(const char *path, const char *name)
{
FILE *f = fopen(path, "r");
char sym[80];
long addr, found = -1;
if (!f) { printf(" cannot read %s\n", path); return -1; }
while (fscanf(f, "%79s %ld", sym, &addr) == 2) if (strcmp(sym, name) == 0) found = addr;
fclose(f);
return found;
}
/* Entry i of `rom` is a jump, in slot 0, to the routine `routine`. */
static int entry_jumps_to(const v4_cell *rom, unsigned i, const char *sym, const char *routine)
{
v4_iword w = (v4_iword)((v4_ucell)rom[i] & 0xFFFFFFFFu);
long target = sym_addr(sym, routine);
return target >= 0 && v4_iword_op(w, 0) == V4_OP_JUMP
&& v4_iword_branch(V4_ROM_BASE + (v4_cell)i, w, 0) == (v4_cell)target;
}
int main(void)
{
printf("v4 ROM math tests: V4_CELL_BITS=%d\n", V4_CELL_BITS);
/* ---- one entry list, in both ROMs ---- */
{
#define CHECK_ENTRY(i, routine, forth, effect) \
CHECK(entry_jumps_to(ROM_IMAGE, i, V4_ROM_SYM, routine), "host ROM entry %d jumps to %s", i, routine); \
CHECK(entry_jumps_to(MESH_ROM, i, V4_MESH_ROM_SYM, routine), "mesh ROM entry %d jumps to %s", i, routine);
V4_ROM_MATH_ENTRIES(CHECK_ENTRY)
#undef CHECK_ENTRY
}
CHECK(entry_jumps_to(ROM_IMAGE, V4_ROM_START, V4_ROM_SYM, "(ROM)"), "the host ROM starts at entry %u, (ROM)", (unsigned)V4_ROM_START);
CHECK(entry_jumps_to(ROM_IMAGE, V4_ROM_MATH_COUNT, V4_ROM_SYM, "(KEY)"), "the assembler's entries follow the math's");
/* ---- the mesh ROM is small and holds nothing of the assembler ---- */
CHECK(MESH_ROM_WORDS < 300u, "the mesh ROM is small: %u words", MESH_ROM_WORDS);
CHECK(sym_addr(V4_MESH_ROM_SYM, "(TOKEN)") < 0 && sym_addr(V4_MESH_ROM_SYM, "(KEY)") < 0 && sym_addr(V4_MESH_ROM_SYM, "(ROM)") < 0,
"the mesh ROM has none of the assembler's routines");
printf(" the host ROM: %u words; the mesh ROM: %u words\n", ROM_IMAGE_WORDS, MESH_ROM_WORDS);
/* ---- each entry, on each ROM, gives what the system's words gave ---- */
cases_on("host", ROM_IMAGE, ROM_IMAGE_WORDS);
cases_on("mesh", MESH_ROM, MESH_ROM_WORDS);
printf(" %d checks, %d failures\n", checks, failures);
return failures != 0;
}
+17 -1
View File
@@ -8,10 +8,12 @@
* host stack sizes) at one cell width. It writes, as `constant` lines in
* the text assembler's notation, the addresses and codes of
* v4/tests/host_map.h that the ROM's source (capsules/v4/rom) uses: the
* console's receive registers and the ROM's state area (MACHINE.md 2.1).
* console's receive registers, the ROM's state area (MACHINE.md 2.1), the
* math's constants and its shared entries (MACHINE.md 2.2).
* The map has one source, host_map.h; this is how the ROM reads it.
*/
#include "../tests/host_map.h"
#include "../include/v4/rom_entries.h"
#include <stdio.h>
static void constant(const char *name, long value)
@@ -147,5 +149,19 @@ int main(int argc, char **argv)
constant("DEC-LIMIT", (long)(v4_cell)(~(v4_ucell)0 / 10u));
constant("DEC-NLAST", -(long)(~(v4_ucell)0 % 10u) - 1);
constant("HEX-HIGH", (long)(v4_cell)~(~(v4_ucell)0 >> 4));
/* the math's constants (capsules/v4/rom/math.f18) */
constant("N-1", (long)(V4_CELL_BITS - 1));
constant("NODE-ERROR", (long)NODE_ERROR);
constant("(Q/)", (long)QBASE);
constant("MAXHI", (long)(v4_cell)(V4_MSB - 1u));
constant("2N+15", (long)(2 * V4_CELL_BITS + 15));
constant("N-18", (long)(V4_CELL_BITS - 18));
constant("N-17", (long)(V4_CELL_BITS - 17));
/* the shared entries, the start of both ROMs' tables (v4/rom_entries.h) */
printf("macro (MATH-ENTRIES)");
#define MATH_ENTRY_JUMP(i, routine, forth, effect) printf(" jump %s", routine);
V4_ROM_MATH_ENTRIES(MATH_ENTRY_JUMP)
#undef MATH_ENTRY_JUMP
printf(" endmacro\n");
return 0;
}
+16 -15
View File
@@ -1,13 +1,14 @@
/* mkrom.c -- build the ROM, off the node, as a bitstream is built.
*
* mkrom OUT.c OUT.sym MAP.f18 SOURCE.f18...
* mkrom NAME OUT.c OUT.sym MAP.f18 SOURCE.f18...
*
* docs/v4.0.0/MACHINE.md section 2. The ROM is F18 code, written in the
* text assembler's notation (include/v4/text.h) in capsules/v4/rom, and this
* is the only thing built off the node. It assembles MAP.f18 (the memory
* map's constants, from tools/mkmap.c) and then each SOURCE in the order
* given, at the ROM's base address, and writes:
* OUT.c the ROM: const v4_cell v4_rom_<width>[] and its length
* OUT.c the ROM: const v4_cell v4_<NAME>_<width>[] and its length (NAME
* rom: the host node's ROM; mesh_rom: a mesh node's, MACHINE.md 2.2)
* OUT.sym every word of it, "NAME ADDRESS" a line, for the tests
*
* This tool is built with a node whose RAM reaches over the ROM's range
@@ -56,37 +57,37 @@ int main(int argc, char **argv)
int i;
unsigned s;
if (argc < 5) die("usage: mkrom OUT.c OUT.sym MAP.f18 SOURCE.f18...", NULL);
if (argc < 6) die("usage: mkrom NAME OUT.c OUT.sym MAP.f18 SOURCE.f18...", NULL);
if ((v4_ucell)V4_NODE_WORDS != (v4_ucell)ROM_ORIGIN + (v4_ucell)ROM_RANGE)
die("built with the wrong node size: RAM must reach over the ROM's range", NULL);
v4_node_reset(&n);
v4_text_begin(&tx, &n, ROM_ORIGIN);
for (i = 3; i < argc; i++)
for (i = 4; i < argc; i++)
if (!v4_text_assemble_file(&tx, argv[i])) die("does not assemble", v4_text_error(&tx));
if (!v4_text_finish(&tx)) die("not everything is defined", v4_text_error(&tx));
here = v4_text_here(&tx);
if (here - (v4_cell)ROM_ORIGIN > (v4_cell)ROM_RANGE) die("the ROM is larger than its range", NULL);
out = fopen(argv[1], "w");
if (!out) die("cannot write", argv[1]);
out = fopen(argv[2], "w");
if (!out) die("cannot write", argv[2]);
fprintf(out, "/* Generated by v4/tools/mkrom.c from capsules/v4/rom -- do not edit. */\n#include \"v4/cell.h\"\n\n");
fprintf(out, "const v4_cell v4_rom_%d[] = {\n", V4_CELL_BITS);
fprintf(out, "const v4_cell v4_%s_%d[] = {\n", argv[1], V4_CELL_BITS);
for (a = ROM_ORIGIN; a < here; a++) {
fprintf(out, " ");
put_cell(out, n.mem[a]);
fprintf(out, ",\n");
}
if (here == ROM_ORIGIN) fprintf(out, " 0\n");
fprintf(out, "};\nconst unsigned v4_rom_%d_words = %ldu;\n", V4_CELL_BITS, (long)(here - (v4_cell)ROM_ORIGIN));
if (fclose(out) != 0) die("cannot write", argv[1]);
out = fopen(argv[2], "w");
if (!out) die("cannot write", argv[2]);
for (s = 0; s < tx.nsym; s++)
if (tx.sym[s].kind == 1) fprintf(out, "%s %ld\n", tx.pool + tx.sym[s].name, (long)tx.sym[s].value);
fprintf(out, "};\nconst unsigned v4_%s_%d_words = %ldu;\n", argv[1], V4_CELL_BITS, (long)(here - (v4_cell)ROM_ORIGIN));
if (fclose(out) != 0) die("cannot write", argv[2]);
fprintf(stderr, "mkrom: a ROM of %ld words, %d-bit cells -> %s\n", (long)(here - (v4_cell)ROM_ORIGIN), V4_CELL_BITS, argv[1]);
out = fopen(argv[3], "w");
if (!out) die("cannot write", argv[3]);
for (s = 0; s < tx.nsym; s++)
if (tx.sym[s].kind == 1) fprintf(out, "%s %ld\n", tx.pool + tx.sym[s].name, (long)tx.sym[s].value);
if (fclose(out) != 0) die("cannot write", argv[3]);
fprintf(stderr, "mkrom: %s, %ld words, %d-bit cells -> %s\n", argv[1], (long)(here - (v4_cell)ROM_ORIGIN), V4_CELL_BITS, argv[2]);
return 0;
}