/* test_console.c -- the CONSOLE-TX capture register (node.h). * * EMIT is a device service (DECOMPOSITION.md 5.10) and the mesh is not built * yet, so the single-node model stands in for the console with one * memory-mapped register: a store to it appends a character to a buffer on * the node. This file checks that register and nothing else: no printing * word is defined here. */ #include "v4/asm.h" #include "v4/testcode.h" #include #include 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) /* The memory map is open (D-4); the test chooses the address. */ #define CONSOLE_TX ((v4_cell)(V4_NODE_WORDS - 4u)) #define MARK ((v4_cell)0x1234) static v4_node n; static v4_exec_state es; static v4_heat h; static v4_asm as; #define O(name) v4_asm_op(&as, V4_OP_##name) #define LIT(v) v4_asm_lit(&as, (v4_cell)(v)) static int run(v4_cell word) { v4_dstack_reset(&n.ds); v4_rstack_reset(&n.rs); v4_exec_reset(&es); v4_heat_reset(&h); return v4_test_call(&n, &es, &h, word, 1000) > 0; } int main(void) { v4_cell w_hi; unsigned i; printf("v4 console tests: V4_CELL_BITS=%d, capture %u characters\n", V4_CELL_BITS, (unsigned)V4_CONSOLE_CAP); /* A fresh node has no console: every store is a memory store. */ v4_node_reset(&n); CHECK(n.console_tx == -1 && n.console_len == 0 && n.console_dropped == 0, "reset leaves no console attached"); v4_node_store(&n, CONSOLE_TX, MARK); CHECK(v4_node_load(&n, CONSOLE_TX) == MARK && n.console_len == 0, "with no console the address is ordinary memory"); /* Attached: a store is captured and memory is left alone. */ v4_node_console_attach(&n, CONSOLE_TX); v4_node_store(&n, CONSOLE_TX, 'A'); CHECK(n.console_len == 1 && n.console[0] == 'A', "a store is captured"); CHECK(n.mem[CONSOLE_TX] == MARK, "and does not write memory"); CHECK(v4_node_load(&n, CONSOLE_TX) == MARK, "a load reads the memory word, as before"); /* Only the low byte is the character. */ v4_node_store(&n, CONSOLE_TX, 0x141); v4_node_store(&n, CONSOLE_TX, -1); v4_node_store(&n, CONSOLE_TX, 0); CHECK(n.console_len == 4 && n.console[1] == 'A' && n.console[2] == 0xFF && n.console[3] == 0, "the low 8 bits are the character"); /* Other addresses are still memory. */ v4_node_store(&n, CONSOLE_TX - 1, 'x'); v4_node_store(&n, CONSOLE_TX + 1, 'y'); CHECK(n.console_len == 4 && n.mem[CONSOLE_TX - 1] == 'x' && n.mem[CONSOLE_TX + 1] == 'y', "neighbouring addresses are ordinary memory"); /* Through the executor: !b, ! and !+ each reach the register, in order. */ v4_asm_begin(&as, &n, 16); w_hi = v4_asm_label(&as); LIT('H'); LIT(CONSOLE_TX); O(BANG_B); O(STORE_B); /* !b */ LIT('i'); LIT(CONSOLE_TX); O(BANG_A); O(STORE_A); /* ! */ LIT('!'); LIT(CONSOLE_TX); O(BANG_A); O(STORE_INC); /* !+ */ O(SEMI); CHECK(v4_asm_ok(&as), "test word assembles"); n.mem[CONSOLE_TX - 1] = 0; n.mem[CONSOLE_TX + 1] = 0; v4_node_console_attach(&n, CONSOLE_TX); CHECK(n.console_len == 0 && n.console_dropped == 0, "attaching empties the capture"); CHECK(run(w_hi), "the word returns"); CHECK(n.console_len == 3 && memcmp(n.console, "Hi!", 3) == 0, "!b, ! and !+ print \"Hi!\""); CHECK(n.a == CONSOLE_TX + 1, "!+ still advances A"); CHECK(n.mem[CONSOLE_TX] == MARK && n.mem[CONSOLE_TX - 1] == 0 && n.mem[CONSOLE_TX + 1] == 0, "memory at and around the register is untouched"); /* Run it again: the capture appends. */ CHECK(run(w_hi) && n.console_len == 6 && memcmp(n.console, "Hi!Hi!", 6) == 0, "a second run appends"); /* A full buffer drops and counts; nothing past it is written. */ v4_node_console_attach(&n, CONSOLE_TX); for (i = 0; i < V4_CONSOLE_CAP + 5u; i++) v4_node_store(&n, CONSOLE_TX, (v4_cell)('a' + i % 26u)); CHECK(n.console_len == V4_CONSOLE_CAP, "the capture stops at its capacity"); CHECK(n.console_dropped == 5, "and counts what it dropped: %u", n.console_dropped); CHECK(n.console[0] == 'a' && n.console[V4_CONSOLE_CAP - 1] == (unsigned char)('a' + (V4_CONSOLE_CAP - 1u) % 26u), "the first and last captured characters are intact"); CHECK(n.console_tx == CONSOLE_TX, "the register address survives an overflow"); /* Detaching returns the address to memory. */ v4_node_console_attach(&n, -1); v4_node_store(&n, CONSOLE_TX, 77); CHECK(n.console_len == 0 && n.mem[CONSOLE_TX] == 77, "detached, the address is memory again"); /* The console can sit anywhere, including word 0. */ v4_node_console_attach(&n, 0); n.mem[0] = MARK; v4_node_store(&n, 0, 'z'); CHECK(n.console_len == 1 && n.console[0] == 'z' && n.mem[0] == MARK, "a console at word 0"); CHECK(v4_node_guards_intact(&n), "guards intact"); /* Reset detaches and empties. */ v4_node_reset(&n); CHECK(n.console_tx == -1 && n.console_len == 0 && n.console_dropped == 0, "reset detaches the console and empties the capture"); printf(" %d checks, %d failures\n", checks, failures); return failures ? 1 : 0; }