/* instruction.c */ #include #include "project.h" static void do_la(unsigned preg1, unsigned nreg2, unsigned imm16) { int i; char pre; char base; reg_t* reg1 = reg_by_code(preg1, R_PSEUDO); reg_t* reg2 = reg_by_code(nreg2, R_NORMAL); int word = mem_read(reg_read(reg2) + imm16); if (strcmp(reg1->name, "uh") == 0) {pre = 'u'; base = '4';} else if (strcmp(reg1->name, "ul") == 0) {pre = 'u'; base = '0';} else if (strcmp(reg1->name, "vh") == 0) {pre = 'v'; base = '4';} else if (strcmp(reg1->name, "vl") == 0) {pre = 'v'; base = '0';} else die("Invalid register '%d'", preg1); for (i = 0; i < 4; i++) { char name[3] = {pre, base + i, 0}; reg_t* dest = reg_by_name(name); reg_write(dest, (word >> (i * 8)) - '0'); } } static void do_lb(unsigned preg1, unsigned nreg2, unsigned imm16) { int i; char pre; reg_t* reg1 = reg_by_code(preg1, R_PSEUDO); reg_t* reg2 = reg_by_code(nreg2, R_NORMAL); int word = mem_read(reg_read(reg2) + imm16); if (strcmp(reg1->name, "u") == 0) pre = 'u'; else if (strcmp(reg1->name, "v") == 0) pre = 'v'; else die("Invalid register '%d'", preg1); for (i = 0; i < 8; i++) { char name[3] = {pre, '7' - i, 0}; reg_write(reg_by_name(name), word >> (i * 4)); } } static void do_sb(unsigned preg1, unsigned nreg2, unsigned imm16) { int i; int word = 0; char pre; reg_t* reg1 = reg_by_code(preg1, R_PSEUDO); reg_t* reg2 = reg_by_code(nreg2, R_NORMAL); if (strcmp(reg1->name, "u") == 0) pre = 'u'; else if (strcmp(reg1->name, "v") == 0) pre = 'v'; else die("Invalid register '%d'", preg1); for (i = 0; i < 8; i++) { char name[3] = {pre, '7' - i, 0}; word |= reg_read(reg_by_name(name)) << (i * 4); } mem_write(reg_read(reg2) + imm16, word); } static void do_sw(unsigned nreg1, unsigned nreg2, unsigned imm16) { reg_t* reg1 = reg_by_code(nreg1, R_NORMAL); reg_t* reg2 = reg_by_code(nreg2, R_NORMAL); mem_write(reg_read(reg2) + imm16, reg_read(reg1)); } static void do_beqi(unsigned nreg1, unsigned imm4, unsigned imm16) { unsigned val = reg_read(reg_by_code(nreg1, R_NORMAL)); if (val == imm4) reg_write(reg_by_name("pc"), imm16); } static void do_bgt(unsigned nreg1, unsigned nreg2, unsigned imm16) { unsigned val1 = reg_read(reg_by_code(nreg1, R_NORMAL)); unsigned val2 = reg_read(reg_by_code(nreg2, R_NORMAL)); if (val1 > val2) reg_write(reg_by_name("pc"),imm16); } static void do_lui(unsigned nreg1, unsigned nreg2, unsigned imm16) { reg_t* reg1 = reg_by_code(nreg1, R_NORMAL); reg_t* reg2 = reg_by_code(nreg2, R_NORMAL); reg_write(reg1, (((reg_read(reg2) + imm16) & 0xffff) << 16) | (reg_read(reg1) & 0xffff)); } static void do_min(unsigned nreg1, unsigned nreg2, unsigned nreg3) { reg_t* reg1 = reg_by_code(nreg1, R_NORMAL); reg_t* reg2 = reg_by_code(nreg2, R_NORMAL); reg_t* reg3 = reg_by_code(nreg3, R_NORMAL); reg_write(reg1, reg_read(reg_read(reg2) < reg_read(reg3) ? reg2 :reg3)); } static void do_add(unsigned nreg1, unsigned nreg2, unsigned nreg3) { reg_t* reg1 = reg_by_code(nreg1, R_NORMAL); reg_t* reg2 = reg_by_code(nreg2, R_NORMAL); reg_t* reg3 = reg_by_code(nreg3, R_NORMAL); reg_write(reg1, reg_read(reg2) + reg_read(reg3)); } static void do_addi(unsigned nreg1, unsigned nreg2, unsigned imm16) { reg_t* reg1 = reg_by_code(nreg1, R_NORMAL); reg_t* reg2 = reg_by_code(nreg2, R_NORMAL); reg_write(reg1, reg_read(reg2) + imm16); } static void do_adds(unsigned nreg1, unsigned nreg2, unsigned imm16) { reg_t* reg1 = reg_by_code(nreg1, R_NORMAL); reg_t* reg2 = reg_by_code(nreg2, R_NORMAL); reg_write(reg1, reg_read(reg1) + (reg_read(reg2) << imm16)); } static void do_subi(unsigned nreg1, unsigned nreg2, unsigned imm16) { reg_t* reg1 = reg_by_code(nreg1, R_NORMAL); reg_t* reg2 = reg_by_code(nreg2, R_NORMAL); reg_write(reg1, imm16 - reg_read(reg2)); } static void do_cshri(unsigned nreg1, unsigned nreg2, unsigned imm16) { reg_t* reg1 = reg_by_code(nreg1, R_NORMAL); reg_t* reg2 = reg_by_code(nreg2, R_NORMAL); int val = reg_read(reg2); int ceil = (val & ((1 << imm16) - 1)) > 0 ? 1 : 0; reg_write(reg1, ceil + (val >> imm16)); } /* static void do_shli(unsigned nreg1, unsigned nreg2, unsigned imm16) { reg_t* reg1 = reg_by_code(nreg1, R_NORMAL); reg_t* reg2 = reg_by_code(nreg2, R_NORMAL); reg_write(reg1, reg_read(reg2) << imm16); } */ static void do_badd(unsigned breg1, unsigned breg2, unsigned breg3) { reg_t* reg1 = reg_by_code(breg1, R_BCD); reg_t* reg2 = reg_by_code(breg2, R_BCD); reg_t* reg3 = reg_by_code(breg3, R_BCD); reg_t* cf = reg_by_name("cf"); unsigned r2_val = reg_read(reg2); unsigned r3_val = reg_read(reg3); unsigned cf_val = reg_read(cf) != 0 ? 1 : 0; unsigned result = r2_val + r3_val + cf_val; if (r2_val > 9) die("Invalid value '%01x' in register '%s'\n", r2_val, reg2->name); if (r3_val > 9) die("Invalid value '%01x' in register '%s'\n", r3_val, reg3->name); if (result < 10) { reg_write(cf, 0); reg_write(reg1, result); } else { reg_write(cf, 1); reg_write(reg1, result - 10); } } static void do_baddi(unsigned breg1, unsigned breg2, unsigned imm4) { reg_t* reg1 = reg_by_code(breg1, R_BCD); reg_t* reg2 = reg_by_code(breg2, R_BCD); reg_t* cf = reg_by_name("cf"); unsigned r2_val = reg_read(reg2); unsigned cf_val = reg_read(cf) != 0 ? 1 : 0; unsigned result = r2_val + imm4 + cf_val; if (r2_val > 9) die("Invalid value '%01x' in register '%s'\n", r2_val, reg2->name); if (result < 10) { reg_write(cf, 0); reg_write(reg1, result); } else { reg_write(cf, 1); reg_write(reg1, result - 10); } } /* List of all instructions */ static insn_t insns[] = { {"read", 0x00, I_NORMAL, vm_read, (arg_t){8, 4, A_NREG}, (arg_t){12, 4, A_NREG}, (arg_t){16, 16, A_IMM16}}, {"disp", 0x01, I_NORMAL, vm_disp, (arg_t){8, 4, A_NREG}, (arg_t){12, 4, A_NREG}, (arg_t){16, 16, A_IMM16}}, {"la", 0x10, I_MEMORY, do_la, (arg_t){8, 4, A_PREG}, (arg_t){12, 4, A_NREG}, (arg_t){16, 16, A_IMM16}}, {"lb", 0x11, I_MEMORY, do_lb, (arg_t){8, 4, A_PREG}, (arg_t){12, 4, A_NREG}, (arg_t){16, 16, A_IMM16}}, {"sb", 0x12, I_MEMORY, do_sb, (arg_t){8, 4, A_PREG}, (arg_t){12, 4, A_NREG}, (arg_t){16, 16, A_IMM16}}, {"sw", 0x13, I_MEMORY, do_sw, (arg_t){8, 4, A_NREG}, (arg_t){12, 4, A_NREG}, (arg_t){16, 16, A_IMM16}}, {"beqi", 0x20, I_NORMAL, do_beqi, (arg_t){8, 4, A_NREG}, (arg_t){12, 4, A_IMM4}, (arg_t){16, 16, A_IMM16}}, {"bgt", 0x21, I_NORMAL, do_bgt, (arg_t){8, 4, A_NREG}, (arg_t){12, 4, A_NREG}, (arg_t){16, 16, A_IMM16}}, {"min", 0x30, I_NORMAL, do_min, (arg_t){8, 4, A_NREG}, (arg_t){12, 4, A_NREG}, (arg_t){16, 4, A_NREG}}, {"add", 0x31, I_NORMAL, do_add, (arg_t){8, 4, A_NREG}, (arg_t){12, 4, A_NREG}, (arg_t){16, 4, A_NREG}}, {"addi", 0x32, I_NORMAL, do_addi, (arg_t){8, 4, A_NREG}, (arg_t){12, 4, A_NREG}, (arg_t){16, 16, A_IMM16}}, {"adds", 0x33, I_NORMAL, do_adds, (arg_t){8, 4, A_NREG}, (arg_t){12, 4, A_NREG}, (arg_t){16, 16, A_IMM16}}, {"subi", 0x34, I_NORMAL, do_subi, (arg_t){8, 4, A_NREG}, (arg_t){12, 4, A_NREG}, (arg_t){16, 16, A_IMM16}}, {"cshri", 0x35, I_NORMAL, do_cshri, (arg_t){8, 4, A_NREG}, (arg_t){12, 4, A_NREG}, (arg_t){16, 16, A_IMM16}}, /* {"shli", 0x36, I_NORMAL, do_shli, (arg_t){8, 4, A_NREG}, (arg_t){12, 4, A_NREG}, (arg_t){16, 16, A_IMM16}}, */ {"badd", 0x40, I_NORMAL, do_badd, (arg_t){8, 4, A_BREG}, (arg_t){12, 4, A_BREG}, (arg_t){16, 4, A_BREG}}, {"baddi", 0x41, I_NORMAL, do_baddi, (arg_t){8, 4, A_BREG}, (arg_t){12, 4, A_BREG}, (arg_t){16, 4, A_IMM4}}, {NULL} }; /* Given the name of an instruction, returns its insn_t*. */ insn_t* insn_by_name(char* name) { insn_t* p = insns; while (p->name != NULL && strcmp(p->name, name) != 0) p++; if (p->name == NULL) return NULL; return p; } /* Given the code for an instruction, returns its insn_t*. */ insn_t* insn_by_code(unsigned code) { insn_t* p = insns; while (p->name != NULL && p->opcode != (code & 0xff)) p++; if (p->name == NULL) return NULL; return p; } /* Encodes the instruction given by 'insn' through 'arg3' and returns the * result. */ unsigned insn_encode(insn_t* insn, unsigned arg1, unsigned arg2, unsigned arg3) { return (insn->opcode & 0xff) | ((arg1 & ((1 << insn->arg1.width) - 1)) << insn->arg1.pos) | ((arg2 & ((1 << insn->arg2.width) - 1)) << insn->arg2.pos) | ((arg3 & ((1 << insn->arg3.width) - 1)) << insn->arg3.pos); } /* Decodes the instruction given by 'code' into 'insn' through '*arg3'. '*insn' * is set to NULL on failure. */ void insn_decode(unsigned code, insn_t** insn, unsigned* arg1, unsigned* arg2, unsigned* arg3) { *insn = insn_by_code(code); if (*insn == NULL) return; *arg1 = (code >> (*insn)->arg1.pos) & ((1 << (*insn)->arg1.width) - 1); *arg2 = (code >> (*insn)->arg2.pos) & ((1 << (*insn)->arg2.width) - 1); *arg3 = (code >> (*insn)->arg3.pos) & ((1 << (*insn)->arg3.width) - 1); } /* EOF */