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:
co-authored by
Claude Opus 5.5
parent
473e105a69
commit
11b5d402b1
+17
-14
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user