feat(v4): word accounts at call -- the word ID in the header, the kernel's records, the Stadium charged per tick (#189)
ENGINE step 4 Phase 2 (plan docs/v4.0.0/plans/2026-11-13-engine-step4-phase2-word-accounts.md). Rulings, Captain Bob 2026-11-13: "ID field in the header"; "Per heartbeat tick"; the ACL "All of it in Phase 4". - The word ID, at xt-4 before the flags (ENGINE.md 3b). The system's words are numbered as they are assembled, by a new directive, #id, (and #ids, the count), in the C assembler (text.c) and in the machine ROM's (capsules/v4/rom), which counts in the system's own (NEXT-ID). (HEADER) numbers later words; FORGET gives back the IDs it frees, always the newest; COLD resets to (BOOT-ID); (SEAL) records it. All 313 system headers carry the cell. - The kernel's records (v4/mesh.h): (TELL-DEFINED) and (TELL-FORGOTTEN), requests -19/-20 on port 0, which the kernel puts in (WORD-DEFINED)/(WORD-FORGOTTEN) when it seals a node, and reads the system's own words once. A bit per address marks a word's xt (calls into the ROM or headerless code are not words); at call, the bit test and a count. v4_mesh_charge hands the owner the calls since. - The Stadium: stadium_words_charge -- N calls as N of stadium_word_dispatch's dispatches, shared by calls when the reservoir is short; sk_v4.c charges every node's words at the tick beside its opcodes, and stadium_word_forget before an ID is used again. - Tests first: test_text.c, test_host_dict.c, test_host_quit.c (numbering, FORGET, COLD), test_mesh_boot.c (44 checks), kernel/tests/test_stadium_words.c; qemu_session.py: Hera's words admitted after POST. - amd64: Stadium words: promotions=141 after POST, CONSERVED; POST 539/539; word_count=377; dict_hash 0xa854ba15b437d288 (the new cell); the boot 142 s against Phase 1's 153 s. - v4 test, test-32: all pass; v4-checks: 80 programs under the sanitizers, 0 failures; the hosted builds alike. docs/reference/v4-dictionary.tsv: the two new words; FABRIC-3 XLVIII.24. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017RzZgrf7V7Zk2iczBXDRbm
This commit is contained in:
@@ -212,6 +212,8 @@ enum { V4_ROM_ERRORS(ROM_ERR_ENUM_) ROM_ERR_END_ };
|
||||
#define WORD_FORGOTTEN (BUF0_W - 12) /* the xt of the word that is told when entries go, or 0 */
|
||||
#define LOG_LEVEL (BUF0_W - 5) /* the level at or below which a message is printed */
|
||||
#define BOOT_CELLS (BVARS + 14) /* (BOOT): DP and LATEST as the loader left them, 2 cells */
|
||||
#define NEXT_ID (BVARS + 10) /* (NEXT-ID): the word ID the next entry is given (#189, Phase 2) */
|
||||
#define BOOT_ID (BVARS + 11) /* (BOOT-ID): (NEXT-ID) as the loader left it, which COLD comes back to */
|
||||
#define BUF0_W (BVARS - (v4_cell)(V4_BLOCK_SLOTS * V4_BLOCK_CELLS)) /* the block window: the kernel's slots, 256 cells each (v4/blocks.h) */
|
||||
/* The ports and the addresses after them, through V4_PORT_HAVE (node.h, MESH.md 7d.3), are not memory and must be where
|
||||
* no variable is: after the output buffer, which ends at BUF0_W - 144, and before OUT_HDR, the first variable above it.
|
||||
@@ -285,6 +287,8 @@ static unsigned host_branch_slots(void)
|
||||
X("(W)", WVARS) \
|
||||
X("(X)", XVARS) \
|
||||
X("(BOOT)", BOOT_CELLS) \
|
||||
X("(NEXT-ID)", NEXT_ID) \
|
||||
X("(BOOT-ID)", BOOT_ID) \
|
||||
X("(LOG-LEVEL)", LOG_LEVEL) \
|
||||
X("(ACL-HOOK)", ACL_HOOK) \
|
||||
X("SCR", SCR) \
|
||||
@@ -430,6 +434,9 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned
|
||||
n->mem[VOC_LINK] = 0;
|
||||
/* and the text interpreted is the terminal's */
|
||||
n->mem[SRC] = TIB;
|
||||
/* the word IDs the source's headers took (#id,), as end.f18 stores them on the machine (#189, Phase 2) */
|
||||
n->mem[NEXT_ID] = tx->next_id;
|
||||
n->mem[BOOT_ID] = tx->next_id;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
+12
-12
@@ -133,7 +133,7 @@ static int entry_is(v4_cell xt, const char *want, v4_cell link, v4_cell flags)
|
||||
v4_cell nfa;
|
||||
if (len > 31) len = 31;
|
||||
cells = (len + 4) / 4;
|
||||
nfa = xt - 3 - (v4_cell)cells;
|
||||
nfa = xt - 4 - (v4_cell)cells; /* the name, then its word ID (#189, Phase 2), flags, NFA, link */
|
||||
if (n.mem[xt - 1] != link || n.mem[xt - 2] != nfa || n.mem[xt - 3] != flags) return 0;
|
||||
if (byte_at(nfa * 4) != len) return 0;
|
||||
for (i = 0; i < len; i++) if (byte_at(nfa * 4 + 1 + (v4_cell)i) != (unsigned char)want[i]) return 0;
|
||||
@@ -258,21 +258,21 @@ int main(void)
|
||||
empty_dictionary();
|
||||
CHECK(get("LATEST", 0, 0) == 0, "LATEST is 0 in an empty dictionary");
|
||||
a = define("ALPHA");
|
||||
CHECK(a == DICT_W + 2 + 3 && !err(), "an entry's xt follows its name and three cells: %ld", (long)a);
|
||||
CHECK(a == DICT_W + 2 + 4 && !err(), "an entry's xt follows its name and four cells: %ld", (long)a);
|
||||
CHECK(entry_is(a, "ALPHA", 0, 0), "the first entry: name, no flags, link 0");
|
||||
CHECK(get("LATEST", 0, 0) == a && get("HERE", 0, 0) == a, "LATEST is its xt, and its code starts at HERE");
|
||||
CHECK(run(",", 1, 1001, 0) && clean(), "some code");
|
||||
b = define("B");
|
||||
CHECK(b == a + 1 + 1 + 3 && entry_is(b, "B", a, 0), "the second entry links to the first");
|
||||
CHECK(b == a + 1 + 1 + 4 && entry_is(b, "B", a, 0), "the second entry links to the first");
|
||||
(void)run(",", 1, 1002, 0);
|
||||
c = define("GAMMA-DELTA");
|
||||
CHECK(c == b + 1 + 3 + 3 && entry_is(c, "GAMMA-DELTA", b, 0), "an eleven-character name takes three cells");
|
||||
CHECK(c == b + 1 + 3 + 4 && entry_is(c, "GAMMA-DELTA", b, 0), "an eleven-character name takes three cells");
|
||||
CHECK(get("LATEST", 0, 0) == c, "LATEST is the newest");
|
||||
|
||||
/* the field words, all from the xt */
|
||||
CHECK(get(">LINK", 1, c) == c - 1 && get("LFA", 1, c) == c - 1, ">LINK and LFA are the link's address");
|
||||
CHECK(get("LINK>", 1, c - 1) == b && get("LINK>", 1, b - 1) == a && get("LINK>", 1, a - 1) == 0, "LINK> follows it, to 0 at the end");
|
||||
CHECK(get(">NAME", 1, c) == (c - 6) * 4 && get("NFA", 1, c) == (c - 6) * 4, ">NAME and NFA are the name's byte address");
|
||||
CHECK(get(">NAME", 1, c) == (c - 7) * 4 && get("NFA", 1, c) == (c - 7) * 4, ">NAME and NFA are the name's byte address");
|
||||
CHECK(byte_at(get(">NAME", 1, b)) == 1 && byte_at(get(">NAME", 1, b) + 1) == 'B', "which is its count byte, then its characters");
|
||||
CHECK(get("NAME>", 1, get(">NAME", 1, a)) == a && get("NAME>", 1, get(">NAME", 1, b)) == b && get("NAME>", 1, get(">NAME", 1, c)) == c, "NAME> goes back to the xt");
|
||||
CHECK(get("CFA", 1, c) == c, "CFA is the xt itself, as v3");
|
||||
@@ -306,15 +306,15 @@ int main(void)
|
||||
memset(long31, 'q', 31); long31[31] = 0;
|
||||
memset(long40, 'q', 40); long40[40] = 0;
|
||||
a = define(long31);
|
||||
CHECK(a == DICT_W + 8 + 3 && entry_is(a, long31, 0, 0), "a 31-character name is kept whole");
|
||||
CHECK(a == DICT_W + 8 + 4 && entry_is(a, long31, 0, 0), "a 31-character name is kept whole");
|
||||
b = define(long40);
|
||||
CHECK(b != 0 && entry_is(b, long31, a, 0) && b - a == 8 + 3, "a 40-character name is cut to 31");
|
||||
CHECK(b != 0 && entry_is(b, long31, a, 0) && b - a == 8 + 4, "a 40-character name is cut to 31");
|
||||
}
|
||||
empty_dictionary();
|
||||
n.mem[DP] = (DICT_END_W - 4) * 4;
|
||||
CHECK(define("ABCD") == 0 && err() && n.mem[LATEST] == 0 && n.mem[DP] == (DICT_END_W - 4) * 4 && n.mem[DICT_END_W - 4] == GUARD,
|
||||
"an entry that does not fit makes nothing, moves nothing, and sets NODE-ERROR");
|
||||
n.mem[DP] = (DICT_END_W - 5) * 4;
|
||||
CHECK(define("ABCD") == 0 && err() && n.mem[LATEST] == 0 && n.mem[DP] == (DICT_END_W - 5) * 4 && n.mem[DICT_END_W - 5] == GUARD,
|
||||
"an entry that does not fit makes nothing, moves nothing, and sets NODE-ERROR");
|
||||
n.mem[DP] = (DICT_END_W - 6) * 4;
|
||||
CHECK(define("ABCD") == DICT_END_W && !err() && n.mem[DICT_END_W] == GUARD, "one that just fits is made");
|
||||
|
||||
/* ---- FIND ---- */
|
||||
@@ -376,9 +376,9 @@ int main(void)
|
||||
/* the same name through (HEADER): the same cells */
|
||||
made = define("FIRST-HEADER");
|
||||
CHECK(made != 0 && n.mem[made - 3] == n.mem[xa - 3] && made - n.mem[made - 2] == xa - n.mem[xa - 2], "an entry made by (HEADER) has the same shape");
|
||||
for (i = 0; i < (unsigned)(xa - 3 - n.mem[xa - 2]); i++)
|
||||
for (i = 0; i < (unsigned)(xa - 4 - n.mem[xa - 2]); i++)
|
||||
if (n.mem[n.mem[xa - 2] + (v4_cell)i] != n.mem[n.mem[made - 2] + (v4_cell)i]) break;
|
||||
CHECK(i == (unsigned)(xa - 3 - n.mem[xa - 2]), "and the same name cells");
|
||||
CHECK(i == (unsigned)(xa - 4 - n.mem[xa - 2]), "and the same name cells");
|
||||
CHECK(n.mem[made - 1] == xp && find("FIRST-HEADER") == made && find("B") == xb, "new entries link on to the assembler's");
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ static v4_node n;
|
||||
static v4_exec_state es;
|
||||
static v4_heat h;
|
||||
static v4_text tx;
|
||||
static v4_cell w_key, w_key_end, w_fault, capsule_latest;
|
||||
static v4_cell w_key, w_key_end, w_fault, capsule_latest, capsule_ids;
|
||||
static v4_cell w_idle; /* (IDLE): where the node waits for a message */
|
||||
static char out[V4_CONSOLE_CAP + 1];
|
||||
|
||||
@@ -327,6 +327,8 @@ static void boot_with(unsigned depth)
|
||||
n.mem[VOC_LINK] = 0;
|
||||
n.mem[SRC] = TIB;
|
||||
for (v4_cell k = BVARS; k < BVARS + 14; k++) if (k != SRC) n.mem[k] = 0; /* empty buffers, SCR and BLK 0, no hook */
|
||||
n.mem[NEXT_ID] = capsule_ids; /* the next word ID is the first after the capsule's */
|
||||
n.mem[BOOT_ID] = capsule_ids;
|
||||
memset(ram, 0, sizeof ram); memset(common_dev, 0, sizeof common_dev);
|
||||
v4_blocks_init(&kblocks, BUF0_W);
|
||||
v4_node_console_input_attach(&n, CONSOLE_RX, CONSOLE_ST);
|
||||
@@ -934,6 +936,7 @@ int main(void)
|
||||
w_key_end = v4_text_word(&tx, "CR"); /* the word after KEY in core.f18 */
|
||||
w_fault = v4_text_word(&tx, "(FAULTS)");
|
||||
capsule_latest = v4_text_latest(&tx);
|
||||
capsule_ids = tx.next_id; /* the word IDs the capsule's headers took (#189, Phase 2) */
|
||||
CHECK(w_key_end > w_key && w_key_end - w_key <= 12, "KEY is the few words before CR");
|
||||
|
||||
/* ---- switch-on ---- */
|
||||
@@ -1766,24 +1769,36 @@ int main(void)
|
||||
n.mem[BOOT_CELLS] = n.mem[DP]; /* and all of it is the system, which COLD keeps */
|
||||
n.mem[BOOT_CELLS + 1] = n.mem[LATEST];
|
||||
n.mem[FENCE] = (n.mem[DP] + 3) / 4;
|
||||
n.mem[BOOT_ID] = n.mem[NEXT_ID];
|
||||
{
|
||||
unsigned before = known_n;
|
||||
v4_cell id0 = n.mem[NEXT_ID], c1;
|
||||
CHECK(kernel_agrees() && before == 3, "the kernel starts knowing the words that are there: %u", before);
|
||||
CHECK(is(say(": A1 ;\n"), " ok\nok> ") && known_n == before + 1 && kernel_agrees(), "a colon definition is told of");
|
||||
CHECK(strcmp(node_name(known[known_n - 1]), "A1") == 0, "by its xt, from which its name is read: %s", node_name(known[known_n - 1]));
|
||||
/* the word ID (#189 Phase 2; Captain Bob, 2026-11-13: "ID field in the header"): a cell before the flags,
|
||||
* the next number, so the kernel can join its record to the word */
|
||||
CHECK(id0 > 300 && n.mem[known[known_n - 1] - 4] == id0 && n.mem[NEXT_ID] == id0 + 1,
|
||||
"A1's word ID, at xt-4, is the next one: %ld (the system's words have the ones below)", (long)n.mem[known[known_n - 1] - 4]);
|
||||
CHECK(is(say("VARIABLE V1 5 CONSTANT C1 CREATE D1\n"), " ok\nok> ") && known_n == before + 4 && kernel_agrees(),
|
||||
"and a variable, a constant and a CREATEd word");
|
||||
c1 = known[known_n - 2];
|
||||
CHECK(n.mem[known[known_n - 3] - 4] == id0 + 1 && n.mem[c1 - 4] == id0 + 2 && n.mem[known[known_n - 1] - 4] == id0 + 3,
|
||||
"each numbered in turn");
|
||||
CHECK(is(say("VOCABULARY VOC1 VOC1 DEFINITIONS : IN1 ; FORTH DEFINITIONS : A2 ;\n"), " ok\nok> ")
|
||||
&& known_n == before + 7 && kernel_agrees(), "a vocabulary, a word in it, and a word after it");
|
||||
CHECK(strstr(say(": BAD NOSUCH ;\n"), "ERROR") != NULL && known_n == before + 8 && kernel_agrees(),
|
||||
"a definition that is abandoned is still an entry, and is told of");
|
||||
CHECK(is(say("FORGET C1\n"), " ok\nok> ") && known_n == before + 2 && kernel_agrees(),
|
||||
"FORGET tells that every entry from that one up has gone: %u left of ours", known_n - before);
|
||||
CHECK(n.mem[NEXT_ID] == id0 + 2, "and gives its IDs back: the next is C1's again (%ld)", (long)n.mem[NEXT_ID]);
|
||||
CHECK(is(say(": A3 ;\n"), " ok\nok> ") && known_n == before + 3 && kernel_agrees(), "and what is defined next is told of as before");
|
||||
CHECK(n.mem[known[known_n - 1] - 4] == id0 + 2, "with C1's ID, which FORGET gave back");
|
||||
CHECK(strstr(say("FORGET KD\n"), "Protected word") != NULL && known_n == before + 3 && kernel_agrees(),
|
||||
"a FORGET that is refused tells nothing");
|
||||
(void)say("COLD\n");
|
||||
CHECK(known_n == before && kernel_agrees(), "COLD tells that everything above the system has gone: %u", known_n);
|
||||
CHECK(n.mem[NEXT_ID] == id0, "and the IDs go back to the system's");
|
||||
CHECK(is(say(": A4 ; A4\n"), " ok\nok> ") && known_n == before + 1 && kernel_agrees(), "and the node goes on telling");
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "block_subsystem.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.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)
|
||||
@@ -32,6 +33,13 @@ static int used[PLACES];
|
||||
static unsigned roomed, given_back, borns, gones, parities;
|
||||
static v4_image im;
|
||||
static v4_mesh ms;
|
||||
static v4_mesh_account accounts[PLACES * V4_MESH_WORD_IDS]; /* the words' records (#189, Phase 2) */
|
||||
static unsigned char is_word[PLACES * (V4_NODE_WORDS / 8u)];
|
||||
static unsigned forgotten;
|
||||
static v4_cell forgotten_id = -1;
|
||||
static void forgot(void *self, unsigned place, v4_cell id) { (void)self; (void)place; forgotten++; forgotten_id = id; }
|
||||
static v4_uheat_t charged[V4_MESH_WORD_IDS];
|
||||
static void charge(void *ctx, v4_cell id, v4_uheat_t calls) { (void)ctx; charged[id] += calls; }
|
||||
|
||||
static int no_room; /* the owner has no room to give */
|
||||
static v4_fabric_node *room(void *self) { unsigned k; (void)self; if (no_room) return 0; for (k = 0; k < PLACES; k++) if (!used[k]) { used[k] = 1; roomed++; return &pool[k]; } return 0; }
|
||||
@@ -80,6 +88,7 @@ int main(void)
|
||||
ms.room = room; ms.unroom = unroom; ms.born = born; ms.gone = gone; ms.parity = parity;
|
||||
ms.out = out; ms.key = key; ms.words = words; ms.word_count = 1; ms.serve = serve;
|
||||
ms.capsules = capsules; ms.capsule_count = 2; ms.im = &im; ms.self = 0;
|
||||
ms.accounts = accounts; ms.is_word = is_word; ms.forgot = forgot;
|
||||
|
||||
/* ---- the boot ---- */
|
||||
CHECK(v4_mesh_boot(&ms) == 1, "the boot: %s", said);
|
||||
@@ -165,6 +174,63 @@ int main(void)
|
||||
r = line(V4_MESH_HERA, "3 4 + .", &how);
|
||||
CHECK(how == V4_TEXT_COMPLETED && strcmp(r, "7 ") == 0 && ms.owed == 0, "and nothing more is owed: \"%s\"", r);
|
||||
|
||||
/* ---- the words' accounts (#189, Phase 2; Captain Bob, 2026-11-13: the word ID in the header, charged
|
||||
* per heartbeat tick) ---- */
|
||||
{
|
||||
v4_cell xt, xt2;
|
||||
const v4_mesh_account *a;
|
||||
r = line(V4_MESH_HERA, ": SQ DUP * ; ' SQ .", &how);
|
||||
xt = (v4_cell)strtol(r, 0, 10);
|
||||
a = v4_mesh_account_at(&ms, 0, xt);
|
||||
CHECK(how == V4_TEXT_COMPLETED && xt > 0 && a && a->calls == 0 && a->xt == xt,
|
||||
"a word defined at the prompt has a record, called 0 times: \"%s\"", r);
|
||||
r = line(V4_MESH_HERA, "3 SQ .", &how);
|
||||
CHECK(strcmp(r, "9 ") == 0 && a && a->calls == 1, "a call to it counts one: %lu", a ? (unsigned long)a->calls : 0ul);
|
||||
r = line(V4_MESH_HERA, "5 SQ 6 SQ + .", &how);
|
||||
CHECK(strcmp(r, "61 ") == 0 && a && a->calls == 3, "and each call one more: %lu", a ? (unsigned long)a->calls : 0ul);
|
||||
r = line(V4_MESH_HERA, ": QUAD SQ SQ ; 2 QUAD .", &how);
|
||||
CHECK(strcmp(r, "16 ") == 0 && a && a->calls == 5, "calls from inside a definition count: %lu", a ? (unsigned long)a->calls : 0ul);
|
||||
r = line(V4_MESH_HERA, "4 ' SQ EXECUTE .", &how);
|
||||
CHECK(strcmp(r, "16 ") == 0 && a && a->calls == 6, "and through EXECUTE: %lu", a ? (unsigned long)a->calls : 0ul);
|
||||
{
|
||||
v4_cell id = v4_mesh_node(&ms, V4_MESH_HERA)->n.mem[xt - 4];
|
||||
memset(charged, 0, sizeof charged);
|
||||
v4_mesh_charge(&ms, 0, charge, 0);
|
||||
CHECK(id >= 0 && id < (v4_cell)V4_MESH_WORD_IDS && charged[id] == 6 && a && a->since == 0 && a->calls == 6,
|
||||
"the owner's charge takes the calls since, and clears them; the record keeps its count");
|
||||
memset(charged, 0, sizeof charged);
|
||||
v4_mesh_charge(&ms, 0, charge, 0);
|
||||
CHECK(charged[id] == 0, "a second charge with no calls between takes nothing for it");
|
||||
r = line(V4_MESH_HERA, "FORGET SQ", &how);
|
||||
CHECK(how == V4_TEXT_COMPLETED && v4_mesh_account_at(&ms, 0, xt) == 0 && forgotten >= 2 && forgotten_id >= id,
|
||||
"FORGET: its record goes, and QUAD's after it; the owner told (%u)", forgotten);
|
||||
r = line(V4_MESH_HERA, ": CUBE DUP DUP * * ; ' CUBE .", &how);
|
||||
xt2 = (v4_cell)strtol(r, 0, 10);
|
||||
CHECK(xt2 > 0 && v4_mesh_node(&ms, V4_MESH_HERA)->n.mem[xt2 - 4] == id && v4_mesh_account_at(&ms, 0, xt2)
|
||||
&& v4_mesh_account_at(&ms, 0, xt2)->calls == 0, "the next word defined takes SQ's ID, with a record of its own");
|
||||
}
|
||||
{
|
||||
v4_fabric_node *h = v4_mesh_node(&ms, V4_MESH_HERA);
|
||||
v4_cell w = h->n.mem[im.latest];
|
||||
unsigned k, words = 0, recorded = 0;
|
||||
for (k = 0; w > 4 && k < 2000; w = h->n.mem[w - 1], k++) { words++; if (v4_mesh_account_at(&ms, 0, w)) recorded++; }
|
||||
CHECK(words > 300 && recorded == words, "every word on Hera has a record: %u of %u", recorded, words);
|
||||
}
|
||||
r = line(V4_MESH_HERA, "11 2 2 BIRTH .", &how);
|
||||
{
|
||||
v4_fabric_node *x = v4_mesh_node(&ms, 11);
|
||||
unsigned place = 0, k;
|
||||
for (k = 0; k < PLACES; k++) if (v4_fabric_node_at(&ms.f, k) == x) place = k;
|
||||
r = line(11, ": TWICE 2* ; ' TWICE .", &how);
|
||||
xt = (v4_cell)strtol(r, 0, 10);
|
||||
r = line(11, "21 TWICE .", &how);
|
||||
CHECK(x && place != 0 && strcmp(r, "42 ") == 0 && v4_mesh_account_at(&ms, place, xt)
|
||||
&& v4_mesh_account_at(&ms, place, xt)->calls == 1, "a born node's words are kept too, by its own place");
|
||||
r = line(V4_MESH_HERA, "11 KILL", &how);
|
||||
CHECK(how == V4_TEXT_COMPLETED && v4_mesh_account_at(&ms, place, xt) == 0, "and go when it is killed");
|
||||
}
|
||||
}
|
||||
|
||||
printf(" %d checks, %d failures\n", checks, failures);
|
||||
return failures ? 1 : 0;
|
||||
}
|
||||
|
||||
+21
-8
@@ -431,27 +431,40 @@ int main(void)
|
||||
CHECK(ok("header DUP : (DUP) dup + ;"), "a header assembles: %s", v4_text_error(&tx));
|
||||
{
|
||||
v4_cell xt = v4_text_word(&tx, "(DUP)");
|
||||
CHECK(xt == ORIGIN + 4 && v4_text_latest(&tx) == xt, "the code follows the name cell and three more: %ld", (long)xt);
|
||||
CHECK(xt == ORIGIN + 5 && v4_text_latest(&tx) == xt, "the code follows the name cell and four more: %ld", (long)xt);
|
||||
CHECK((v4_ucell)nb.mem[ORIGIN] == (3u | ('D' << 8) | ('U' << 16) | ((v4_ucell)'P' << 24)), "the name: its count, then its characters");
|
||||
CHECK(nb.mem[xt - 4] == 0, "the word ID: the first header's is 0 (#189, Phase 2)");
|
||||
CHECK(nb.mem[xt - 3] == 0 && nb.mem[xt - 2] == ORIGIN && nb.mem[xt - 1] == 0, "flags 0, the name's address, link 0");
|
||||
CHECK(result("(DUP)", 1, 7, 0) == 14, "and the word runs");
|
||||
}
|
||||
/* the word ID (#189, Phase 2; Captain Bob, 2026-11-13: "ID field in the header"): numbered in the
|
||||
* order the headers are assembled, from 0, by `header` and by `#id,`; `#ids` is how many so far */
|
||||
CHECK(ok("header A : A' 1 ; header B : B' 2 ; header C : C' 3 ;"), "three headers: %s", v4_text_error(&tx));
|
||||
CHECK(nb.mem[v4_text_word(&tx, "A'") - 4] == 0 && nb.mem[v4_text_word(&tx, "B'") - 4] == 1
|
||||
&& nb.mem[v4_text_word(&tx, "C'") - 4] == 2, "their word IDs are 0, 1, 2");
|
||||
CHECK(ok("#id, #id, #id, #, #ids"), "#id, three times, then #ids: %s", v4_text_error(&tx));
|
||||
CHECK(nb.mem[ORIGIN] == 0 && nb.mem[ORIGIN + 1] == 1 && nb.mem[ORIGIN + 2] == 2 && nb.mem[ORIGIN + 3] == 3,
|
||||
"#id, lays 0, 1, 2; #ids is then 3");
|
||||
CHECK(ok("header A : A' 1 ; #id, header B : B' 2 ;"), "a header and #id, share the count: %s", v4_text_error(&tx));
|
||||
CHECK(nb.mem[v4_text_word(&tx, "B'") - 4] == 2, "so the next header's ID is 2");
|
||||
CHECK(ok("#, #ids") && nb.mem[ORIGIN] == 0, "with none yet, #ids is 0");
|
||||
CHECK(refused("macro M #id, endmacro M", "not allowed in a macro", 0), "#id, is refused in a macro");
|
||||
CHECK(ok(": BEFORE 1 ; header A : A' 2 ; header LONGER-NAM immediate : B' 3 ; : MID 4 ; header + inline compile-only : PLUS + ;")
|
||||
&& result("BEFORE", 0, 0, 0) == 1 && result("B'", 0, 0, 0) == 3 && result("MID", 0, 0, 0) == 4, "headers among other words");
|
||||
{
|
||||
v4_cell a = v4_text_word(&tx, "A'"), b = v4_text_word(&tx, "B'"), c = v4_text_word(&tx, "PLUS");
|
||||
CHECK(nb.mem[a - 1] == 0 && nb.mem[b - 1] == a && nb.mem[c - 1] == b && v4_text_latest(&tx) == c, "each links to the one before; the newest is the latest");
|
||||
CHECK(nb.mem[a - 3] == 0 && nb.mem[b - 3] == 1 && nb.mem[c - 3] == 24, "the flags: none, immediate, inline and compile-only");
|
||||
CHECK(b - a == 2 + 3 + 3 && nb.mem[b - 2] == b - 6, "a ten-character name takes three cells");
|
||||
CHECK((v4_ucell)nb.mem[b - 4] == (('N') | ('A' << 8) | ('M' << 16)), "and is padded with zeros");
|
||||
CHECK((v4_ucell)nb.mem[c - 4] == (1u | ('+' << 8)), "an opcode's name is taken as it stands");
|
||||
CHECK(b - a == 2 + 3 + 4 && nb.mem[b - 2] == b - 7, "a ten-character name takes three cells");
|
||||
CHECK((v4_ucell)nb.mem[b - 5] == (('N') | ('A' << 8) | ('M' << 16)), "and is padded with zeros");
|
||||
CHECK((v4_ucell)nb.mem[c - 5] == (1u | ('+' << 8)), "an opcode's name is taken as it stands");
|
||||
}
|
||||
CHECK(ok("header ( immediate : PAREN ; header \\ immediate : BACKSLASH ; header : : COLON ; header ; : SEMI ; header header : H ;"),
|
||||
"names the assembler would otherwise read as its own: %s", v4_text_error(&tx));
|
||||
CHECK((v4_ucell)nb.mem[v4_text_word(&tx, "PAREN") - 4] == (1u | ('(' << 8))
|
||||
&& (v4_ucell)nb.mem[v4_text_word(&tx, "BACKSLASH") - 4] == (1u | ('\\' << 8))
|
||||
&& (v4_ucell)nb.mem[v4_text_word(&tx, "COLON") - 4] == (1u | (':' << 8))
|
||||
&& (v4_ucell)nb.mem[v4_text_word(&tx, "SEMI") - 4] == (1u | (';' << 8)), "are names like any other");
|
||||
CHECK((v4_ucell)nb.mem[v4_text_word(&tx, "PAREN") - 5] == (1u | ('(' << 8))
|
||||
&& (v4_ucell)nb.mem[v4_text_word(&tx, "BACKSLASH") - 5] == (1u | ('\\' << 8))
|
||||
&& (v4_ucell)nb.mem[v4_text_word(&tx, "COLON") - 5] == (1u | (':' << 8))
|
||||
&& (v4_ucell)nb.mem[v4_text_word(&tx, "SEMI") - 5] == (1u | (';' << 8)), "are names like any other");
|
||||
CHECK(ok("header data-word data : D 1 ;") && nb.mem[v4_text_word(&tx, "D") - 3] == 4, "the data flag");
|
||||
CHECK(ok("header 1234567890123456789012345678901 : W ;"), "a 31-character name fits");
|
||||
CHECK(refused("header 12345678901234567890123456789012 : W ;", "at most 31 characters", 1), "a 32-character one is refused");
|
||||
|
||||
Reference in New Issue
Block a user