/* asm.c -- slot packer for test programs. See asm.h. */ #include "v4/asm.h" static void put(v4_asm *as, unsigned op) { as->cur |= (v4_iword)(op & 0x1Fu) << V4_SLOT_LOW_BIT(as->slot); as->slot++; } static void emit(v4_asm *as, v4_cell value) { if (as->here < 0 || (v4_ucell)as->here >= V4_NODE_WORDS) { as->error = 1; return; } v4_node_store(as->n, as->here, value); as->here++; } /* Write out the word being built, then the literals it owes. */ static void flush(v4_asm *as) { if (as->slot == 0) return; while (as->slot < V4_SLOT_COUNT) put(as, V4_OP_NOP); emit(as, (v4_cell)as->cur); for (unsigned i = 0; i < as->nlit; i++) emit(as, as->lit[i]); as->cur = 0; as->slot = 0; as->nlit = 0; } /* 1 if a branch in `slot`, executing with P = p, can reach `target`. */ static int reachable(v4_cell p, unsigned slot, v4_cell target) { v4_ucell mask = (v4_ucell)v4_iword_slot_mask(slot); return (((v4_ucell)p ^ (v4_ucell)target) & ~mask) == 0; } void v4_asm_begin(v4_asm *as, v4_node *n, v4_cell origin) { as->n = n; as->here = origin; as->cur = 0; as->slot = 0; as->nlit = 0; as->error = 0; } void v4_asm_op(v4_asm *as, unsigned op) { if (op >= V4_OPCODE_COUNT || v4_op_is_branch(op) || op == V4_OP_FETCH_P) { as->error = 1; return; } put(as, op); if (op == V4_OP_SEMI || op == V4_OP_EX || as->slot == V4_SLOT_COUNT) flush(as); } void v4_asm_lit(v4_asm *as, v4_cell value) { put(as, V4_OP_FETCH_P); as->lit[as->nlit++] = value; if (as->slot == V4_SLOT_COUNT) flush(as); } v4_asm_ref v4_asm_branch_fwd(v4_asm *as, unsigned op) { v4_asm_ref ref; if (!v4_op_is_branch(op)) as->error = 1; if (!v4_iword_branch_legal(as->slot)) flush(as); ref.addr = as->here; ref.slot = as->slot; /* The word has been fetched and every literal before the branch consumed * by the time the branch executes. */ ref.p = as->here + 1 + (v4_cell)as->nlit; put(as, op); as->slot = V4_SLOT_COUNT; /* the rest of the word is address */ flush(as); return ref; } void v4_asm_resolve(v4_asm *as, v4_asm_ref ref, v4_cell target) { v4_ucell mask = (v4_ucell)v4_iword_slot_mask(ref.slot); v4_ucell word; if (as->error) return; if (!reachable(ref.p, ref.slot, target)) { as->error = 1; return; } word = (v4_ucell)v4_node_load(as->n, ref.addr); word = (word & ~mask) | ((v4_ucell)target & mask); v4_node_store(as->n, ref.addr, (v4_cell)word); } void v4_asm_branch(v4_asm *as, unsigned op, v4_cell target) { v4_asm_resolve(as, v4_asm_branch_fwd(as, op), target); } v4_cell v4_asm_label(v4_asm *as) { flush(as); return as->here; } int v4_asm_ok(v4_asm *as) { flush(as); return !as->error; }