feat(v4): HEAT-OP[0..31] and ANTICLOCK, read by HEAT-OP@ and ANTICLOCK@
#189 -- Captain Bob, 2026-11-12: "Add HEAT-OP. If it's a huge performance hit, we'll revert it. If not ... it stands"; a full 33-cell array, as DECOMPOSITION.md section 7 draws it; the Stadium feed is Phase 1's. - node.h/node.c: v4_node_heat_regs_attach; a data fetch from base+k gives opcode k's count, base+32 the anti-clock (the low cell at 32 bits); a store changes nothing. Reset detaches them. - host_map.h: HEAT_REG at word 13824, in what was free between the dictionary space and SEND_TEXT; the compile-time check covers it. Nothing moved. map-*.f18: HEAT-OP 13824, ANTICLOCK 13856. - image.h/image.c/mkmap.c: the image carries the address; v4_image_born attaches the node's own counts. - system.f18: HEAT-OP@ ( op -- n ), op mod 32; ANTICLOCK@ ( -- n ). word_count 318 -> 320 (hosted 317 -> 319): dict_hash changes. - v4-dictionary.tsv: both words; DECOMPOSITION.md 7 records the build. Also in this batch: - Makefile: make init installs qemu-user -- hosted-check needs it (Captain Bob: "Add to make init now"). - .claude/memory/rules.md, rule 5: stop and ask when something comes up (Captain Bob: "you are supposed to stop and ask"). Tests first (logs/20261113-005047/heat-op): test_node did not compile; test_host_quit 3 of 1337 failed. Suite: 199 programs, 7448554 checks, 0 failures; hosted-check, the three alike. Timing: no measurable cost. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017RzZgrf7V7Zk2iczBXDRbm
This commit is contained in:
@@ -48,6 +48,7 @@ typedef struct {
|
||||
v4_cell console_tx, console_rx, console_status;
|
||||
v4_cell dstack_reg, rstack_reg;
|
||||
v4_cell node_error;
|
||||
v4_cell heat_reg; /* HEAT-OP[0]; ANTICLOCK is 32 after it (node.h, #189) */
|
||||
|
||||
/* the dictionary's two variables: DP holds HERE, a byte address, and
|
||||
* LATEST the newest word of FORTH. What lies below HERE, and LATEST,
|
||||
|
||||
@@ -167,8 +167,16 @@ typedef struct {
|
||||
|
||||
/* NODE-ERROR as a trap (D-18). See v4_node_error_attach. */
|
||||
v4_cell error_reg; /* its word address, or -1 */
|
||||
|
||||
/* HEAT-OP[0..31] and ANTICLOCK (section 7). See v4_node_heat_regs_attach. */
|
||||
v4_cell heat_reg; /* the word address of HEAT-OP[0], or -1 */
|
||||
const v4_uheat_t *heat_op; /* the 32 per-opcode counts (heat.h) */
|
||||
const v4_uheat_t *heat_anticlock;
|
||||
} v4_node;
|
||||
|
||||
/* How many cells the heat registers take: HEAT-OP[0..31], then ANTICLOCK. */
|
||||
#define V4_HEAT_REGS 33u
|
||||
|
||||
/* Zero P, A and B, empty the stacks, and clear memory. Installs every
|
||||
* boundary (memory and both stacks) and leaves the node in a defined state. */
|
||||
void v4_node_reset(v4_node *n);
|
||||
@@ -427,6 +435,18 @@ void v4_node_fault(v4_node *n, unsigned kind, v4_cell addr);
|
||||
* detaches both (-1); the addresses are the caller's choice (D-4). */
|
||||
void v4_node_stack_regs_attach(v4_node *n, v4_cell d, v4_cell r);
|
||||
|
||||
/* HEAT-OP[0..31] and ANTICLOCK, the heat registers (DECOMPOSITION.md section 7; Captain Bob,
|
||||
* 2026-11-12: "Add HEAT-OP", a full array; #189).
|
||||
*
|
||||
* After v4_node_heat_regs_attach(n, base, op, anticlock):
|
||||
* a data fetch from base + k, k = 0 .. 31, gives op[k], opcode k's retirement count
|
||||
* (v4_heat.op, heat.h), and from base + 32 *anticlock -- each as a cell: its low cell at
|
||||
* 32-bit cells, where the counts are 64-bit;
|
||||
* a store to any of them changes nothing: they are read-only (R), and not memory.
|
||||
* The counts are read as they are at the fetch. v4_node_reset detaches them (-1); the address is
|
||||
* the caller's choice (D-4). */
|
||||
void v4_node_heat_regs_attach(v4_node *n, v4_cell base, const v4_uheat_t *op, const v4_uheat_t *anticlock);
|
||||
|
||||
/* A BLOCK (DECOMPOSITION.md D-19, 5.11). Mass storage is the kernel's: a
|
||||
* node asks it for a block by a request on port 0 (docs/v4.0.0/MESH.md
|
||||
* section 8; blocks.h), and has nothing of it but these. A block is 1024 bytes, and a byte
|
||||
|
||||
@@ -21,6 +21,7 @@ int v4_image_born(v4_node *n, v4_exec_state *es, v4_heat *h, const v4_image *im)
|
||||
v4_node_console_input_attach(n, im->console_rx, im->console_status);
|
||||
v4_node_stack_regs_attach(n, im->dstack_reg, im->rstack_reg);
|
||||
v4_node_error_attach(n, im->node_error);
|
||||
v4_node_heat_regs_attach(n, im->heat_reg, h->op, &es->anticlock);
|
||||
v4_node_fault_attach(n, im->fault_table);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -21,10 +21,27 @@ void v4_node_reset(v4_node *n)
|
||||
v4_node_console_input_attach(n, -1, -1);
|
||||
v4_node_fault_attach(n, -1);
|
||||
v4_node_stack_regs_attach(n, -1, -1);
|
||||
v4_node_heat_regs_attach(n, -1, 0, 0);
|
||||
v4_node_error_attach(n, -1);
|
||||
v4_node_port_attach(n, -1);
|
||||
}
|
||||
|
||||
void v4_node_heat_regs_attach(v4_node *n, v4_cell base, const v4_uheat_t *op, const v4_uheat_t *anticlock)
|
||||
{
|
||||
n->heat_reg = (op && anticlock) ? base : -1;
|
||||
n->heat_op = op;
|
||||
n->heat_anticlock = anticlock;
|
||||
}
|
||||
|
||||
/* Which heat register addr is, 0 .. 32, or -1: none (detached, or another address). */
|
||||
static int heat_reg_index(const v4_node *n, v4_cell addr)
|
||||
{
|
||||
v4_ucell k;
|
||||
if (n->heat_reg < 0) return -1;
|
||||
k = (v4_ucell)addr - (v4_ucell)n->heat_reg;
|
||||
return k < (v4_ucell)V4_HEAT_REGS ? (int)k : -1;
|
||||
}
|
||||
|
||||
void v4_node_error_attach(v4_node *n, v4_cell addr)
|
||||
{
|
||||
n->error_reg = addr;
|
||||
@@ -121,6 +138,7 @@ void v4_node_store(v4_node *n, v4_cell addr, v4_cell value)
|
||||
/* The stack registers: a store empties the stack. -1 when not attached. */
|
||||
if (addr == n->dstack_reg) { v4_dstack_clear(&n->ds); return; }
|
||||
if (addr == n->rstack_reg) { v4_rstack_clear(&n->rs); return; }
|
||||
if (heat_reg_index(n, addr) >= 0) return; /* HEAT-OP and ANTICLOCK are read-only */
|
||||
if (!v4_node_addr_ok(addr)) return;
|
||||
n->mem[(unsigned)addr] = value;
|
||||
/* NODE-ERROR: a non-zero code raises an error. -1 when not attached. */
|
||||
@@ -161,6 +179,10 @@ v4_cell v4_node_fetch(v4_node *n, v4_cell addr)
|
||||
}
|
||||
if (addr == n->dstack_reg) return (v4_cell)n->ds.depth;
|
||||
if (addr == n->rstack_reg) return (v4_cell)n->rs.depth;
|
||||
{
|
||||
int k = heat_reg_index(n, addr);
|
||||
if (k >= 0) return (v4_cell)(v4_ucell)(k < 32 ? n->heat_op[k] : *n->heat_anticlock);
|
||||
}
|
||||
return v4_node_load(n, addr);
|
||||
}
|
||||
|
||||
|
||||
+9
-2
@@ -231,8 +231,13 @@ typedef char host_map_ports_fit[(PORT >= OUT_W + OUT_CELLS && PORT + (v4_cell)V4
|
||||
#ifndef DICT_END_W
|
||||
#define DICT_END_W ((v4_cell)13824) /* 512 cells lower than it was: the block window is four slots now */
|
||||
#endif
|
||||
/* the texts made ready for the wire are above the dictionary */
|
||||
typedef char host_map_texts_fit[(SEND_TEXT >= DICT_END_W && SEND_TEXT + 256 <= OUT_TEXT && OUT_TEXT + 64 == OUT_W) ? 1 : -1];
|
||||
/* HEAT-OP[0..31] and ANTICLOCK (DECOMPOSITION.md section 7; Captain Bob, 2026-11-12: "Add HEAT-OP", a
|
||||
* full array; #189): 33 read-only registers, not memory (node.h), in what was free between the dictionary
|
||||
* space and the texts made ready for the wire. Nothing else moved. */
|
||||
#define HEAT_REG DICT_END_W
|
||||
/* the heat registers, then the texts made ready for the wire, are above the dictionary */
|
||||
typedef char host_map_texts_fit[(HEAT_REG >= DICT_END_W && HEAT_REG + (v4_cell)V4_HEAT_REGS <= SEND_TEXT
|
||||
&& SEND_TEXT + 256 <= OUT_TEXT && OUT_TEXT + 64 == OUT_W) ? 1 : -1];
|
||||
|
||||
/* How many slots, from slot 0, a branch may sit in on a node this size: those
|
||||
* whose address field reaches every word of it. */
|
||||
@@ -362,6 +367,8 @@ static unsigned host_branch_slots(void)
|
||||
X("-HFLOOR", -(HEND - 62)) \
|
||||
X("DSTACK-DEPTH", DSTACK_REG) \
|
||||
X("RSTACK-DEPTH", RSTACK_REG) \
|
||||
X("HEAT-OP", HEAT_REG) \
|
||||
X("ANTICLOCK", HEAT_REG + 32) \
|
||||
X("CFBASE", CFS_W) \
|
||||
X("CFEND", CFS_W + CFS_CELLS)
|
||||
|
||||
|
||||
@@ -332,6 +332,7 @@ static void boot_with(unsigned depth)
|
||||
v4_node_console_input_attach(&n, CONSOLE_RX, CONSOLE_ST);
|
||||
v4_node_fault_attach(&n, w_fault);
|
||||
v4_node_error_attach(&n, NODE_ERROR);
|
||||
v4_node_heat_regs_attach(&n, HEAT_REG, h.op, &es.anticlock); /* HEAT-OP[0..31] and ANTICLOCK (#189) */
|
||||
v4_dstack_reset(&n.ds);
|
||||
v4_rstack_reset(&n.rs);
|
||||
v4_exec_reset(&es);
|
||||
@@ -962,6 +963,15 @@ int main(void)
|
||||
CHECK(is(say(" SHOW\n"), "88 ok\nok> "), "and then all of it is read");
|
||||
CHECK(canary_under_expect(), "the data stack is as it was at switch-on");
|
||||
|
||||
/* ---- the heat registers (#189; Captain Bob, 2026-11-12: "Add HEAT-OP"): HEAT-OP@ ( op -- n ) and
|
||||
* ANTICLOCK@ ( -- n ) read the node's counts as they are now ---- */
|
||||
boot();
|
||||
CHECK(is(say(": T1 ANTICLOCK@ ANTICLOCK@ < IF 89 EMIT THEN ; T1\n"), "Y ok\nok> "), "ANTICLOCK@ goes forward: one tick per instruction");
|
||||
CHECK(is(say(": T2 20 HEAT-OP@ 1 1 + DROP 20 HEAT-OP@ SWAP - 0> IF 89 EMIT THEN ; T2\n"), "Y ok\nok> "),
|
||||
"HEAT-OP@ of + (opcode 20) counts the + between the two reads");
|
||||
CHECK(is(say(": T3 24 HEAT-OP@ 56 HEAT-OP@ = IF 89 EMIT THEN ; T3\n"), "Y ok\nok> "),
|
||||
"an opcode number is taken mod 32: 56 reads dup's count (opcode 24), with no dup between");
|
||||
|
||||
/* ---- a definition over several lines, and an error in the middle of one ---- */
|
||||
boot();
|
||||
CHECK(is(say(": LONG\n"), " ok\nok> ") && n.mem[STATE] != 0, "a line that leaves a definition open still says ok");
|
||||
|
||||
@@ -243,6 +243,41 @@ static void test_workload_never_false_alarms(void)
|
||||
CHECK(n.b < (v4_cell)V4_NODE_WORDS, "B left the address space");
|
||||
}
|
||||
|
||||
/* HEAT-OP[0..31] and ANTICLOCK (DECOMPOSITION.md section 7; Captain Bob, 2026-11-12: "Add
|
||||
* HEAT-OP", a full array; #189). After v4_node_heat_regs_attach(n, base, op, anticlock), a data
|
||||
* fetch from base + k (k < 32) gives op[k] and from base + 32 the anti-clock, as cells (the low cell
|
||||
* of a count at 32-bit cells); a store there changes nothing -- they are read-only and not memory.
|
||||
* Detached (reset): base .. base + 32 are memory. */
|
||||
static void test_heat_regs(void)
|
||||
{
|
||||
static v4_node n;
|
||||
v4_uheat_t op[32], ac = 777;
|
||||
const v4_cell base = 900;
|
||||
unsigned k, right = 0;
|
||||
|
||||
for (k = 0; k < 32; k++) op[k] = 1000u + k;
|
||||
v4_node_reset(&n);
|
||||
v4_node_store(&n, base + 5, 55);
|
||||
CHECK(v4_node_fetch(&n, base + 5) == 55, "detached: base + 5 is memory");
|
||||
CHECK(n.heat_reg == -1, "reset leaves the heat registers detached");
|
||||
|
||||
v4_node_heat_regs_attach(&n, base, op, &ac);
|
||||
for (k = 0; k < 32; k++) if (v4_node_fetch(&n, base + (v4_cell)k) == (v4_cell)(1000u + k)) right++;
|
||||
CHECK(right == 32, "HEAT-OP[k] reads opcode k's count (%u of 32)", right);
|
||||
CHECK(v4_node_fetch(&n, base + 32) == 777, "ANTICLOCK reads the anti-clock");
|
||||
op[20]++; ac++;
|
||||
CHECK(v4_node_fetch(&n, base + 20) == 1021 && v4_node_fetch(&n, base + 32) == 778, "they read the counts as they are now");
|
||||
v4_node_store(&n, base + 20, 5);
|
||||
v4_node_store(&n, base + 32, 5);
|
||||
CHECK(op[20] == 1021 && ac == 778, "a store does not change a count");
|
||||
CHECK(n.mem[base + 20] == 0 && n.mem[base + 32] == 0, "nor memory");
|
||||
CHECK(v4_node_fetch(&n, base - 1) == 0 && v4_node_fetch(&n, base + 33) == 0, "the cells beside them are memory");
|
||||
op[3] = ((v4_uheat_t)1 << 40) + 9u;
|
||||
CHECK(v4_node_fetch(&n, base + 3) == (v4_cell)(v4_ucell)op[3], "a count is read as a cell (its low cell at 32 bits)");
|
||||
v4_node_reset(&n);
|
||||
CHECK(n.heat_reg == -1, "a reset detaches them");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("v4 node tests: V4_CELL_BITS=%d, %u words\n",
|
||||
@@ -259,6 +294,7 @@ int main(void)
|
||||
test_out_of_range_word_address_touches_nothing();
|
||||
test_ends_are_distinguishable();
|
||||
test_workload_never_false_alarms();
|
||||
test_heat_regs();
|
||||
|
||||
printf(" %d checks, %d failures\n", checks, failures);
|
||||
return failures ? 1 : 0;
|
||||
|
||||
+2
-2
@@ -53,9 +53,9 @@ static int image_description(void)
|
||||
printf("#include \"v4/image.h\"\n\nconst v4_image v4_map_image = {\n");
|
||||
printf(" %uu, %uu, %uu, %uu,\n", (unsigned)V4_CELL_BITS, (unsigned)V4_NODE_WORDS, (unsigned)V4_DATA_RING, (unsigned)V4_RET_RING);
|
||||
printf(" -1,\n -1, %ld,\n -1, -1,\n", (long)PORT);
|
||||
printf(" %ld, %ld, %ld,\n %ld, %ld,\n %ld,\n %ld, %ld,\n %ld, %ld,\n %ld,\n",
|
||||
printf(" %ld, %ld, %ld,\n %ld, %ld,\n %ld,\n %ld,\n %ld, %ld,\n %ld, %ld,\n %ld,\n",
|
||||
(long)CONSOLE_TX, (long)CONSOLE_RX, (long)CONSOLE_ST, (long)DSTACK_REG, (long)RSTACK_REG,
|
||||
(long)NODE_ERROR, (long)DP, (long)LATEST, (long)BOOT_CELLS, (long)FENCE, (long)BUF0_W);
|
||||
(long)NODE_ERROR, (long)HEAT_REG, (long)DP, (long)LATEST, (long)BOOT_CELLS, (long)FENCE, (long)BUF0_W);
|
||||
printf(" %ld, %ld, %ld\n};\n", (long)ROM_IMG, (long)ROM_FAILED, (long)ROM_ERRLINE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user