/* test_asm.c -- the slot packer lays words out as asm.h says it does. */ #include "v4/asm.h" #include "v4/testcode.h" #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) static v4_node n; static v4_exec_state es; static v4_heat h; static v4_asm as; static void fresh(v4_cell origin) { v4_node_reset(&n); v4_exec_reset(&es); v4_heat_reset(&h); v4_asm_begin(&as, &n, origin); } static v4_iword word_at(v4_cell addr) { return (v4_iword)(v4_ucell)n.mem[addr]; } int main(void) { v4_asm_ref ref; v4_cell loop, other; printf("v4 asm tests: V4_CELL_BITS=%d\n", V4_CELL_BITS); /* Six opcodes fill a word; the seventh starts the next, padded with nop. */ fresh(0); for (unsigned k = 0; k < 7; k++) v4_asm_op(&as, V4_OP_DUP); CHECK(v4_asm_label(&as) == 2, "seven opcodes take two words"); for (unsigned k = 0; k < 6; k++) CHECK(v4_iword_op(word_at(0), k) == V4_OP_DUP, "word 0 slot %u", k); CHECK(v4_iword_op(word_at(1), 0) == V4_OP_DUP, "word 1 slot 0"); for (unsigned k = 1; k < 6; k++) CHECK(v4_iword_op(word_at(1), k) == V4_OP_NOP, "word 1 pad %u", k); /* `;` closes the word. */ fresh(0); v4_asm_op(&as, V4_OP_DUP); v4_asm_op(&as, V4_OP_SEMI); CHECK(v4_asm_label(&as) == 1, "; closes the word"); /* Literals follow the instruction word, in order, and the code runs. */ fresh(0); v4_asm_lit(&as, 30); v4_asm_lit(&as, 12); v4_asm_op(&as, V4_OP_ADD); v4_asm_op(&as, V4_OP_SEMI); CHECK(v4_asm_ok(&as), "literal program assembles"); CHECK(n.mem[1] == 30 && n.mem[2] == 12 && v4_asm_label(&as) == 3, "literal layout"); CHECK(v4_test_call(&n, &es, &h, 0, 10) == 1 && n.ds.t == 42, "30 12 + ;"); /* A branch takes the next free slot when that slot is legal. */ fresh(0); v4_asm_op(&as, V4_OP_DUP); v4_asm_op(&as, V4_OP_DROP); v4_asm_branch(&as, V4_OP_JUMP, 0x155); CHECK(v4_iword_op(word_at(0), 2) == V4_OP_JUMP, "branch in slot 2"); CHECK(v4_iword_target(word_at(0), 2) == 0x155, "branch target in slot 2"); CHECK(v4_asm_label(&as) == 1, "a branch closes the word"); /* With slots 0-3 used, the branch moves to slot 0 of a new word. */ fresh(0); for (unsigned k = 0; k < 4; k++) v4_asm_op(&as, V4_OP_DUP); v4_asm_branch(&as, V4_OP_JUMP, 0x155); CHECK(v4_iword_op(word_at(0), 4) == V4_OP_NOP && v4_iword_op(word_at(0), 5) == V4_OP_NOP, "word before the branch is padded"); CHECK(v4_iword_op(word_at(1), 0) == V4_OP_JUMP && v4_iword_target(word_at(1), 0) == 0x155, "branch moved to slot 0"); /* Forward reference, section 2's IF ... ELSE ... THEN with the flag left * on the stack by `if`: if L1 drop 111 ; L1: drop 222 ; */ fresh(0); ref = v4_asm_branch_fwd(&as, V4_OP_IF); v4_asm_op(&as, V4_OP_DROP); v4_asm_lit(&as, 111); v4_asm_op(&as, V4_OP_SEMI); other = v4_asm_label(&as); v4_asm_resolve(&as, ref, other); v4_asm_op(&as, V4_OP_DROP); v4_asm_lit(&as, 222); v4_asm_op(&as, V4_OP_SEMI); CHECK(v4_asm_ok(&as), "forward branch assembles"); v4_dstack_push(&n.ds, 5); CHECK(v4_test_call(&n, &es, &h, 0, 10) > 0 && n.ds.t == 111, "flag nonzero takes the first arm"); v4_dstack_reset(&n.ds); v4_rstack_reset(&n.rs); v4_dstack_push(&n.ds, 0); CHECK(v4_test_call(&n, &es, &h, 0, 10) > 0 && n.ds.t == 222, "flag zero takes the second arm"); /* Backward reference: 4 FOR 1 + NEXT runs the body n+1 times. */ fresh(0); v4_asm_lit(&as, 4); v4_asm_op(&as, V4_OP_PUSH); loop = v4_asm_label(&as); v4_asm_lit(&as, 1); v4_asm_op(&as, V4_OP_ADD); v4_asm_branch(&as, V4_OP_NEXT, loop); v4_asm_op(&as, V4_OP_SEMI); CHECK(v4_asm_ok(&as), "loop assembles"); v4_dstack_push(&n.ds, 0); CHECK(v4_test_call(&n, &es, &h, 0, 50) > 0 && n.ds.t == 5, "4 FOR 1 + NEXT"); /* Errors are reported, not encoded. */ fresh(0); v4_asm_op(&as, V4_OP_JUMP); CHECK(!v4_asm_ok(&as), "a branch through v4_asm_op is refused"); fresh(0); v4_asm_op(&as, V4_OPCODE_COUNT); CHECK(!v4_asm_ok(&as), "an out-of-range opcode is refused"); fresh(0); for (unsigned k = 0; k < 3; k++) v4_asm_op(&as, V4_OP_DUP); v4_asm_branch(&as, V4_OP_JUMP, 5000); /* slot 3 reaches 4096 words */ CHECK(!v4_asm_ok(&as), "a target outside the page is refused"); fresh((v4_cell)(V4_NODE_WORDS - 1u)); v4_asm_lit(&as, 1); v4_asm_op(&as, V4_OP_SEMI); CHECK(!v4_asm_ok(&as), "running off the end of memory is refused"); CHECK(v4_node_guards_intact(&n), "and does not touch the guard band"); printf(" %d checks, %d failures\n", checks, failures); return failures ? 1 : 0; }