/* disassemble a SimpleScalar instruction */ void md_print_insn(md_inst_t inst, /* instruction to disassemble */ md_addr_t pc, /* addr of inst, used for PC-rels */ FILE *stream) /* output stream */ { enum md_opcode op; /* use stderr as default output stream */ if (!stream) stream = stderr; /* decode the instruction, assumes predecoded text segment */ MD_SET_OPCODE(op, inst); /* disassemble the instruction */ if (op == OP_NA || op >= OP_MAX) { /* bogus instruction */ fprintf(stream, "<invalid inst: 0x%08x:%08x>", inst.a, inst.b); } else { char *s; fprintf(stream, "%-10s", MD_OP_NAME(op)); s = MD_OP_FORMAT(op); while (*s) { switch (*s) { case 'd': fprintf(stream, "r%d", RD); break; case 's': fprintf(stream, "r%d", RS); break; case 't': fprintf(stream, "r%d", RT); break; case 'b': fprintf(stream, "r%d", BS); break; case 'D': fprintf(stream, "f%d", FD); break; case 'S': fprintf(stream, "f%d", FS); break; case 'T': fprintf(stream, "f%d", FT); break; case 'j': fprintf(stream, "0x%x", (pc + 8 + (OFS << 2))); break; case 'o': case 'i': fprintf(stream, "%d", IMM); break; case 'H': fprintf(stream, "%d", SHAMT); break; case 'u': fprintf(stream, "%u", UIMM); break; case 'U': fprintf(stream, "0x%x", UIMM); break; case 'J': fprintf(stream, "0x%x", ((pc & 036000000000) | (TARG << 2))); break; case 'B': fprintf(stream, "0x%x", BCODE); break; #if 0 /* FIXME: obsolete... */ case ')': /* handle pre- or post-inc/dec */ if (SS_COMP_OP == SS_COMP_NOP) fprintf(stream, ")"); else if (SS_COMP_OP == SS_COMP_POST_INC) fprintf(stream, ")+"); else if (SS_COMP_OP == SS_COMP_POST_DEC) fprintf(stream, ")-"); else if (SS_COMP_OP == SS_COMP_PRE_INC) fprintf(stream, ")^+"); else if (SS_COMP_OP == SS_COMP_PRE_DEC) fprintf(stream, ")^-"); else if (SS_COMP_OP == SS_COMP_POST_DBL_INC) fprintf(stream, ")++"); else if (SS_COMP_OP == SS_COMP_POST_DBL_DEC) fprintf(stream, ")--"); else if (SS_COMP_OP == SS_COMP_PRE_DBL_INC) fprintf(stream, ")^++"); else if (SS_COMP_OP == SS_COMP_PRE_DBL_DEC) fprintf(stream, ")^--"); else panic("bogus SS_COMP_OP"); break; #endif default: /* anything unrecognized, e.g., '.' is just passed through */ fputc(*s, stream); } s++; } } }
/* disassemble an Alpha instruction */ void md_print_insn(md_inst_t inst, /* instruction to disassemble */ md_addr_t pc, /* addr of inst, used for PC-rels */ FILE *stream) /* output stream */ { enum md_opcode op; /* use stderr as default output stream */ if (!stream) stream = stderr; /* decode the instruction, assumes predecoded text segment */ MD_SET_OPCODE(op, inst); /* disassemble the instruction */ if (op <= OP_NA || op >= OP_MAX) { /* bogus instruction */ fprintf(stream, "<invalid inst: 0x%08x>", inst); } else { char *s; fprintf(stream, "%-10s", MD_OP_NAME(op)); s = MD_OP_FORMAT(op); while (*s) { switch (*s) { case 'a': fprintf(stream, "r%d", RA); break; case 'b': fprintf(stream, "r%d", RB); break; case 'c': fprintf(stream, "r%d", RC); break; case 'A': fprintf(stream, "f%d", RA); break; case 'B': fprintf(stream, "f%d", RB); break; case 'C': fprintf(stream, "f%d", RC); break; case 'o': fprintf(stream, "%d", (sword_t)SEXT(OFS)); break; case 'j': myfprintf(stream, "0x%p", pc + (SEXT(OFS) << 2) + 4); break; case 'J': myfprintf(stream, "0x%p", pc + (SEXT21(TARG) << 2) + 4); break; case 'i': fprintf(stream, "%d", (word_t)IMM); break; default: /* anything unrecognized, e.g., '.' is just passed through */ fputc(*s, stream); } s++; } } }
/* start simulation, program loaded, processor precise state initialized */ void sim_main(void) { md_inst_t inst; register md_addr_t addr, target_PC; enum md_opcode op; register int is_write; int stack_idx; enum md_fault_type fault; int out1, out2, in1, in2, in3; /* register names */ int v_out1, v_out2, v_in1, v_in2, v_in3;/* register values */ int vl_out1, vl_out2, vl_in1, vl_in2, vl_in3; md_addr_t addr_dispatch; /* access_address */ int m_size; /* access_size */ FILE *stream; md_addr_t predicted_PC; fprintf(stderr, "sim: ** starting functional simulation w/ predictors **\n"); /* set up initial default next PC */ regs.regs_NPC = regs.regs_PC + sizeof(md_inst_t); /* check for DLite debugger entry condition */ if (dlite_check_break(regs.regs_PC, /* no access */0, /* addr */0, 0, 0)) dlite_main(regs.regs_PC - sizeof(md_inst_t), regs.regs_PC, sim_num_insn, ®s, mem); while (TRUE) { /* maintain $r0 semantics */ regs.regs_R[MD_REG_ZERO] = 0; #ifdef TARGET_ALPHA regs.regs_F.d[MD_REG_ZERO] = 0.0; #endif /* TARGET_ALPHA */ /* get the next instruction to execute */ mem_access(mem, Read, regs.regs_PC, &inst, sizeof(md_inst_t)); /* keep an instruction count */ sim_num_insn++; /* set default reference address and access mode */ addr = 0; is_write = FALSE; /* set default fault - none */ fault = md_fault_none; /* decode the instruction */ MD_SET_OPCODE(op, inst); /* execute the instruction */ switch (op) { #define DEFINST(OP,MSK,NAME,OPFORM,RES,CLASS,O1,O2,I1,I2,I3, \ VO1,LO1,VO2,LO2,ADDR,VI1,LI1,VI2,LI2,VI3,LI3,M_Size) \ case OP: \ in1 = I1; in2 = I2; in3 = I3; \ v_in1 = VI1; v_in2 = VI2; v_in3 = VI3; \ vl_in1 = LI1; vl_in2 = LI2; vl_in3 = LI3; \ SYMCAT(OP,_IMPL); \ out1 = O1; out2 = O2; \ v_out1 = VO1; v_out2 = VO2; \ vl_out1 = LO1; vl_out2 = LO2; \ addr_dispatch = ADDR; \ m_size = M_Size; \ break; #define DEFLINK(OP,MSK,NAME,MASK,SHIFT) \ case OP: \ panic("attempted to execute a linking opcode"); #define CONNECT(OP) #define DECLARE_FAULT(FAULT) \ { fault = (FAULT); break; } #include "operand.def" default: panic("attempted to execute a bogus opcode"); } if (fault != md_fault_none) fatal("fault (%d) detected @ 0x%08p", fault, regs.regs_PC); if (MD_OP_FLAGS(op) & F_MEM) { sim_num_refs++; if (MD_OP_FLAGS(op) & F_STORE) is_write = TRUE; } if (MD_OP_FLAGS(op) & F_CTRL) { md_addr_t pred_PC; struct bpred_update_t update_rec; sim_num_branches++; if (pred) { /* get the next predicted fetch address */ pred_PC = bpred_lookup(pred, /* branch addr */regs.regs_PC, /* target */target_PC, /* inst opcode */op, /* call? */MD_IS_CALL(op), /* return? */MD_IS_RETURN(op), /* stash an update ptr */&update_rec, /* stash return stack ptr */&stack_idx); /* valid address returned from branch predictor? */ if (!pred_PC) { /* no predicted taken target, attempt not taken target */ pred_PC = regs.regs_PC + sizeof(md_inst_t); } bpred_update(pred, /* branch addr */regs.regs_PC, /* resolved branch target */regs.regs_NPC, /* taken? */regs.regs_NPC != (regs.regs_PC + sizeof(md_inst_t)), /* pred taken? */pred_PC != (regs.regs_PC + sizeof(md_inst_t)), /* correct pred? */pred_PC == regs.regs_NPC, /* opcode */op, /* predictor update pointer */&update_rec); } predicted_PC = pred_PC; } /* ここから下を見ればどの変数に何が入っているか分かるはず。 */ stream = stdout; fprintf(stream, "############################################################ \n"); fprintf(stream, " --- trace count(%d) \n", (int)sim_num_insn); fprintf(sim_fd, "%s, inst: `", fprintf(stream, " opcode: %s, inst: `", MD_OP_NAME(op)); md_print_insn(inst, regs.regs_PC, stream); fprintf(stream, "'\n"); myfprintf(stream, " PC: 0x%08p, NPC: 0x%08p", regs.regs_PC, regs.regs_NPC); if(pred) myfprintf(stream, " (pred_PC: 0x%08p)\n", regs.regs_PC, regs.regs_NPC, predicted_PC); else myfprintf(stream, "\n"); fprintf(stream," | in(r%02d,r%02d,r%02d),out(r%02d,r%02d),\n", in1, in2, in3, out1, out2); fprintf(stream," | IN(%8x,%8x,%8x),OUT(%8x,%8x)addr(%8x)|\n", v_in1, v_in2, v_in3, v_out1, v_out2, addr_dispatch); /* ダブルワードだった場合,この変数がセットされる */ fprintf(stream," | in(%8x,%8x,%8x),out(%8x,%8x) |\n", vl_in1, vl_in2, vl_in3, vl_out1, vl_out2); /* machine.h をみるとopから命令の種類を判別する方法が分かる。以下は例 */ if (op == MD_NOP_OP) { fprintf(stream, " NOP instruction \n"); } else if (MD_OP_FLAGS(op) & F_MEM) { fprintf(stream, " MEMORY instruction \n"); if (MD_OP_FLAGS(op) & F_STORE) fprintf(stream, " store instruction \n"); else if (MD_OP_FLAGS(op) & F_LOAD) fprintf(stream, " load instruction \n"); fprintf(stream, " ld/st address is %010x\n", addr_dispatch); fprintf(stream, " ld/st size is %2d\n", m_size); /* ロードストアがバイト単位なのか,ワード単位なのか, ハーフワード単位なのかは,opを見て判断できる。 例えば, md_op2name[op] が "sh"ならハーフワードである。 しかし、これだと面倒なのでoperand.defを改良した方が良い かも知れない。これはおまかせ。 */ /* ロードストアの詳細に関しては,machine.def を参照。 例えば,store halfの場合,(machine.def をエディタで開いて "sh"と言う文字列をサーチすれば該当箇所が見つかる) 以下のようにかかれている。*/ /* #define SH_IMPL { half_t _src; enum md_fault_type _fault; _src = (half_t)(word_t)GPR(RT); WRITE_HALF(_src, GPR(BS) + OFS, _fault); if (_fault != md_fault_none) DECLARE_FAULT(_fault); } */ /*ここで、GPR(BS)はBS番レジスタの値 OFS はオフセットを示す。 そして,GPR(RT)はRT番レジスタの値を示す。 そしてWRITE_HALF(_src, GPR(BS) + OFS, _fault);は 値 _src をアドレス GPR(BS) + OFS に書き込む事を意味する。 書き込みのサイズは,halfワードである。*/ } else if (MD_OP_FLAGS(op) & F_CTRL) { fprintf(stream, " BRANCH instruction \n"); if (MD_IS_CALL(op)) fprintf(stream, " function call \n"); else if (MD_IS_RETURN(op)) fprintf(stream, " function return \n"); else if (MD_IS_INDIR(op)) fprintf(stream, " indirect jump \n"); else if ((MD_OP_FLAGS(op) & (F_CTRL|F_DIRJMP)) == (F_CTRL|F_DIRJMP)) fprintf(stream, " direct jump \n"); } else { fprintf(stream, " other instruction \n"); } /* この応用として,いきなりopからstoreを判別するには,以下のようにすれば良い*/ if ((MD_OP_FLAGS(op) & (F_MEM|F_STORE)) == (F_MEM|F_STORE)) { /*fprintf(stream, " store instruction \n");*/ } else if ((MD_OP_FLAGS(op) & (F_MEM|F_LOAD)) == (F_MEM|F_LOAD)) { /*fprintf(stream, " load instruction \n");*/ } /* ここまで */ /* check for DLite debugger entry condition */ if (dlite_check_break(regs.regs_NPC, is_write ? ACCESS_WRITE : ACCESS_READ, addr, sim_num_insn, sim_num_insn)) dlite_main(regs.regs_PC, regs.regs_NPC, sim_num_insn, ®s, mem); /* go to the next instruction */ regs.regs_PC = regs.regs_NPC; regs.regs_NPC += sizeof(md_inst_t); /* finish early? */ if (max_insts && sim_num_insn >= max_insts) return; } }