/* exec_prog.c */ #include #include #include #include #include "pcode.h" #include "vec/vec.h" /* Return values for an opcode_func_t. */ typedef enum opcode_ret { RET_NEXT, /* Increment PC prior to next instruction. */ RET_GOTO, /* PC was modified by opcode; leave it alone. */ RET_HALT, /* Machine is halted. */ RET_FAIL /* Program terminated with error. */ } opcode_ret_t; /* A function that implements the behavior of an opcode. */ typedef opcode_ret_t (*opcode_func_t)(insn_t*, interp_t*); static int check_range(data_t* val) { /* Does this machine wrap numbers or die on overflow? */ return mpz_cmp(val->val, data_min.val) >= 0 && mpz_cmp(val->val, data_max.val) <= 0; } static opcode_ret_t opcode_move(insn_t* insn, interp_t* interp) { vm_t* vm = &interp->vm; mpz_set(vm->data[insn->arg[2]].val, vm->data[insn->arg[0]].val); return RET_NEXT; } static opcode_ret_t opcode_add(insn_t* insn, interp_t* interp) { vm_t* vm = &interp->vm; mpz_add(vm->data[insn->arg[2]].val, vm->data[insn->arg[0]].val, vm->data[insn->arg[1]].val); if (!check_range(&vm->data[insn->arg[2]])) { fprintf(stderr, "Addition result out of bounds\n"); return RET_FAIL; } return RET_NEXT; } static opcode_ret_t opcode_sub(insn_t* insn, interp_t* interp) { vm_t* vm = &interp->vm; mpz_sub(vm->data[insn->arg[2]].val, vm->data[insn->arg[0]].val, vm->data[insn->arg[1]].val); if (!check_range(&vm->data[insn->arg[2]])) { fprintf(stderr, "Subtraction result out of bounds\n"); return RET_FAIL; } return RET_NEXT; } static opcode_ret_t opcode_mul(insn_t* insn, interp_t* interp) { vm_t* vm = &interp->vm; mpz_mul(vm->data[insn->arg[2]].val, vm->data[insn->arg[0]].val, vm->data[insn->arg[1]].val); if (!check_range(&vm->data[insn->arg[2]])) { fprintf(stderr, "Multiplication result out of bounds\n"); return RET_FAIL; } return RET_NEXT; } static opcode_ret_t opcode_div(insn_t* insn, interp_t* interp) { vm_t* vm = &interp->vm; if (mpz_cmp_si(vm->data[insn->arg[1]].val, 0) == 0) { fprintf(stderr, "Division by zero\n"); return RET_FAIL; } else { mpz_tdiv_q(vm->data[insn->arg[2]].val, vm->data[insn->arg[0]].val, vm->data[insn->arg[1]].val); return RET_NEXT; } } static opcode_ret_t opcode_sqr(insn_t* insn, interp_t* interp) { vm_t* vm = &interp->vm; mpz_mul(vm->data[insn->arg[2]].val, vm->data[insn->arg[0]].val, vm->data[insn->arg[0]].val); if (!check_range(&vm->data[insn->arg[2]])) { fprintf(stderr, "Square result out of bounds\n"); return RET_FAIL; } return RET_NEXT; } static opcode_ret_t opcode_sqrt(insn_t* insn, interp_t* interp) { vm_t* vm = &interp->vm; if (mpz_cmp_si(vm->data[insn->arg[1]].val, 0) < 0) { fprintf(stderr, "Square root of negative number\n"); return RET_FAIL; } else { mpz_sqrt(vm->data[insn->arg[2]].val, vm->data[insn->arg[0]].val); return RET_NEXT; } } static opcode_ret_t opcode_jeq(insn_t* insn, interp_t* interp) { vm_t* vm = &interp->vm; if (mpz_cmp(vm->data[insn->arg[0]].val, vm->data[insn->arg[1]].val) == 0) { vm->pc = insn->arg[2]; return RET_GOTO; } return RET_NEXT; } static opcode_ret_t opcode_jne(insn_t* insn, interp_t* interp) { vm_t* vm = &interp->vm; if (mpz_cmp(vm->data[insn->arg[0]].val, vm->data[insn->arg[1]].val) != 0) { vm->pc = insn->arg[2]; return RET_GOTO; } return RET_NEXT; } static opcode_ret_t opcode_jge(insn_t* insn, interp_t* interp) { vm_t* vm = &interp->vm; if (mpz_cmp(vm->data[insn->arg[0]].val, vm->data[insn->arg[1]].val) >= 0) { vm->pc = insn->arg[2]; return RET_GOTO; } return RET_NEXT; } static opcode_ret_t opcode_jlt(insn_t* insn, interp_t* interp) { vm_t* vm = &interp->vm; if (mpz_cmp(vm->data[insn->arg[0]].val, vm->data[insn->arg[1]].val) < 0) { vm->pc = insn->arg[2]; return RET_GOTO; } return RET_NEXT; } static opcode_ret_t opcode_load(insn_t* insn, interp_t* interp) { vm_t* vm = &interp->vm; mpz_t addr; mpz_init(addr); mpz_add_ui(addr, vm->data[insn->arg[1]].val, insn->arg[0]); if (mpz_cmp_si(addr, 0) < 0 || mpz_cmp_si(addr, 999) > 0) { fprintf(stderr, "Load from out-of-bounds memory address\n"); mpz_clear(addr); return RET_FAIL; } else { mpz_set(vm->data[insn->arg[2]].val, vm->data[mpz_get_si(addr)].val); mpz_clear(addr); return RET_NEXT; } } static opcode_ret_t opcode_store(insn_t* insn, interp_t* interp) { vm_t* vm = &interp->vm; mpz_t addr; mpz_init(addr); mpz_add_ui(addr, vm->data[insn->arg[2]].val, insn->arg[1]); if (mpz_cmp_si(addr, 0) < 0 || mpz_cmp_si(addr, 999) > 0) { fprintf(stderr, "Store to out-of-bounds memory address\n"); mpz_clear(addr); return RET_FAIL; } else { mpz_set(vm->data[mpz_get_si(addr)].val, vm->data[insn->arg[0]].val); mpz_clear(addr); return RET_NEXT; } } static opcode_ret_t opcode_loop(insn_t* insn, interp_t* interp) { /* This function behaves differently than its analogue in Szczurowska, * which tests then increments. Here I increment then test, as is * described in MacLennan. */ vm_t* vm = &interp->vm; mpz_add_ui(vm->data[insn->arg[0]].val, vm->data[insn->arg[0]].val, 1); if (!check_range(&vm->data[insn->arg[0]])) { fprintf(stderr, "Loop increment result out of bounds\n"); return RET_FAIL; } else if (mpz_cmp(vm->data[insn->arg[0]].val, vm->data[insn->arg[1]].val) < 0) { vm->pc = insn->arg[2]; return RET_GOTO; } else { return RET_NEXT; } } static opcode_ret_t opcode_read(insn_t* insn, interp_t* interp) { vm_t* vm = &interp->vm; data_t* card; card = VEC_POP1(data_t, &vm->cards); if (card == NULL) { fprintf(stderr, "No more input cards to read\n"); return RET_FAIL; } else { mpz_set(vm->data[insn->arg[2]].val, card->val); mpz_clear(card->val); return RET_NEXT; } } static opcode_ret_t opcode_print(insn_t* insn, interp_t* interp) { gmp_fprintf(interp->output, "%+011Zd\n", interp->vm.data[insn->arg[0]].val); return RET_NEXT; } static opcode_ret_t opcode_halt(insn_t* insn, interp_t* interp) { return RET_HALT; } static opcode_ret_t opcode_nval(insn_t* insn, interp_t* interp) { fprintf(stderr, "Undefined opcode executed\n"); return RET_FAIL; } int exec_prog(interp_t* interp) { opcode_func_t opcodes[20] = { /* + - */ opcode_move , opcode_nval , /* 0 */ opcode_add , opcode_sub , /* 1 */ opcode_mul , opcode_div , /* 2 */ opcode_sqr , opcode_sqrt , /* 3 */ opcode_jeq , opcode_jne , /* 4 */ opcode_jge , opcode_jlt , /* 5 */ opcode_load , opcode_store, /* 6 */ opcode_loop , opcode_nval , /* 7 */ opcode_read , opcode_print, /* 8 */ opcode_halt , opcode_nval /* 9 */ }; vm_t* vm = &interp->vm; insn_t* insn; vm->pc = 0; while (1) { insn = &vm->text[vm->pc]; switch (opcodes[insn->opcode](insn, interp)) { case RET_NEXT: ++vm->pc; if (vm->pc == 1000) vm->pc = 0; case RET_GOTO: break; case RET_HALT: return 0; case RET_FAIL: return -1; } } } /* vim: set ts=4 sts=4 sw=4 tw=80 et: */