/* test_port.c -- the node's port: a write blocks the node until its * neighbour has served it (node.h; DECOMPOSITION.md section 6). * * At the level of opcodes: no word of the vocabulary 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 PORT ((v4_cell)(V4_NODE_WORDS - 16u)) #define MARK ((v4_cell)0x1234) #define HOME ((v4_cell)8) /* where a word returns to when the test called it */ 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 void start(v4_cell word) { v4_dstack_reset(&n.ds); v4_rstack_reset(&n.rs); v4_exec_reset(&es); v4_heat_reset(&h); v4_rstack_push(&n.rs, HOME); n.p = word; } /* Run until the node is blocked, is back at HOME, or `max` words. */ static unsigned run(unsigned max) { unsigned words = 0; while (words < max && !n.asking && n.p != HOME) { (void)v4_exec_step_word(&n, &es, &h); words++; } return words; } int main(void) { v4_cell w_ask, w_mid, w_two; v4_uheat_t clock; printf("v4 port tests: V4_CELL_BITS=%d\n", V4_CELL_BITS); v4_asm_begin(&as, &n, 16); /* what KERNEL-WORD lays down: the request, the port, b! !b ; */ w_ask = v4_asm_label(&as); LIT(7); LIT(PORT); O(BANG_B); O(STORE_B); O(SEMI); /* a write with more of the instruction word after it: dup + */ w_mid = v4_asm_label(&as); LIT(5); LIT(PORT); O(BANG_B); O(STORE_B); O(DUP); O(ADD); O(SEMI); /* two writes one after the other */ w_two = v4_asm_label(&as); LIT(PORT); O(BANG_B); LIT(1); O(STORE_B); LIT(2); O(STORE_B); O(SEMI); CHECK(v4_asm_ok(&as), "the test words assemble"); /* ---- no port: the address is memory ---- */ v4_node_reset(&n); v4_asm_begin(&as, &n, 16); w_ask = v4_asm_label(&as); LIT(7); LIT(PORT); O(BANG_B); O(STORE_B); O(SEMI); w_mid = v4_asm_label(&as); LIT(5); LIT(PORT); O(BANG_B); O(STORE_B); O(DUP); O(ADD); O(SEMI); w_two = v4_asm_label(&as); LIT(PORT); O(BANG_B); LIT(1); O(STORE_B); LIT(2); O(STORE_B); O(SEMI); CHECK(n.port == -1 && !n.asking, "a node has no port until one is attached"); n.mem[PORT] = MARK; start(w_ask); (void)run(100); CHECK(n.p == HOME && !n.asking && n.mem[PORT] == 7, "with none, a store to the address writes memory and the word returns"); /* ---- the write blocks ---- */ v4_node_port_attach(&n, PORT); n.mem[PORT] = MARK; start(w_ask); v4_dstack_push(&n.ds, 11); v4_dstack_push(&n.ds, 22); (void)run(100); CHECK(n.asking && n.request == 7, "a store to the port blocks the node, with what it wrote: %ld", (long)n.request); CHECK(n.mem[PORT] == MARK, "and does not write memory"); CHECK(n.p != HOME, "the word has not returned"); CHECK(n.ds.depth == 2, "what was on the data stack is there for its neighbour"); clock = es.anticlock; CHECK(v4_exec_step_word(&n, &es, &h) == 0 && v4_exec_step_word(&n, &es, &h) == 0, "a blocked node executes nothing"); CHECK(es.anticlock == clock && n.asking, "however often it is stepped: the anti-clock does not move"); /* the neighbour takes two values and leaves one, then says it is served */ { v4_cell b = v4_dstack_pop(&n.ds), a = v4_dstack_pop(&n.ds); v4_dstack_push(&n.ds, a + b); } v4_node_port_served(&n); (void)run(100); CHECK(n.p == HOME && !n.asking, "served, the word goes on and returns"); CHECK(n.ds.depth == 1 && v4_dstack_pop(&n.ds) == 33, "with what its neighbour left on the stack"); /* ---- the rest of the instruction word waits ---- */ start(w_mid); v4_dstack_push(&n.ds, 100); /* to be replaced while the node is blocked */ (void)run(100); CHECK(n.asking && n.request == 5, "blocked in the middle of an instruction word"); (void)v4_dstack_pop(&n.ds); v4_dstack_push(&n.ds, 21); /* the neighbour's result */ v4_node_port_served(&n); (void)run(100); CHECK(n.p == HOME && n.ds.depth == 1 && v4_dstack_pop(&n.ds) == 42, "the opcodes after the store run when it is served, on what the neighbour left: dup + of 21"); /* ---- one write after another ---- */ start(w_two); (void)run(100); CHECK(n.asking && n.request == 1, "the first write blocks"); v4_node_port_served(&n); (void)run(100); CHECK(n.asking && n.request == 2, "served, the node runs to the second and blocks again"); v4_node_port_served(&n); (void)run(100); CHECK(n.p == HOME && !n.asking, "and returns when that is served"); /* ---- a fault while blocked abandons the rest of the word ---- */ start(w_mid); v4_dstack_push(&n.ds, 1); (void)run(100); CHECK(n.asking, "blocked again"); v4_node_port_served(&n); v4_node_fault(&n, V4_FAULT_RAISED, 12); /* no handler attached: the node stops */ CHECK(v4_exec_step_word(&n, &es, &h) == 0 && n.stopped, "a node its neighbour faults does not go on with the word"); /* ---- detaching ---- */ v4_node_reset(&n); CHECK(n.port == -1 && !n.asking, "reset detaches the port"); printf(" %d checks, %d failures\n", checks, failures); return failures != 0; }