From 07911dcf91798985586c0526743028577dcb86c6 Mon Sep 17 00:00:00 2001 From: rajames Date: Thu, 15 Oct 2026 11:54:15 -0400 Subject: [PATCH] feat(v4): the ROM's symbol table (FIND) ( -- rec | 0 ), (DEFINE) ( value kind -- ) and (OPCODE) ( -- op | -1 ), entries 3-5. Names are refused as text.c's define refuses them, compared case by case with text.c; a set variable may be set again. 256 hash heads, records growing up and names growing down in dictionary space, used only while the ROM assembles. text.c gains the data directives #, and ", (the ROM's language; its mnemonic and reserved-word tables are data); the nucleus image is byte-identical. Co-Authored-By: Claude Opus 5.5 --- capsules/v4/rom/main.f18 | 3 + capsules/v4/rom/sym.f18 | 110 +++++++++++++++++++++++++++ v4/include/v4/text.h | 7 ++ v4/src/text.c | 97 +++++++++++++++++++++--- v4/tests/host_map.h | 23 ++++++ v4/tests/rom_harness.h | 2 +- v4/tests/test_host_rom_sym.c | 140 +++++++++++++++++++++++++++++++++++ v4/tests/test_text.c | 22 ++++++ v4/tools/mkmap.c | 13 ++++ 9 files changed, 404 insertions(+), 13 deletions(-) create mode 100644 capsules/v4/rom/sym.f18 create mode 100644 v4/tests/test_host_rom_sym.c diff --git a/capsules/v4/rom/main.f18 b/capsules/v4/rom/main.f18 index be11fb8f..a2d5fc49 100644 --- a/capsules/v4/rom/main.f18 +++ b/capsules/v4/rom/main.f18 @@ -10,3 +10,6 @@ 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 ) diff --git a/capsules/v4/rom/sym.f18 b/capsules/v4/rom/sym.f18 new file mode 100644 index 00000000..0e9ba649 --- /dev/null +++ b/capsules/v4/rom/sym.f18 @@ -0,0 +1,110 @@ +\ sym.f18 -- the ROM's symbol table. +\ +\ A name the ROM assembles is given a kind and a value: 1 a word (its +\ address), 2 a constant, 3 a macro, 4 a set variable. The rules are +\ v4/src/text.c's define: a name may not be an opcode, a reserved word, a +\ label (NAME:), a number or start with &; nothing is defined twice, except +\ that a set variable may be set again. +\ +\ The table is used only while the ROM assembles, and lives in dictionary +\ space: 256 hash heads at (SYMH); records of four cells -- link, name, +\ kind, value -- growing up from SYMREC; names, as counted strings, growing +\ down from POOLTOP. When the two would meet, "too many names". (RECN) and +\ (POOLN) count the cells used, so an empty table is all zeroes. Names are +\ compared as the token is held, a counted string, a cell at a time. +\ (REC) (CNT) (PTR) (I) (WI) are working cells. Every routine uses A; some B. + +\ ( addr -- f ) 1 if the counted string at addr is the token +: (SAME?) + (PTR) a! ! (TOK) (I) a! ! + (TOKN) a! @ 4 + 2/ 2/ + L: (PTR) a! @ b! @b (I) a! @ a! @ xor if EQ drop drop 0 ; + EQ: drop (PTR) a! @ 1 + ! (I) a! @ 1 + ! + -1 + if DONE jump L + DONE: drop 1 ; + +\ ( table -- i | -1 ) which counted string of a table the token is; the +\ table ends with a zero cell +: (WALK) + 0 (WI) a! ! + L: dup a! @ if END + drop dup (SAME?) if NO + drop drop (WI) a! @ ; + NO: drop dup a! @ 255 and 4 + 2/ 2/ + + (WI) a! @ 1 + ! jump L + END: drop drop -1 ; + +\ the opcodes' names, in the order of their numbers +: (MNEMONICS) + ", ; ", ex ", jump ", call ", unext ", next ", if ", -if + ", @p ", @+ ", @b ", @ ", !p ", !+ ", !b ", ! + ", +* ", 2* ", 2/ ", inv ", + ", and ", xor ", drop + ", dup ", pop ", over ", a ", nop ", push ", b! ", a! + #, 0 + +\ the ROM's reserved words: its notation and its directives +: (RESERVED) + ", : ", macro ", endmacro ", FOR ", NEXT ", UNEXT + ", constant ", set ", here ", #, ", ", ", store + ", start + #, 0 + +\ ( -- op | -1 ) which opcode the token names +: (OPCODE) &(MNEMONICS) jump (WALK) + +\ ( -- h ) the token's hash, 0 .. 255, from its first two cells +: (HASH) + (TOK) a! @+ @ xor + dup 2/ 2/ 2/ 2/ 2/ 2/ 2/ 2/ + + dup 2/ 2/ 2/ 2/ 2/ 2/ 2/ 2/ 2/ 2/ 2/ 2/ 2/ 2/ 2/ 2/ + + 255 and ; + +\ ( -- rec | 0 ) the newest record whose name is the token +: (FIND) + (HASH) (SYMH) + a! @ + L: if NONE + dup (REC) a! ! 1 + a! @ (SAME?) if NO + drop (REC) a! @ ; + NO: drop (REC) a! @ a! @ jump L + NONE: ; + +\ ( -- f ) 1 if the token cannot be a name +: (BADNAME?) + (OPCODE) -if BAD + drop &(RESERVED) (WALK) -if BAD + drop (NUMBER) push drop pop if NOTNUM drop 1 ; + NOTNUM: drop 0 (CHAR@) 38 xor if AMP + drop (TOKN) a! @ -1 + if ONE + (CHAR@) 58 xor if COLON drop 0 ; + COLON: drop 1 ; + ONE: drop 0 ; + AMP: drop 1 ; + BAD: drop 1 ; + +\ ( value kind -- ) give the token a kind and a value +: (DEFINE) + (BADNAME?) if OKNAME drop drop drop ERR-BAD-NAME jump (FAIL) + OKNAME: drop + (FIND) if NEW + over 4 xor if K4 drop drop drop drop ERR-DEFINED jump (FAIL) + K4: drop 2 + a! @ 4 xor if BOTH drop drop drop ERR-DEFINED jump (FAIL) + BOTH: drop drop (REC) a! @ 3 + a! ! ; + NEW: drop + (TOKN) a! @ 4 + 2/ 2/ (CNT) a! ! + POOLTOP (POOLN) a! @ inv 1 + + (CNT) a! @ inv 1 + + + dup SYMREC (RECN) a! @ + 4 + inv 1 + + + -if ROOM drop drop drop drop ERR-NAMES jump (FAIL) + ROOM: drop + dup (PTR) a! ! (TOK) (I) a! ! (CNT) a! @ + C: (I) a! @ a! @ (PTR) a! @ b! !b + (PTR) a! @ 1 + ! (I) a! @ 1 + ! + -1 + if CD jump C + CD: drop + (POOLN) a! @ (CNT) a! @ + (POOLN) a! ! + SYMREC (RECN) a! @ + (REC) a! ! + (REC) a! @ 1 + a! ! + (REC) a! @ 2 + a! ! + (REC) a! @ 3 + a! ! + (HASH) (SYMH) + dup a! @ (REC) a! @ a! ! + (REC) a! @ over a! ! drop + (RECN) a! @ 4 + ! ; diff --git a/v4/include/v4/text.h b/v4/include/v4/text.h index 66830306..682eaab1 100644 --- a/v4/include/v4/text.h +++ b/v4/include/v4/text.h @@ -49,6 +49,13 @@ * constant NAME VALUE * NAME is the number VALUE, as v4_text_constant gives it. * + * #, VALUE one cell of data at the next word address (the word + * being built is ended first). VALUE is a number, a + * constant, CONST+N, or &NAME of a word already defined. + * ", TOKEN TOKEN, whatever its characters, as a counted string: its + * count byte, then its characters, four to a cell, low + * byte first, zero-padded to a whole cell. + * * header NAME a dictionary entry for NAME (capsule/dict.v4), laid down * in front of the word that the next `:` starts, and * linked to the header before it. NAME is taken as it diff --git a/v4/src/text.c b/v4/src/text.c index 5269379b..4f477166 100644 --- a/v4/src/text.c +++ b/v4/src/text.c @@ -65,7 +65,8 @@ static int is_reserved(const char *s) return strcmp(s, ":") == 0 || strcmp(s, "macro") == 0 || strcmp(s, "endmacro") == 0 || strcmp(s, "\\") == 0 || strcmp(s, "(") == 0 || strcmp(s, "FOR") == 0 || strcmp(s, "NEXT") == 0 || strcmp(s, "UNEXT") == 0 - || strcmp(s, "header") == 0 || strcmp(s, "constant") == 0 || flag_of(s) != 0; + || strcmp(s, "header") == 0 || strcmp(s, "constant") == 0 + || strcmp(s, "#,") == 0 || strcmp(s, "\",") == 0 || flag_of(s) != 0; } static int is_label_def(const char *s) @@ -171,6 +172,23 @@ static void put_cell(v4_text *t, v4_cell value) as->here++; } +/* NAME as a counted string at the next word address: its count byte, then + * its characters, four bytes to a cell, low byte first, zero-padded to a + * whole cell. The layout of a header's name, and of `",`. */ +static void lay_name(v4_text *t, const char *name) +{ + unsigned len = (unsigned)strlen(name), cells = (len + 4u) / 4u, i, b; + for (i = 0; i < cells; i++) { + v4_ucell w = 0; + for (b = 0; b < 4; b++) { + unsigned at = 4u * i + b; /* byte 0 is the count */ + unsigned ch = at == 0 ? len : (at <= len ? (unsigned char)name[at - 1u] : 0u); + w |= (v4_ucell)ch << (8u * b); + } + put_cell(t, (v4_cell)w); + } +} + /* `header NAME`: a dictionary entry for NAME, as capsule/dict.v4 lays one * out, in front of the word the next `:` starts: * the name: its count byte, then its characters, four bytes to a cell, @@ -180,7 +198,7 @@ static void put_cell(v4_text *t, v4_cell value) static void header(v4_text *t, reader *r) { char name[V4_TEXT_MAX_TOKEN + 1u]; - unsigned len, cells, i; + unsigned len; v4_cell nfa; if (r->depth != 1) { fail(t, "not allowed in a macro", "header"); return; } @@ -190,16 +208,7 @@ static void header(v4_text *t, reader *r) if (len > 31) { fail(t, "a header's name is at most 31 characters", name); return; } end_word(t); nfa = v4_asm_label(&t->as); - cells = (len + 4u) / 4u; - for (i = 0; i < cells; i++) { - v4_ucell w = 0; - for (unsigned b = 0; b < 4; b++) { - unsigned at = 4u * i + b; /* byte 0 is the count */ - unsigned ch = at == 0 ? len : (at <= len ? (unsigned char)name[at - 1u] : 0u); - w |= (v4_ucell)ch << (8u * b); - } - put_cell(t, (v4_cell)w); - } + lay_name(t, name); put_cell(t, 0); put_cell(t, nfa); put_cell(t, t->latest); @@ -357,6 +366,68 @@ static void constant_directive(v4_text *t, reader *r) define(t, name, KIND_CONST, v); } +/* A VALUE in a directive: a number, a constant, CONST+N, or &NAME of a word + * already defined. 1 and its value, or 0 having failed. */ +static int value_of(v4_text *t, const char *tok, v4_cell *out) +{ + const v4_text_symbol *s; + int big; + if (number_of(tok, out, &big)) { + if (big) { fail(t, "number does not fit a cell", tok); return 0; } + return 1; + } + if (tok[0] == '&' && tok[1] != 0) { + s = find(t, tok + 1); + if (s && s->kind == KIND_WORD) { *out = s->value; return 1; } + fail(t, "& needs a word that is already defined", tok + 1); + return 0; + } + s = find(t, tok); + if (s && s->kind == KIND_CONST) { *out = s->value; return 1; } + if (!s) { + const char *plus = strrchr(tok, '+'); + if (plus && plus != tok && plus[1] != 0) { + char left[V4_TEXT_MAX_TOKEN + 1u]; + const v4_text_symbol *c; + v4_cell n; + size_t len = (size_t)(plus - tok); + memcpy(left, tok, len); + left[len] = 0; + c = find(t, left); + if (c && c->kind == KIND_CONST && number_of(plus + 1, &n, &big) && !big) { + *out = (v4_cell)((v4_ucell)c->value + (v4_ucell)n); + return 1; + } + } + } + fail(t, "not a value", tok); + return 0; +} + +/* `#, VALUE`: one cell of data at the next word address. */ +static void cell_directive(v4_text *t, reader *r) +{ + char tok[V4_TEXT_MAX_TOKEN + 1u]; + v4_cell v; + if (r->depth != 1) { fail(t, "not allowed in a macro", "#,"); return; } + if (!next_token(t, r, tok)) { if (!t->failed) fail(t, "#, without a value", NULL); return; } + if (!value_of(t, tok, &v)) return; + (void)v4_asm_label(&t->as); + put_cell(t, v); + check_asm(t, "#,"); +} + +/* `", TOKEN`: TOKEN, whatever its characters, as a counted string. */ +static void string_directive(v4_text *t, reader *r) +{ + char tok[V4_TEXT_MAX_TOKEN + 1u]; + if (r->depth != 1) { fail(t, "not allowed in a macro", "\","); return; } + if (!raw_token(t, r, tok)) { fail(t, "\", without a string, or one too long", NULL); return; } + (void)v4_asm_label(&t->as); + lay_name(t, tok); + check_asm(t, tok); +} + static void one_token(v4_text *t, reader *r, const char *tok) { const v4_text_symbol *s; @@ -381,6 +452,8 @@ static void one_token(v4_text *t, reader *r, const char *tok) } if (strcmp(tok, "macro") == 0) { define_macro(t, r); return; } if (strcmp(tok, "constant") == 0) { constant_directive(t, r); return; } + if (strcmp(tok, "#,") == 0) { cell_directive(t, r); return; } + if (strcmp(tok, "\",") == 0) { string_directive(t, r); return; } if (strcmp(tok, "endmacro") == 0) { fail(t, "endmacro without macro", NULL); return; } if (is_label_def(tok)) { diff --git a/v4/tests/host_map.h b/v4/tests/host_map.h index fdcfd472..a2777802 100644 --- a/v4/tests/host_map.h +++ b/v4/tests/host_map.h @@ -133,9 +133,32 @@ #define ROM_NI (OUT_W - 72) /* the next character of the token */ #define ROM_NU (OUT_W - 71) /* the value so far, unsigned */ #define ROM_NBIG (OUT_W - 70) /* 1 once it has not fitted a cell */ +#define ROM_RECN (OUT_W - 128) /* (DEFINE): cells of symbol records used */ +#define ROM_POOLN (OUT_W - 127) /* cells of the name pool used */ +#define ROM_REC (OUT_W - 126) /* (FIND): the record being looked at */ +#define ROM_CNT (OUT_W - 125) /* (DEFINE): cells in the token's name */ +#define ROM_PTR (OUT_W - 124) /* (SAME?), (DEFINE): where in a name */ +#define ROM_I (OUT_W - 123) /* where in the token */ +#define ROM_WI (OUT_W - 122) /* (WALK): which entry of a table */ +/* THE ROM'S SYMBOL TABLE, used only while it assembles, in dictionary space (cleared at `start`): 256 hash heads, + * then records growing up -- link, name, kind, value -- and names, as counted strings, growing down from the top. */ +#define ROM_SYMH DICT_W +#define ROM_SYMREC (DICT_W + 256) +#define ROM_POOLTOP DICT_END_W +#define ROM_REC_LINK 0 +#define ROM_REC_NAME 1 +#define ROM_REC_KIND 2 +#define ROM_REC_VALUE 3 +#define ROM_KIND_WORD 1 +#define ROM_KIND_CONST 2 +#define ROM_KIND_MACRO 3 +#define ROM_KIND_SET 4 /* the ROM's error codes */ #define ROM_ERR_TOO_LONG 1 /* a token longer than 63 characters: "name too long" */ #define ROM_ERR_COMMENT 2 /* a ( with no ) before the end: "comment not closed" */ +#define ROM_ERR_BAD_NAME 3 /* an opcode, a reserved word, a label, a number or &...: "cannot be used as a name" */ +#define ROM_ERR_DEFINED 4 /* "already defined" */ +#define ROM_ERR_NAMES 5 /* the symbol table is full: "too many names" */ #define WORD_DEFINED (BUF0_W - 11) /* the xt of the word that is told of each new entry, or 0 */ #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 */ diff --git a/v4/tests/rom_harness.h b/v4/tests/rom_harness.h index 8416f499..18c46c8b 100644 --- a/v4/tests/rom_harness.h +++ b/v4/tests/rom_harness.h @@ -29,7 +29,7 @@ extern const unsigned v4_rom_32_words; /* The entry table, in its fixed order: entry i is at V4_ROM_BASE + i. */ static const char *const rom_entries[] = { - "(KEY)", "(TOKEN)", "(NUMBER)", "(FIND)", "(DEFINE)", + "(KEY)", "(TOKEN)", "(NUMBER)", "(FIND)", "(DEFINE)", "(OPCODE)", "(OP)", "(LIT)", "(BRANCH)", "(BRANCH>)", "(RESOLVE)", "(LABEL)" }; #define ROM_ENTRIES (sizeof rom_entries / sizeof rom_entries[0]) diff --git a/v4/tests/test_host_rom_sym.c b/v4/tests/test_host_rom_sym.c new file mode 100644 index 00000000..afff22db --- /dev/null +++ b/v4/tests/test_host_rom_sym.c @@ -0,0 +1,140 @@ +/* test_host_rom_sym.c -- the ROM's symbol table. + * + * Plan Task 4. (DEFINE) ( value kind -- ) gives the token (TOKEN) read a + * value and a kind; (FIND) ( -- rec | 0 ) finds it, newest first; (OPCODE) + * ( -- op | -1 ) says which of the 32 mnemonics it is. A name is refused as + * v4/src/text.c's define refuses one -- an opcode, a reserved word, a label + * (NAME:), a number, &... -- and every such case is also given to text.c, + * the oracle. A set variable (kind 4) may be set again; nothing else may be + * defined twice. The table is in dictionary space: records growing up, + * names growing down, and "too many names" when they meet. + */ +#include "rom_harness.h" +#include "v4/text.h" + +static int failures = 0, checks = 0; +#define CHECK(c,...) do{checks++; if(!(c)){failures++; printf("FAIL %s:%d: ",__FILE__,__LINE__); printf(__VA_ARGS__); printf("\n");}}while(0) + +static v4_node n, oracle_node; +static v4_text tx; + +static void tokenize(const char *name) +{ + char in[80]; + snprintf(in, sizeof in, "%s ", name); + v4_node_console_feed(&n, in, (unsigned)strlen(in)); + if (!rom_call(&n, "(TOKEN)", 100000)) printf(" (TOKEN) did not return for %s\n", name); + (void)v4_dstack_pop(&n.ds); +} + +/* (DEFINE) the name; the error code it raised (0: none), cleared again */ +static v4_cell define(const char *name, v4_cell value, v4_cell kind) +{ + v4_cell err; + tokenize(name); + v4_dstack_push(&n.ds, value); + v4_dstack_push(&n.ds, kind); + if (!rom_call(&n, "(DEFINE)", 1000000)) return -99; + err = n.mem[ROM_FAILED]; + n.mem[ROM_FAILED] = 0; + if (n.ds.depth != 0) return -98; /* (DEFINE) leaves nothing, refused or not */ + return err; +} + +static v4_cell find(const char *name) +{ + tokenize(name); + if (!rom_call(&n, "(FIND)", 1000000)) return -99; + return v4_dstack_pop(&n.ds); +} + +static v4_cell opcode(const char *name) +{ + tokenize(name); + if (!rom_call(&n, "(OPCODE)", 1000000)) return -99; + return v4_dstack_pop(&n.ds); +} + +/* text.c's verdict on NAME as a name: 0 accepted, ROM_ERR_BAD_NAME refused */ +static v4_cell oracle(const char *name) +{ + char src[120]; + v4_node_reset(&oracle_node); + v4_text_begin(&tx, &oracle_node, 16); + snprintf(src, sizeof src, "constant %s 1", name); + if (v4_text_assemble(&tx, src)) return 0; + return strstr(v4_text_error(&tx), "cannot be used as a name") ? ROM_ERR_BAD_NAME : -1; +} + +int main(void) +{ + static const char *const mnemonic[32] = { + ";", "ex", "jump", "call", "unext", "next", "if", "-if", "@p", "@+", "@b", "@", "!p", "!+", "!b", "!", + "+*", "2*", "2/", "inv", "+", "and", "xor", "drop", "dup", "pop", "over", "a", "nop", "push", "b!", "a!" + }; + static const char *const names[] = { + "dup", "a!", ";", "-if", ":", "macro", "endmacro", "FOR", "NEXT", "UNEXT", "constant", "#,", "\",", + "x:", "ab:", "12", "$ff", "-1", "99999999999999999999999", "&y", "&", + "a1", "abc", "(x", "x:y", "DUP", "Dup", "+1", "-", "$", "LONGERNAME", ":x" + }; + unsigned i, count; + v4_cell rec; + char nm[16]; + + rom_boot(&n); + + /* the 32 mnemonics, and things that are not one */ + for (i = 0; i < 32; i++) CHECK(opcode(mnemonic[i]) == (v4_cell)i, "%s is opcode %u", mnemonic[i], i); + CHECK(opcode("DUP") == -1 && opcode("dupe") == -1 && opcode("du") == -1 && opcode("@pp") == -1, "not mnemonics"); + + /* define and find */ + CHECK(define("A", 5, ROM_KIND_CONST) == 0, "A defined"); + CHECK(define("B", 100, ROM_KIND_WORD) == 0, "B defined"); + CHECK(define("Cee", 7, ROM_KIND_MACRO) == 0, "Cee defined"); + rec = find("A"); + CHECK(rec > 0 && n.mem[rec + ROM_REC_KIND] == ROM_KIND_CONST && n.mem[rec + ROM_REC_VALUE] == 5, "A found: rec %ld", (long)rec); + rec = find("B"); + CHECK(rec > 0 && n.mem[rec + ROM_REC_KIND] == ROM_KIND_WORD && n.mem[rec + ROM_REC_VALUE] == 100, "B found"); + rec = find("Cee"); + CHECK(rec > 0 && n.mem[rec + ROM_REC_KIND] == ROM_KIND_MACRO && n.mem[rec + ROM_REC_VALUE] == 7, "Cee found"); + CHECK(find("C") == 0 && find("Ce") == 0 && find("Ceee") == 0 && find("a") == 0, "near misses are not found"); + + /* twice */ + CHECK(define("A", 6, ROM_KIND_CONST) == ROM_ERR_DEFINED, "A twice: already defined"); + CHECK(define("A", 6, ROM_KIND_SET) == ROM_ERR_DEFINED, "a set variable over a constant: already defined"); + CHECK(define("V", 1, ROM_KIND_SET) == 0 && define("V", 2, ROM_KIND_SET) == 0, "a set variable may be set again"); + rec = find("V"); + CHECK(rec > 0 && n.mem[rec + ROM_REC_VALUE] == 2, "and has its new value"); + CHECK(define("V", 3, ROM_KIND_CONST) == ROM_ERR_DEFINED, "but not defined as something else"); + + /* names refused as text.c refuses them */ + for (i = 0; i < sizeof names / sizeof names[0]; i++) { + v4_cell o = oracle(names[i]), r = define(names[i], 1, ROM_KIND_CONST); + CHECK(o == r, "%s: text.c %ld, ROM %ld", names[i], (long)o, (long)r); + } + /* and the ROM's own directives, which text.c does not have */ + CHECK(define("set", 1, ROM_KIND_CONST) == ROM_ERR_BAD_NAME && define("here", 1, ROM_KIND_CONST) == ROM_ERR_BAD_NAME + && define("store", 1, ROM_KIND_CONST) == ROM_ERR_BAD_NAME && define("start", 1, ROM_KIND_CONST) == ROM_ERR_BAD_NAME, + "set here store start are reserved"); + + /* capacity: fill the table, then find every name with its value */ + rom_boot(&n); + for (count = 0; count < 5000; count++) { + v4_cell err; + snprintf(nm, sizeof nm, "S%04u", count); + err = define(nm, (v4_cell)count * 3, ROM_KIND_WORD); + if (err == ROM_ERR_NAMES) break; + if (err != 0) { CHECK(0, "%s: error %ld", nm, (long)err); break; } + } + CHECK(count >= 800, "at least 800 five-character names fit: %u", count); + CHECK(count < 5000, "and the table does fill"); + for (i = 0; i < count; i++) { + snprintf(nm, sizeof nm, "S%04u", i); + rec = find(nm); + if (!(rec > 0 && n.mem[rec + ROM_REC_VALUE] == (v4_cell)i * 3)) { CHECK(0, "%s not found with its value", nm); break; } + } + CHECK(i == count, "every one of them is found"); + + printf("test_host_rom_sym: %d checks, %d failures\n", checks, failures); + return failures != 0; +} diff --git a/v4/tests/test_text.c b/v4/tests/test_text.c index d6dd7de0..db2e88e9 100644 --- a/v4/tests/test_text.c +++ b/v4/tests/test_text.c @@ -499,6 +499,28 @@ int main(void) CHECK(refused("constant K x", "not a number", 1), "a constant's value is a number"); CHECK(refused(": constant ;", "cannot be used as a name", 1), "constant is reserved"); + /* ---- `#, VALUE` and `", TOKEN`, data in the text (the ROM's language) ---- */ + { + v4_ucell ab = 2u | ((v4_ucell)'a' << 8) | ((v4_ucell)'b' << 16), paren = 1u | ((v4_ucell)'(' << 8); + v4_ucell five_l = 5u | ((v4_ucell)'a' << 8) | ((v4_ucell)'b' << 16) | ((v4_ucell)'c' << 24); + v4_ucell five_h = (v4_ucell)'d' | ((v4_ucell)'e' << 8); + CHECK(ok("constant K 9 : T ; #, 5 #, -1 #, K #, K+2 #, &T \", ab \", ( \", abcde"), + "data assembles: %s", v4_text_error(&tx)); + CHECK(nb.mem[ORIGIN + 1] == 5 && nb.mem[ORIGIN + 2] == -1 && nb.mem[ORIGIN + 3] == 9 && nb.mem[ORIGIN + 4] == 11 + && nb.mem[ORIGIN + 5] == ORIGIN, "#, lays a number, a constant, CONST+N and &NAME"); + CHECK((v4_ucell)nb.mem[ORIGIN + 6] == ab && (v4_ucell)nb.mem[ORIGIN + 7] == paren, + "\", lays a counted string, padded to a whole cell; ( is a name here"); + CHECK((v4_ucell)nb.mem[ORIGIN + 8] == five_l && (v4_ucell)nb.mem[ORIGIN + 9] == five_h && v4_text_here(&tx) == ORIGIN + 10, + "a longer one runs on into the next cell"); + CHECK(ok(": T dup #, 7 ;") && nb.mem[ORIGIN + 1] == 7 && v4_text_word(&tx, "T") == ORIGIN, + "#, inside a word ends the instruction word first"); + CHECK(refused("#, x", "not a value", 1), "#, needs a value"); + CHECK(refused("#,", "#, without a value", 1), "#, at the end"); + CHECK(refused("\",", "\", without a string", 1), "\", at the end"); + CHECK(refused(": #, ;", "cannot be used as a name", 1) && refused(": \", ;", "cannot be used as a name", 1), + "#, and \", are reserved"); + } + CHECK(v4_node_guards_intact(&na) && v4_node_guards_intact(&nb), "guards intact"); printf(" %d checks, %d failures\n", checks, failures); diff --git a/v4/tools/mkmap.c b/v4/tools/mkmap.c index 3de9d7fb..850f37da 100644 --- a/v4/tools/mkmap.c +++ b/v4/tools/mkmap.c @@ -36,6 +36,19 @@ int main(void) constant("(NI)", (long)ROM_NI); constant("(NU)", (long)ROM_NU); constant("(NBIG)", (long)ROM_NBIG); + constant("(RECN)", (long)ROM_RECN); + constant("(POOLN)", (long)ROM_POOLN); + constant("(REC)", (long)ROM_REC); + constant("(CNT)", (long)ROM_CNT); + constant("(PTR)", (long)ROM_PTR); + constant("(I)", (long)ROM_I); + constant("(WI)", (long)ROM_WI); + constant("(SYMH)", (long)ROM_SYMH); + constant("SYMREC", (long)ROM_SYMREC); + constant("POOLTOP", (long)ROM_POOLTOP); + constant("ERR-BAD-NAME", (long)ROM_ERR_BAD_NAME); + constant("ERR-DEFINED", (long)ROM_ERR_DEFINED); + constant("ERR-NAMES", (long)ROM_ERR_NAMES); /* the cell width's constants (NUMBER) needs, written as signed cells */ constant("MSB", (long)(v4_cell)V4_MSB); constant("DEC-LIMIT", (long)(v4_cell)(~(v4_ucell)0 / 10u));